141 lines
7.1 KiB
Plaintext
141 lines
7.1 KiB
Plaintext
<<<
|
|
: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
|
|
|=======================
|