Browse Source

add synthesizable configurable SPI slave component and unit test

pull/1/head
T. Meissner 10 years ago
parent
commit
ac5925c717
3 changed files with 356 additions and 1 deletions
  1. +192
    -0
      syn/SpiSlaveE.vhd
  2. +11
    -1
      test/Makefile
  3. +153
    -0
      test/SpiT.vhd

+ 192
- 0
syn/SpiSlaveE.vhd View File

@ -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;

+ 11
- 1
test/Makefile View File

@ -1,8 +1,9 @@
SIM_SRC = ../sim SIM_SRC = ../sim
SYN_SRC = ../syn
VHD_STD = 02 VHD_STD = 02
.PHONY: sim .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 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 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 -e --std=$(VHD_STD) SimT
ghdl -r --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 .PHONY: clean
clean: clean:
rm -f *.o rm -f *.o
rm -f *.cf rm -f *.cf
rm -f *.ghw
rm -f queuet rm -f queuet
rm -f stringt rm -f stringt
rm -f simt rm -f simt
rm -f spit

+ 153
- 0
test/SpiT.vhd View File

@ -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;

Loading…
Cancel
Save