LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.NUMERIC_STD.ALL; use std.textio.all; -- Imports the standard textio package. use work.utils_pkg.all; use work.spi_types.all; ENTITY spi_tx IS Generic ( word_width : natural := 32; CAT_FIFO_DEPTH : natural := 2; DATA_FIFO_DEPTH : natural := 16 ); Port ( rst : in STD_LOGIC; clk : in STD_LOGIC; cmd_vld : in STD_LOGIC; cmd_rdy : out STD_LOGIC; cmd : in cmd_t; din_vld : in STD_LOGIC; din_rdy : out STD_LOGIC; din : in unsigned(word_width-1 downto 0); dout : out unsigned(word_width-1 downto 0); dout_vld : out STD_LOGIC; dout_re : in STD_LOGIC; shift_en : in STD_LOGIC; ctrl : in ctrl_t; status : out status_t; mosi : out STD_LOGIC; miso : in STD_LOGIC; sclk : out STD_LOGIC; mso : out STD_LOGIC ); END spi_tx; ARCHITECTURE behavior OF spi_tx IS type state_t is (reset, idle, load_data, shift, finish); signal xfer_count : unsigned(XFER_MAX_SIZE_BITS-1 downto 0); signal data_count : unsigned(XFER_MAX_SIZE_BITS-1 downto 0); signal cmd_reg : cmd_t; signal state : state_t; signal state_next : state_t; signal xfer_count_en : std_logic; signal xfer_count_busy : std_logic; signal data_count_busy : std_logic; signal spi_clk : std_logic := '0'; signal spi_clk_out : std_logic; signal clk_en : std_logic; signal spi_clk_data : std_logic; signal spi_rst_count : unsigned(5 downto 0); signal spi_rst : std_logic; signal cat_fifo_din : unsigned(to_unsigned(cmd)'length-1 downto 0); signal cat_fifo_dout : unsigned(to_unsigned(cmd)'length-1 downto 0); signal cat_fifo_full : std_logic; signal cat_fifo_empty : std_logic; signal write_fifo_din : unsigned(word_width-1 downto 0); signal write_fifo_dout : unsigned(word_width-1 downto 0); signal write_fifo_full : std_logic; signal write_fifo_empty : std_logic; signal read_fifo_din : unsigned(word_width-1 downto 0); signal read_fifo_dout : unsigned(word_width-1 downto 0); signal read_fifo_full : std_logic; signal read_fifo_empty : std_logic; signal read_fifo_we : std_logic; signal tx_shift_reg : unsigned(word_width-1 downto 0); signal rx_shift_reg : unsigned(word_width-1 downto 0); signal shift_cnt_pipe : unsigned(word_width-1 downto 0); signal data_load_en : std_logic; signal xfer_start_en : std_logic; signal ctrl_reg : ctrl_t; signal rx_enable : std_logic; signal slv_enable : std_logic; -------------------------------------------------------------------------- begin sclk <= spi_clk_out; cat_fifo_din <= to_unsigned(cmd); write_fifo_din <= din; cmd_reg <= to_cmd(cat_fifo_dout); mosi <= tx_shift_reg(tx_shift_reg'left) when ctrl_reg.msb_first = '1' else tx_shift_reg(tx_shift_reg'right); spi_clk_data <= spi_clk xor ctrl_reg.cpha; dout_vld <= not read_fifo_empty; status.cmd_fifo_full <= cat_fifo_full; status.cmd_fifo_empty <= cat_fifo_empty; status.write_fifo_full <= write_fifo_full; status.write_fifo_empty <= write_fifo_empty; status.read_fifo_full <= read_fifo_full; status.read_fifo_empty <= read_fifo_empty; status.rx_valid <= read_fifo_we; mso <= slv_enable; rx_enable <= '1' when cmd_reg.xfer_size > cmd_reg.data_size else '0'; -------------------------------------------------------------------------- -- Instantiate synchronous FIFO inst_cat_fifo: entity work.fifo_async GENERIC MAP ( addr_width => NextExpBaseTwo(CAT_FIFO_DEPTH), data_width => cmd_size ) PORT MAP ( rst => spi_rst, clk_w => clk, clk_r => spi_clk, we => cmd_vld, re => xfer_start_en, fifo_full => cat_fifo_full, fifo_empty => cat_fifo_empty, fifo_afull => open, fifo_aempty => open, data_w => cat_fifo_din, data_r => cat_fifo_dout ); -- Instantiate synchronous FIFO inst_write_fifo: entity work.fifo_async GENERIC MAP ( addr_width => NextExpBaseTwo(DATA_FIFO_DEPTH), data_width => word_width ) PORT MAP ( rst => spi_rst, clk_w => clk, clk_r => spi_clk, we => din_vld, re => data_load_en, fifo_full => write_fifo_full, fifo_empty => write_fifo_empty, fifo_afull => open, fifo_aempty => open, data_w => write_fifo_din, data_r => write_fifo_dout ); -- Instantiate synchronous FIFO inst_read_fifo: entity work.fifo_async GENERIC MAP ( addr_width => NextExpBaseTwo(DATA_FIFO_DEPTH), data_width => word_width ) PORT MAP ( rst => spi_rst, clk_w => clk, clk_r => spi_clk, we => read_fifo_we, re => dout_re, fifo_full => read_fifo_full, fifo_empty => read_fifo_empty, fifo_afull => open, fifo_aempty => open, data_w => rx_shift_reg, data_r => dout ); -------------------------------------------------------------------------- proc_spi_clk_gen: process(clk) begin if rising_edge(clk) then if rst = '1' then spi_rst <= '1'; spi_clk <= '0'; spi_rst_count <= (others => '1'); else spi_clk <= not spi_clk; if spi_rst_count /= to_unsigned(0, spi_rst_count'length) then spi_rst_count <= spi_rst_count - 1; else spi_rst <= '0'; end if; end if ; end if; end process; proc_spi_clk_out_gen: process(clk) begin if rising_edge(clk) then if spi_rst = '1' then spi_clk_out <= '0'; elsif clk_en = '0' then spi_clk_out <= ctrl_reg.cpol; else spi_clk_out <= not spi_clk_out; end if; end if; end process; proc_state_next: process(spi_clk) begin if rising_edge(spi_clk) then if spi_rst = '1' then state <= reset; else state <= state_next; end if; end if; end process; proc_ctrl_reg: process(spi_clk) begin if rising_edge(spi_clk) then if xfer_start_en = '1' or spi_rst = '1' then ctrl_reg <= ctrl; end if; end if; end process; proc_fsm: process(state, rx_enable, cat_fifo_empty, cat_fifo_full, write_fifo_empty, write_fifo_full, xfer_count_busy, data_count_busy, shift_cnt_pipe, ctrl_reg) begin state_next <= state; xfer_start_en <= '0'; xfer_count_en <= '0'; data_load_en <= '0'; clk_en <= '0'; cmd_rdy <= not cat_fifo_full; din_rdy <= not write_fifo_full; slv_enable <= '1'; read_fifo_we <= '0'; case state is when reset => cmd_rdy <= '0'; din_rdy <= '0'; slv_enable <= '0'; state_next <= idle; when idle => slv_enable <= '0'; if cat_fifo_empty = '0' then state_next <= load_data; xfer_start_en <= '1'; end if; when load_data => if xfer_count_busy = '0' then state_next <= idle; elsif write_fifo_empty = '0' then data_load_en <= '1'; state_next <= shift; clk_en <= ctrl_reg.cpha; end if; when shift => clk_en <= '1'; xfer_count_en <= '1'; if xfer_count_busy = '0' then state_next <= finish; elsif shift_cnt_pipe(shift_cnt_pipe'left) = '1' and data_count_busy = '1' then if write_fifo_empty = '0' then data_load_en <= '1'; else state_next <= load_data; end if; end if; when finish => read_fifo_we <= rx_enable; state_next <= idle; when others => state_next <= idle; end case; end process; PROC_XFER_COUNT: process(spi_clk) begin if rising_edge(spi_clk) then if xfer_start_en = '1' then xfer_count <= cmd_reg.xfer_size; xfer_count_busy <= '1'; elsif shift_en = '1' and xfer_count_en = '1' then if xfer_count /= to_unsigned(2, xfer_count'length) then xfer_count <= xfer_count - 1; else xfer_count_busy <= '0'; end if; end if; end if; end process; PROC_SHIFT_COUNT: process(spi_clk) begin if rising_edge(spi_clk) then if xfer_start_en = '1' then shift_cnt_pipe <= (shift_cnt_pipe'left downto 1 => '0') & '1'; elsif shift_en = '1' and xfer_count_en = '1' then if shift_cnt_pipe(shift_cnt_pipe'left) = '0' then shift_cnt_pipe <= shift_cnt_pipe(shift_cnt_pipe'left-1 downto 0) & '0'; else shift_cnt_pipe <= (shift_cnt_pipe'left downto 1 => '0') & '1'; end if; end if; end if; end process; -- Transmitter PROC_DATA_COUNT: process(spi_clk) begin if rising_edge(spi_clk) then if xfer_start_en = '1' then data_count <= cmd_reg.data_size; data_count_busy <= '1'; elsif shift_en = '1' and xfer_count_en = '1' then if data_count /= to_unsigned(2, data_count'length) then data_count <= data_count - 1; else data_count_busy <= '0'; end if; end if; end if; end process; PROC_TX_SHIFT_REG: process(spi_clk_data) begin if rising_edge(spi_clk_data) then if data_load_en = '1' then tx_shift_reg <= write_fifo_dout; elsif shift_en = '1' and xfer_count_en = '1' then if (ctrl_reg.msb_first = '1') then tx_shift_reg <= tx_shift_reg(tx_shift_reg'left-1 downto 0) & '0'; else tx_shift_reg <= '0' & tx_shift_reg(tx_shift_reg'left downto 1); end if; end if; end if; end process; -- Receiver PROC_RX_SHIFT_REG: process(spi_clk_out) begin if rising_edge(spi_clk_out) then if shift_en = '1' and xfer_count_en = '1' then if slv_enable = '1' then if (ctrl_reg.msb_first = '1') then rx_shift_reg <= rx_shift_reg(rx_shift_reg'left-1 downto 0) & miso; else rx_shift_reg <= miso & rx_shift_reg(rx_shift_reg'left downto 1); end if; end if; end if; end if; end process; end behavior;