diff --git a/README.md b/README.md index ffef499..86b86b3 100644 --- a/README.md +++ b/README.md @@ -25,12 +25,6 @@ Package with various components general useful for simulation * `spi_master()` configurable master for SPI protocol, supports all cpol/cpha modes * `spi_slave()` configurable slave for SPI protocol, supports all cpol/cpha modes -##### StringP -Package with various functions to convert to string - -* `to_char(x)` returns string with binary value of std_logic x -* `to_string(x)` returns string with binary value of std_logic_vector x - ##### QueueP Package with various implementations of queue types: @@ -41,12 +35,12 @@ Package with various implementations of queue types: ## syn Synthesizable components for implementing in FPGA +##### SpiMasterE +Configurable SPI master with support modes 0-3 and simple VAI local backend. + ##### SpiSlaveE Configurable SPI slave with support modes 0-3 and simple VAI local backend. -Implementation results: -* Microsemi SmartFusion2 (speed grade std): 49 logic elements, 397 MHz on -* Xilinx Kintex7 (speed grade -3): 24 slices, 649 MHz on ##test @@ -61,16 +55,26 @@ Unit tests for components of SimP package ##### SpiT Unit tests for SpiSlave component -##### StringT -Unit tests for components of SimP package - ## Dependencies -To run the tests, you have to install GHDL. You can get it from [http://sourceforge.net/projects/ghdl-updates/](http://sourceforge.net/projects/ghdl-updates/). +To run the tests, you have to install GHDL. You can get it from +[http://sourceforge.net/projects/ghdl-updates/](http://sourceforge.net/projects/ghdl-updates/). +Furthermore, you need the VHDL 2008 proposed packages because I'm using various VHDL 2008 features. +You can get the packages from [http://www.eda.org/fphdl/](http://www.eda.org/fphdl/). +Save the following files into the test folder: + +* standard_additions_c.vhd +* standard_textio_additions_c.vhd +* std_logic_1164_additions.vhd +* numeric_std_additions.vhd +* numeric_std_unsigned_c.vhd +* env_c.vhd + +Another useful tool is GTKWave, install it if you want to use the waveform files generated by some of the tests. ## Building -Type `make` and you should see the successfully running tests +Type `make` to do all tests. You should see the successfully running tests like this: ``` $ make diff --git a/sim/AssertP.vhd b/sim/AssertP.vhd index 3cad3fc..a70b8fc 100644 --- a/sim/AssertP.vhd +++ b/sim/AssertP.vhd @@ -1,8 +1,12 @@ library ieee; use ieee.std_logic_1164.all; -library libvhdl; - use libvhdl.StringP.all; +--+ including vhdl 2008 libraries +--+ These lines can be commented out when using +--+ a simulator with built-in VHDL 2008 support +library ieee_proposed; + use ieee_proposed.standard_additions.all; + use ieee_proposed.std_logic_1164_additions.all; @@ -77,7 +81,7 @@ package body AssertP is begin if (str'length = 0) then assert a = b - report integer'image(a) & " should be equal to " & integer'image(b) + report to_string(a) & " should be equal to " & integer'image(b) severity level; else assert a = b @@ -125,7 +129,7 @@ package body AssertP is begin if (str'length = 0) then assert a /= b - report integer'image(a) & " should not be equal to " & integer'image(b) + report to_string(a) & " should not be equal to " & integer'image(b) severity level; else assert a /= b diff --git a/sim/SimP.vhd b/sim/SimP.vhd index 3cf1ab6..e4b0dd2 100644 --- a/sim/SimP.vhd +++ b/sim/SimP.vhd @@ -1,11 +1,19 @@ library ieee; use ieee.std_logic_1164.all; +--+ including vhdl 2008 libraries +--+ These lines can be commented out when using +--+ a simulator with built-in VHDL 2008 support +library ieee_proposed; + use ieee_proposed.standard_additions.all; + use ieee_proposed.std_logic_1164_additions.all; + library libvhdl; use libvhdl.AssertP.all; + package SimP is diff --git a/test/Makefile b/test/Makefile index 8519f5d..010b0f0 100644 --- a/test/Makefile +++ b/test/Makefile @@ -3,7 +3,11 @@ SYN_SRC = ../syn VHD_STD = 02 .PHONY: sim -sim: queuet stringt simt spit +sim: vhdl2008 queuet simt spit + +.PHONY: vhdl2008 +vhdl2008 : env_c.vhd numeric_std_additions.vhd numeric_std_unsigned_c.vhd standard_additions_c.vhd standard_textio_additions_c.vhd std_logic_1164_additions.vhd + ghdl -a --std=$(VHD_STD) --work=ieee_proposed standard_additions_c.vhd standard_textio_additions_c.vhd std_logic_1164_additions.vhd numeric_std_additions.vhd numeric_std_unsigned_c.vhd env_c.vhd 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 @@ -11,13 +15,7 @@ queuet : QueueT.vhd $(SIM_SRC)/QueueP.vhd $(SIM_SRC)/StringP.vhd $(SIM_SRC)/Asse ghdl -e --std=$(VHD_STD) QueueT ghdl -r --std=$(VHD_STD) QueueT -stringt : StringT.vhd $(SIM_SRC)/StringP.vhd $(SIM_SRC)/AssertP.vhd - ghdl -a --std=$(VHD_STD) --work=libvhdl $(SIM_SRC)/StringP.vhd $(SIM_SRC)/AssertP.vhd - ghdl -a --std=$(VHD_STD) StringT.vhd - ghdl -e --std=$(VHD_STD) StringT - ghdl -r --std=$(VHD_STD) StringT - -simt : SimT.vhd $(SIM_SRC)/StringP.vhd $(SIM_SRC)/AssertP.vhd $(SIM_SRC)/SimP.vhd +simt : vhdl2008 SimT.vhd $(SIM_SRC)/StringP.vhd $(SIM_SRC)/AssertP.vhd $(SIM_SRC)/SimP.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) SimT.vhd ghdl -e --std=$(VHD_STD) SimT diff --git a/test/SpiT.vhd b/test/SpiT.vhd index 0a2d0ac..92378ea 100644 --- a/test/SpiT.vhd +++ b/test/SpiT.vhd @@ -3,10 +3,15 @@ library ieee; use ieee.numeric_std.all; library libvhdl; - use libvhdl.StringP.all; use libvhdl.AssertP.all; use libvhdl.SimP.all; +--+ including vhdl 2008 libraries +library ieee_proposed; + use ieee_proposed.standard_additions.all; + use ieee_proposed.std_logic_1164_additions.all; + use ieee_proposed.numeric_std_additions.all; + entity SpiT is @@ -17,6 +22,33 @@ end entity SpiT; architecture sim of SpiT is + component SpiMasterE 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; + G_SCLK_DIVIDER : positive := 10 + ); + port ( + --+ system if + Reset_n_i : in std_logic; + Clk_i : in std_logic; + --+ SPI slave if + SpiSclk_o : out std_logic; + SpiSte_o : out std_logic; + SpiMosi_o : out std_logic; + SpiMiso_i : in 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 SpiMasterE; + + component SpiSlaveE is generic ( G_DATA_WIDTH : positive := 8; @@ -43,86 +75,185 @@ architecture sim of SpiT is end component SpiSlaveE; + --* testbench global clock period constant C_PERIOD : time := 5 ns; + --* SPI data transfer data width constant C_DATA_WIDTH : natural := 8; - signal s_done : boolean := false; - + --* testbench global clock signal s_clk : std_logic := '0'; + --* testbench global reset 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; - + --* SPI mode range subtype subtype t_spi_mode is natural range 0 to 3; - signal s_spi_mode : t_spi_mode; + + --+ test done array with entry for each test + signal s_test_done : boolean_vector(t_spi_mode'low to 2*t_spi_mode'high+1) := (others => false); begin - s_clk <= not(s_clk) after C_PERIOD when not(s_done) else '0'; + --* testbench global clock + s_clk <= not(s_clk) after C_PERIOD/2 when not(and_reduce(s_test_done)) else '0'; + --* testbench global reset 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 + SpiMastersG : for mode in t_spi_mode'low to t_spi_mode'high generate - --+ spi ste demultiplexing - SpiSlavesG : for mode in t_spi_mode'low to t_spi_mode'high generate + signal s_sclk : std_logic; + signal s_ste : std_logic; + signal s_mosi : std_logic; + signal s_miso : std_logic; - subtype t_control_array is std_logic_vector(t_spi_mode'low to t_spi_mode'high); - signal s_spislave_ste : t_control_array; + signal s_din : std_logic_vector(C_DATA_WIDTH-1 downto 0); + signal s_din_valid : std_logic; + signal s_din_accept : std_logic; + signal s_dout : std_logic_vector(C_DATA_WIDTH-1 downto 0); + signal s_dout_valid : std_logic; + signal s_dout_accept : std_logic; - 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 - begin + SpiMasterStimP : process is + begin + wait until s_reset_n = '1'; + for i in 0 to integer'(2**C_DATA_WIDTH-1) loop + s_din <= std_logic_vector(to_unsigned(i, C_DATA_WIDTH)); + s_din_valid <= '1'; + wait until rising_edge(s_clk) and s_din_accept = '1'; + s_din_valid <= '0'; + wait until rising_edge(s_clk); + end loop; + wait; + end process SpiMasterStimP; - s_din(mode) <= std_logic_vector(unsigned(s_dout(mode)) + 1); + i_SpiMasterE : SpiMasterE + generic map ( + G_DATA_WIDTH => 8, + G_SPI_CPOL => mode / 2, + G_SPI_CPHA => mode mod 2, + G_SCLK_DIVIDER => 10 + ) + port map ( + --+ system if + Reset_n_i => s_reset_n, + Clk_i => s_clk, + --+ SPI slave if + SpiSclk_o => s_sclk, + SpiSte_o => s_ste, + SpiMosi_o => s_mosi, + SpiMiso_i => s_miso, + --+ local VAI if + Data_i => s_din, + DataValid_i => s_din_valid, + DataAccept_o => s_din_accept, + Data_o => s_dout, + DataValid_o => s_dout_valid, + DataAccept_i => s_dout_accept + ); - s_spislave_ste(mode) <= s_ste when s_spi_mode = mode else '1'; - i0_SpiSlaveE : SpiSlaveE + SpiMasterCheckP : process is + begin + wait until s_reset_n = '1'; + for i in 0 to integer'(2**C_DATA_WIDTH-1) loop + wait until rising_edge(s_clk) and s_dout_valid = '1'; + s_dout_accept <= '1'; + assert_equal(s_dout, std_logic_vector(to_unsigned(i, C_DATA_WIDTH))); + wait until rising_edge(s_clk); + s_dout_accept <= '0'; + end loop; + report "INFO: SpiMaster (mode=" & to_string(mode) & ") test successfully"; + s_test_done(mode) <= true; + wait; + end process SpiMasterCheckP; + + + -- Unit test of spi slave procedure, checks all combinations + -- of cpol & cpha against spi master procedure + SpiSlaveP : process is + variable v_master_data : std_logic_vector(7 downto 0) := (others => '0'); + begin + for i in 0 to integer'(2**C_DATA_WIDTH-1) loop + spi_slave (data_in => v_master_data, + data_out => v_master_data, + sclk => s_sclk, + ste => s_ste, + mosi => s_mosi, + miso => s_miso, + cpol => mode / 2, + cpha => mode mod 2 + ); + assert_equal(v_master_data, std_logic_vector(to_unsigned(i, C_DATA_WIDTH))); + v_master_data := std_logic_vector(unsigned(v_master_data) + 1); + end loop; + wait; + end process SpiSlaveP; + + + end generate SpiMastersG; + + + + + --+ spi ste demultiplexing + SpiSlavesG : for mode in t_spi_mode'low to t_spi_mode'high generate + + + signal s_sclk : std_logic; + signal s_ste : std_logic; + signal s_mosi : std_logic; + signal s_miso : std_logic; + + signal s_din : std_logic_vector(C_DATA_WIDTH-1 downto 0); + signal s_dout : std_logic_vector(C_DATA_WIDTH-1 downto 0); + signal s_dout_valid : std_logic; + signal s_dout_accept : std_logic; + + + begin + + + -- 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) := (others => '0'); + begin + s_sclk <= '1'; + s_ste <= '1'; + s_mosi <= '1'; + wait until s_reset_n = '1'; + 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 => 100 ns + ); + assert_equal(v_slave_data, std_logic_vector(to_unsigned(i, C_DATA_WIDTH))); + end loop; + report "INFO: SpiSlave (mode=" & to_string(mode) & ") test successfully"; + s_test_done(mode+4) <= true; + wait; + end process SpiMasterP; + + + s_din <= std_logic_vector(unsigned(s_dout) + 1); + + + i_SpiSlaveE : SpiSlaveE generic map ( G_DATA_WIDTH => 8, G_SPI_CPOL => mode / 2, @@ -134,20 +265,21 @@ begin Clk_i => s_clk, --+ SPI slave if SpiSclk_i => s_sclk, - SpiSte_i => s_spislave_ste(mode), + SpiSte_i => s_ste, 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) + Data_i => s_din, + DataValid_i => s_dout_valid, + DataAccept_o => s_dout_accept, + Data_o => s_dout, + DataValid_o => s_dout_valid, + DataAccept_i => s_dout_accept ); - end generate SpiSlavesG; + end generate SpiSlavesG; + end architecture sim;