[twd] add hide read option

Add hide read option to only ACK own Device-Address when the TX Fifo is not empty
This commit is contained in:
LukasP46 2025-03-17 13:05:13 +01:00
parent b78531bb2d
commit 7fbac0f441
5 changed files with 28 additions and 12 deletions

View file

@ -139,6 +139,7 @@ or **read** (leaving the SDA line high). If the transferred address matches the
control register bits the TWD module will response with an **ACK** (acknowledge) by pulling the SDA bus line actively
low during the 9th SCL clock pulse. If there is no address match the TWD will not interfere with the bus and move back
to idle state.
If the `hide_read` option is enabled, the address gets not acknowledged if the tx fifo is empty.
For a **write transaction** (upper timing diagram) the host can now transfer an arbitrary number of bytes (blue signals
`D7` to `D0`, MSB-first) to the TWD module. Each byte is acknowledged by the TWD by pulling SDA low during the 9th SCL

View file

@ -54,6 +54,8 @@ architecture neorv32_twd_rtl of neorv32_twd is
constant ctrl_tx_fifo_size0_c : natural := 19; -- r/-: log2(TX_FIFO size), bit 0 (LSB)
constant ctrl_tx_fifo_size3_c : natural := 22; -- r/-: log2(TX_FIFO size), bit 3 (MSB)
--
constant ctrl_hide_read_c : natural := 23; -- r/w: generate NACK ony READ-access when TX FIFO is empty
--
constant ctrl_rx_avail_c : natural := 25; -- r/-: RX FIFO data available
constant ctrl_rx_full_c : natural := 26; -- r/-: RX FIFO full
constant ctrl_tx_empty_c : natural := 27; -- r/-: TX FIFO empty
@ -77,6 +79,7 @@ architecture neorv32_twd_rtl of neorv32_twd is
irq_rx_full : std_ulogic;
irq_tx_empty : std_ulogic;
tx_dummy_en : std_ulogic;
hide_read : std_ulogic;
end record;
signal ctrl : ctrl_t;
@ -143,6 +146,7 @@ begin
ctrl.irq_rx_full <= '0';
ctrl.irq_tx_empty <= '0';
ctrl.tx_dummy_en <= '0';
ctrl.hide_read <= '0';
elsif rising_edge(clk_i) then
-- bus handshake defaults --
bus_rsp_o.ack <= bus_req_i.stb;
@ -163,6 +167,7 @@ begin
ctrl.irq_rx_full <= bus_req_i.data(ctrl_irq_rx_full_c);
ctrl.irq_tx_empty <= bus_req_i.data(ctrl_irq_tx_empty_c);
ctrl.tx_dummy_en <= bus_req_i.data(ctrl_tx_dummy_en_c);
ctrl.hide_read <= bus_req_i.data(ctrl_hide_read_c);
end if;
else -- read access
if (bus_req_i.addr(2) = '0') then -- control register
@ -177,6 +182,8 @@ begin
bus_rsp_o.data(ctrl_rx_fifo_size3_c downto ctrl_rx_fifo_size0_c) <= std_ulogic_vector(to_unsigned(log2_rx_fifo_size_c, 4));
bus_rsp_o.data(ctrl_tx_fifo_size3_c downto ctrl_tx_fifo_size0_c) <= std_ulogic_vector(to_unsigned(log2_tx_fifo_size_c, 4));
--
bus_rsp_o.data(ctrl_hide_read_c) <= ctrl.hide_read;
--
bus_rsp_o.data(ctrl_rx_avail_c) <= rx_fifo.avail;
bus_rsp_o.data(ctrl_rx_full_c) <= not rx_fifo.free;
bus_rsp_o.data(ctrl_tx_empty_c) <= not tx_fifo.avail;
@ -391,7 +398,7 @@ begin
engine.state <= S_ADDR;
end if;
when S_ADDR => -- sample address + R/W bit and check if address match
when S_ADDR => -- sample address + R/W bit and check if address match and data is available
-- ------------------------------------------------------------
if (ctrl.enable = '0') or (smp.stop = '1') then -- disabled or stop-condition received?
engine.state <= S_IDLE;
@ -399,9 +406,13 @@ begin
engine.state <= S_INIT;
elsif (engine.cnt(3) = '1') and (smp.scl_fall = '1') then -- 8 bits received?
if (ctrl.device_addr = engine.sreg(7 downto 1)) then -- address match?
engine.state <= S_RESP; -- access device
else
engine.state <= S_IDLE; -- no match, go back to idle
-- ------------------------------------------------------------
if (engine.sreg(0) = '1' and (ctrl.hide_read = '1' and (tx_fifo.free = '0'))) then -- READ but tx fifo is empty and hide_read is enabled
engine.state <= S_IDLE;
else
engine.state <= S_RESP; -- access device
end if;
-- ------------------------------------------------------------
end if;
end if;
-- sample bus on rising edge --
@ -437,8 +448,8 @@ begin
engine.wr_we <= '0'; -- Don't write into RX FIFO
engine.state <= S_IDLE; -- Don't acknowledge (NACK)
else
engine.wr_we <= not engine.cmd; -- write byte to RX FIFO (only if WRITE command)
engine.state <= S_ACK;
engine.wr_we <= not engine.cmd; -- write byte to RX FIFO (only if WRITE command)
engine.state <= S_ACK;
end if;
end if;
-- sample bus on rising edge --

View file

@ -1058,7 +1058,7 @@ int main() {
cnt_test++;
// configure TWD and enable RX-available interrupt
neorv32_twd_setup(0b1101001, 0, 1, 0, 0);
neorv32_twd_setup(0b1101001, 0, 1, 0, 0, 0);
// configure TWI with third-fastest clock, no clock stretching
neorv32_twi_setup(CLK_PRSC_8, 1, 0);
@ -1339,7 +1339,7 @@ int main() {
neorv32_twi_setup(CLK_PRSC_8, 1, 0);
// configure TWD, no interrupts
neorv32_twd_setup(0b0010110, 0, 0, 0, 0);
neorv32_twd_setup(0b0010110, 0, 0, 0, 0, 0);
neorv32_twd_put(0x8e);
// program sequence: read data via TWI

View file

@ -23,8 +23,8 @@
/**@{*/
/** TWD module prototype */
typedef volatile struct __attribute__((packed,aligned(4))) {
uint32_t CTRL; /**< offset 0: control register (#NEORV32_TWD_CTRL_enum) */
uint32_t DATA; /**< offset 4: data register (#NEORV32_TWD_DATA_enum) */
uint32_t CTRL; /**< offset 0: control register (#NEORV32_TWD_CTRL_enum) */
uint32_t DATA; /**< offset 4: data register (#NEORV32_TWD_DATA_enum) */
} neorv32_twd_t;
/** TWD module hardware access (#neorv32_twd_t) */
@ -49,6 +49,8 @@ enum NEORV32_TWD_CTRL_enum {
TWD_CTRL_TX_FIFO_LSB = 19, /**< TWD control register(19) (r/-): log2(TX_FIFO size), LSB */
TWD_CTRL_TX_FIFO_MSB = 22, /**< TWD control register(22) (r/-): log2(TX_FIFO size), MSB */
TWD_CTRL_HIDE_READ = 23, /**< TWD control register(14) (r/w): Generate NACK ony READ-access when TX FIFO is empty */
TWD_CTRL_RX_AVAIL = 25, /**< TWD control register(25) (r/-): RX FIFO data available */
TWD_CTRL_RX_FULL = 26, /**< TWD control register(26) (r/-): RX FIFO full */
TWD_CTRL_TX_EMPTY = 27, /**< TWD control register(27) (r/-): TX FIFO empty */

View file

@ -43,8 +43,9 @@ int neorv32_twd_available(void) {
* @param[in] irq_rx_full IRQ if RX FIFO full.
* @param[in] irq_tx_empty IRQ if TX FIFO empty.
* @param[in] tx_dummy_en enable sending tx_dummy (last sent byte) when fifo is empty
* @param[in] hide_read generate NACK ony READ-access when TX FIFO is empty
**************************************************************************/
void neorv32_twd_setup(int device_addr, int fsel, int irq_rx_avail, int irq_rx_full, int irq_tx_empty, int tx_dummy_en) {
void neorv32_twd_setup(int device_addr, int fsel, int irq_rx_avail, int irq_rx_full, int irq_tx_empty, int tx_dummy_en, int hide_read) {
NEORV32_TWD->CTRL = 0; // reset
@ -56,6 +57,7 @@ void neorv32_twd_setup(int device_addr, int fsel, int irq_rx_avail, int irq_rx_f
ctrl |= ((uint32_t)(irq_rx_full & 0x01) << TWD_CTRL_IRQ_RX_FULL);
ctrl |= ((uint32_t)(irq_tx_empty & 0x01) << TWD_CTRL_IRQ_TX_EMPTY);
ctrl |= ((uint32_t)(tx_dummy_en & 0x01) << TWD_CTRL_TX_DUMMY_EN);
ctrl |= ((uint32_t)(hide_read & 0x01) << TWD_CTRL_HIDE_READ);
NEORV32_TWD->CTRL = ctrl;
}
@ -286,4 +288,4 @@ uint8_t neorv32_twd_get(void) {
void neorv32_twd_set_dummy(uint8_t data) {
neorv32_twd_clear_tx();
neorv32_twd_put(data);
}
}