2222--! 3. If implementing the SPI controller, consider using clock enables at registers
2323--! rather than gating the SPI clock output
2424--!
25- --!
2625--! @license This project is released under the terms of the MIT License. See LICENSE for more details.
2726--!
2827
2928library ieee;
3029use ieee.std_logic_1164.all ;
3130use ieee.numeric_std.all ;
3231
32+ use work.utils_pkg.all ;
33+
3334entity spi_interface is
3435 generic (
3536 SPI_CLK_POLARITY: bit := '0' ; -- Clock polarity
@@ -39,36 +40,160 @@ entity spi_interface is
3940 CONTROLLER_AND_NOT_PERIPHERAL: boolean := true ;
4041 MSB_FIRST_AND_NOT_LSB: boolean := true ;
4142 ENABLE_INTERNAL_CLOCK_GATING: boolean := true ;
42- USE_XILINX_CLK_GATE_AND_NOT_INTERNAL: boolean := false -- Use Xilinx clock gating instead of internal logic
43+ USE_XILINX_CLK_GATE_AND_NOT_INTERNAL: boolean := false ;
44+ TX_FIFO_DEPTH_IN_BITS: natural := 4 -- Set to 0 for single-word mode
4345 );
4446 port (
4547 spi_clk_in: in std_ulogic ;
4648 rst_n: in std_ulogic ;
4749
48- selected_chips: in std_ulogic_vector ;
50+ selected_chips: in std_ulogic_vector (SPI_CHIPS_AMOUNT - 1 downto 0 );
51+
52+ tx_fifo_write_clk: in std_ulogic ;
53+ tx_fifo_write_enable: in std_ulogic ;
54+ tx_fifo_write_data: in std_ulogic_vector (DATA_WIDTH - 1 downto 0 );
55+ tx_fifo_write_blocked: out std_ulogic ;
56+ tx_fifo_full: out std_ulogic ;
57+ tx_fifo_empty: out std_ulogic ;
58+ tx_fifo_words_stored: out natural range 0 to 2 ** TX_FIFO_DEPTH_IN_BITS;
4959
50- tx_data: in std_ulogic_vector (DATA_WIDTH - 1 downto 0 );
51- tx_data_valid: in std_ulogic ;
60+ -- Streaming control
61+ tx_trigger: in std_ulogic ;
62+ spi_busy: out std_ulogic ;
5263
5364 rx_data: out std_ulogic_vector (DATA_WIDTH - 1 downto 0 );
5465 rx_data_valid: out std_ulogic ;
5566
5667 spi_clk_out: out std_ulogic ;
5768 serial_data_out: out std_logic ;
5869 serial_data_in: in std_ulogic ;
59- spi_chip_select_n: inout std_ulogic_vector (SPI_CHIPS_AMOUNT - 1 downto 0 );
60-
61- tx_is_ongoing: out std_ulogic
70+ spi_chip_select_n: inout std_ulogic_vector (SPI_CHIPS_AMOUNT - 1 downto 0 )
6271 );
6372end entity ;
6473
6574architecture behavioural of spi_interface is
75+ constant CHIP_INDEX_OUT_OF_RANGE: natural := selected_chips'length ;
76+ signal current_chip_index: natural range 0 to CHIP_INDEX_OUT_OF_RANGE;
77+
78+ -- FSM state
79+ type state_t is (idle, fetch_data, wait_for_data, wait_for_acknowledge, reset_for_next_chip, wait_for_transfer_end);
80+ signal state: state_t;
81+
82+ -- FIFO signals
83+ signal tx_fifo_write_enable_internal: std_ulogic ;
84+
85+ signal tx_fifo_read_enable: std_ulogic ;
86+ signal tx_fifo_read_data: std_ulogic_vector (DATA_WIDTH - 1 downto 0 );
87+ signal tx_fifo_read_data_valid: std_ulogic ;
88+ signal tx_fifo_reset_read_pointer: std_ulogic ;
89+
90+ -- TX interface signals
91+ signal tx_is_ongoing: std_ulogic ;
92+ signal tx_data: std_ulogic_vector (DATA_WIDTH - 1 downto 0 );
93+ signal tx_data_valid: std_ulogic ;
94+ signal tx_data_ack: std_ulogic ;
95+
96+ signal spi_chip_select_n_internal: std_ulogic ;
6697begin
98+ fsm: process (spi_clk_in)
99+ variable current_chip_index_v: current_chip_index'subtype ;
100+ variable selected_chips_reg: selected_chips'subtype ;
101+
102+ -- Select only one chip at a time
103+ impure function get_next_selected_chip return natural is begin
104+ if current_chip_index_v >= CHIP_INDEX_OUT_OF_RANGE then
105+ return get_lowest_active_bit(selected_chips);
106+ end if ;
107+
108+ for i in spi_chip_select_n'subtype 'low to spi_chip_select_n'subtype 'high loop
109+ current_chip_index_v := current_chip_index_v + 1 ;
110+ -- Out of range or found the next active chip
111+ exit when current_chip_index_v >= CHIP_INDEX_OUT_OF_RANGE or (?? selected_chips_reg(current_chip_index_v));
112+ end loop ;
113+
114+ return current_chip_index_v;
115+ end function ;
116+ begin
117+ if rising_edge (spi_clk_in) then
118+ tx_data_valid <= '0' ;
119+ tx_fifo_read_enable <= '0' ;
120+ tx_fifo_reset_read_pointer <= '0' ;
121+ spi_busy <= '1' ;
122+
123+ if rst_n = '0' then
124+ tx_fifo_write_blocked <= '0' ;
125+ state <= idle;
126+ current_chip_index <= 0 ;
127+ else
128+ case state is
129+ when idle =>
130+ spi_busy <= '0' ;
131+ tx_fifo_write_blocked <= '0' ;
132+ current_chip_index_v := CHIP_INDEX_OUT_OF_RANGE;
133+
134+ if tx_trigger and not tx_fifo_empty then
135+ selected_chips_reg := selected_chips;
136+ current_chip_index_v := get_next_selected_chip;
137+ if current_chip_index_v /= CHIP_INDEX_OUT_OF_RANGE then
138+ current_chip_index <= current_chip_index_v;
139+ tx_fifo_write_blocked <= '1' ;
140+ state <= fetch_data;
141+ end if ;
142+ end if ;
143+ when fetch_data =>
144+ if not tx_fifo_empty then
145+ tx_fifo_read_enable <= '1' ;
146+ state <= wait_for_data;
147+ else
148+ current_chip_index_v := get_next_selected_chip;
149+ if current_chip_index_v /= CHIP_INDEX_OUT_OF_RANGE then
150+ state <= reset_for_next_chip;
151+ elsif not tx_is_ongoing then
152+ state <= idle; -- All chips done
153+ else
154+ state <= wait_for_transfer_end; -- Wait for ongoing transfer to finish
155+ end if ;
156+ end if ;
157+ when wait_for_data =>
158+ if tx_fifo_read_data_valid then
159+ tx_data_valid <= '1' ;
160+ tx_data <= tx_fifo_read_data;
161+ state <= wait_for_acknowledge;
162+ end if ;
163+ when wait_for_acknowledge =>
164+ tx_data_valid <= '1' ;
165+ if tx_data_ack then
166+ state <= fetch_data;
167+ end if ;
168+ when reset_for_next_chip =>
169+ tx_fifo_reset_read_pointer <= '1' ; -- Reset read pointer to replay data
170+ current_chip_index <= current_chip_index_v;
171+ if not tx_fifo_empty then
172+ state <= fetch_data;
173+ end if ;
174+ when wait_for_transfer_end =>
175+ if not tx_is_ongoing then
176+ state <= idle; -- All chips done
177+ end if ;
178+ when others =>
179+ state <= idle;
180+ end case ;
181+ end if ;
182+ end if ;
183+ end process ;
184+
185+ chip_select: process (all )
186+ begin
187+ spi_chip_select_n <= (others => '1' );
188+ spi_chip_select_n(current_chip_index) <= spi_chip_select_n_internal;
189+ end process ;
190+
191+ tx_fifo_write_enable_internal <= rst_n and not tx_fifo_full and not tx_fifo_write_blocked and tx_fifo_write_enable;
192+
67193 spi_tx_inst: entity work.spi_tx
68194 generic map (
69195 SPI_CLK_POLARITY => SPI_CLK_POLARITY,
70196 SPI_CLK_PHASE => SPI_CLK_PHASE,
71- SPI_CHIPS_AMOUNT => SPI_CHIPS_AMOUNT,
72197 DATA_WIDTH => DATA_WIDTH,
73198 CONTROLLER_AND_NOT_PERIPHERAL => CONTROLLER_AND_NOT_PERIPHERAL,
74199 MSB_FIRST_AND_NOT_LSB => MSB_FIRST_AND_NOT_LSB,
@@ -78,12 +203,12 @@ begin
78203 port map (
79204 spi_clk_in => spi_clk_in,
80205 rst_n => rst_n,
81- selected_chips => selected_chips,
82206 tx_data => tx_data,
83207 tx_data_valid => tx_data_valid,
208+ tx_data_ack => tx_data_ack,
84209 spi_clk_out => spi_clk_out,
85210 serial_data_out => serial_data_out,
86- spi_chip_select_n => spi_chip_select_n ,
211+ spi_chip_select_n => spi_chip_select_n_internal ,
87212 tx_is_ongoing => tx_is_ongoing
88213 );
89214
@@ -98,8 +223,28 @@ begin
98223 spi_clk => spi_clk_in,
99224 rst_n => rst_n,
100225 serial_data_in => serial_data_in,
101- spi_chip_select_n => and (spi_chip_select_n) ,
226+ spi_chip_select_n => spi_chip_select_n_internal ,
102227 rx_data => rx_data,
103228 rx_data_valid => rx_data_valid
104229 );
230+
231+ fifo_async_inst: entity work.fifo_async(own_behavioural_async_fifo)
232+ generic map (
233+ FIFO_DEPTH_IN_BITS => TX_FIFO_DEPTH_IN_BITS,
234+ CDC_SYNC_STAGES => 2
235+ )
236+ port map (
237+ aclr => not rst_n,
238+ write_clk => tx_fifo_write_clk,
239+ read_clk => spi_clk_in,
240+ write_enable => tx_fifo_write_enable_internal,
241+ write_data => tx_fifo_write_data,
242+ read_enable => tx_fifo_read_enable,
243+ read_data => tx_fifo_read_data,
244+ read_data_valid => tx_fifo_read_data_valid,
245+ full => tx_fifo_full,
246+ empty => tx_fifo_empty,
247+ words_stored => tx_fifo_words_stored,
248+ reset_read_pointer => tx_fifo_reset_read_pointer
249+ );
105250end architecture ;
0 commit comments