From 88802885bee07d584c0d74a579f3118d7dd59b02 Mon Sep 17 00:00:00 2001 From: Rutherther Date: Sun, 29 Dec 2024 13:38:22 +0100 Subject: [PATCH] feat: implement spi memory mapped peripheral --- hdl_spi/src/spi_peripheral.vhd | 220 +++++++++++++++++++++++++++------ 1 file changed, 181 insertions(+), 39 deletions(-) diff --git a/hdl_spi/src/spi_peripheral.vhd b/hdl_spi/src/spi_peripheral.vhd index 722a62f..90e3926 100644 --- a/hdl_spi/src/spi_peripheral.vhd +++ b/hdl_spi/src/spi_peripheral.vhd @@ -13,63 +13,186 @@ entity spi_peripheral is ); port ( - address_i : in std_logic_vector(2 downto 0); - data_i : in std_logic_vector(31 downto 0); - data_o : out std_logic_vector(31 downto 0); - write_i : in std_logic; - read_i : in std_logic; - + waddress_i : in std_logic_vector(2 downto 0); + wdata_i : in std_logic_vector(31 downto 0); + raddress_i : in std_logic_vector(2 downto 0); + rdata_o : out std_logic_vector(31 downto 0); + write_i : in std_logic; + read_i : in std_logic; -- IOs - sck_o : out std_logic; - miso_i : in std_logic; - mosi_o : out std_logic; - csn_o : out std_logic; - + sck_io : inout std_logic; + miso_io : inout std_logic; + mosi_io : inout std_logic; + csn_io : inout std_logic; -- Control - clk_i : in std_logic; - rst_in : in std_logic; - rx_data_o : out std_logic_vector(get_max_natural(SIZES) - 1 downto 0); - rx_valid_o : out std_logic; - tx_ready_o : out std_logic; - busy_o : out std_logic; - err_lost_rx_data_o : out std_logic); + clk_i : in std_logic; + rst_in : in std_logic; + interrupt_o : out std_logic + -- rx_valid_o : out std_logic; + -- tx_ready_o : out std_logic; + -- busy_o : out std_logic; + -- err_lost_rx_data_o : out std_logic + ); end entity spi_peripheral; architecture a1 of spi_peripheral is constant MAX_SIZE : natural := get_max_natural(SIZES); - constant CTRL_ADDRESS : std_logic_vector(2 downto 0) := "000"; - constant STATUS_ADDRESS : std_logic_vector(2 downto 0) := "010"; - constant DATA_ADDRESS : std_logic_vector(2 downto 0) := "011"; + constant CTRL_ADDRESS : std_logic_vector(2 downto 0) := "000"; + constant INTMASK_ADDRESS : std_logic_vector(2 downto 0) := "001"; + constant STATUS_ADDRESS : std_logic_vector(2 downto 0) := "010"; + constant DATA_ADDRESS : std_logic_vector(2 downto 0) := "011"; + + constant CTRL_EN_BIT : natural := 0; + constant CTRL_MASTER_BIT : natural := 1; + constant CTRL_TX_EN_BIT : natural := 2; + constant CTRL_RX_EN_BIT : natural := 3; + constant CTRL_CLOCK_POLARITY_BIT : natural := 4; + constant CTRL_CLOCK_PHASE_BIT : natural := 5; + constant CTRL_PULSE_CSN_BIT : natural := 6; + constant CTRL_LSBFIRST_BIT : natural := 7; + -- constant CTRL_RX_BLOCK_ON_FULL_BIT : natural := 8; + constant CTRL_SIZE_SEL_BIT : natural := 10; + constant CTRL_DIV_SEL_BIT : natural := 20; - signal rx_buffer : std_logic_vector(MAX_SIZE - 1 downto 0); + constant INTMASK_RX_BUFFER_FULL : natural := 0; + constant INTMASK_TX_BUFFER_EMPTY : natural := 1; + constant INTMASK_LOST_RX_DATA : natural := 4; + + constant CTRL_DEFAULT : std_logic_vector(31 downto 0) + := (CTRL_TX_EN_BIT => '1', + CTRL_RX_EN_BIT => '1', + others => '0'); + constant INTMASK_DEFAULT : std_logic_vector(31 downto 0) := (others => '0'); + + signal rx_buffer : std_logic_vector(MAX_SIZE - 1 downto 0); signal rx_buffer_full : std_logic; - signal tx_buffer : std_logic_vector(MAX_SIZE - 1 downto 0); + signal tx_buffer : std_logic_vector(MAX_SIZE - 1 downto 0); signal tx_buffer_full : std_logic; - signal rx_ready : std_logic; - signal rx_valid : std_logic; - signal tx_valid : std_logic; - signal tx_ready : std_logic; - signal tx_data : std_logic_vector(MAX_SIZE - 1 downto 0); - signal rx_data : std_logic_vector(MAX_SIZE - 1 downto 0); - signal rx_data_lost : std_logic; - signal busy : std_logic; + signal fill_tx_buffer : std_logic; signal reg_control : std_logic_vector(31 downto 0); - signal reg_status : std_logic_vector(31 downto 0); + signal reg_status : std_logic_vector(31 downto 0); + signal reg_intmask : std_logic_vector(31 downto 0); signal clear_rx_buffer_full : std_logic; + + signal en : std_logic; + signal master : std_logic; + signal clock_polarity : std_logic; + signal clock_phase : std_logic; + signal size_sel : std_logic_vector(SIZES_2LOG - 1 downto 0); + signal div_sel : std_logic_vector(DIVISORS_LOG2 - 1 downto 0); + signal pulse_csn : std_logic; + signal rx_block_on_full : std_logic; + signal lsbfirst : std_logic; + -- Rx + signal rx_en : std_logic; + signal rx_data : std_logic_vector(MAX_SIZE - 1 downto 0); + signal rx_valid : std_logic; + signal rx_ready : std_logic; + -- Tx + signal tx_en : std_logic; + signal tx_data : std_logic_vector(MAX_SIZE - 1 downto 0); + signal tx_valid : std_logic; + signal tx_ready : std_logic; + -- State + signal busy : std_logic; + signal err_lost_rx_data : std_logic; + signal clear_lost_rx_data : std_logic; begin -- architecture a1 + interrupt_o <= (rx_buffer_full and reg_intmask(INTMASK_RX_BUFFER_FULL)) or + (not tx_buffer_full and reg_intmask(INTMASK_TX_BUFFER_EMPTY)) or + (err_lost_rx_data and reg_intmask(INTMASK_LOST_RX_DATA)); + + en <= reg_control(CTRL_EN_BIT); + master <= reg_control(CTRL_MASTER_BIT); + clock_polarity <= reg_control(CTRL_CLOCK_POLARITY_BIT); + clock_phase <= reg_control(CTRL_CLOCK_PHASE_BIT); + pulse_csn <= reg_control(CTRL_PULSE_CSN_BIT); + size_sel <= reg_control(CTRL_SIZE_SEL_BIT + SIZES_2LOG - 1 downto CTRL_SIZE_SEL_BIT); + div_sel <= reg_control(CTRL_DIV_SEL_BIT + DIVISORS_LOG2 - 1 downto CTRL_DIV_SEL_BIT); + lsbfirst <= reg_control(CTRL_LSBFIRST_BIT); + + tx_en <= reg_control(CTRL_TX_EN_BIT); + rx_en <= reg_control(CTRL_RX_EN_BIT); + + rx_block_on_full <= '0'; + + masterslave : entity work.spi_masterslave + generic map ( + DIVISORS => DIVISORS, + DIVISORS_LOG2 => DIVISORS_LOG2, + SIZES => SIZES, + SIZES_2LOG => SIZES_2LOG, + CSN_PULSE_CYCLES => CSN_PULSE_CYCLES) + port map ( + -- IOs + sck_io => sck_io, + miso_io => miso_io, + mosi_io => mosi_io, + csn_io => csn_io, + -- Control + clk_i => clk_i, + rst_in => rst_in, + en_i => en, + master_i => master, + clock_polarity_i => clock_polarity, + clock_phase_i => clock_phase, + size_sel_i => size_sel, + div_sel_i => div_sel, + pulse_csn_i => pulse_csn, + rx_block_on_full_i => rx_block_on_full, + lsbfirst_i => lsbfirst, + -- Data + -- Rx + rx_en_i => rx_en, + rx_data_o => rx_data, + rx_valid_o => rx_valid, + rx_ready_i => rx_ready, + -- Tx + tx_en_i => tx_en, + tx_data_i => tx_data, + tx_valid_i => tx_valid, + tx_ready_o => tx_ready, + -- State + busy_o => busy, + err_lost_rx_data_o => err_lost_rx_data, + clear_lost_rx_data_i => clear_lost_rx_data); + + writer: process (clk_i) is + begin -- process writer + if rising_edge(clk_i) then -- rising clock edge + if rst_in = '0' then -- synchronous reset (active low) + reg_control <= CTRL_DEFAULT; + reg_intmask <= INTMASK_DEFAULT; + fill_tx_buffer <= '0'; + elsif write_i = '1' then + case waddress_i is + when CTRL_ADDRESS => reg_control <= wdata_i; + when INTMASK_ADDRESS => reg_intmask <= wdata_i; + when DATA_ADDRESS => fill_tx_buffer <= '1'; + when STATUS_ADDRESS => + clear_lost_rx_data <= wdata_i(4); + when others => null; + end case; + else + fill_tx_buffer <= '0'; + clear_lost_rx_data <= '0'; + end if; + end if; + end process writer; + reader: process (clk_i) is begin -- process reader if rising_edge(clk_i) then -- rising clock edge if rst_in = '0' then -- synchronous reset (active low) - + clear_rx_buffer_full <= '0'; elsif read_i = '1' then - case address_i is + case raddress_i is when DATA_ADDRESS => clear_rx_buffer_full <= '1'; when others => null; end case; @@ -81,14 +204,33 @@ begin -- architecture a1 output_register: process (all) is begin -- process output_register - case address_i is - when CTRL_ADDRESS => data_o(reg_control'range) <= reg_control(reg_control'range); - when STATUS_ADDRESS => data_o <= reg_status; - when DATA_ADDRESS => data_o <= rx_buffer; + case raddress_i is + when CTRL_ADDRESS => rdata_o <= reg_control; + when STATUS_ADDRESS => rdata_o <= reg_status; + when DATA_ADDRESS => rdata_o <= (rx_buffer'range => rx_buffer, others => '0'); + when INTMASK_ADDRESS => rdata_o <= reg_intmask; when others => null; end case; end process output_register; + tx_buffer_writer: process (clk_i) is + begin -- process tx_buffer_writer + if rising_edge(clk_i) then -- rising clock edge + if rst_in = '0' then -- synchronous reset (active low) + tx_buffer <= (others => '0'); + tx_buffer_full <= '0'; + else + if fill_tx_buffer = '1' and tx_buffer_full = '0' then + tx_buffer_full <= '1'; + tx_buffer <= wdata_i(tx_buffer'range); + tx_buffer_full <= '1'; + elsif tx_ready = '1' and tx_buffer_full = '1' then + tx_buffer_full <= '0'; + end if; + end if; + end if; + end process tx_buffer_writer; + rx_buffer_reader: process (clk_i) is begin -- process rx_buffer_full if rising_edge(clk_i) then -- rising clock edge @@ -108,7 +250,7 @@ begin -- architecture a1 end process rx_buffer_reader; reg_status <= "000000000000000000000000000" & - rx_data_lost & + err_lost_rx_data & busy & "0" & tx_buffer_full & -- 2.48.1