<<<
:sectnums:
==== Two-Wire Serial Device Controller (TWD)

[cols="<3,<3,<4"]
[grid="none"]
|=======================
| Hardware source files:  | neorv32_twd.vhd    |
| Software driver files:  | neorv32_twd.c      | link:https://stnolting.github.io/neorv32/sw/neorv32__twd_8c.html[Online software reference (Doxygen)]
|                         | neorv32_twd.h      | link:https://stnolting.github.io/neorv32/sw/neorv32__twd_8h.html[Online software reference (Doxygen)]
| Top entity ports:       | `twd_sda_i`        | 1-bit serial data line sense input
|                         | `twd_sda_o`        | 1-bit serial data line output (pull low only)
|                         | `twd_scl_i`        | 1-bit serial clock line sense input
| Configuration generics: | `IO_TWD_EN`        | implement TWD controller when `true`
|                         | `IO_TWD_RX_FIFO`   | RX FIFO depth, has to be a power of two, min 1
|                         | `IO_TWD_TX_FIFO`   | TX FIFO depth, has to be a power of two, min 1
| CPU interrupts:         | fast IRQ channel 4 | FIFO status interrupt (see <<_processor_interrupts>>)
|=======================

**Key Features**

* I²C-compatible device-side controller
* Programmable 7-bit device address
* Optional RX/TX data FIFOs
* Programmable interrupt conditions


**Overview**

The NEORV32 TWD implements an I2C-compatible **device-side** interface. Processor-external I2C hosts can communicate
with this module by issuing I2C transactions. The TWD is entirely passive an only reacts to external transmissions.

.Device-Mode Only
[NOTE]
The NEORV32 TWD controller only supports **device mode**. Transmission are initiated by processor-external modules
and not by an external TWD. If you are looking for a _host-mode_ module (transactions initiated by the processor)
check out the <<_two_wire_serial_interface_controller_twi>>.


**Theory of Operation**

The TWD module provides two memory-mapped registers that are used for configuration & status check (`CTRL`) and
for accessing transmission data (`DATA`). The `DATA` register is transparently buffered by separate RX and TX FIFOs.
The size of those FIFOs can be configured by the `IO_TWD_RX_FIFO` and `IO_TWD_TX_FIFO` generics. Software can determine
the FIFO size via the control register's `TWD_CTRL_FIFO_*` bits. The current status of the RX and TX FIFO can be polled
by software via the `TWD_CTRL_RX_*` and `TWD_CTRL_TX_*` flags.

The module is globally enabled by setting the control register's `TWD_CTRL_EN` bit. Clearing this bit will disable
and reset the entire module also clearing the internal RX and TX FIFOs. Each FIFO can also be cleared individually at
any time by setting `TWD_CTRL_CLR_RX` or `TWD_CTRL_CLR_TX` control register bits, respectively.

The external I²C bus is sampled and synchronized into the processor's clock domain with a sampling frequency of
1/8 of the processor's main clock. In order to increase the resistance to glitches the sampling frequency can be lowered
to 1/64 of the processor clock by setting the control register's `TWD_CTRL_FSEL` bit.

The TWD only responds to a configurable 7-bit device address that is programmed by the `TWD_CTRL_DEV_ADDR` bits.
Specific general-call or broadcast addresses are not supported yet.

Depending on the transaction type, data is either read from the TX FIFO and transferred back to the host
(**read operation**) or data is received from the host and written to the RX FIFO (**write operation**).


**Read Operation: Host Reads Data from TWD Module**

.Exemplary single-byte TWD read operation (split across two lines)
[wavedrom, format="png", align="center"]
----
{signal: [
  {name: 'SDA', wave: '10.7..7..7..7..7..7..7..1..0..x|.', node: 'a.b.....................c..d..e', data: ['A6', 'A5', 'A4', 'A3', 'A2', 'A1', 'A0']},
  {name: 'SCL', wave: '1.0.10.10.10.10.10.10.10.10.10.|.'},
  {},
  {name: 'SDA', wave: 'x|.9..9..9..9..9..9..9..9..0..0.1', node: '...........................f..gh.i', data: ['D7', 'D6', 'D5', 'D4', 'D3', 'D2', 'D1', 'D0']},
  {name: 'SCL', wave: '0|..10.10.10.10.10.10.10.10.10.1.'}
 ],
   edge: [
    'a-b START',
    'c-d READ',
    'd-e ACK by TWD',
    'f-g ACK by HOST',
    'h-i STOP'
 ]
}
----

For a read operation the host first generates START or REPEATED-START condition. Then, the host transmits the 7 bit device
address (green signals `A6` to `A0`) plus the read-command bit. If the transferred address matches the one programmed to to
`TWD_CTRL_DEV_ADDR` control register bits the TWD module will response with an ACK 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 will wait for
the next START or REPEATED-START condition.

For the actual data transmission the host keeps the SDA line at high state while sending the clock pulses. The TWD will
read a byte from the internal TX FIFO and will transmit it MSB-first to the host. During the 9th clock pulse the host has
to acknowledged the transfer (ACK) by pulling SDA low. If no ACK is received by the TWD no data is taken from the TX FIFO
and the same byte is transmitted in the next data phase. If the TX FIFO becomes empty while the host keeps reading data,
all-one bytes are sent back to the host. The transaction is terminated by a STOP condition.


**Write Operation: Host Writes Data to TWD Module**

.Exemplary single-byte TWD write operation timing (split across two lines)
[wavedrom, format="png", align="center"]
----
{signal: [
  {name: 'SDA', wave: '10.7..7..7..7..7..7..7..0..0..x|.', node: 'a.b.....................c..d..e', data: ['A6', 'A5', 'A4', 'A3', 'A2', 'A1', 'A0']},
  {name: 'SCL', wave: '1.0.10.10.10.10.10.10.10.10.10.|.'},
  {},
  {name: 'SDA', wave: 'x|.5..5..5..5..5..5..5..5..0..0.1', node: '...........................f..gh.i', data: ['D7', 'D6', 'D5', 'D4', 'D3', 'D2', 'D1', 'D0']},
  {name: 'SCL', wave: '0|..10.10.10.10.10.10.10.10.10.1.'}
 ],
   edge: [
    'a-b START',
    'c-d WRITE',
    'd-e ACK by TWD',
    'f-g ACK by TWD',
    'h-i STOP'
 ]
}
----

For a write operation the host first generates START or REPEATED-START condition. Then, the host transmits the 7 bit device
address (green signals `A6` to `A0`) plus the write-command bit. If the transferred address matches the one programmed to to
`TWD_CTRL_DEV_ADDR` control register bits the TWD module will response with an ACK 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 will wait for
the next START or REPEATED-START condition.

For the actual data transmission the host sends the write-data MSB first together with the clock pulses. During the 9th clock
pulse the TWD will respond with an ACK by pulling SDA low. Note that the TWD will respond with a NACK if the RX FIFO is full.
The transaction is terminated by a STOP condition.


**Communication Status**

Actual communication with the TWD is (re-)**started** when a START or REPEATED-START condition followed by the TWD device
address is received. This communication is **terminated** as soon as a STOP condition is received. The current state of the
communication can be polled via the `TWD_CTRL_COM` control register flag.

Whenever a communication start has been observed the `TWD_CTRL_COM_BEG` control register flag becomes set.
Accordingly, the `TWD_CTRL_COM_END` control register flag becomes set whenever a communication end / termination
has been observed. These flags retain their value until they are either manually deleted (by writing a `1` to
the respective flag) or the TWD module is deactivated and thus reset.


**TWD Interrupt**

The TWD module provides a single interrupt to signal certain FIFO conditions and/or communication states to the CPU.
The control register's `TWD_CTRL_IRQ_*` bits are used to enabled individual interrupt conditions. Note that all enabled
conditions are logically OR-ed.

.TWD Interrupts Sources
[cols="<3,<6,<3"]
[options="header",grid="all"]
|=======================
| Interrupt Enable | Description | Interrupt Condition
| `TWD_CTRL_IRQ_RX_AVAIL` | Interrupt if at least one data byte is available in the RX FIFO. | `TWD_CTRL_RX_AVAIL == 1`
| `TWD_CTRL_IRQ_RX_FULL`  | Interrupt if the RX FIFO is full.                                | `TWD_CTRL_RX_FULL == 1`
| `TWD_CTRL_IRQ_TX_EMPTY` | Interrupt if the TX FIFO is empty.                               | `TWD_CTRL_TX_EMPTY == 1`
| `TWD_CTRL_IRQ_TX_NFULL` | Interrupt if the TX FIFO is not full yet.                        | `TWD_CTRL_TX_FULL == 0`
| `TWD_CTRL_IRQ_COM_BEG`  | Interrupt if a communication-start has been observed.            | `TWD_CTRL_COM_BEG == 1`
| `TWD_CTRL_IRQ_COM_END`  | Interrupt if a communication-end has been observed.              | `TWD_CTRL_COM_END == 1`
|=======================

The CPU interrupt request remains active until all enabled interrupt-causing conditions are resolved.
The interrupt can only trigger if the module is actually enabled (`TWD_CTRL_EN` is set).


**Tristate Driver**

The TWD module requires a single tristate drives (actually: open-drain drivers - signals can only be actively driven low) for
the SDA line, which have to be implemented by the user in the setup's top module / IO ring. The SCL signal is only read by the
TWD module (input only) and therefore does not require a tristate driver. A generic VHDL example is shown below.

.TWD Tristate Driver Example (VHDL)
[source,VHDL]
----
-- IO ports / bus lines --
sda_io : inout std_logic; -- bidirectional
scl_io : in    std_logic; -- input only

-- tristate driver --
sda_io <= '0' when (twd_sda_o = '0') else 'Z'; -- drive (pull low)

-- sense --
twd_sda_i <= std_ulogic(sda_io);
twd_scl_i <= std_ulogic(scl_io);
----


**Register Map**

.TWD register map (`struct NEORV32_TWD`)
[cols="<2,<1,<4,^1,<6"]
[options="header",grid="all"]
|=======================
| Address | Name [C] | Bit(s), Name [C] | R/W | Function
.20+<| `0xffea0000` .20+<| `CTRL` <|`0`     `TWD_CTRL_EN`                                 ^| r/w <| TWD enable, reset if cleared
                                  <|`1`     `TWD_CTRL_CLR_RX`                             ^| -/w <| Clear RX FIFO, flag auto-clears
                                  <|`2`     `TWD_CTRL_CLR_TX`                             ^| -/w <| Clear TX FIFO, flag auto-clears
                                  <|`3`     `TWD_CTRL_FSEL`                               ^| r/w <| Bus sample clock / filter select
                                  <|`10:4`  `TWD_CTRL_DEV_ADDR6 : TWD_CTRL_DEV_ADDR0`     ^| r/w <| Device address (7-bit)
                                  <|`11`    `TWD_CTRL_IRQ_RX_AVAIL`                       ^| r/w <| IRQ if RX FIFO data available
                                  <|`12`    `TWD_CTRL_IRQ_RX_FULL`                        ^| r/w <| IRQ if RX FIFO full
                                  <|`13`    `TWD_CTRL_IRQ_TX_EMPTY`                       ^| r/w <| IRQ if TX FIFO empty
                                  <|`14`    `TWD_CTRL_IRQ_TX_NFULL`                       ^| r/w <| IRQ if TX FIFO is not full
                                  <|`15`    `TWD_CTRL_IRQ_COM_BEG`                        ^| r/w <| IRQ if begin of communication
                                  <|`16`    `TWD_CTRL_IRQ_COM_END`                        ^| r/w <| IRQ if end of communication
                                  <|`20:17` `TWD_CTRL_RX_FIFO_MSB : TWD_CTRL_RX_FIFO_LSB` ^| r/- <| FIFO depth; log2(`IO_TWD_RX_FIFO`)
                                  <|`24:21` `TWD_CTRL_TX_FIFO_MSB : TWD_CTRL_TX_FIFO_LSB` ^| r/- <| FIFO depth; log2(`IO_TWD_TX_FIFO`)
                                  <|`25`    `TWD_CTRL_RX_AVAIL`                           ^| r/- <| RX FIFO data available
                                  <|`26`    `TWD_CTRL_RX_FULL`                            ^| r/- <| RX FIFO full
                                  <|`27`    `TWD_CTRL_TX_EMPTY`                           ^| r/- <| TX FIFO empty
                                  <|`28`    `TWD_CTRL_TX_FULL`                            ^| r/- <| TX FIFO full
                                  <|`29`    `TWD_CTRL_COM_BEG`                            ^| r/c <| Communication has started; clear by writing 1
                                  <|`30`    `TWD_CTRL_COM_END`                            ^| r/c <| Communication has ended; clear by writing 1
                                  <|`31`    `TWD_CTRL_COM`                                ^| r/- <| Active communication
.2+<| `0xffea0004` .2+<| `DATA`   <|`7:0`   `TWD_DATA_MSB : TWD_DATA_LSB`                 ^| r/w <| RX/TX data FIFO access
                                  <|`31:8`  -                                             ^| r/- <| _reserved_, read as zero
|=======================
