-
Notifications
You must be signed in to change notification settings - Fork 231
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
✨ add new module: device-mode I²C controller ("TWD") (#1121)
- Loading branch information
Showing
24 changed files
with
1,336 additions
and
79 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
<<< | ||
:sectnums: | ||
==== Two-Wire Serial Device Controller (TWD) | ||
|
||
[cols="<3,<3,<4"] | ||
[frame="topbot",grid="none"] | ||
|======================= | ||
| Hardware source files: | neorv32_twd.vhd | | ||
| Software driver files: | neorv32_twd.c | | ||
| | neorv32_twd.h | | ||
| 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 | ||
| | `twd_scl_o` | 1-bit serial clock line output (pull low only) | ||
| Configuration generics: | `IO_TWD_EN` | implement TWD controller when `true` | ||
| | `IO_TWD_FIFO` | RX/TX FIFO depth, has to be a power of two, min 1 | ||
| CPU interrupts: | fast IRQ channel 0 | FIFO status interrupt (see <<_processor_interrupts>>) | ||
| Access restrictions: 2+| privileged access only, non-32-bit write accesses are ignored | ||
|======================= | ||
|
||
|
||
**Overview** | ||
|
||
The NEORV32 TWD implements a I2C-compatible **device-mode** controller. Processor-external hosts can communicate | ||
with this module by issuing I2C transactions. The TWD is entirely passive an only reacts on those transmissions. | ||
|
||
Key features: | ||
|
||
* Programmable 7-bit device address | ||
* Programmable interrupt conditions | ||
* Configurable RX/TX data FIFO to "program" large TWD sequences without further involvement of the CPU | ||
.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_FIFO` generic. Software can determine the FIFO size via the | ||
`TWD_CTRL_FIFO_*` bits. | ||
|
||
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`, respectively. | ||
|
||
The external two wire bus is sampled sampled and synchronized to processor's clock domain with a sampling frequency | ||
of 1/8 of the processor's main clock. To increase the resistance to glitches the sampling frequency can be lowered | ||
to 1/64 of the processor clock by setting the `TWD_CTRL_FSEL` bit. | ||
|
||
.Current Bus State | ||
[TIP] | ||
The current state of the I²C bus lines (SCL and SDA) can be checked by software via the `TWD_CTRL_SENSE_*` control | ||
register bits. Note that the TWD module needs to be enabled in order to sample the bus state. | ||
|
||
The actual 7-bit device address of the TWD is programmed by the `TWD_CTRL_DEV_ADDR` bits. Note that the TWD will | ||
only response to a host transactions if the host issues the according address. Specific general-call or broadcast | ||
addresses are not supported. | ||
|
||
Depending on the transaction type, data is either read from the RX FIFO and transferred to the host ("read operation") | ||
or data is received from the host and written to the TX FIFO ("write operation"). Hence, data sequences can be | ||
programmed to the TX FIFO to be fetched from the host. If the TX FIFO is empty and the host keeps performing read | ||
transaction, the transferred data byte is automatically set to all-one. | ||
|
||
The current status of the RX and TX FIFO can be polled by software via the `TWD_CTRL_RX_*` and `TWD_CTRL_TX_*` | ||
flags. | ||
|
||
|
||
**TWD Interrupt** | ||
|
||
The TWD module provides a single interrupt to signal certain FIFO conditions 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_CTRL_IRQ_RX_AVAIL`: trigger interrupt if at least one data byte is available in the RX FIFO | ||
* `TWD_CTRL_IRQ_RX_FULL`: trigger interrupt if the RX FIFO is completely full | ||
* `TWD_CTRL_IRQ_TX_EMPTY`: trigger interrupt if the TX FIFO is empty | ||
The interrupt 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). | ||
|
||
|
||
**TWD Transmissions** | ||
|
||
Two standard I²C-compatible transaction types are supported: **read** operations and **write** operations. These | ||
two operation types are illustrated in the following figure (note that the transactions are split across two lines | ||
to improve readability). | ||
|
||
.TWD single-byte read and write transaction timing (not to scale) | ||
image::twd_sequences.png[] | ||
|
||
Any new transaction starts with a **START** condition. Then, the host transmits the 7 bit device address MSB-first | ||
(green signals `A6` to `A0`) plus a command bit. The command bit can be either **write** (pulling the SDA line low) | ||
or **read** (leaving the SDA line high). 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** (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. | ||
|
||
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 | ||
clock pules (**ACK**). Each received data byte is pushed to the internal RX FIFO. Data will be lost if the FIFO overflows. | ||
The transaction is terminated when the host issues a **STOP** condition. | ||
|
||
For a **read transaction** (lower timing diagram) the cost keeps the SDA line at high state while sending the clock | ||
pulse. The TWD will read a byte from the internal TX FIFO and will transmit it MSB-first to the host (blue signals `D7` | ||
to `D0)`. During the 9th clock pulse the host has to acknowledged the transfer (**ACK**). If no ACK is received by the | ||
TWD no data is taken from the TX FIFO and the same byte can be transmitted in the next data phase. If the TX FIFO becomes | ||
empty while the host keeps reading data, all-one bytes are transmitted. The transaction is terminated when the host | ||
issues a **STOP** condition. | ||
|
||
A **repeated-START** condition can be issued at any time bringing the TWD back to the start of the address/command | ||
transmission phase. The control register's `TWD_CTRL_BUSY` flag remains high while a bus transaction is in progress. | ||
|
||
.Abort / Termination | ||
[TIP] | ||
An active or even stuck transmission can be terminated at any time by disabling the TWD module. | ||
This will also clear the RX/TX FIFOs. | ||
|
||
|
||
**Tristate Drivers** | ||
|
||
The TWD module requires two tristate drivers (actually: open-drain drivers - signals can only be actively driven low) for | ||
the SDA and SCL lines, which have to be implemented by the user in the setup's top module / IO ring. A generic VHDL example | ||
is shown below (here, `sda_io` and `scl_io` are the actual TWD bus lines, which are of type `std_logic`). | ||
|
||
.TWD VHDL Tristate Driver Example | ||
[source,VHDL] | ||
---- | ||
sda_io <= '0' when (twd_sda_o = '0') else 'Z'; -- drive | ||
scl_io <= '0' when (twd_scl_o = '0') else 'Z'; -- drive | ||
twd_sda_i <= std_ulogic(sda_io); -- sense | ||
twd_scl_i <= std_ulogic(scl_io); -- sense | ||
---- | ||
|
||
|
||
**Register Map** | ||
|
||
.TWD register map (`struct NEORV32_TWD`) | ||
[cols="<2,<1,<4,^1,<7"] | ||
[options="header",grid="all"] | ||
|======================= | ||
| Address | Name [C] | Bit(s), Name [C] | R/W | Function | ||
.18+<| `0xffffea00` .18+<| `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:9` - ^| r/- <| _reserved_, read as zero | ||
<|`18:15` `TWD_CTRL_FIFO_MSB : TWD_CTRL_FIFO_LSB` ^| r/- <| FIFO depth; log2(`IO_TWD_FIFO`) | ||
<|`24:12` - ^| r/- <| _reserved_, read as zero | ||
<|`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_SENSE_SCL` ^| r/- <| current state of the SCL bus line | ||
<|`30` `TWD_CTRL_SENSE_SDA` ^| r/- <| current state of the SDA bus line | ||
<|`31` `TWD_CTRL_BUSY` ^| r/- <| bus engine is busy (transaction in progress) | ||
.2+<| `0xffffea04` .2+<| `DATA` <|`7:0` `TWD_DATA_MSB : TWD_DATA_LSB` ^| r/w <| RX/TX data FIFO access | ||
<|`31:8` - ^| r/- <| _reserved_, read as zero | ||
|======================= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Oops, something went wrong.