From d990ecaaf7335612b4f8f2d3ed8e1bb1407a4237 Mon Sep 17 00:00:00 2001 From: Rutherther Date: Wed, 25 Dec 2024 17:04:18 +0100 Subject: [PATCH] feat: implement masterslave spi switch peripheral --- hdl_spi/src/spi_clkmon.vhd | 51 ++++++ hdl_spi/src/spi_master.vhd | 2 +- hdl_spi/src/spi_master_ctrl.vhd | 18 +- hdl_spi/src/spi_masterslave.vhd | 308 ++++++++++++++++++++++++++++++++ hdl_spi/src/spi_multiplexor.vhd | 63 +++++++ hdl_spi/src/spi_peripheral.vhd | 122 +++++++++++++ hdl_spi/src/spi_slave_ctrl.vhd | 48 +++++ 7 files changed, 609 insertions(+), 3 deletions(-) create mode 100644 hdl_spi/src/spi_clkmon.vhd create mode 100644 hdl_spi/src/spi_masterslave.vhd create mode 100644 hdl_spi/src/spi_multiplexor.vhd create mode 100644 hdl_spi/src/spi_peripheral.vhd create mode 100644 hdl_spi/src/spi_slave_ctrl.vhd diff --git a/hdl_spi/src/spi_clkmon.vhd b/hdl_spi/src/spi_clkmon.vhd new file mode 100644 index 0000000..cd52ded --- /dev/null +++ b/hdl_spi/src/spi_clkmon.vhd @@ -0,0 +1,51 @@ +library ieee; +use ieee.std_logic_1164.all; + +entity spi_clkmon is + + port ( + clk_i : in std_logic; + rst_in : in std_logic; + sck_i : in std_logic; + csn_i : in std_logic; + clock_polarity_i : in std_logic; + clock_phase_i : in std_logic; + clock_rising_o : out std_logic; + sample_data_o : out std_logic; + change_data_o : out std_logic); + +end entity spi_clkmon; + +architecture a1 of spi_clkmon is + signal changing : std_logic; + + signal sample_data : std_logic; + signal change_data : std_logic; + + signal curr_sck : std_logic; + signal next_sck : std_logic; + +begin -- architecture a1 + + set_curr_sck: process (clk_i) is + begin -- process set_curr_sck + if rising_edge(clk_i) then -- rising clock edge + if rst_in = '0' or csn_i = '1' then -- synchronous reset (active low) + curr_sck <= '0'; + else + curr_sck <= next_sck; + end if; + end if; + end process set_curr_sck; + + next_sck <= sck_i when clock_polarity_i = '0' else not sck_i; + + changing <= '1' when next_sck /= curr_sck else '0'; + sample_data <= '1' when curr_sck = clock_phase_i and changing = '1' else '0'; + change_data <= '1' when curr_sck /= clock_phase_i and changing = '1' else '0'; + + clock_rising_o <= sample_data; + sample_data_o <= sample_data; + change_data_o <= change_data; + +end architecture a1; diff --git a/hdl_spi/src/spi_master.vhd b/hdl_spi/src/spi_master.vhd index 7af9a4b..08bf9fa 100644 --- a/hdl_spi/src/spi_master.vhd +++ b/hdl_spi/src/spi_master.vhd @@ -134,7 +134,7 @@ begin -- architecture a1 SIZE => MAX_SIZE) port map ( clk_i => clk_i, - rst_in => rst_in, + rst_in => rst_n, -- Control shift_i => latch_sample_data, -- sampling latch_i => latch_new_tx_data, -- latching tx data diff --git a/hdl_spi/src/spi_master_ctrl.vhd b/hdl_spi/src/spi_master_ctrl.vhd index 6ede053..a54eb31 100644 --- a/hdl_spi/src/spi_master_ctrl.vhd +++ b/hdl_spi/src/spi_master_ctrl.vhd @@ -169,6 +169,10 @@ begin -- architecture a1 when others => next_state <= RESET; end case; + + if en_i = '0' then + next_state <= RESET; + end if; end process state; tx_state: process(all) is @@ -210,6 +214,11 @@ begin -- architecture a1 if curr_state = RESET then next_tx_state <= IDLE; end if; + + if tx_en_i = '0' then + next_tx_state <= IDLE; + tx_got_data <= not rx_block; -- simulate always receiving new data + end if; end process tx_state; rx_state: process(all) is @@ -248,6 +257,11 @@ begin -- architecture a1 if curr_state = RESET then next_rx_state <= IDLE; end if; + + if rx_en_i = '0' then + next_rx_state <= IDLE; + rx_block <= '0'; -- do not block if disabled + end if; end process rx_state; error_rx_lost : entity work.rs_latch @@ -260,8 +274,8 @@ begin -- architecture a1 shifting_length <= SIZES(to_integer(unsigned(size_sel_i))); zero <= '1' when curr_counter = 0 else '0'; - -- Enable IOs - miso_en_o <= en_i and rx_en_i; + -- Enable Outputs + miso_en_o <= '0'; sck_en_o <= en_i; mosi_en_o <= en_i and tx_en_i; csn_en_o <= en_i; diff --git a/hdl_spi/src/spi_masterslave.vhd b/hdl_spi/src/spi_masterslave.vhd new file mode 100644 index 0000000..912f39c --- /dev/null +++ b/hdl_spi/src/spi_masterslave.vhd @@ -0,0 +1,308 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +use work.spi_pkg.all; + +entity spi_masterslave is + + generic ( + SIZES : natural_vector := (8, 16); + SIZES_2LOG : natural := 1; + DIVISORS : natural_vector := (2, 4, 6, 8, 16, 32, 64, 128, 256); + DIVISORS_LOG2 : natural := 3; + CSN_PULSE_CYCLES : natural := 1 + ); + + port ( + -- IOs + 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; + en_i : in std_logic; + master_i : in std_logic; + clock_polarity_i : in std_logic; + clock_phase_i : in std_logic; + size_sel_i : in std_logic_vector(SIZES_2LOG - 1 downto 0); + div_sel_i : in std_logic_vector(DIVISORS_LOG2 - 1 downto 0); + pulse_csn_i : in std_logic; + rx_block_on_full_i : in std_logic; + -- Data + -- Rx + rx_en_i : in std_logic; + rx_data_o : out std_logic_vector(get_max_natural(SIZES) - 1 downto 0); + rx_valid_o : out std_logic; + rx_ready_i : out std_logic; + -- Tx + tx_en_i : in std_logic; + tx_data_i : in std_logic_vector(get_max_natural(SIZES) - 1 downto 0); + tx_valid_i : out std_logic; + tx_ready_o : out std_logic; + -- State + busy_o : out std_logic; + err_lost_rx_data_o : out std_logic; + clear_lost_rx_data_i : in std_logic); + +end entity spi_masterslave; + +architecture a1 of spi_masterslave is + constant MAX_SIZE : natural := get_max_natural(SIZES); + + signal rst_n : std_logic; + + signal ctrl_rst_n : std_logic; + signal master_ctrl_rst_n : std_logic; + signal slave_ctrl_rst_n : std_logic; + + signal latch_sample_data : std_logic; + signal latch_change_data_out : std_logic; + signal latch_new_tx_data : std_logic; + + signal tx_input_data : std_logic; + signal tx_serial_data : std_logic_vector(0 downto 0); + signal rx_serial_data : std_logic; + + signal start_clock : std_logic; + + signal selected_size : natural; + + signal master_en : std_logic; + signal slave_en : std_logic; + + signal slave_clock_rising : std_logic; + signal slave_rx_valid : std_logic; + signal slave_tx_ready : std_logic; + signal slave_busy : std_logic; + signal slave_err_lost_rx_data : std_logic; + signal slave_ctrl_rst : std_logic; + signal slave_csn_en : std_logic; + signal slave_mosi_en : std_logic; + signal slave_miso_en : std_logic; + signal slave_sck_mask : std_logic; + signal slave_sck_en : std_logic; + signal slave_start_clock : std_logic; + signal slave_latch_new_tx_data : std_logic; + signal slave_latch_sample_data : std_logic; + signal slave_latch_change_data : std_logic; + + signal master_sck : std_logic; + signal master_clock_rising : std_logic; + signal master_rx_valid : std_logic; + signal master_tx_ready : std_logic; + signal master_busy : std_logic; + signal master_err_lost_rx_data : std_logic; + signal master_ctrl_rst : std_logic; + signal master_csn : std_logic; + signal master_csn_en : std_logic; + signal master_mosi_en : std_logic; + signal master_miso_en : std_logic; + signal master_sck_mask : std_logic; + signal master_sck_en : std_logic; + signal master_start_clock : std_logic; + signal master_latch_new_tx_data : std_logic; + signal master_latch_sample_data : std_logic; + signal master_latch_change_data : std_logic; + +begin -- architecture a1 + master_en <= en_i and master_i; + slave_en <= en_i and not master_i; + + rst_n <= rst_in and ctrl_rst_n; + selected_size <= SIZES(to_integer(unsigned(size_sel_i))); + + master_ctrl : entity work.spi_master_ctrl + generic map ( + SIZES => SIZES, + SIZES_2LOG => SIZES_2LOG, + CSN_PULSE_CYCLES => CSN_PULSE_CYCLES) + port map ( + clk_i => clk_i, + rst_in => rst_in, + en_i => master_en, + size_sel_i => size_sel_i, + pulse_csn_i => pulse_csn_i, + rx_block_on_full_i => rx_block_on_full_i, + rx_en_i => rx_en_i, + rx_ready_i => rx_ready_i, + tx_en_i => tx_en_i, + tx_valid_i => tx_valid_i, + clear_lost_rx_data_i => clear_lost_rx_data_i, + + clock_rising_i => master_clock_rising, + rx_valid_o => master_rx_valid, + tx_ready_o => master_tx_ready, + busy_o => master_busy, + err_lost_rx_data_o => master_err_lost_rx_data, + rst_on => master_ctrl_rst, + csn_o => master_csn, + csn_en_o => master_csn_en, + mosi_en_o => master_mosi_en, + miso_en_o => master_miso_en, + sck_mask_o => master_sck_mask, + sck_en_o => master_sck_en, + gen_clk_en_o => master_start_clock, + latch_tx_data_o => master_latch_new_tx_data); + + slave_ctrl : entity work.spi_slave_ctrl + generic map ( + SIZES => SIZES, + SIZES_2LOG => SIZES_2LOG, + CSN_PULSE_CYCLES => CSN_PULSE_CYCLES) + port map ( + clk_i => clk_i, + rst_in => rst_in, + en_i => master_en, + size_sel_i => size_sel_i, + pulse_csn_i => pulse_csn_i, + rx_block_on_full_i => rx_block_on_full_i, + rx_en_i => rx_en_i, + rx_ready_i => rx_ready_i, + tx_en_i => tx_en_i, + tx_valid_i => tx_valid_i, + clear_lost_rx_data_i => clear_lost_rx_data_i, + + clock_rising_i => slave_clock_rising, + rx_valid_o => slave_rx_valid, + tx_ready_o => slave_tx_ready, + busy_o => slave_busy, + err_lost_rx_data_o => slave_err_lost_rx_data, + rst_on => slave_ctrl_rst, + csn_en_o => slave_csn_en, + mosi_en_o => slave_mosi_en, + miso_en_o => slave_miso_en, + sck_mask_o => slave_sck_mask, + sck_en_o => slave_sck_en, + gen_clk_en_o => slave_start_clock, + latch_tx_data_o => slave_latch_new_tx_data); + + clkgen : entity work.spi_clkgen + generic map ( + DIVISORS => DIVISORS, + DIVISORS_LOG2 => DIVISORS_LOG2) + port map ( + clk_i => clk_i, + rst_in => master_ctrl_rst_n, + start_i => start_clock, + div_sel_i => div_sel_i, + clock_polarity_i => clock_polarity_i, + clock_phase_i => clock_phase_i, + sck_o => master_sck, + sck_mask_i => master_sck_mask, + clock_rising_o => master_clock_rising, + sample_data_o => master_latch_sample_data, + change_data_o => master_latch_change_data); + + clkmon : entity work.spi_clkmon + port map ( + clk_i => clk_i, + rst_in => slave_ctrl_rst_n, + clock_polarity_i => clock_polarity_i, + clock_phase_i => clock_phase_i, + sck_i => sck_io, + csn_i => csn_io, + clock_rising_o => slave_clock_rising, + sample_data_o => slave_latch_sample_data, + change_data_o => slave_latch_change_data); + + shift_register: entity work.shift_register + generic map ( + SIZE => MAX_SIZE) + port map ( + clk_i => clk_i, + rst_in => rst_n, + -- Control + shift_i => latch_sample_data, -- sampling + latch_i => latch_new_tx_data, -- latching tx data + -- Parallel + data_i => tx_data_i, + data_o => rx_data_o, + -- Serial + sd_i => rx_serial_data, + sd_o => open); + + tx_input_data <= rx_data_o(selected_size - 1); + + mosi_reg : entity work.reg + generic map ( + SIZE => 1) + port map ( + -- outputting different bit on mosi + clk_i => clk_i, + rst_in => rst_n, + d_i => (0 => tx_input_data), + q_o => tx_serial_data, + latch_i => latch_change_data_out); + + master_connection : entity work.spi_multiplexor + port map ( + en_i => master_en, + mosi_en_i => master_mosi_en, + miso_en_i => master_miso_en, + sck_en_i => master_sck_en, + csn_en_i => master_csn_en, + mosi_i => tx_serial_data(0), + miso_i => '0', + sck_i => master_sck, + csn_i => master_csn, + mosi_o => mosi_io, + miso_o => miso_io, + sck_o => sck_io, + csn_o => csn_io, + rx_valid_i => master_rx_valid, + rx_valid_o => rx_valid_o, + tx_ready_i => master_tx_ready, + tx_ready_o => tx_ready_o, + busy_i => master_busy, + busy_o => busy_o, + err_lost_rx_data_i => master_err_lost_rx_data, + err_lost_rx_data_o => err_lost_rx_data_o, + rst_in => master_ctrl_rst, + rst_on => ctrl_rst_n, + latch_tx_data_i => master_latch_new_tx_data, + latch_tx_data_o => latch_new_tx_data, + latch_sample_data_i => master_latch_sample_data, + latch_sample_data_o => latch_sample_data, + latch_change_data_i => master_latch_change_data, + latch_change_data_o => latch_change_data_out, + rx_serial_i => miso_io, + rx_serial_o => rx_serial_data); + + slave_connection : entity work.spi_multiplexor + port map ( + en_i => slave_en, + mosi_en_i => slave_mosi_en, + miso_en_i => slave_miso_en, + sck_en_i => slave_sck_en, + csn_en_i => slave_csn_en, + mosi_i => '0', + miso_i => tx_serial_data(0), + sck_i => '0', + csn_i => '0', + mosi_o => mosi_io, + miso_o => miso_io, + sck_o => sck_io, + csn_o => csn_io, + rx_valid_i => slave_rx_valid, + rx_valid_o => rx_valid_o, + tx_ready_i => slave_tx_ready, + tx_ready_o => tx_ready_o, + busy_i => slave_busy, + busy_o => busy_o, + err_lost_rx_data_i => slave_err_lost_rx_data, + err_lost_rx_data_o => err_lost_rx_data_o, + rst_in => slave_ctrl_rst, + rst_on => ctrl_rst_n, + latch_tx_data_i => slave_latch_new_tx_data, + latch_tx_data_o => latch_new_tx_data, + latch_sample_data_i => slave_latch_sample_data, + latch_sample_data_o => latch_sample_data, + latch_change_data_i => slave_latch_change_data, + latch_change_data_o => latch_change_data_out, + rx_serial_i => mosi_io, + rx_serial_o => rx_serial_data); + +end architecture a1; diff --git a/hdl_spi/src/spi_multiplexor.vhd b/hdl_spi/src/spi_multiplexor.vhd new file mode 100644 index 0000000..ac85d1a --- /dev/null +++ b/hdl_spi/src/spi_multiplexor.vhd @@ -0,0 +1,63 @@ +library ieee; +use ieee.std_logic_1164.all; + +entity spi_multiplexor is + + port ( + en_i : in std_logic; + mosi_en_i : in std_logic; + miso_en_i : in std_logic; + sck_en_i : in std_logic; + csn_en_i : in std_logic; + mosi_i : in std_logic; + miso_i : in std_logic; + sck_i : in std_logic; + csn_i : in std_logic; + mosi_o : out std_logic; + miso_o : out std_logic; + sck_o : out std_logic; + csn_o : out std_logic; + rx_valid_i : in std_logic; + rx_valid_o : out std_logic; + tx_ready_i : in std_logic; + tx_ready_o : out std_logic; + busy_i : in std_logic; + busy_o : out std_logic; + err_lost_rx_data_i : in std_logic; + err_lost_rx_data_o : out std_logic; + rst_in : in std_logic; + rst_on : out std_logic; + + latch_tx_data_i : in std_logic; + latch_tx_data_o : out std_logic; + + latch_sample_data_i : in std_logic; + latch_sample_data_o : out std_logic; + + latch_change_data_i : in std_logic; + latch_change_data_o : out std_logic; + + rx_serial_i : in std_logic; + rx_serial_o : out std_logic); + +end entity spi_multiplexor; + +architecture a1 of spi_multiplexor is + +begin -- architecture a1 + + mosi_o <= mosi_i when mosi_en_i = '1' and en_i = '1' else 'Z'; + miso_o <= miso_i when miso_en_i = '1' and en_i = '1' else 'Z'; + sck_o <= sck_i when sck_en_i = '1' and en_i = '1' else'Z'; + csn_o <= csn_i when csn_en_i = '1' and en_i = '1' else'Z'; + rx_valid_o <= rx_valid_i when en_i = '1' else'Z'; + tx_ready_o <= tx_ready_i when en_i = '1' else'Z'; + busy_o <= busy_i when en_i = '1' else'Z'; + err_lost_rx_data_o <= err_lost_rx_data_i when en_i = '1' else'Z'; + rst_on <= rst_in when en_i = '1' else'Z'; + latch_tx_data_o <= latch_tx_data_i when en_i = '1' else'Z'; + latch_sample_data_o <= latch_sample_data_i when en_i = '1' else'Z'; + latch_change_data_o <= latch_change_data_i when en_i = '1' else'Z'; + rx_serial_o <= rx_serial_i; + +end architecture a1; diff --git a/hdl_spi/src/spi_peripheral.vhd b/hdl_spi/src/spi_peripheral.vhd new file mode 100644 index 0000000..722a62f --- /dev/null +++ b/hdl_spi/src/spi_peripheral.vhd @@ -0,0 +1,122 @@ +library ieee; +use ieee.std_logic_1164.all; +use work.spi_pkg.all; + +entity spi_peripheral is + + generic ( + SIZES : natural_vector := (8, 16); + SIZES_2LOG : natural := 1; + DIVISORS : natural_vector := (2, 4, 6, 8, 16, 32, 64, 128, 256); + DIVISORS_LOG2 : natural := 3; + CSN_PULSE_CYCLES : natural := 1 + ); + + 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; + + -- IOs + sck_o : out std_logic; + miso_i : in std_logic; + mosi_o : out std_logic; + csn_o : out 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); + +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"; + + 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_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 reg_control : std_logic_vector(31 downto 0); + signal reg_status : std_logic_vector(31 downto 0); + + signal clear_rx_buffer_full : std_logic; +begin -- architecture a1 + + 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) + + elsif read_i = '1' then + case address_i is + when DATA_ADDRESS => clear_rx_buffer_full <= '1'; + when others => null; + end case; + else + clear_rx_buffer_full <= '0'; + end if; + end if; + end process reader; + + 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; + when others => null; + end case; + end process output_register; + + rx_buffer_reader: process (clk_i) is + begin -- process rx_buffer_full + if rising_edge(clk_i) then -- rising clock edge + if rst_in = '0' then -- synchronous reset (active low) + rx_buffer_full <= '0'; + else + if clear_rx_buffer_full = '1' then + rx_buffer_full <= '0'; + end if; + + if rx_valid = '1' then + rx_buffer_full <= '1'; + rx_buffer <= rx_data; + end if; + end if; + end if; + end process rx_buffer_reader; + + reg_status <= "000000000000000000000000000" & + rx_data_lost & + busy & + "0" & + tx_buffer_full & + rx_buffer_full; + + tx_data <= tx_buffer; + + rx_ready <= not rx_buffer_full; + tx_valid <= tx_buffer_full; + +end architecture a1; diff --git a/hdl_spi/src/spi_slave_ctrl.vhd b/hdl_spi/src/spi_slave_ctrl.vhd new file mode 100644 index 0000000..bf1b536 --- /dev/null +++ b/hdl_spi/src/spi_slave_ctrl.vhd @@ -0,0 +1,48 @@ +library ieee; +use ieee.std_logic_1164.all; +use work.spi_pkg.all; + +entity spi_slave_ctrl is + + generic ( + SIZES : natural_vector := (8, 16); + SIZES_2LOG : natural := 1; + CSN_PULSE_CYCLES : natural := 1 + ); + + port ( + clk_i : in std_logic; + rst_in : in std_logic; + en_i : in std_logic; + size_sel_i : in std_logic_vector(SIZES_2LOG - 1 downto 0); + pulse_csn_i : in std_logic; + clock_rising_i : in std_logic; + rx_block_on_full_i : in std_logic; + rx_en_i : in std_logic; + rx_valid_o : out std_logic; + rx_ready_i : in std_logic; + tx_en_i : in std_logic; + tx_valid_i : in std_logic; + tx_ready_o : out std_logic; + busy_o : out std_logic; + err_lost_rx_data_o : out std_logic; + clear_lost_rx_data_i : in std_logic; + rst_on : out std_logic; + csn_o : out std_logic; + csn_en_o : out std_logic; + mosi_en_o : out std_logic; + miso_en_o : out std_logic; + sck_mask_o : out std_logic; + sck_en_o : out std_logic; + gen_clk_en_o : out std_logic; + latch_tx_data_o : out std_logic); + +end entity spi_slave_ctrl; + +architecture a1 of spi_slave_ctrl is + +begin -- architecture a1 + + + +end architecture a1; -- 2.48.1