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_master IS Generic ( word_width : natural := 32; CAT_FIFO_DEPTH : natural := 4; 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_master; ARCHITECTURE behavior OF spi_master IS type state_t is (reset, idle, load_data, shift_write, shift_read, commit, 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; signal spi_clk_out : std_logic; signal clk_en : std_logic; signal spi_clk_write : std_logic; signal spi_clk_read : std_logic; signal spi_rst_count : unsigned(3 downto 0); signal spi_rst : std_logic; signal cat_fifo_din : unsigned(SIZEOF_CMD-1 downto 0); signal cat_fifo_dout : unsigned(SIZEOF_CMD-1 downto 0); signal cat_fifo_full : std_logic; signal cat_fifo_empty : std_logic; signal cat_fifo_re : 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_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 rx_shift_reg2 : unsigned(word_width-1 downto 0); signal word_cnt_pipe : unsigned(word_width-1 downto 0); signal word_cnt_rst : std_logic; signal data_load_en : std_logic; signal xfer_start_en : std_logic; signal ctrl_reg : ctrl_t; signal slv_enable : std_logic; signal spi_clk_div_en : std_logic; signal spi_clk_div_count : unsigned(7 downto 0); -------------------------------------------------------------------------- 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_write <= spi_clk xor ctrl_reg.cpha; spi_clk_read <= spi_clk_out; 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; -------------------------------------------------------------------------- -- Instantiate synchronous FIFO inst_cat_fifo: entity work.fifo_async GENERIC MAP ( addr_width => NextExpBaseTwo(CAT_FIFO_DEPTH), data_width => SIZEOF_CMD ) PORT MAP ( rst => spi_rst, clk_w => clk, clk_r => spi_clk, we => cmd_vld, re => cat_fifo_re, 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 => spi_clk, clk_r => 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_reg2, data_r => dout ); -------------------------------------------------------------------------- proc_spi_clk_divider: process(clk) begin if rising_edge(clk) then spi_clk_div_en <= '0'; if rst = '1' then spi_clk_div_count <= (others => '0'); spi_rst_count <= (others => '1'); spi_rst <= '1'; elsif spi_clk_div_count = ctrl.clk_div then spi_clk_div_count <= (others => '0'); spi_clk_div_en <= '1'; 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; else spi_clk_div_count <= spi_clk_div_count + 1; end if; end if; end process; proc_spi_clk_gen: process(clk) begin if rising_edge(clk) then if rst = '1' then spi_clk <= '0'; elsif spi_clk_div_en = '1' then spi_clk <= not spi_clk; 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 spi_clk_div_en = '1' then if clk_en = '0' then spi_clk_out <= ctrl_reg.cpol; else spi_clk_out <= not spi_clk_out; end if; 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, cat_fifo_empty, cat_fifo_full, write_fifo_empty, write_fifo_full, read_fifo_full, xfer_count_busy, data_count_busy, word_cnt_pipe, ctrl_reg) begin state_next <= state; xfer_start_en <= '0'; xfer_count_en <= '0'; word_cnt_rst <= '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'; cat_fifo_re <= '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'; word_cnt_rst <= '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_write; clk_en <= ctrl_reg.cpha; end if; when shift_write => clk_en <= '1'; xfer_count_en <= '1'; if xfer_count_busy = '0' then state_next <= finish; elsif data_count_busy = '1' then if word_cnt_pipe(word_cnt_pipe'left) = '1' then if data_count_busy = '1' then if write_fifo_empty = '0' then data_load_en <= '1'; else state_next <= load_data; end if; end if; end if; else state_next <= shift_read; word_cnt_rst <= '1'; end if; when shift_read => clk_en <= '1'; xfer_count_en <= '1'; if xfer_count_busy = '0' or word_cnt_pipe(word_cnt_pipe'left) = '1' then state_next <= commit; end if; when commit => clk_en <= not read_fifo_full and xfer_count_busy; xfer_count_en <= not read_fifo_full and xfer_count_busy; read_fifo_we <= '1'; if read_fifo_full = '0' then if xfer_count_busy = '0' then state_next <= finish; else state_next <= shift_read; end if; end if; when finish => cat_fifo_re <= '1'; 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_WORD_COUNT: process(spi_clk) begin if rising_edge(spi_clk) then if word_cnt_rst = '1' then word_cnt_pipe <= (word_cnt_pipe'left downto 1 => '0') & '1'; elsif shift_en = '1' and xfer_count_en = '1' then if word_cnt_pipe(word_cnt_pipe'left) = '0' then word_cnt_pipe <= word_cnt_pipe(word_cnt_pipe'left-1 downto 0) & '0'; else word_cnt_pipe <= (word_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_write) begin if rising_edge(spi_clk_write) 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_read) begin if rising_edge(spi_clk_read) 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; PROC_RX_SHIFT_REG2: process(spi_clk) begin if rising_edge(spi_clk) then rx_shift_reg2 <= rx_shift_reg; end if; end process; end behavior;