diff --git a/syn/SpiMasterE.vhd b/syn/SpiMasterE.vhd new file mode 100644 index 0000000..7e2666d --- /dev/null +++ b/syn/SpiMasterE.vhd @@ -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; \ No newline at end of file diff --git a/test/Makefile b/test/Makefile index a641e2d..8519f5d 100644 --- a/test/Makefile +++ b/test/Makefile @@ -23,9 +23,9 @@ 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 +spit : vhdl2008 SpiT.vhd $(SIM_SRC)/StringP.vhd $(SIM_SRC)/AssertP.vhd $(SIM_SRC)/SimP.vhd $(SYN_SRC)/SpiSlaveE.vhd $(SYN_SRC)/SpiMasterE.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 $(SYN_SRC)/SpiSlaveE.vhd $(SYN_SRC)/SpiMasterE.vhd ghdl -a --std=$(VHD_STD) -fpsl SpiT.vhd ghdl -e --std=$(VHD_STD) SpiT ghdl -r --std=$(VHD_STD) SpiT --wave=spit.ghw