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