OpenCores
URL https://opencores.org/ocsvn/spi_master_slave/spi_master_slave/trunk

Subversion Repositories spi_master_slave

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /
    from Rev 2 to Rev 3
    Reverse comparison

Rev 2 → Rev 3

/spi_master_slave/trunk/bench/spi_loopback_test.vhd
0,0 → 1,296
--------------------------------------------------------------------------------
-- Company:
-- Engineer: Jonny Doin
--
-- Create Date: 22:59:18 04/25/2011
-- Design Name:
-- Module Name: C:/dropbox/Dropbox/VHDL_training/projects/SPI_interface/spi_loopback_test.vhd
-- Project Name: SPI_interface
-- Target Device: Spartan-6
-- Tool versions: ISE 13.1
-- Description: Testbench to simulate the master and slave SPI interfaces. Each module can be tested
-- in a "real" environment, where the 'spi_master' exchanges data with the 'spi_slave'
-- module, simulating the internal working of each design.
-- In behavioral simulation, select a matching data width (N) and spi mode (CPOL, CPHA) for
-- both modules, and also a different clock domain for each parallel interface.
-- Different values for PREFETCH for each interface can be tested, to model the best value
-- for the pipelined memory / bus that is attached to the di/do ports.
-- To test the parallel interfaces, a simple ROM memory is simulated for each interface, with
-- 8 words of data to be sent, synchronous to each clock and flow control signals.
--
--
-- VHDL Test Bench Created by ISE for modules: 'spi_master' and 'spi_slave'
--
-- Dependencies:
--
-- Revision:
-- Revision 0.01 - File Created
-- Revision 1.05 - Implemented FIFO simulation for each interface.
-- Additional Comments:
--
-- Notes:
-- This testbench has been automatically generated using types std_logic and
-- std_logic_vector for the ports of the unit under test. Xilinx recommends
-- that these types always be used for the top-level I/O of a design in order
-- to guarantee that the testbench will bind correctly to the post-implementation
-- simulation model.
--------------------------------------------------------------------------------
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE ieee.numeric_std.ALL;
 
--library WORK;
--use WORK.DEBUG_PKG.ALL;
 
ENTITY spi_loopback_test IS
GENERIC (
N : positive := 32; -- 32bit serial word length is default
CPOL : std_logic := '0'; -- SPI mode selection (mode 0 default)
CPHA : std_logic := '0'; -- CPOL = clock polarity, CPHA = clock phase.
PREFETCH : positive := 1 -- prefetch lookahead cycles
);
END spi_loopback_test;
ARCHITECTURE behavior OF spi_loopback_test IS
 
--=========================================================
-- Component Declarations for the Unit Under Test (UUT)
--=========================================================
 
COMPONENT spi_loopback
-- GENERIC (
-- N : positive := 32; -- 32bit serial word length is default
-- CPOL : std_logic := '0'; -- SPI mode selection (mode 0 default)
-- CPHA : std_logic := '0'; -- CPOL = clock polarity, CPHA = clock phase.
-- PREFETCH : positive := 1); -- prefetch lookahead cycles
PORT(
----------------MASTER-----------------------
m_spi_clk_i : IN std_logic := 'X';
m_par_clk_i : IN std_logic := 'X';
m_rst_i : IN std_logic := 'X';
m_spi_ssel_o : OUT std_logic;
m_spi_sck_o : OUT std_logic;
m_spi_mosi_o : OUT std_logic;
m_spi_miso_i : IN std_logic := 'X';
m_di_i : IN std_logic_vector(N-1 downto 0) := (others => 'X');
m_do_o : OUT std_logic_vector(N-1 downto 0);
m_di_rdy_o : OUT std_logic;
m_wren_i : IN std_logic := 'X';
m_do_valid_o : OUT std_logic;
m_do_transfer_o : OUT std_logic;
m_state_dbg_o : OUT std_logic_vector(5 downto 0);
m_rx_bit_reg_o : OUT std_logic;
m_sh_reg_dbg_o : OUT std_logic_vector(N-1 downto 0);
----------------SLAVE-----------------------
s_clk_i : IN std_logic := 'X';
s_rst_i : IN std_logic := 'X';
s_spi_ssel_i : IN std_logic := 'X';
s_spi_sck_i : IN std_logic := 'X';
s_spi_mosi_i : IN std_logic := 'X';
s_spi_miso_o : OUT std_logic;
s_di_i : IN std_logic_vector(N-1 downto 0) := (others => 'X');
s_do_o : OUT std_logic_vector(N-1 downto 0);
s_di_rdy_o : OUT std_logic;
s_wren_i : IN std_logic := 'X';
s_do_valid_o : OUT std_logic;
s_do_transfer_o : OUT std_logic;
s_state_dbg_o : OUT std_logic_vector(5 downto 0)
-- s_sh_reg_dbg_o : OUT std_logic_vector(31 downto 0)
);
END COMPONENT;
 
 
--=========================================================
-- constants
--=========================================================
constant fifo_memory_size : integer := 8;
--=========================================================
-- types
--=========================================================
type fifo_memory_type is array (0 to fifo_memory_size-1) of std_logic_vector (N-1 downto 0);
 
--=========================================================
-- signals to connect the instances
--=========================================================
-- internal clk and rst
signal spi_2x_clk : std_logic := '0'; -- This is the SPI_SCK clock source. Must be 2x spi sck.
signal m_clk : std_logic := '0'; -- clock domain for the master parallel interface. Must be faster than spi bus sck.
signal s_clk : std_logic := '0'; -- clock domain for the slave parallel interface. Must be faster than spi bus sck.
signal rst : std_logic := 'X';
-- spi bus wires
signal spi_sck : std_logic;
signal spi_ssel : std_logic;
signal spi_miso : std_logic;
signal spi_mosi : std_logic;
-- master parallel interface
signal di_m : std_logic_vector (N-1 downto 0) := (others => '0');
signal do_m : std_logic_vector (N-1 downto 0);
signal do_valid_m : std_logic;
signal do_transfer_m : std_logic;
signal di_rdy_m : std_logic;
signal wren_m : std_logic := '0';
signal rx_bit_reg_m : std_logic;
signal sh_reg_m : std_logic_vector (N-1 downto 0) := (others => '0');
signal state_m : std_logic_vector (5 downto 0);
-- slave parallel interface
signal di_s : std_logic_vector (N-1 downto 0) := (others => '0');
signal do_s : std_logic_vector (N-1 downto 0);
signal do_valid_s : std_logic;
signal do_transfer_s : std_logic;
signal di_rdy_s : std_logic;
signal wren_s : std_logic := '0';
-- signal sh_reg_s : std_logic_vector (N-1 downto 0);
signal state_s : std_logic_vector (5 downto 0);
 
--=========================================================
-- Clock period definitions
--=========================================================
constant spi_2x_clk_period : time := 20 ns; -- 25MHz SPI SCK clock
constant m_clk_period : time := 10 ns; -- 100MHz master parallel clock
constant s_clk_period : time := 8 ns; -- 125MHz slave parallel clock
 
BEGIN
--=========================================================
-- instantiation of UUT
--=========================================================
 
Inst_spi_loopback: spi_loopback PORT MAP(
----------------MASTER-----------------------
m_spi_clk_i => spi_2x_clk,
m_par_clk_i => m_clk,
m_rst_i => rst,
m_spi_ssel_o => spi_ssel,
m_spi_sck_o => spi_sck,
m_spi_mosi_o => spi_mosi,
m_spi_miso_i => spi_miso,
m_di_i => di_m,
m_do_o => do_m,
m_di_rdy_o => di_rdy_m,
m_wren_i => wren_m,
m_do_valid_o => do_valid_m,
m_do_transfer_o => do_transfer_m,
m_state_dbg_o => state_m,
m_rx_bit_reg_o => rx_bit_reg_m,
m_sh_reg_dbg_o => sh_reg_m,
----------------SLAVE-----------------------
s_clk_i => s_clk,
s_rst_i => rst,
s_spi_ssel_i => spi_ssel,
s_spi_sck_i => spi_sck,
s_spi_mosi_i => spi_mosi,
s_spi_miso_o => spi_miso,
s_di_i => di_s,
s_do_o => do_s,
s_di_rdy_o => di_rdy_s,
s_wren_i => wren_s,
s_do_valid_o => do_valid_s,
s_do_transfer_o => do_transfer_s,
s_state_dbg_o => state_s
-- s_sh_reg_dbg_o => sh_reg_s
);
 
--=========================================================
-- Clock generator processes
--=========================================================
spi_2x_clk_process : process
begin
spi_2x_clk <= '0';
wait for spi_2x_clk_period/2;
spi_2x_clk <= '1';
wait for spi_2x_clk_period/2;
end process spi_2x_clk_process;
 
m_clk_process : process
begin
m_clk <= '0';
wait for m_clk_period/2;
m_clk <= '1';
wait for m_clk_period/2;
end process m_clk_process;
 
s_clk_process : process
begin
s_clk <= '0';
wait for s_clk_period/2;
s_clk <= '1';
wait for s_clk_period/2;
end process s_clk_process;
 
--=========================================================
-- rst_i process
--=========================================================
rst <= '0', '1' after 20 ns, '0' after 50 ns;
--=========================================================
-- Master interface process
--=========================================================
master_tx_fifo_proc: process is
variable fifo_memory : fifo_memory_type :=
(X"87654321",X"abcdef01",X"faceb007",X"10203049",X"85a5a5a5",X"7aaa5551",X"7adecabe",X"57564789");
variable fifo_head : integer range 0 to fifo_memory_size-1;
begin
-- synchronous rst_i
wait until rst = '1';
wait until m_clk'event and m_clk = '1';
di_m <= (others => '0');
wren_m <= '0';
fifo_head := 0;
wait until rst = '0';
-- load next fifo contents into shift register
for cnt in 0 to fifo_memory_size-1 loop
fifo_head := cnt; -- pre-compute next pointer
wait until di_rdy_m = '1'; -- wait shift register request for data
wait until m_clk'event and m_clk = '1'; -- sync fifo data load at next rising edge
di_m <= fifo_memory(fifo_head); -- place data into tx_data input bus
wait until m_clk'event and m_clk = '1'; -- sync fifo data load at next rising edge
wren_m <= '1'; -- write data into spi master
wait until di_rdy_m = '0'; -- wait data be accepted to compute next pointer
wait until m_clk'event and m_clk = '1'; -- sync fifo data load at next rising edge
wren_m <= '0'; -- remove write enable signal
end loop;
wait;
end process master_tx_fifo_proc;
 
 
--=========================================================
-- Slave interface process
--=========================================================
slave_tx_fifo_proc: process is
variable fifo_memory : fifo_memory_type :=
(X"90201031",X"97640231",X"ef55aaf1",X"babaca51",X"b00b1ee5",X"51525354",X"81828384",X"91929394");
variable fifo_head : integer range 0 to fifo_memory_size-1;
begin
-- synchronous rst_i
wait until rst = '1';
wait until s_clk'event and s_clk = '1';
di_s <= (others => '0');
wren_s <= '0';
fifo_head := 0;
wait until rst = '0';
-- load next fifo contents into shift register
for cnt in 0 to fifo_memory_size-1 loop
fifo_head := cnt; -- pre-compute next pointer
wait until di_rdy_s = '1'; -- wait shift register request for data
wait until s_clk'event and s_clk = '1'; -- sync fifo data load at next rising edge
di_s <= fifo_memory(fifo_head); -- place data into tx_data input bus
wait until s_clk'event and s_clk = '1'; -- sync fifo data load at next rising edge
wren_s <= '1'; -- write data into shift register
wait until di_rdy_s = '0'; -- wait data be accepted to compute next pointer
wait until s_clk'event and s_clk = '1'; -- sync fifo data load at next rising edge
wren_s <= '0'; -- remove write enable signal
end loop;
wait;
end process slave_tx_fifo_proc;
--=========================================================
-- Debug processes
--=========================================================
 
-- sh_reg_m <= dbg_shift_m;
-- state_m <= dbg_state_m;
-- sh_reg_s <= dbg_shift_s;
-- state_s <= dbg_state_s;
 
END ARCHITECTURE behavior;
/spi_master_slave/trunk/bench/spi_loopback_test_dbg.wcfg
0,0 → 1,123
<?xml version="1.0" encoding="UTF-8"?>
<wave_config>
<wave_state>
</wave_state>
<db_ref_list>
<db_ref path="C:/dropbox/Dropbox/VHDL_training/ISE_projects/spi_master_slave/spi_loopback_test_isim_par.wdb" id="1" type="auto">
<top_modules>
<top_module name="numeric_std" />
<top_module name="spi_loopback_test" />
<top_module name="std_logic_1164" />
<top_module name="textio" />
<top_module name="vcomponents" />
<top_module name="vital_primitives" />
<top_module name="vital_timing" />
<top_module name="vpackage" />
</top_modules>
</db_ref>
</db_ref_list>
<WVObjectSize size="24" />
<wvobject fp_name="/spi_loopback_test/spi_2x_clk" type="logic" db_ref_id="1">
<obj_property name="ElementShortName">spi_2x_clk</obj_property>
<obj_property name="ObjectShortName">spi_2x_clk</obj_property>
</wvobject>
<wvobject fp_name="/spi_loopback_test/m_clk" type="logic" db_ref_id="1">
<obj_property name="ElementShortName">m_clk</obj_property>
<obj_property name="ObjectShortName">m_clk</obj_property>
</wvobject>
<wvobject fp_name="/spi_loopback_test/rst" type="logic" db_ref_id="1">
<obj_property name="ElementShortName">rst</obj_property>
<obj_property name="ObjectShortName">rst</obj_property>
</wvobject>
<wvobject fp_name="/spi_loopback_test/spi_ssel" type="logic" db_ref_id="1">
<obj_property name="ElementShortName">spi_ssel</obj_property>
<obj_property name="ObjectShortName">spi_ssel</obj_property>
</wvobject>
<wvobject fp_name="/spi_loopback_test/spi_sck" type="logic" db_ref_id="1">
<obj_property name="ElementShortName">spi_sck</obj_property>
<obj_property name="ObjectShortName">spi_sck</obj_property>
</wvobject>
<wvobject fp_name="/spi_loopback_test/spi_mosi" type="logic" db_ref_id="1">
<obj_property name="ElementShortName">spi_mosi</obj_property>
<obj_property name="ObjectShortName">spi_mosi</obj_property>
</wvobject>
<wvobject fp_name="/spi_loopback_test/spi_miso" type="logic" db_ref_id="1">
<obj_property name="ElementShortName">spi_miso</obj_property>
<obj_property name="ObjectShortName">spi_miso</obj_property>
</wvobject>
<wvobject fp_name="/spi_loopback_test/rx_bit_reg_m" type="logic" db_ref_id="1">
<obj_property name="ElementShortName">rx_bit_reg_m</obj_property>
<obj_property name="ObjectShortName">rx_bit_reg_m</obj_property>
</wvobject>
<wvobject fp_name="/spi_loopback_test/sh_reg_m" type="array" db_ref_id="1">
<obj_property name="ElementShortName">sh_reg_m[31:0]</obj_property>
<obj_property name="ObjectShortName">sh_reg_m[31:0]</obj_property>
<obj_property name="Radix">HEXRADIX</obj_property>
</wvobject>
<wvobject fp_name="/spi_loopback_test/do_m" type="array" db_ref_id="1">
<obj_property name="ElementShortName">do_m[31:0]</obj_property>
<obj_property name="ObjectShortName">do_m[31:0]</obj_property>
<obj_property name="Radix">HEXRADIX</obj_property>
</wvobject>
<wvobject fp_name="/spi_loopback_test/do_transfer_m" type="logic" db_ref_id="1">
<obj_property name="ElementShortName">do_transfer_m</obj_property>
<obj_property name="ObjectShortName">do_transfer_m</obj_property>
</wvobject>
<wvobject fp_name="/spi_loopback_test/do_valid_m" type="logic" db_ref_id="1">
<obj_property name="ElementShortName">do_valid_m</obj_property>
<obj_property name="ObjectShortName">do_valid_m</obj_property>
</wvobject>
<wvobject fp_name="/spi_loopback_test/di_rdy_m" type="logic" db_ref_id="1">
<obj_property name="ElementShortName">di_rdy_m</obj_property>
<obj_property name="ObjectShortName">di_rdy_m</obj_property>
</wvobject>
<wvobject fp_name="/spi_loopback_test/di_m" type="array" db_ref_id="1">
<obj_property name="ElementShortName">di_m[31:0]</obj_property>
<obj_property name="ObjectShortName">di_m[31:0]</obj_property>
<obj_property name="Radix">HEXRADIX</obj_property>
</wvobject>
<wvobject fp_name="/spi_loopback_test/wren_m" type="logic" db_ref_id="1">
<obj_property name="ElementShortName">wren_m</obj_property>
<obj_property name="ObjectShortName">wren_m</obj_property>
</wvobject>
<wvobject fp_name="/spi_loopback_test/state_m" type="array" db_ref_id="1">
<obj_property name="ElementShortName">state_m[5:0]</obj_property>
<obj_property name="ObjectShortName">state_m[5:0]</obj_property>
<obj_property name="Radix">UNSIGNEDDECRADIX</obj_property>
</wvobject>
<wvobject fp_name="/spi_loopback_test/s_clk" type="logic" db_ref_id="1">
<obj_property name="ElementShortName">s_clk</obj_property>
<obj_property name="ObjectShortName">s_clk</obj_property>
</wvobject>
<wvobject fp_name="/spi_loopback_test/do_s" type="array" db_ref_id="1">
<obj_property name="ElementShortName">do_s[31:0]</obj_property>
<obj_property name="ObjectShortName">do_s[31:0]</obj_property>
<obj_property name="Radix">HEXRADIX</obj_property>
</wvobject>
<wvobject fp_name="/spi_loopback_test/do_transfer_s" type="logic" db_ref_id="1">
<obj_property name="ElementShortName">do_transfer_s</obj_property>
<obj_property name="ObjectShortName">do_transfer_s</obj_property>
</wvobject>
<wvobject fp_name="/spi_loopback_test/do_valid_s" type="logic" db_ref_id="1">
<obj_property name="ElementShortName">do_valid_s</obj_property>
<obj_property name="ObjectShortName">do_valid_s</obj_property>
</wvobject>
<wvobject fp_name="/spi_loopback_test/di_rdy_s" type="logic" db_ref_id="1">
<obj_property name="ElementShortName">di_rdy_s</obj_property>
<obj_property name="ObjectShortName">di_rdy_s</obj_property>
</wvobject>
<wvobject fp_name="/spi_loopback_test/di_s" type="array" db_ref_id="1">
<obj_property name="ElementShortName">di_s[31:0]</obj_property>
<obj_property name="ObjectShortName">di_s[31:0]</obj_property>
<obj_property name="Radix">HEXRADIX</obj_property>
</wvobject>
<wvobject fp_name="/spi_loopback_test/wren_s" type="logic" db_ref_id="1">
<obj_property name="ElementShortName">wren_s</obj_property>
<obj_property name="ObjectShortName">wren_s</obj_property>
</wvobject>
<wvobject fp_name="/spi_loopback_test/state_s" type="array" db_ref_id="1">
<obj_property name="ElementShortName">state_s[5:0]</obj_property>
<obj_property name="ObjectShortName">state_s[5:0]</obj_property>
<obj_property name="Radix">UNSIGNEDDECRADIX</obj_property>
</wvobject>
</wave_config>
/spi_master_slave/trunk/rtl/spi_loopback.ucf
0,0 → 1,79
 
#Created by Constraints Editor (xc6slx45t-csg484-3) - 2011/06/05
NET "m_spi_clk_i" TNM_NET = m_spi_clk_i;
TIMESPEC TS_m_spi_clk_i = PERIOD "m_spi_clk_i" 20 ns HIGH 50%;
NET "s_spi_sck_i" TNM_NET = s_spi_sck_i;
TIMESPEC TS_s_spi_sck_i = PERIOD "s_spi_sck_i" 40 ns HIGH 50%;
NET "m_par_clk_i" TNM_NET = m_par_clk_i;
TIMESPEC TS_m_par_clk_i = PERIOD "m_par_clk_i" 10 ns HIGH 50%;
NET "s_clk_i" TNM_NET = s_clk_i;
TIMESPEC TS_s_clk_i = PERIOD "s_clk_i" 10 ns HIGH 50%;
INST "m_di_i<0>" TNM = m_di;
INST "m_di_i<1>" TNM = m_di;
INST "m_di_i<2>" TNM = m_di;
INST "m_di_i<3>" TNM = m_di;
INST "m_di_i<4>" TNM = m_di;
INST "m_di_i<5>" TNM = m_di;
INST "m_di_i<6>" TNM = m_di;
INST "m_di_i<7>" TNM = m_di;
INST "m_di_i<8>" TNM = m_di;
INST "m_di_i<9>" TNM = m_di;
INST "m_di_i<10>" TNM = m_di;
INST "m_di_i<11>" TNM = m_di;
INST "m_di_i<12>" TNM = m_di;
INST "m_di_i<13>" TNM = m_di;
INST "m_di_i<14>" TNM = m_di;
INST "m_di_i<15>" TNM = m_di;
INST "m_di_i<16>" TNM = m_di;
INST "m_di_i<17>" TNM = m_di;
INST "m_di_i<18>" TNM = m_di;
INST "m_di_i<19>" TNM = m_di;
INST "m_di_i<20>" TNM = m_di;
INST "m_di_i<21>" TNM = m_di;
INST "m_di_i<22>" TNM = m_di;
INST "m_di_i<23>" TNM = m_di;
INST "m_di_i<24>" TNM = m_di;
INST "m_di_i<25>" TNM = m_di;
INST "m_di_i<26>" TNM = m_di;
INST "m_di_i<27>" TNM = m_di;
INST "m_di_i<28>" TNM = m_di;
INST "m_di_i<29>" TNM = m_di;
INST "m_di_i<30>" TNM = m_di;
INST "m_di_i<31>" TNM = m_di;
TIMEGRP "m_di" OFFSET = IN 10 ns VALID 10 ns BEFORE "m_par_clk_i" RISING;
INST "s_di_i<0>" TNM = s_di;
INST "s_di_i<1>" TNM = s_di;
INST "s_di_i<2>" TNM = s_di;
INST "s_di_i<3>" TNM = s_di;
INST "s_di_i<4>" TNM = s_di;
INST "s_di_i<5>" TNM = s_di;
INST "s_di_i<6>" TNM = s_di;
INST "s_di_i<7>" TNM = s_di;
INST "s_di_i<8>" TNM = s_di;
INST "s_di_i<9>" TNM = s_di;
INST "s_di_i<10>" TNM = s_di;
INST "s_di_i<11>" TNM = s_di;
INST "s_di_i<12>" TNM = s_di;
INST "s_di_i<13>" TNM = s_di;
INST "s_di_i<14>" TNM = s_di;
INST "s_di_i<15>" TNM = s_di;
INST "s_di_i<16>" TNM = s_di;
INST "s_di_i<17>" TNM = s_di;
INST "s_di_i<18>" TNM = s_di;
INST "s_di_i<19>" TNM = s_di;
INST "s_di_i<20>" TNM = s_di;
INST "s_di_i<21>" TNM = s_di;
INST "s_di_i<22>" TNM = s_di;
INST "s_di_i<23>" TNM = s_di;
INST "s_di_i<24>" TNM = s_di;
INST "s_di_i<25>" TNM = s_di;
INST "s_di_i<26>" TNM = s_di;
INST "s_di_i<27>" TNM = s_di;
INST "s_di_i<28>" TNM = s_di;
INST "s_di_i<29>" TNM = s_di;
INST "s_di_i<30>" TNM = s_di;
INST "s_di_i<31>" TNM = s_di;
TIMEGRP "s_di" OFFSET = IN 10 ns VALID 10 ns BEFORE "s_clk_i" RISING;
NET "s_spi_mosi_i" OFFSET = IN 20 ns VALID 20 ns BEFORE "s_spi_sck_i" RISING;
#Created by Constraints Editor (xc6slx45t-csg484-3) - 2011/06/05
NET "s_spi_miso_o" OFFSET = OUT 40 ns AFTER "s_spi_sck_i";
/spi_master_slave/trunk/rtl/spi_loopback.vhd
29,8 → 29,17
--library UNISIM;
--use UNISIM.VComponents.all;
 
--library WORK;
--use WORK.ALL;
 
entity spi_loopback is
port(
Generic (
N : positive := 32; -- 32bit serial word length is default
CPOL : std_logic := '0'; -- SPI mode selection (mode 0 default)
CPHA : std_logic := '0'; -- CPOL = clock polarity, CPHA = clock phase.
PREFETCH : positive := 1 -- prefetch lookahead cycles
);
Port(
----------------MASTER-----------------------
m_spi_clk_i : IN std_logic;
m_par_clk_i : IN std_logic;
39,13 → 48,15
m_spi_sck_o : OUT std_logic;
m_spi_mosi_o : OUT std_logic;
m_spi_miso_i : IN std_logic;
m_di_i : IN std_logic_vector(31 downto 0);
m_do_o : OUT std_logic_vector(31 downto 0);
m_di_i : IN std_logic_vector(N-1 downto 0);
m_do_o : OUT std_logic_vector(N-1 downto 0);
m_di_rdy_o : OUT std_logic;
m_wren_i : IN std_logic;
m_do_valid_o : OUT std_logic;
-- m_state_dbg_o : OUT std_logic_vector(5 downto 0);
-- m_sh_reg_dbg_o : OUT std_logic_vector(31 downto 0);
m_do_transfer_o : OUT std_logic;
m_state_dbg_o : OUT std_logic_vector(5 downto 0);
m_rx_bit_reg_o : OUT std_logic;
m_sh_reg_dbg_o : OUT std_logic_vector(N-1 downto 0);
----------------SLAVE-----------------------
s_clk_i : IN std_logic;
s_rst_i : IN std_logic;
53,38 → 64,53
s_spi_sck_i : IN std_logic;
s_spi_mosi_i : IN std_logic;
s_spi_miso_o : OUT std_logic;
s_di_i : IN std_logic_vector(31 downto 0);
s_do_o : OUT std_logic_vector(31 downto 0);
s_di_i : IN std_logic_vector(N-1 downto 0);
s_do_o : OUT std_logic_vector(N-1 downto 0);
s_di_rdy_o : OUT std_logic;
s_wren_i : IN std_logic;
s_do_valid_o : OUT std_logic
-- s_state_dbg_o : OUT std_logic_vector(5 downto 0);
s_do_valid_o : OUT std_logic;
s_do_transfer_o : OUT std_logic;
s_state_dbg_o : OUT std_logic_vector(5 downto 0)
-- s_sh_reg_dbg_o : OUT std_logic_vector(31 downto 0)
);
end spi_loopback;
 
architecture Behavioral of spi_loopback is
architecture Structural of spi_loopback is
 
COMPONENT spi_master
GENERIC (
N : positive := 32;
CPOL : std_logic := '0';
CPHA : std_logic := '0';
PREFETCH : positive := 1
);
PORT(
spi_2x_clk_i : IN std_logic;
par_clk_i : IN std_logic;
rst_i : IN std_logic;
spi_miso_i : IN std_logic;
di_i : IN std_logic_vector(31 downto 0);
di_i : IN std_logic_vector(N-1 downto 0);
wren_i : IN std_logic;
spi_ssel_o : OUT std_logic;
spi_sck_o : OUT std_logic;
spi_mosi_o : OUT std_logic;
do_o : OUT std_logic_vector(31 downto 0);
do_o : OUT std_logic_vector(N-1 downto 0);
di_rdy_o : OUT std_logic;
do_valid_o : OUT std_logic
-- state_dbg_o : OUT std_logic_vector(5 downto 0);
-- sh_reg_dbg_o : OUT std_logic_vector(31 downto 0)
do_valid_o : OUT std_logic;
do_transfer_o : OUT std_logic;
state_dbg_o : OUT std_logic_vector(5 downto 0);
rx_bit_reg_o : OUT std_logic;
sh_reg_dbg_o : OUT std_logic_vector(N-1 downto 0)
);
END COMPONENT;
 
COMPONENT spi_slave
GENERIC (
N : positive := 32;
CPOL : std_logic := '0';
CPHA : std_logic := '0';
PREFETCH : positive := 1
);
PORT(
clk_i : IN std_logic;
rst_i : IN std_logic;
91,20 → 117,27
spi_ssel_i : IN std_logic;
spi_sck_i : IN std_logic;
spi_mosi_i : IN std_logic;
di_i : IN std_logic_vector(31 downto 0);
di_i : IN std_logic_vector(N-1 downto 0);
wren_i : IN std_logic;
spi_miso_o : OUT std_logic;
do_o : OUT std_logic_vector(31 downto 0);
do_o : OUT std_logic_vector(N-1 downto 0);
di_rdy_o : OUT std_logic;
do_valid_o : OUT std_logic
-- state_dbg_o : OUT std_logic_vector(5 downto 0);
-- sh_reg_dbg_o : OUT std_logic_vector(31 downto 0)
do_valid_o : OUT std_logic;
do_transfer_o : OUT std_logic;
state_dbg_o : OUT std_logic_vector(5 downto 0)
-- sh_reg_dbg_o : OUT std_logic_vector(N-1 downto 0)
);
END COMPONENT;
 
begin
 
Inst_spi_master: spi_master PORT MAP(
Inst_spi_master: spi_master
GENERIC MAP (
N => N,
CPOL => CPOL,
CPHA => CPHA,
PREFETCH => PREFETCH)
PORT MAP(
spi_2x_clk_i => m_spi_clk_i,
par_clk_i => m_par_clk_i,
rst_i => m_rst_i,
116,12 → 149,20
do_o => m_do_o,
di_rdy_o => m_di_rdy_o,
wren_i => m_wren_i,
do_valid_o => m_do_valid_o
-- state_dbg_o => m_state_dbg_o,
-- sh_reg_dbg_o => m_sh_reg_dbg_o
do_valid_o => m_do_valid_o,
do_transfer_o => m_do_transfer_o,
state_dbg_o => m_state_dbg_o,
rx_bit_reg_o => m_rx_bit_reg_o,
sh_reg_dbg_o => m_sh_reg_dbg_o
);
 
Inst_spi_slave: spi_slave PORT MAP(
Inst_spi_slave: spi_slave
GENERIC MAP (
N => N,
CPOL => CPOL,
CPHA => CPHA,
PREFETCH => PREFETCH)
PORT MAP(
clk_i => s_clk_i,
rst_i => s_rst_i,
spi_ssel_i => s_spi_ssel_i,
132,12 → 173,13
do_o => s_do_o,
di_rdy_o => s_di_rdy_o,
wren_i => s_wren_i,
do_valid_o => s_do_valid_o
-- state_dbg_o => s_state_dbg_o,
do_valid_o => s_do_valid_o,
do_transfer_o => s_do_transfer_o,
state_dbg_o => s_state_dbg_o
-- sh_reg_dbg_o => s_sh_reg_dbg_o
);
 
end Behavioral;
end Structural;
 
 
 
/spi_master_slave/trunk/rtl/spi_slave.vhd
53,22 → 53,18
--
-- PARALLEL READ PIPELINED SEQUENCE
-- ================================
-- ______ ______ ______ ______ ______ ______
-- clk_spi_i ___/ bit1 \______/ bitN \______/bitN-1\______/bitN-2\______/bitN-3\______/bitN-4\_____... -- spi base clock
-- __ __ __ __ __ __ __ __ __ __ __ __ __ __
-- clk_par_i __/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__... -- parallel interface clock
-- _________________ ____________________________________________________________________... -- 1) received data is transferred to 'do_buffer_reg'
-- do_o __old_data_______X__________new_data__________________________________________________... -- after last bit received, at 'clk_spi_i' rising edge.
-- _________________ -- 2) 'do_valid_o' asserted on rising edge of 'clk_par_i',
-- do_valid_o ________________________________/ \___________________________________... -- at next bit (bit N-1) of the SPI transfer.
-- _________________
-- RAM_we ______________________________________/ \_____________________________... -- 3) Generates a write enable, or uses 'do_valid_o' as 'we'.
--
-- ______ ______ ______ ______ _
-- clk_spi_i ___/ bit1 \______/ bitN \______/bitN-1\______/bitN-2\______/b... -- spi base clock
-- __ __ __ __ __ __ __ __ __ __
-- clk_par_i __/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \_... -- parallel interface clock
-- _________________ ___________________________________________... -- 1) received data is transferred to 'do_buffer_reg'
-- do_o __old_data_______X__________new_data_________________________... -- after last bit received, at 'clk_spi_i' rising edge.
-- _________________ -- 2) 'do_valid_o' asserted on rising edge of 'clk_par_i',
-- do_valid_o ______________________________________/ \____... -- at next bit (bit N-1) of the SPI transfer.
--
--
-- This module takes 85 FFs (47 slices in a Spartan-6 fabric), synthesized with XST and normal constraints.
-- The design is tested in a Spartan-6 XC6SLX45t-2CSG324 device, in the Atlys board.
-- This design was originally targeted to a Spartan-6 platform, synthesized with XST and normal constraints.
--
--
------------------------------ COPYRIGHT NOTICE -----------------------------------------------------------------------
--
-- This file is part of the SPI MASTER/SLAVE INTERFACE project http://opencores.org/project,spi_master_slave
99,6 → 95,8
-- 2011/05/15 v0.15.0055 [JD] fixed logic for starting state when CPHA='1'.
-- 2011/05/17 v0.80.0049 [JD] added explicit clock synchronization circuitry across clock boundaries.
-- 2011/05/18 v0.95.0050 [JD] clock generation circuitry, with generators for all-rising-edge clock core.
-- 2011/06/05 v0.96.0053 [JD] changed async clear to sync resets.
-- 2011/06/07 v0.97.0065 [JD] added cross-clock buffers, fixed fsm async glitches.
--
--
-----------------------------------------------------------------------------------------------------------------------
115,28 → 113,29
use IEEE.NUMERIC_STD.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
 
--library WORK;
--use WORK.DEBUG_PKG.ALL;
 
entity spi_slave is
Generic ( N : positive := 32; -- 32bit serial word length is default
CPOL : std_logic := '0'; -- SPI mode selection (mode 0 default)
CPHA : std_logic := '0'; -- CPOL = clock polarity, CPHA = clock phase.
PREFETCH : positive := 1); -- prefetch lookahead cycles
Port ( clk_i : in std_logic := 'X'; -- internal interface clock (clocks di/do registers)
rst_i : in std_logic := 'X'; -- synchronous rst_i: clear registers at clk_i rising edge
spi_ssel_i : in std_logic; -- spi bus slave select line
spi_sck_i : in std_logic; -- spi bus sck clock (clocks the shift register core)
spi_mosi_i : in std_logic; -- spi bus mosi input
spi_miso_o : out std_logic := 'X'; -- spi bus spi_miso_o output
di_i : in std_logic_vector (N-1 downto 0) := (others => 'X'); -- parallel load data in (clocked in on rising edge of clk_i)
do_o : out std_logic_vector (N-1 downto 0); -- parallel output (clocked out on falling clk_i)
di_rdy_o : out std_logic; -- preload lookahead: HIGH when ready for new input data
wren_i : in std_logic := 'X'; -- write enable (write di_i data at next rising clk_i edge)
-- wren_i starts transmission. must be valid 1 clk_i cycle before current transmission ends.
do_valid_o : out std_logic -- do_o data valid signal, valid during one clk_i rising edge.
-- state_dbg_o : out std_logic_vector (5 downto 0); -- debug: internal state register
-- sh_reg_dbg_o : out std_logic_vector (N-1 downto 0) -- debug: internal shift register
Generic (
N : positive := 32; -- 32bit serial word length is default
CPOL : std_logic := '0'; -- SPI mode selection (mode 0 default)
CPHA : std_logic := '0'; -- CPOL = clock polarity, CPHA = clock phase.
PREFETCH : positive := 1); -- prefetch lookahead cycles
Port (
clk_i : in std_logic := 'X'; -- internal interface clock (clocks di/do registers)
rst_i : in std_logic := 'X'; -- synchronous rst_i: clear registers at clk_i rising edge
spi_ssel_i : in std_logic; -- spi bus slave select line
spi_sck_i : in std_logic; -- spi bus sck clock (clocks the shift register core)
spi_mosi_i : in std_logic; -- spi bus mosi input
spi_miso_o : out std_logic := 'X'; -- spi bus spi_miso_o output
di_i : in std_logic_vector (N-1 downto 0) := (others => 'X'); -- parallel load data in (clocked in on rising edge of clk_i)
do_o : out std_logic_vector (N-1 downto 0); -- parallel output (clocked out on falling clk_i)
di_rdy_o : out std_logic; -- preload lookahead: HIGH when ready for new input data
wren_i : in std_logic := 'X'; -- write enable (write di_i data at next rising clk_i edge)
-- wren_i starts transmission. must be valid 1 clk_i cycle
-- before current transmission ends.
do_valid_o : out std_logic; -- do_o data valid signal, valid during one clk_i rising edge.
do_transfer_o : out std_logic; -- debug: internal transfer driver
state_dbg_o : out std_logic_vector (5 downto 0) -- debug: internal state register
-- sh_reg_dbg_o : out std_logic_vector (N-1 downto 0) -- debug: internal shift register
);
end spi_slave;
 
148,28 → 147,31
architecture RTL of spi_slave is
-- constants to control FlipFlop synthesis
constant SAMPLE_EDGE : std_logic := (CPOL xnor CPHA);
constant SAMPLE_LEVEL : std_logic := SAMPLE_EDGE;
constant SHIFT_EDGE : std_logic := (CPOL xor CPHA);
-- internal state signals for register and combinational stages
signal state_reg : natural range N+1 downto 0 := 0;
signal state_next : natural range N+1 downto 0 := 0;
-- shifter signals for register and combinational stages
signal sh_reg : std_logic_vector (N-1 downto 0) := (others => '0');
signal sh_next : std_logic_vector (N-1 downto 0) := (others => '0');
signal sh_reg : std_logic_vector (N-1 downto 0);
signal sh_next : std_logic_vector (N-1 downto 0);
-- input bit sampled buffer
signal rx_bit_reg : std_logic := '0';
signal di_reg : std_logic_vector (N-1 downto 0) := (others => '0');
signal rx_bit_reg : std_logic;
signal di_reg : std_logic_vector (N-1 downto 0);
-- buffered do_o data signals for register and combinational stages
signal do_buffer_reg : std_logic_vector (N-1 downto 0) := (others => '0');
signal do_buffer_next : std_logic_vector (N-1 downto 0) := (others => '0');
signal do_buffer_reg : std_logic_vector (N-1 downto 0);
signal do_buffer_next : std_logic_vector (N-1 downto 0);
-- internal signal to flag transfer to do_buffer_reg
signal do_transfer_reg : std_logic := '0';
signal do_transfer_next : std_logic := '0';
signal do_transfer_reg : std_logic;
signal do_transfer_next : std_logic;
-- internal registered do_valid_o
signal do_valid_reg : std_logic := '0';
signal do_valid_next : std_logic := '0';
signal do_valid_oreg : std_logic;
signal do_valid_reg : std_logic;
signal do_valid_next : std_logic;
-- internal registered di_rdy_o
signal di_ready_reg : std_logic := '0';
signal di_ready_next : std_logic := '0';
signal di_rdy_oreg : std_logic;
signal di_rdy_reg : std_logic;
signal di_rdy_next : std_logic;
begin
--=============================================================================================
-- GENERICS CONSTRAINTS CHECKING
196,32 → 198,56
-- state and data registers change on SHIFT edge of sck (ffd with async clear)
core_reg_proc : process (spi_sck_i, rst_i, spi_ssel_i) is
begin
if rst_i = '1' then -- async rst for the parallel interface registers
-- registers cleared on reset
if rst_i = '1' then -- async clr
do_buffer_reg <= (others => '0');
do_transfer_reg <= '0';
elsif spi_ssel_i = '1' then -- async clear for core registers
elsif spi_sck_i'event and spi_sck_i = SHIFT_EDGE then -- on SHIFT edge, update all core registers
do_buffer_reg <= do_buffer_next;
do_transfer_reg <= do_transfer_next;
end if;
-- registers cleared on idle (spi_ssel_i = 1)
if spi_ssel_i = '1' then -- async clr
state_reg <= 0;
sh_reg <= (others => '0');
elsif spi_sck_i'event and spi_sck_i = SHIFT_EDGE then -- on SHIFT edge, update all core registers
state_reg <= state_next;
sh_reg <= sh_next;
do_buffer_reg <= do_buffer_next;
do_transfer_reg <= do_transfer_next;
end if;
end process core_reg_proc;
-- cross-clock registers change on half-cycle of sck (ffd with async clear)
-- this is to prevent fsm state change glitches causing setup time artifacts at async clk_i edges
cross_reg_proc : process (rst_i, spi_sck_i, spi_ssel_i) is
begin
if spi_ssel_i = '1' then
di_rdy_reg <= '1'; -- di_rdy true during idle
elsif spi_sck_i'event and spi_sck_i = SAMPLE_EDGE then -- on half-cycle edge, update cross registers
di_rdy_reg <= di_rdy_next;
end if;
if rst_i = '1' then
do_valid_reg <= '0'; -- async clear on do_valid
elsif spi_sck_i'event and spi_sck_i = SAMPLE_EDGE then -- on half-cycle edge, update cross registers
do_valid_reg <= do_valid_next;
end if;
end process cross_reg_proc;
-- parallel load input registers (to elliminate async clock glitches)
par_reg_proc: process (clk_i, rst_i, wren_i) is
par_reg_proc: process (clk_i, rst_i, wren_i, spi_sck_i, spi_ssel_i) is
begin
if rst_i = '1' then -- synchronous reset for the parallel interface
di_reg <= (others => '0');
di_ready_reg <= '0';
do_valid_reg <= '0';
elsif clk_i'event and clk_i = '1' then
di_ready_reg <= di_ready_next;
do_valid_reg <= do_valid_next;
if wren_i = '1' then -- wren_i is the clock enable for di_reg
di_reg <= di_i; -- buffer for parallel data, to avoid 'clk_i' and 'spi_sck_i' async glitches
if clk_i'event and clk_i = '1' then
-- output flags registers
if rst_i = '1' then -- sync rst for output flags
di_rdy_oreg <= '0';
do_valid_oreg <= '0';
else
di_rdy_oreg <= di_rdy_reg; -- transfer buffer regs to out regs
do_valid_oreg <= (do_valid_reg and not spi_ssel_i) or (do_transfer_reg and spi_ssel_i);
end if;
-- input register, with 'rst_i' sync reset and 'wren_i' clock enable
if rst_i = '1' then -- sync rst for di_reg
di_reg <= (others => '0');
elsif wren_i = '1' then -- wren_i is the clock enable for di_reg
di_reg <= di_i; -- parallel data input buffer register
end if;
end if;
end process par_reg_proc;
 
230,23 → 256,23
--=============================================================================================
-- state and datapath combinational logic
core_combi_proc : process ( rst_i, sh_reg, state_reg, rx_bit_reg, do_buffer_reg,
do_valid_reg, do_transfer_reg, di_reg, di_ready_reg, wren_i ) is
do_valid_reg, do_transfer_reg, di_reg, di_rdy_reg, wren_i ) is
begin
sh_next <= sh_reg; -- all output signals are assigned to (avoid latches)
do_buffer_next <= do_buffer_reg;
do_valid_next <= do_valid_reg;
do_transfer_next <= do_transfer_reg;
di_ready_next <= di_ready_reg;
di_rdy_next <= di_rdy_reg;
spi_miso_o <= '0'; -- will output '0' when shifter is empty
state_next <= state_reg - 1; -- update next state at each sck pulse
case state_reg is
when (N) =>
di_ready_next <= '0'; -- deassert next-data request when start shifting
di_rdy_next <= '0'; -- deassert next-data request when start shifting
spi_miso_o <= sh_reg(N-1); -- shift out tx bit from the MSb
sh_next(N-1 downto 1) <= sh_reg(N-2 downto 0); -- shift inner bits
sh_next(0) <= rx_bit_reg; -- shift in rx bit into LSb
when (N-1) downto (PREFETCH+3) =>
di_ready_next <= '0'; -- deassert next-data request when start shifting
di_rdy_next <= '0'; -- deassert next-data request when start shifting
do_valid_next <= do_transfer_reg; -- assert valid rx data, with plenty of pipeline delay for 'do_buffer'
do_transfer_next <= '0'; -- reset transfer signal
spi_miso_o <= sh_reg(N-1); -- shift out tx bit from the MSb
253,8 → 279,8
sh_next(N-1 downto 1) <= sh_reg(N-2 downto 0); -- shift inner bits
sh_next(0) <= rx_bit_reg; -- shift in rx bit into LSb
when (PREFETCH+2) downto 2 =>
-- raise prefetch 'di_ready_next' signal and remove 'do_valid'
di_ready_next <= '1'; -- request data in advance to allow for pipeline delays
-- raise prefetch 'di_rdy_next' signal and remove 'do_valid'
di_rdy_next <= '1'; -- request data in advance to allow for pipeline delays
do_valid_next <= '0'; -- make do_valid_o HIGH for one cycle only
spi_miso_o <= sh_reg(N-1); -- shift out tx bit from the MSb
sh_next(N-1 downto 1) <= sh_reg(N-2 downto 0); -- shift inner bits
274,7 → 300,7
when 0 =>
do_transfer_next <= '0'; -- clear signal transfer to do_buffer
do_valid_next <= do_transfer_reg; -- assert valid rx data after data received, when interface idle
di_ready_next <= '1'; -- will request data if shifter empty
di_rdy_next <= '1'; -- will request data if shifter empty
spi_miso_o <= di_reg(N-1); -- shift out first tx bit from the MSb
if CPHA = '0' then
-- initial state for CPHA=0, when slave interface is first selected or idle
296,18 → 322,14
--=============================================================================================
-- data output processes
do_proc : do_o <= do_buffer_reg; -- do_o always available
do_valid_proc: do_valid_o <= do_valid_reg; -- copy registered do_valid_o to output
di_ready_proc: di_rdy_o <= di_ready_reg; -- copy registered di_rdy_o to output
do_valid_proc: do_valid_o <= do_valid_oreg; -- copy registered do_valid_o to output
di_rdy_proc: di_rdy_o <= di_rdy_oreg; -- copy registered di_rdy_o to output
 
--=============================================================================================
-- DEBUG LOGIC PROCESSES
--=============================================================================================
-- The debug signals are declared in package debug_pkg
-- These processes can be removed from the entity after verification
-- sh_reg_dbg_proc: dbg_shift_s <= conv_integer(sh_reg); -- export sh_reg to debug
-- state_dbg_proc: dbg_state_s <= state_reg; -- export internal state to debug
-- sh_reg_debug_proc: sh_reg_dbg_o <= sh_reg; -- export sh_reg to debug
-- state_debug_proc: state_dbg_o <= std_logic_vector(to_unsigned(state_reg, 6)); -- export internal state to debug
do_transfer_proc: do_transfer_o <= do_transfer_reg;
state_debug_proc: state_dbg_o <= std_logic_vector(to_unsigned(state_reg, 6)); -- export internal state to debug
-- sh_reg_debug_proc: sh_reg_dbg_o <= sh_reg; -- export sh_reg to debug
end architecture RTL;
 
/spi_master_slave/trunk/rtl/spi_master.vhd
38,8 → 38,8
-- di_rdy_o ________/ \___________... -- 'di_rdy_o' asserted on rising edge of 'par_clk_i'
-- ______________ _________________________________________
-- di_i __old_data____X__________new_data_______________________... -- user circuit loads data on 'di_i' at next rising edge
-- ________________________________ -- user circuit asserts 'wren_i' at next edge, and removes
-- wren_i __________________/ \____... -- 'wren_i' after 'di_rdy_o' is removed
-- ________________________________ -- user circuit asserts 'wren_i' at next edge,
-- wren_i __________________/ \____... -- and removes 'wren_i' after 'di_rdy_o' is removed
--
--
-- PARALLEL READ INTERFACE
51,26 → 51,21
--
-- PARALLEL READ PIPELINED SEQUENCE
-- ================================
-- ______ ______ ______ ______ ______
-- spi_2x_clk_i ___/ bit1 \______/ bitN \______/bitN-1\______/bitN-2\______/bitN-3\_... -- spi base clock
-- __ __ __ __ __ __ __ __ __ __ __
-- par_clk_i __/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__... -- parallel interface clock
-- ________________ ___________________________________________________... -- 1) received data is transferred to 'do_buffer_reg'
-- do_o __old_data______X__________new_data_________________________________... -- after last bit received, at 'spi_2x_clk_i' rising edge.
-- _________________ -- 2) 'do_valid_o' asserted on rising edge of 'par_clk_i',
-- do_valid_o ________________________________/ \_________________... -- at next bit (bit N-1) of the SPI transfer.
-- _________________
-- RAM_we ______________________________________/ \___________... -- 3) User can use 'do_valid_o' as write enable of sync RAM.
-- ______ ______ ______ ______ ______
-- spi_2x_clk_i bit1 \______/ bitN \______/bitN-1\______/bitN-2\______/bitN-3\_... -- spi 2x base clock
-- _ __ __ __ __ __ __ __ __ __ __
-- par_clk_i \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__... -- parallel interface clock
-- _____________ __________________________________________________... -- 1) rx data is transferred to 'do_buffer_reg'
-- do_o ___old_data__X__________new_data________________________________... -- after last rx bit, at rising 'spi_2x_clk_i'.
-- ___________ -- 2) 'do_valid_o' asserted on rising 'par_clk_i',
-- do_valid_o __________________________________/ \_________________... -- at next bit (bit N-1) of the SPI transfer.
--
--
-- The propagation delay of spi_sck_o and spi_mosi_o, referred to the internal clock, is balanced by similar path delays,
-- but the sampling delay of spi_miso_i imposes a setup time referred to the sck signal that limits the high frequency
-- of the interface, for full duplex operation.
-- The synthesizable architecture is fully static, with a classic RTL pipelined architecture, and follows the KISS
-- methodology (Keep It Synchronous, Stupid!).
--
-- This module takes 76 FFs (25 slices on a Spartan-6 fabric), synthesized with XST and normal constraints.
-- The design is tested in a Spartan-6 XC6SLX45t-2CSG324 device, in the Atlys board.
-- This design was originally targeted to a Spartan-6 platform, synthesized with XST and normal constraints.
--
------------------------------ COPYRIGHT NOTICE -----------------------------------------------------------------------
--
106,6 → 101,8
-- 2011/05/13 v0.20.0045 [JD] streamlined signal names, added PREFETCH parameter, added assertions.
-- 2011/05/17 v0.80.0049 [JD] added explicit clock synchronization circuitry across clock boundaries.
-- 2011/05/18 v0.95.0050 [JD] clock generation circuitry, with generators for all-rising-edge clock core.
-- 2011/06/05 v0.96.0053 [JD] changed async clear to sync resets.
-- 2011/06/07 v0.97.0065 [JD] added cross-clock buffers, fixed fsm async glitches.
--
--
-----------------------------------------------------------------------------------------------------------------------
122,29 → 119,31
use IEEE.NUMERIC_STD.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
 
--library WORK;
--use WORK.DEBUG_PKG.ALL;
 
entity spi_master is
Generic ( N : positive := 32; -- 32bit serial word length is default
CPOL : std_logic := '0'; -- SPI mode selection (mode 0 default)
CPHA : std_logic := '0'; -- CPOL = clock polarity, CPHA = clock phase.
PREFETCH : positive := 1); -- prefetch lookahead cycles
Port ( spi_2x_clk_i : in std_logic := 'X'; -- spi base reference clock: 2x 'spi_sck_o'
par_clk_i : in std_logic := 'X'; -- parallel interface clock
rst_i : in std_logic := 'X'; -- async reset: clear all registers
spi_ssel_o : out std_logic; -- spi bus slave select line
spi_sck_o : out std_logic; -- spi bus sck
spi_mosi_o : out std_logic; -- spi bus mosi output
spi_miso_i : in std_logic := 'X'; -- spi bus spi_miso_i input
di_i : in std_logic_vector (N-1 downto 0) := (others => 'X'); -- parallel load data in (clocked in on rising spi_2x_clk_i after last bit)
do_o : out std_logic_vector (N-1 downto 0); -- parallel output (clocked out on rising spi_2x_clk_i after last bit)
di_rdy_o : out std_logic; -- preload lookahead: HIGH for PREFETCH cycles before last bit
wren_i : in std_logic := 'X'; -- write enable (write di_i data at next rising spi_2x_clk_i edge)
-- wren_i starts transmission. must be valid 1 spi_2x_clk_i cycle before current transmission ends.
do_valid_o : out std_logic -- do_o data valid signal, valid during one spi_2x_clk_i rising edge.
-- state_dbg_o : out std_logic_vector (5 downto 0); -- debug: internal state register
-- sh_reg_dbg_o : out std_logic_vector (N-1 downto 0) -- debug: internal shift register
Generic (
N : positive := 32; -- 32bit serial word length is default
CPOL : std_logic := '0'; -- SPI mode selection (mode 0 default)
CPHA : std_logic := '0'; -- CPOL = clock polarity, CPHA = clock phase.
PREFETCH : positive := 1); -- prefetch lookahead cycles
Port (
spi_2x_clk_i : in std_logic := 'X'; -- spi base reference clock: 2x 'spi_sck_o'
par_clk_i : in std_logic := 'X'; -- parallel interface clock
rst_i : in std_logic := 'X'; -- async reset: clear all registers
spi_ssel_o : out std_logic; -- spi bus slave select line
spi_sck_o : out std_logic; -- spi bus sck
spi_mosi_o : out std_logic; -- spi bus mosi output
spi_miso_i : in std_logic := 'X'; -- spi bus spi_miso_i input
di_i : in std_logic_vector (N-1 downto 0) := (others => 'X'); -- parallel data in (clocked on rising spi_2x_clk_i after last bit)
do_o : out std_logic_vector (N-1 downto 0); -- parallel output (clocked on rising spi_2x_clk_i after last bit)
di_rdy_o : out std_logic; -- preload lookahead: HIGH for PREFETCH cycles before last bit
wren_i : in std_logic := 'X'; -- write enable (write di_i data at next rising spi_2x_clk_i edge)
-- wren_i starts transmission. must be valid 1 spi_2x_clk_i cycle
-- before current transmission ends.
do_valid_o : out std_logic; -- do_o data valid signal, valid during one spi_2x_clk_i rising edge.
do_transfer_o : out std_logic; -- debug: internal transfer driver
state_dbg_o : out std_logic_vector (5 downto 0); -- debug: internal state register
rx_bit_reg_o : out std_logic; -- debug: internal rx bit
sh_reg_dbg_o : out std_logic_vector (N-1 downto 0) -- debug: internal shift register
);
end spi_master;
 
153,6 → 152,7
-- all signals are clocked at the rising edge of the system clock 'spi_2x_clk_i'.
--================================================================================================================
architecture RTL of spi_master is
 
-- core clocks, generated from 'spi_2x_clk_i'
signal core_clk : std_logic; -- continuous fsm core clock, positive logic
signal core_n_clk : std_logic; -- continuous fsm core clock, negative logic
185,11 → 185,13
signal do_transfer_reg : std_logic := '0';
signal do_transfer_next : std_logic := '0';
-- internal registered do_valid_o
signal do_valid_oreg : std_logic := '0';
signal do_valid_reg : std_logic := '0';
signal do_valid_next : std_logic := '0';
-- internal registered di_rdy_o
signal di_ready_reg : std_logic := '0';
signal di_ready_next : std_logic := '0';
signal di_rdy_oreg : std_logic := '0';
signal di_rdy_reg : std_logic := '1';
signal di_rdy_next : std_logic := '1';
begin
--=============================================================================================
-- GENERICS CONSTRAINTS CHECKING
215,28 → 217,30
-- setup time of the serial input related to the data setup time of the serial output.
-----------------------------------------------------------------------------------------------
-- divide down 'spi_2x_clk_i' by 2
-- this should be synthesized as a single ffd
-- this should be synthesized as a single ffd with sync reset
core_clock_gen_proc : process (rst_i, spi_2x_clk_i) is
begin
if rst_i = '1' then
core_clk <= '0'; -- positive logic clk: idle LOW
core_n_clk <= '1'; -- negative logic clk: idle HIGH
elsif spi_2x_clk_i'event and spi_2x_clk_i = '1' then
core_clk <= core_n_clk; -- divided by 2 clock, differential
core_n_clk <= not core_n_clk;
if spi_2x_clk_i'event and spi_2x_clk_i = '1' then
if rst_i = '1' then
core_clk <= '0'; -- positive logic clk: idle LOW
core_n_clk <= '1'; -- negative logic clk: idle HIGH
else
core_clk <= core_n_clk; -- divided by 2 clock, differential
core_n_clk <= not core_n_clk;
end if;
end if;
end process core_clock_gen_proc;
-----------------------------------------------------------------------------------------------
-- spi sck generator: divide input 2x clock by 2, with a CE controlled by the fsm
-- this should be sinthesized as a single FFD with async reset and clock enable
-- this should be sinthesized as a single FFD with sync reset and clock enable
spi_clock_gen_proc : process (rst_i, spi_2x_clk_i, ena_sck_reg) is
begin
if rst_i = '1' then
spi_clk <= '0'; -- positive logic clk: idle LOW
spi_n_clk <= '1'; -- negative logic clk: idle HIGH
elsif spi_2x_clk_i'event and spi_2x_clk_i = '1' then
if ena_sck_reg = '1' then
spi_clk <= spi_n_clk; -- divided by 2 clock, differential
if spi_2x_clk_i'event and spi_2x_clk_i = '1' then
if rst_i = '1' then
spi_clk <= '0'; -- positive logic clk: idle LOW
spi_n_clk <= '1'; -- negative logic clk: idle HIGH
elsif ena_sck_reg = '1' then
spi_clk <= spi_n_clk; -- divided by 2 clock, differential
spi_n_clk <= not spi_n_clk;
end if;
end if;
258,12 → 262,12
smp_cpha_0_proc :
if CPHA = '0' generate
begin
samp_clk <= core_clk; -- for CPHA=0, sample at rising edge of positive core clock
samp_clk <= spi_clk; -- for CPHA=0, sample at end of sample cell
end generate;
smp_cpha_1_proc :
if CPHA = '1' generate
begin
samp_clk <= core_n_clk; -- for CPHA=1, sample at rising edge of negative core clock
samp_clk <= spi_n_clk; -- for CPHA=1, sample at end of sample cell
end generate;
-----------------------------------------------------------------------------------------------
-- FSM clock generation: generate 'fsm_clock' from core_clk or core_n_clk depending on CPHA
281,7 → 285,7
--=============================================================================================
-- RTL REGISTER PROCESSES
--=============================================================================================
-- capture rx bit at SAMPLE edge of sck
-- rx bit flop: capture rx bit after SAMPLE edge of sck
rx_bit_proc : process (samp_clk, spi_miso_i) is
begin
if samp_clk'event and samp_clk = '1' then
288,37 → 292,56
rx_bit_reg <= spi_miso_i;
end if;
end process rx_bit_proc;
-- state and data registers synchronous to the spi base reference clock
-- state and data registers: synchronous to the spi base reference clock
core_reg_proc : process (fsm_clk, rst_i) is
begin
if rst_i = '1' then -- asynchronous reset
sh_reg <= (others => '0');
state_reg <= 0;
ena_ssel_reg <= '0';
ena_sck_reg <= '0';
do_buffer_reg <= (others => '0');
do_transfer_reg <= '0';
elsif fsm_clk'event and fsm_clk = '1' then
sh_reg <= sh_next;
state_reg <= state_next;
ena_ssel_reg <= ena_ssel_next;
ena_sck_reg <= ena_sck_next;
do_buffer_reg <= do_buffer_next;
do_transfer_reg <= do_transfer_next;
if fsm_clk'event and fsm_clk = '1' then
if rst_i = '1' then -- sync reset
sh_reg <= (others => '0');
state_reg <= 0;
ena_ssel_reg <= '0';
ena_sck_reg <= '0';
do_buffer_reg <= (others => '0');
do_transfer_reg <= '0';
else
sh_reg <= sh_next;
state_reg <= state_next;
ena_ssel_reg <= ena_ssel_next;
ena_sck_reg <= ena_sck_next;
do_buffer_reg <= do_buffer_next;
do_transfer_reg <= do_transfer_next;
end if;
end if;
end process core_reg_proc;
-- parallel i/o interface registers, synchronous to the parallel interface clock
par_reg_proc : process (par_clk_i, rst_i) is
-- cross-clock registers change on half-cycle of sck (ffd with async clear)
-- this is to prevent fsm state change glitches causing setup time artifacts at async clk_i edges
cross_reg_proc : process (rst_i, fsm_clk, ena_ssel_reg) is
begin
if rst_i = '1' then -- asynchronous reset
di_ready_reg <= '0';
do_valid_reg <= '0';
di_reg <= (others => '0');
elsif par_clk_i'event and par_clk_i = '1' then
di_ready_reg <= di_ready_next; -- di_rdy is synchronous to parallel interface clock
if ena_ssel_reg = '0' then
di_rdy_reg <= '1'; -- di_rdy true during idle
elsif fsm_clk'event and fsm_clk = '0' then -- on half-cycle edge, update cross registers
di_rdy_reg <= di_rdy_next;
end if;
if rst_i = '1' then
do_valid_reg <= '0'; -- async clear on do_valid
elsif fsm_clk'event and fsm_clk = '0' then -- on half-cycle edge, update cross registers
do_valid_reg <= do_valid_next;
di_reg <= di_i; -- sample di_i at interface clock
end if;
end process cross_reg_proc;
-- parallel i/o interface registers: synchronous to the parallel interface clock
par_reg_proc : process (rst_i, par_clk_i, ena_ssel_reg) is
begin
if par_clk_i'event and par_clk_i = '1' then
if rst_i = '1' then -- sync reset
di_rdy_oreg <= '0';
do_valid_oreg <= '0';
di_reg <= (others => '0');
else
di_rdy_oreg <= di_rdy_reg; -- di_rdy is synchronous to parallel interface clock
do_valid_oreg <= (do_valid_reg and ena_ssel_reg) or (do_transfer_reg and not ena_ssel_reg);
di_reg <= di_i; -- sample di_i at interface clock
end if;
end if;
end process par_reg_proc;
 
--=============================================================================================
326,67 → 349,66
--=============================================================================================
-- state and datapath combinational logic
core_combi_proc : process ( sh_reg, state_reg, rx_bit_reg, ena_ssel_reg, ena_sck_reg, do_buffer_reg,
do_valid_reg, do_transfer_reg, di_reg, di_ready_reg, wren_i ) is
do_valid_reg, do_transfer_reg, di_reg, di_rdy_reg, wren_i ) is
begin
sh_next <= sh_reg; -- all output signals are assigned to (avoid latches)
sh_next <= sh_reg; -- all output signals are assigned to (avoid latches)
ena_ssel_next <= ena_ssel_reg;
ena_sck_next <= ena_sck_reg;
do_buffer_next <= do_buffer_reg;
do_valid_next <= do_valid_reg;
do_transfer_next <= do_transfer_reg;
di_ready_next <= di_ready_reg;
spi_mosi_o <= '0'; -- will output '0' when shifter is empty
state_next <= state_reg - 1; -- next state is next bit
di_rdy_next <= di_rdy_reg;
spi_mosi_o <= '0'; -- will output '0' when shifter is empty
state_next <= state_reg - 1; -- next state is next bit
case state_reg is
when (N+1) => -- this state is to enable SSEL before SCK
ena_ssel_next <= '1'; -- tx in progress: will assert SSEL
ena_sck_next <= '1'; -- enable SCK on next cycle (stays off on first SSEL clock cycle)
di_ready_next <= '0'; -- deassert next-data request when shifting data
spi_mosi_o <= sh_reg(N-1); -- shift out tx bit from the MSb
when (N) =>
di_ready_next <= '0'; -- deassert next-data request when shifting data
spi_mosi_o <= sh_reg(N-1); -- shift out tx bit from the MSb
sh_next(N-1 downto 1) <= sh_reg(N-2 downto 0); -- shift inner bits
sh_next(0) <= rx_bit_reg; -- shift in rx bit into LSb
when (N-1) downto (PREFETCH+3) =>
di_ready_next <= '0'; -- deassert next-data request when start shifting
do_valid_next <= do_transfer_reg; -- assert valid rx data, with plenty of pipeline delay for 'do_buffer'
do_transfer_next <= '0'; -- reset transfer signal
spi_mosi_o <= sh_reg(N-1); -- shift out tx bit from the MSb
sh_next(N-1 downto 1) <= sh_reg(N-2 downto 0); -- shift inner bits
sh_next(0) <= rx_bit_reg; -- shift in rx bit into LSb
when (PREFETCH+2) downto 2 =>
-- raise prefetch 'di_ready_next' signal and remove 'do_valid'
di_ready_next <= '1'; -- request data in advance to allow for pipeline delays
do_valid_next <= '0'; -- make do_valid_o HIGH for one cycle only
spi_mosi_o <= sh_reg(N-1); -- shift out tx bit from the MSb
sh_next(N-1 downto 1) <= sh_reg(N-2 downto 0); -- shift inner bits
sh_next(0) <= rx_bit_reg; -- shift in rx bit into LSb
when 1 =>
do_buffer_next(N-1 downto 1) <= sh_reg(N-2 downto 0); -- shift rx data directly into rx buffer
do_buffer_next(0) <= rx_bit_reg; -- shift last rx bit into rx buffer
do_transfer_next <= '1'; -- signal transfer to do_buffer
when (N+1) => -- this state is to enable SSEL before SCK
ena_ssel_next <= '1'; -- tx in progress: will assert SSEL
ena_sck_next <= '1'; -- enable SCK on next cycle (stays off on first SSEL clock cycle)
di_rdy_next <= '0'; -- deassert next-data request when shifting data
spi_mosi_o <= sh_reg(N-1); -- shift out tx bit from the MSb
when (N) => -- deassert 'di_rdy'
di_rdy_next <= '0'; -- deassert next-data request when shifting data
spi_mosi_o <= sh_reg(N-1); -- shift out tx bit from the MSb
sh_next(N-1 downto 1) <= sh_reg(N-2 downto 0); -- shift inner bits
sh_next(0) <= rx_bit_reg; -- shift in rx bit into LSb
when (N-1) downto (PREFETCH+3) => -- if rx data is valid, raise 'do_valid'. remove 'do_transfer'
di_rdy_next <= '0'; -- deassert next-data request when start shifting
do_valid_next <= do_transfer_reg; -- assert valid rx data, with plenty of pipeline delay for 'do_buffer'
do_transfer_next <= '0'; -- reset transfer signal
spi_mosi_o <= sh_reg(N-1); -- shift out tx bit from the MSb
sh_next(N-1 downto 1) <= sh_reg(N-2 downto 0); -- shift inner bits
sh_next(0) <= rx_bit_reg; -- shift in rx bit into LSb
when (PREFETCH+2) downto 2 => -- raise prefetch 'di_rdy_next' signal and remove 'do_valid'
di_rdy_next <= '1'; -- request data in advance to allow for pipeline delays
do_valid_next <= '0'; -- make do_valid_o HIGH for one cycle only
spi_mosi_o <= sh_reg(N-1); -- shift out tx bit from the MSb
sh_next(N-1 downto 1) <= sh_reg(N-2 downto 0); -- shift inner bits
sh_next(0) <= rx_bit_reg; -- shift in rx bit into LSb
when 1 => -- transfer rx data to do_buffer and restart if wren
do_buffer_next(N-1 downto 1) <= sh_reg(N-2 downto 0); -- shift rx data directly into rx buffer
do_buffer_next(0) <= rx_bit_reg; -- shift last rx bit into rx buffer
do_transfer_next <= '1'; -- signal transfer to do_buffer
spi_mosi_o <= sh_reg(N-1); -- shift out last tx bit from the MSb
if wren_i = '1' then -- load tx register if valid data present at di_i
state_next <= N; -- next state is top bit of new data
sh_next <= di_reg; -- load parallel data from di_reg into shifter
ena_sck_next <= '1'; -- SCK enabled
if wren_i = '1' then -- load tx register if valid data present at di_i
state_next <= N; -- next state is top bit of new data
sh_next <= di_reg; -- load parallel data from di_reg into shifter
ena_sck_next <= '1'; -- SCK enabled
else
ena_sck_next <= '0'; -- SCK disabled: tx empty, no data to send
ena_sck_next <= '0'; -- SCK disabled: tx empty, no data to send
end if;
when 0 =>
ena_sck_next <= '0'; -- SCK disabled: tx empty, no data to send
di_ready_next <= '1'; -- will request data if shifter empty
do_valid_next <= do_transfer_reg; -- assert valid rx data after data received, when interface idle
if wren_i = '1' then -- load tx register if valid data present at di_i
ena_ssel_next <= '1'; -- enable interface SSEL
state_next <= N+1; -- start from idle: let one cycle for SSEL settling
do_valid_next <= '0'; -- start: clear rx data valid signal
spi_mosi_o <= di_reg(N-1); -- shift out first tx bit from the MSb
sh_next <= di_reg; -- load bits from di_reg into shifter
ena_sck_next <= '0'; -- SCK disabled: tx empty, no data to send
di_rdy_next <= '1'; -- will request data if shifter empty
do_valid_next <= do_transfer_reg; -- assert valid rx data after data received, when interface idle
if wren_i = '1' then -- load tx register if valid data present at di_i
ena_ssel_next <= '1'; -- enable interface SSEL
state_next <= N+1; -- start from idle: let one cycle for SSEL settling
do_valid_next <= '0'; -- start: clear rx data valid signal
spi_mosi_o <= di_reg(N-1); -- shift out first tx bit from the MSb
sh_next <= di_reg; -- load bits from di_reg into shifter
else
ena_ssel_next <= '0'; -- deassert SSEL: interface is idle
state_next <= 0; -- when idle, keep this state
ena_ssel_next <= '0'; -- deassert SSEL: interface is idle
state_next <= 0; -- when idle, keep this state
end if;
when others =>
null;
397,20 → 419,18
-- OUTPUT LOGIC PROCESSES
--=============================================================================================
-- output signal connections
spi_ssel_proc: spi_ssel_o <= not ena_ssel_reg; -- drive active-low slave select line
do_proc : do_o <= do_buffer_reg; -- do_o always available
do_valid_proc: do_valid_o <= do_valid_reg; -- copy registered do_valid_o to output
di_ready_proc: di_rdy_o <= di_ready_reg; -- copy registered di_rdy_o to output
spi_ssel_proc: spi_ssel_o <= not ena_ssel_reg; -- drive active-low slave select line
do_proc : do_o <= do_buffer_reg; -- do_o always available
do_valid_proc: do_valid_o <= do_valid_oreg; -- copy registered do_valid_o to output
di_rdy_proc: di_rdy_o <= di_rdy_oreg; -- copy registered di_rdy_o to output
 
--=============================================================================================
-- DEBUG LOGIC PROCESSES
--=============================================================================================
-- The debug signals are declared in package debug_pkg
-- These processes can be removed from the entity after verification
-- sh_reg_dbg_proc: dbg_shift_m <= conv_integer(sh_reg); -- export sh_reg to debug
-- state_dbg_proc: dbg_state_m <= state_reg; -- export internal state to debug
-- sh_reg_dbg_proc: sh_reg_dbg_o <= sh_reg; -- export sh_reg to debug
-- state_dbg_proc: state_dbg_o <= std_logic_vector(to_unsigned(state_reg, 6)); -- export internal state to debug
do_transfer_proc: do_transfer_o <= do_transfer_reg;
state_dbg_proc: state_dbg_o <= std_logic_vector(to_unsigned(state_reg, 6)); -- export internal state to debug
sh_reg_dbg_proc: sh_reg_dbg_o <= sh_reg; -- export sh_reg to debug
rx_bit_reg_proc: rx_bit_reg_o <= rx_bit_reg;
 
end architecture RTL;
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.