|
|
@ -0,0 +1,232 @@ |
|
|
|
library ieee; |
|
|
|
use ieee.std_logic_1164.all; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
entity SpiMasterE 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 |
|
|
|
G_SCLK_DIVIDER : positive := 10 --* SCLK divider related to system clock |
|
|
|
); |
|
|
|
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 entity SpiMasterE; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
architecture rtl of SpiMasterE is |
|
|
|
|
|
|
|
|
|
|
|
type t_spi_state is (IDLE, WRITE, READ, CYCLE, STORE, SET_STE); |
|
|
|
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_miso_d : std_logic_vector(1 downto 0); |
|
|
|
|
|
|
|
signal s_mosi : std_logic; |
|
|
|
signal s_sclk : std_logic; |
|
|
|
signal s_ste : std_logic; |
|
|
|
|
|
|
|
signal s_data_valid : std_logic; |
|
|
|
signal s_data_accept : 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_miso : std_logic is s_miso_d(s_miso_d'left); |
|
|
|
|
|
|
|
|
|
|
|
begin |
|
|
|
|
|
|
|
|
|
|
|
--* Sync asynchronous SPI inputs with 2 stage FF line |
|
|
|
SpiSyncP : process (Reset_n_i, Clk_i) is |
|
|
|
begin |
|
|
|
if (Reset_n_i = '0') then |
|
|
|
s_miso_d <= (others => '0'); |
|
|
|
elsif rising_edge(Clk_i) then |
|
|
|
s_miso_d <= s_miso_d(0) & SpiMiso_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'); |
|
|
|
s_data_accept <= '0'; |
|
|
|
elsif rising_edge(Clk_i) then |
|
|
|
s_data_accept <= '0'; |
|
|
|
if (DataValid_i = '1' and s_spi_state = IDLE) then |
|
|
|
s_send_register <= Data_i; |
|
|
|
s_data_accept <= '1'; |
|
|
|
end if; |
|
|
|
end if; |
|
|
|
end process SendRegisterP; |
|
|
|
|
|
|
|
|
|
|
|
--* Spi master control FSM |
|
|
|
SpiControlP : process (Reset_n_i, Clk_i) is |
|
|
|
variable v_bit_counter : natural range 0 to G_DATA_WIDTH-1; |
|
|
|
variable v_sclk_counter : natural range 0 to G_SCLK_DIVIDER-1; |
|
|
|
begin |
|
|
|
if (Reset_n_i = '0') then |
|
|
|
s_recv_register <= (others => '0'); |
|
|
|
v_bit_counter := G_DATA_WIDTH-1; |
|
|
|
v_sclk_counter := G_SCLK_DIVIDER-1; |
|
|
|
s_transfer_valid <= false; |
|
|
|
s_ste <= '1'; |
|
|
|
s_sclk <= std_logic'val(G_SPI_CPOL+2); |
|
|
|
s_mosi <= '1'; |
|
|
|
s_spi_state <= IDLE; |
|
|
|
elsif rising_edge(Clk_i) then |
|
|
|
case s_spi_state is |
|
|
|
|
|
|
|
when IDLE => |
|
|
|
s_ste <= '1'; |
|
|
|
s_sclk <= std_logic'val(G_SPI_CPOL+2); |
|
|
|
s_mosi <= '0'; |
|
|
|
s_recv_register <= (others => '0'); |
|
|
|
v_bit_counter := G_DATA_WIDTH-1; |
|
|
|
v_sclk_counter := G_SCLK_DIVIDER/2-1; |
|
|
|
s_transfer_valid <= false; |
|
|
|
if(DataValid_i = '1' and s_data_accept = '1') then |
|
|
|
s_ste <= '0'; |
|
|
|
s_spi_state <= WRITE; |
|
|
|
end if; |
|
|
|
|
|
|
|
when WRITE => |
|
|
|
if (G_SPI_CPHA = 0 and v_bit_counter = G_DATA_WIDTH-1) then |
|
|
|
s_mosi <= s_send_register(v_bit_counter); |
|
|
|
s_spi_state <= READ; |
|
|
|
else |
|
|
|
if (v_sclk_counter = 0) then |
|
|
|
v_sclk_counter := G_SCLK_DIVIDER/2-1; |
|
|
|
s_sclk <= not(s_sclk); |
|
|
|
s_mosi <= s_send_register(v_bit_counter); |
|
|
|
s_spi_state <= READ; |
|
|
|
else |
|
|
|
v_sclk_counter := v_sclk_counter - 1; |
|
|
|
end if; |
|
|
|
end if; |
|
|
|
|
|
|
|
when READ => |
|
|
|
if (v_sclk_counter = 0) then |
|
|
|
s_sclk <= not(s_sclk); |
|
|
|
s_recv_register(v_bit_counter) <= a_miso; |
|
|
|
v_sclk_counter := G_SCLK_DIVIDER/2-1; |
|
|
|
if (v_bit_counter = 0) then |
|
|
|
if (G_SPI_CPHA = 0) then |
|
|
|
s_spi_state <= CYCLE; |
|
|
|
else |
|
|
|
s_spi_state <= STORE; |
|
|
|
end if; |
|
|
|
else |
|
|
|
v_bit_counter := v_bit_counter - 1; |
|
|
|
s_spi_state <= WRITE; |
|
|
|
end if; |
|
|
|
else |
|
|
|
v_sclk_counter := v_sclk_counter - 1; |
|
|
|
end if; |
|
|
|
|
|
|
|
when CYCLE => |
|
|
|
if (v_sclk_counter = 0) then |
|
|
|
s_sclk <= not(s_sclk); |
|
|
|
v_sclk_counter := G_SCLK_DIVIDER/2-1; |
|
|
|
s_spi_state <= STORE; |
|
|
|
else |
|
|
|
v_sclk_counter := v_sclk_counter - 1; |
|
|
|
end if; |
|
|
|
|
|
|
|
when STORE => |
|
|
|
if (v_sclk_counter = 0) then |
|
|
|
s_transfer_valid <= true; |
|
|
|
v_sclk_counter := G_SCLK_DIVIDER/2-1; |
|
|
|
s_spi_state <= SET_STE; |
|
|
|
else |
|
|
|
v_sclk_counter := v_sclk_counter - 1; |
|
|
|
end if; |
|
|
|
|
|
|
|
when SET_STE => |
|
|
|
s_transfer_valid <= false; |
|
|
|
s_ste <= '1'; |
|
|
|
if (v_sclk_counter = 0) then |
|
|
|
s_spi_state <= IDLE; |
|
|
|
else |
|
|
|
v_sclk_counter := v_sclk_counter - 1; |
|
|
|
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; |
|
|
|
DataAccept_o <= s_data_accept; |
|
|
|
SpiSte_o <= s_ste; |
|
|
|
SpiSclk_o <= s_sclk; |
|
|
|
SpiMosi_o <= s_mosi; |
|
|
|
|
|
|
|
|
|
|
|
assert G_SCLK_DIVIDER rem 2 = 0 |
|
|
|
report "WARNING: " & SpiMasterE'instance_name & LF & "G_SCLK_DIVIDER rounded down to next even value" |
|
|
|
severity warning; |
|
|
|
|
|
|
|
|
|
|
|
-- psl default clock is rising_edge(Clk_i); |
|
|
|
-- |
|
|
|
-- psl assert always (s_spi_state = IDLE or s_spi_state = WRITE or s_spi_state = READ or |
|
|
|
-- s_spi_state = CYCLE or s_spi_state = SET_STE or s_spi_state = STORE); |
|
|
|
-- psl assert always (s_data_valid and DataAccept_i) -> next not(s_data_valid); |
|
|
|
|
|
|
|
|
|
|
|
end architecture rtl; |