neorv32/docs/datasheet/soc_twi.adoc

141 lines
7.1 KiB
Plaintext
Raw Permalink Normal View History

2024-02-24 08:25:27 +00:00
<<<
:sectnums:
==== Two-Wire Serial Interface Controller (TWI)
[cols="<3,<3,<4"]
[frame="topbot",grid="none"]
|=======================
| Hardware source file(s): | neorv32_twi.vhd |
| Software driver file(s): | neorv32_twi.c |
| | neorv32_twi.h |
| Top entity port: | `twi_sda_i` | 1-bit serial data line sense input
| | `twi_sda_o` | 1-bit serial data line output (pull low only)
| | `twi_scl_i` | 1-bit serial clock line sense input
| | `twi_scl_o` | 1-bit serial clock line output (pull low only)
| Configuration generics: | `IO_TWI_EN` | implement TWI controller when `true`
| CPU interrupts: | fast IRQ channel 7 | transmission done interrupt (see <<_processor_interrupts>>)
|=======================
**Overview**
The NEORV32 TWI implements a **TWI controller**. Currently, **no multi-controller support** is available.
Furthermore, the NEORV32 TWI unit cannot operate in peripheral mode.
[IMPORTANT]
The serial clock (SCL) and the serial data (SDA) lines can only be actively driven low by the
controller. Hence, external pull-up resistors are required for these lines.
**Tri-State Drivers**
The TWI module requires two tri-state drivers (actually: open-drain) for the SDA and SCL lines, which have to be
implemented in the top module of the setup. A generic VHDL example is given below (`sda` and `scl` are the actual TWI
bus signal, which are of type `std_logic`).
.TWI VHDL tri-state driver example
[source,VHDL]
----
sda <= '0' when (twi_sda_o = '0') else 'Z'; -- drive
scl <= '0' when (twi_scl_o = '0') else 'Z'; -- drive
twi_sda_i <= std_ulogic(sda); -- sense
twi_scl_i <= std_ulogic(scl); -- sense
----
**TWI Clock Speed**
The TWI clock frequency is programmed by the 3-bit `TWI_CTRL_PRSCx` clock prescaler for a coarse selection
and a 4-bit clock divider `TWI_CTRL_CDIVx` for a fine selection.
.TWI prescaler configuration
[cols="<4,^1,^1,^1,^1,^1,^1,^1,^1"]
[options="header",grid="rows"]
|=======================
| **`TWI_CTRL_PRSCx`** | `0b000` | `0b001` | `0b010` | `0b011` | `0b100` | `0b101` | `0b110` | `0b111`
| Resulting `clock_prescaler` | 2 | 4 | 8 | 64 | 128 | 1024 | 2048 | 4096
|=======================
Based on the the clock configuration, the actual TWI clock frequency f~SCL~ is derived
from the processor's main clock f~main~ according to the following equation:
_**f~SCL~**_ = _f~main~[Hz]_ / (4 * `clock_prescaler` * (1 + TWI_CTRL_CDIV))
Hence, the maximum TWI clock is f~main~ / 8 and the lowest TWI clock is f~main~ / 262144. The generated TWI clock is
always symmetric having a duty cycle of exactly 50%. However, an accessed peripheral can "slow down" the bus clock
by using **clock stretching** (= actively driving the SCL line low). The controller will pause operation in this case
if clock stretching is enabled via the `TWI_CTRL_CSEN` bit of the unit's control register `CTRL`
**TWI Transfers**
The TWI is enabled via the `TWI_CTRL_EN` bit in the `CTRL` control register. The user program can start / stop a
transmission by issuing a START or STOP condition. These conditions are generated by setting the
according bits (`TWI_CTRL_START` or `TWI_CTRL_STOP`) in the control register.
Data is transferred via the TWI bus by writing a byte to the `DATA` register. The written byte is send via the TWI bus
and the received byte from the bus is also available in this register after the transmission is completed.
The TWI operation (transmitting data or performing a START or STOP condition) is in progress as long as the
control register's `TWI_CTRL_BUSY` bit is set.
[TIP]
A transmission can be terminated at any time by disabling the TWI module
by clearing the _TWI_CTRL_EN_ control register bit. This will also reset the whole module.
[NOTE]
When reading data from a device, an all-one byte (`0xFF`) has to be written to TWI data register `NEORV32_TWI.DATA`
so the accessed device can actively pull-down SDA when required.
**TWI ACK/NACK and MACK**
An accessed TWI peripheral has to acknowledge each transferred byte. When the `TWI_CTRL_ACK` bit is set after a
completed transmission the accessed peripheral has send an ACKNOWLEDGE (ACK). If this bit is cleared after a completed
transmission, the peripheral has send a_NOT-ACKNOWLEDGE (NACK).
The NEORV32 TWI controller can also send an ACK generated by itself ("controller acknowledge / MACK") right after
transmitting a byte by driving SDA low during the ACK time slot. Some TWI modules require this MACK to acknowledge
certain data movement operations.
The control register's `TWI_CTRL_MACK` bit has to be set to make the TWI module automatically generate a MACK after
the byte transmission has been completed. If this bit is cleared, the ACK/NACK generated by the peripheral is sampled
in this time slot instead (normal mode).
**TWI Bus Status**
The TWI controller can check if the TWI bus is currently claimed (SCL and SDA both low). The bus can be claimed by the
NEORV32 TWI itself or by any other controller. Bit `TWI_CTRL_CLAIME` of the control register will be set if the bus
is currently claimed.
**TWI Interrupt**
The TWI module provides a single interrupt to signal "transmission done" to the CPU. Whenever the TWI
module completes the current transmission of one byte the interrupt is triggered. Note the the interrupt
is **not** triggered when completing a START or STOP condition. Once triggered, the interrupt has to be
explicitly cleared again by writing zero to the according <<_mip>> CSR bit.
**Register Map**
.TWI register map (`struct NEORV32_TWI`)
[cols="<2,<1,<4,^1,<7"]
[options="header",grid="all"]
|=======================
| Address | Name [C] | Bit(s), Name [C] | R/W | Function
.10+<| `0xfffff900` .10+<| `CTRL` <|`0` `TWI_CTRL_EN` ^| r/w <| TWI enable, reset if cleared
<|`1` `TWI_CTRL_START` ^| -/w <| generate START condition, auto-clears
<|`2` `TWI_CTRL_STOP` ^| -/w <| generate STOP condition, auto-clears
<|`3` `TWI_CTRL_MACK` ^| r/w <| generate controller-ACK for each transmission ("MACK")
<|`4` `TWI_CTRL_CSEN` ^| r/w <| allow clock stretching when set
<|`7:5` `TWI_CTRL_PRSC2 : TWI_CTRL_PRSC0` ^| r/w <| 3-bit clock prescaler select
<|`11:8` `TWI_CTRL_CDIV3 : TWI_CTRL_CDIV0` ^| r/w <| 4-bit clock divider
<|`28:12` - ^| r/- <| _reserved_, read as zero
<|`29` `TWI_CTRL_CLAIMED` ^| r/- <| set if the TWI bus is claimed by any controller
<|`30` `TWI_CTRL_ACK` ^| r/- <| ACK received when set, NACK received when cleared
<|`31` `TWI_CTRL_BUSY` ^| r/- <| transfer/START/STOP in progress when set
| `0xfffff904` | `DATA` |`7:0` | r/w | receive/transmit data
|=======================