From c9fc7388c9f7fb7930c3c2309299028a3879e9ba Mon Sep 17 00:00:00 2001 From: tmeissner Date: Mon, 17 Nov 2014 00:08:15 +0100 Subject: [PATCH] add synthesizable configurable SPI slave component and unit test --- README.md | 16 +++- syn/SpiSlaveE.vhd | 192 ++++++++++++++++++++++++++++++++++++++++++++++ test/Makefile | 12 ++- test/SpiT.vhd | 153 ++++++++++++++++++++++++++++++++++++ 4 files changed, 369 insertions(+), 4 deletions(-) create mode 100644 syn/SpiSlaveE.vhd create mode 100644 test/SpiT.vhd diff --git a/README.md b/README.md index 890f82f..d497663 100644 --- a/README.md +++ b/README.md @@ -38,17 +38,27 @@ Package with various implementations of queue types: * `t_list_queue` linked list FIFO queue using access types +## syn +Synthesizable components for implementing in FPGA + +##### SpiSlaveE +Configurable SPI slave with support modes 0-3 and simple VAI local backend + + ##test Unit tests for each component ##### QueueT -Units tests for components of QueueP package +Unit tests for components of QueueP package ##### SimT -Units tests for components of SimP package +Unit tests for components of SimP package + +##### SpiT +Unit tests for SpiSlave component ##### StringT -Units tests for components of SimP package +Unit tests for components of SimP package ## Dependencies diff --git a/syn/SpiSlaveE.vhd b/syn/SpiSlaveE.vhd new file mode 100644 index 0000000..05ce36f --- /dev/null +++ b/syn/SpiSlaveE.vhd @@ -0,0 +1,192 @@ +library ieee; + use ieee.std_logic_1164.all; + + + +entity SpiSlaveE is + generic ( + G_DATA_WIDTH : positive := 8; --* data bus width + G_SPI_CPOL : natural range 0 to 1 := 0; --* SPI clock polarity + G_SPI_CPHA : natural range 0 to 1 := 0 --* SPI clock phase + ); + port ( + --+ system if + Reset_n_i : in std_logic; + Clk_i : in std_logic; + --+ SPI slave if + SpiSclk_i : in std_logic; + SpiSte_i : in std_logic; + SpiMosi_i : in std_logic; + SpiMiso_o : out std_logic; + --+ local VAI if + Data_i : in std_logic_vector(G_DATA_WIDTH-1 downto 0); + DataValid_i : in std_logic; + DataAccept_o : out std_logic; + Data_o : out std_logic_vector(G_DATA_WIDTH-1 downto 0); + DataValid_o : out std_logic; + DataAccept_i : in std_logic + ); +end entity SpiSlaveE; + + + +architecture rtl of SpiSlaveE is + + + type t_spi_state is (IDLE, TRANSFER, STORE); + signal s_spi_state : t_spi_state; + + signal s_send_register : std_logic_vector(G_DATA_WIDTH-1 downto 0); + signal s_recv_register : std_logic_vector(G_DATA_WIDTH-1 downto 0); + + signal s_sclk_d : std_logic_vector(2 downto 0); + signal s_ste_d : std_logic_vector(2 downto 0); + signal s_mosi_d : std_logic_vector(2 downto 0); + + signal s_miso : std_logic; + + signal s_data_valid : std_logic; + signal s_transfer_valid : boolean; + + signal s_sclk_rising : boolean; + signal s_sclk_falling : boolean; + signal s_read_edge : boolean; + signal s_write_edge : boolean; + + alias a_ste : std_logic is s_ste_d(s_ste_d'left); + alias a_mosi : std_logic is s_mosi_d(s_mosi_d'left); + + +begin + + + --* help signals for edge detection on sclk + s_sclk_rising <= true when s_sclk_d(2 downto 1) = "01" else false; + s_sclk_falling <= true when s_sclk_d(2 downto 1) = "10" else false; + + s_read_edge <= s_sclk_rising when G_SPI_CPOL = G_SPI_CPHA else s_sclk_falling; + s_write_edge <= s_sclk_falling when G_SPI_CPOL = G_SPI_CPHA else s_sclk_rising; + + + --* Sync asynchronous SPI inputs with 3 stage FF line + --* We use 3 FF because of edge detection on sclk line + --* Mosi & ste are also registered with 3 FF to stay in + --* sync with registered sclk + SpiSyncP : process (Reset_n_i, Clk_i) is + begin + if (Reset_n_i = '0') then + s_sclk_d <= (others => std_logic'val(G_SPI_CPOL+2)); + s_ste_d <= (others => '1'); + s_mosi_d <= (others => '0'); + elsif rising_edge(Clk_i) then + s_sclk_d <= s_sclk_d(1 downto 0) & SpiSclk_i; + s_ste_d <= s_ste_d(1 downto 0) & SpiSte_i; + s_mosi_d <= s_mosi_d(1 downto 0) & SpiMosi_i; + end if; + end process SpiSyncP; + + + --* Save local data input when new data is provided and + --* we're not inside a running SPI transmission + SendRegisterP : process (Reset_n_i, Clk_i) is + begin + if (Reset_n_i = '0') then + s_send_register <= (others => '0'); + DataAccept_o <= '0'; + elsif rising_edge(Clk_i) then + DataAccept_o <= '0'; + if (DataValid_i = '1' and s_spi_state = IDLE) then + s_send_register <= Data_i; + DataAccept_o <= '1'; + end if; + end if; + end process SendRegisterP; + + + --* Spi slave control FSM + SpiControlP : process (Reset_n_i, Clk_i) is + variable v_bit_counter : natural range 0 to G_DATA_WIDTH-1; + begin + if (Reset_n_i = '0') then + s_miso <= '0'; + s_recv_register <= (others => '0'); + v_bit_counter := G_DATA_WIDTH-1; + s_transfer_valid <= false; + s_spi_state <= IDLE; + elsif rising_edge(Clk_i) then + case s_spi_state is + + when IDLE => + s_miso <= '0'; + s_recv_register <= (others => '0'); + v_bit_counter := G_DATA_WIDTH-1; + s_transfer_valid <= false; + if (a_ste = '0') then + if (G_SPI_CPHA = 0) then + s_miso <= s_send_register(v_bit_counter); + end if; + s_spi_state <= TRANSFER; + end if; + + when TRANSFER => + if s_read_edge then + s_recv_register(v_bit_counter) <= SpiMosi_i; + if (v_bit_counter = 0) then + s_spi_state <= STORE; + else + v_bit_counter := v_bit_counter - 1; + end if; + elsif s_write_edge then + s_miso <= s_send_register(v_bit_counter); + else + if (a_ste = '1') then + s_spi_state <= IDLE; + end if; + end if; + + when STORE => + if (a_ste = '1') then + s_transfer_valid <= true; + s_spi_state <= IDLE; + end if; + + when others => + s_spi_state <= IDLE; + + end case; + end if; + end process SpiControlP; + + + --* Provide received SPI data to local interface + --* Output data is overwritten if it isn't fetched + --* until next finished SPI transmission + RecvRegisterP : process (Reset_n_i, Clk_i) is + begin + if (Reset_n_i = '0') then + Data_o <= (others => '0'); + s_data_valid <= '0'; + elsif rising_edge(Clk_i) then + if (s_transfer_valid) then + Data_o <= s_recv_register; + s_data_valid <= '1'; + end if; + if (DataAccept_i = '1' and s_data_valid = '1') then + s_data_valid <= '0'; + end if; + end if; + end process RecvRegisterP; + + + --+ Output port connections + DataValid_o <= s_data_valid; + SpiMiso_o <= 'Z' when SpiSte_i = '1' else s_miso; + + + -- psl default clock is rising_edge(Clk_i); + -- + -- psl assert always (s_spi_state = IDLE or s_spi_state = TRANSFER or s_spi_state = STORE); + -- psl assert always (s_data_valid and DataAccept_i) -> next not(s_data_valid); + + +end architecture rtl; diff --git a/test/Makefile b/test/Makefile index beb2ba6..a641e2d 100644 --- a/test/Makefile +++ b/test/Makefile @@ -1,8 +1,9 @@ SIM_SRC = ../sim +SYN_SRC = ../syn VHD_STD = 02 .PHONY: sim -sim: queuet stringt simt +sim: queuet stringt simt spit queuet : QueueT.vhd $(SIM_SRC)/QueueP.vhd $(SIM_SRC)/StringP.vhd $(SIM_SRC)/AssertP.vhd ghdl -a --std=$(VHD_STD) --work=libvhdl $(SIM_SRC)/StringP.vhd $(SIM_SRC)/AssertP.vhd $(SIM_SRC)/QueueP.vhd @@ -22,10 +23,19 @@ simt : SimT.vhd $(SIM_SRC)/StringP.vhd $(SIM_SRC)/AssertP.vhd $(SIM_SRC)/SimP.vh ghdl -e --std=$(VHD_STD) SimT ghdl -r --std=$(VHD_STD) SimT +spit : SpiT.vhd $(SIM_SRC)/StringP.vhd $(SIM_SRC)/AssertP.vhd $(SIM_SRC)/SimP.vhd $(SYN_SRC)/SpiSlaveE.vhd + ghdl -a --std=$(VHD_STD) --work=libvhdl $(SIM_SRC)/StringP.vhd $(SIM_SRC)/AssertP.vhd $(SIM_SRC)/SimP.vhd + ghdl -a --std=$(VHD_STD) -fpsl $(SYN_SRC)/SpiSlaveE.vhd + ghdl -a --std=$(VHD_STD) -fpsl SpiT.vhd + ghdl -e --std=$(VHD_STD) SpiT + ghdl -r --std=$(VHD_STD) SpiT --wave=spit.ghw + .PHONY: clean clean: rm -f *.o rm -f *.cf + rm -f *.ghw rm -f queuet rm -f stringt rm -f simt + rm -f spit diff --git a/test/SpiT.vhd b/test/SpiT.vhd new file mode 100644 index 0000000..0a2d0ac --- /dev/null +++ b/test/SpiT.vhd @@ -0,0 +1,153 @@ +library ieee; + use ieee.std_logic_1164.all; + use ieee.numeric_std.all; + +library libvhdl; + use libvhdl.StringP.all; + use libvhdl.AssertP.all; + use libvhdl.SimP.all; + + + +entity SpiT is +end entity SpiT; + + + +architecture sim of SpiT is + + + component SpiSlaveE is + generic ( + G_DATA_WIDTH : positive := 8; + G_SPI_CPOL : natural range 0 to 1 := 0; + G_SPI_CPHA : natural range 0 to 1 := 0 + ); + port ( + --+ system if + Reset_n_i : in std_logic; + Clk_i : in std_logic; + --+ SPI slave if + SpiSclk_i : in std_logic; + SpiSte_i : in std_logic; + SpiMosi_i : in std_logic; + SpiMiso_o : out std_logic; + --+ local VAI if + Data_i : in std_logic_vector(G_DATA_WIDTH-1 downto 0); + DataValid_i : in std_logic; + DataAccept_o : out std_logic; + Data_o : out std_logic_vector(G_DATA_WIDTH-1 downto 0); + DataValid_o : out std_logic; + DataAccept_i : in std_logic + ); + end component SpiSlaveE; + + + constant C_PERIOD : time := 5 ns; + constant C_DATA_WIDTH : natural := 8; + + signal s_done : boolean := false; + + signal s_clk : std_logic := '0'; + signal s_reset_n : std_logic := '0'; + + signal s_sclk : std_logic; + signal s_ste : std_logic; + signal s_mosi : std_logic; + signal s_miso : std_logic; + + subtype t_spi_mode is natural range 0 to 3; + signal s_spi_mode : t_spi_mode; + + +begin + + + s_clk <= not(s_clk) after C_PERIOD when not(s_done) else '0'; + s_reset_n <= '1' after 100 ns; + + + -- Unit test of spi master procedure, checks all combinations + -- of cpol & cpha against spi slave procedure + SpiMasterP : process is + variable v_slave_data : std_logic_vector(C_DATA_WIDTH-1 downto 0); + begin + s_sclk <= '1'; + s_ste <= '1'; + s_mosi <= '1'; + s_spi_mode <= 0; + wait until s_reset_n = '1'; + for mode in 0 to 3 loop + s_spi_mode <= mode; + for i in 0 to integer'(2**C_DATA_WIDTH-1) loop + spi_master (data_in => std_logic_vector(to_unsigned(i, C_DATA_WIDTH)), + data_out => v_slave_data, + sclk => s_sclk, + ste => s_ste, + mosi => s_mosi, + miso => s_miso, + cpol => mode / 2, + cpha => mode mod 2, + period => 1 us + ); + assert_equal(v_slave_data, std_logic_vector(to_unsigned(i, C_DATA_WIDTH))); + end loop; + report "INFO: SPI mode " & integer'image(mode) & " test successfully"; + end loop; + report "INFO: SpiSlaveE tests finished successfully"; + s_done <= true; + wait; + end process SpiMasterP; + + + --+ spi ste demultiplexing + SpiSlavesG : for mode in t_spi_mode'low to t_spi_mode'high generate + + + subtype t_control_array is std_logic_vector(t_spi_mode'low to t_spi_mode'high); + signal s_spislave_ste : t_control_array; + + type t_data_array is array (t_spi_mode'low to t_spi_mode'high) of std_logic_vector(C_DATA_WIDTH-1 downto 0); + + signal s_din : t_data_array; + signal s_dout : t_data_array; + signal s_dout_valid : t_control_array; + signal s_dout_accept : t_control_array; + + + begin + + + s_din(mode) <= std_logic_vector(unsigned(s_dout(mode)) + 1); + + s_spislave_ste(mode) <= s_ste when s_spi_mode = mode else '1'; + + i0_SpiSlaveE : SpiSlaveE + generic map ( + G_DATA_WIDTH => 8, + G_SPI_CPOL => mode / 2, + G_SPI_CPHA => mode mod 2 + ) + port map ( + --+ system if + Reset_n_i => s_reset_n, + Clk_i => s_clk, + --+ SPI slave if + SpiSclk_i => s_sclk, + SpiSte_i => s_spislave_ste(mode), + SpiMosi_i => s_mosi, + SpiMiso_o => s_miso, + --+ local VAI if + Data_i => s_din(mode), + DataValid_i => s_dout_valid(mode), + DataAccept_o => s_dout_accept(mode), + Data_o => s_dout(mode), + DataValid_o => s_dout_valid(mode), + DataAccept_i => s_dout_accept(mode) + ); + + + end generate SpiSlavesG; + + +end architecture sim;