Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TWI] add bus sensing logic #1111

Merged
merged 7 commits into from
Dec 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ mimpid = 0x01040312 -> Version 01.04.03.12 -> v1.4.3.12

| Date | Version | Comment | Ticket |
|:----:|:-------:|:--------|:------:|
| 01.12.2024 | 1.10.6.8 | add TWI bus sensing logic | [#1111](https://github.com/stnolting/neorv32/pull/1111) |
| 26.11.2024 | 1.10.6.7 | :bug: fix some HDL issues that caused problems when auto-converting to Verilog | [#1103](https://github.com/stnolting/neorv32/pull/1103) |
| 23.11.2024 | 1.10.6.6 | CPU control: large code edits and cleanups | [#1099](https://github.com/stnolting/neorv32/pull/1099) |
| 10.11.2024 | 1.10.6.5 | :warning: switch to [xPack](https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack) as default prebuilt RISC-V GCC toolchain (now using `riscv-none-elf-` as default gcc prefix) | [#1091](https://github.com/stnolting/neorv32/pull/1091) |
Expand Down
23 changes: 14 additions & 9 deletions docs/datasheet/soc_twi.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,6 @@
The NEORV32 TWI implements an I2C-compatible host controller to communicate with arbitrary I2C-devices.
Note that peripheral-mode (controller acts as a device) and multi-controller mode are not supported yet.

The TWI controller provides two memory-mapped registers that are used for configuring the module and
for triggering operation: `CTRL` is the control and status register, `DCMD` is the command and data register.


Key features:

Expand All @@ -37,10 +34,13 @@ Key features:
* Generating a host-ACK (ACK send by the TWI controller)
* Configurable data/command FIFO to "program" large TWI sequences without further involvement of the CPU

The TWI controller provides two memory-mapped registers that are used for configuring the module and
for triggering operations: the control and status register `CTRL` and the command and data register `DCMD`.


**Tristate Drivers**

The TWI module requires two tristate drivers (actually: open-drain drivers; signals can only be actively driven low) for
The TWI 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 TWI bus lines, which are of type `std_logic`).

Expand All @@ -56,8 +56,8 @@ twi_scl_i <= std_ulogic(scl_io); -- sense

**TWI Clock Speed**

The TWI clock frequency is programmed by two bit-fields in the device's control register `CTRL`: a 3-bit `TWI_CTRL_PRSCx`
clock prescaler is sued for a coarse clock configuration and a 4-bit clock divider `TWI_CTRL_CDIVx` is used for a fine
The TWI clock frequency is programmed by two bit-fields in the device's control register `CTRL`: a 3-bit clock prescaler
(`TWI_CTRL_PRSCx`) is used for a coarse clock configuration and a 4-bit clock divider (`TWI_CTRL_CDIVx`) is used for a fine
clock configuration.

.TWI prescaler configuration
Expand All @@ -74,7 +74,7 @@ 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%.
always symmetric having a duty cycle of exactly 50% (if the clock is not haled by a device during clock stretching).

.Clock Stretching
[NOTE]
Expand Down Expand Up @@ -110,6 +110,9 @@ that have not been executed yet) or of the TWI bus engine is still processing an
[TIP]
An active transmission can be terminated at any time by disabling the TWI module. This will also clear the data/command FIFO.

[TIP]
The current state of the TWI bus lines (SCL and SDA) can be checked by software via the `TWI_CTRL_SENSE_*` control register bits.

[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.
Expand All @@ -128,13 +131,15 @@ TWI module is enabled (`TWI_CTRL_EN` = `1`) and the TX FIFO is empty and the TWI
[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
.12+<| `0xfffff900` .12+<| `CTRL` <|`0` `TWI_CTRL_EN` ^| r/w <| TWI enable, reset if cleared
<|`3:1` `TWI_CTRL_PRSC2 : TWI_CTRL_PRSC0` ^| r/w <| 3-bit clock prescaler select
<|`7:4` `TWI_CTRL_CDIV3 : TWI_CTRL_CDIV0` ^| r/w <| 4-bit clock divider
<|`8` `TWI_CTRL_CLKSTR` ^| r/w <| Enable (allow) clock stretching
<|`14:9` - ^| r/- <| _reserved_, read as zero
<|`18:15` `TWI_CTRL_FIFO_MSB : TWI_CTRL_FIFO_LSB` ^| r/- <| FIFO depth; log2(`IO_TWI_FIFO`)
<|`28:12` - ^| r/- <| _reserved_, read as zero
<|`26:12` - ^| r/- <| _reserved_, read as zero
<|`27` `TWI_CTRL_SENSE_SCL` ^| r/- <| current state of the SCL bus line
<|`28` `TWI_CTRL_SENSE_SDA` ^| r/- <| current state of the SDA bus line
<|`29` `TWI_CTRL_TX_FULL` ^| r/- <| set if the TWI bus is claimed by any controller
<|`30` `TWI_CTRL_RX_AVAIL` ^| r/- <| RX FIFO data available
<|`31` `TWI_CTRL_BUSY` ^| r/- <| TWI bus engine busy or TX FIFO not empty
Expand Down
2 changes: 1 addition & 1 deletion rtl/core/neorv32_package.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ package neorv32_package is

-- Architecture Constants -----------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
constant hw_version_c : std_ulogic_vector(31 downto 0) := x"01100607"; -- hardware version
constant hw_version_c : std_ulogic_vector(31 downto 0) := x"01100608"; -- hardware version
constant archid_c : natural := 19; -- official RISC-V architecture ID
constant XLEN : natural := 32; -- native data path width

Expand Down
12 changes: 8 additions & 4 deletions rtl/core/neorv32_twi.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ architecture neorv32_twi_rtl of neorv32_twi is
constant ctrl_fifo_size0_c : natural := 15; -- r/-: log2(fifo size), bit 0 (lsb)
constant ctrl_fifo_size3_c : natural := 18; -- r/-: log2(fifo size), bit 3 (msb)
--
constant ctrl_sense_scl_c : natural := 27; -- r/-: current state of the SCL bus line
constant ctrl_sense_sda_c : natural := 28; -- r/-: current state of the SDA bus line
constant ctrl_tx_full_c : natural := 29; -- r/-: TX FIFO full
constant ctrl_rx_avail_c : natural := 30; -- r/-: RX FIFO data available
constant ctrl_busy_c : natural := 31; -- r/-: Set if TWI unit is busy
Expand Down Expand Up @@ -142,9 +144,11 @@ begin
--
bus_rsp_o.data(ctrl_fifo_size3_c downto ctrl_fifo_size0_c) <= std_ulogic_vector(to_unsigned(index_size_f(IO_TWI_FIFO), 4));
--
bus_rsp_o.data(ctrl_tx_full_c) <= not fifo.tx_free;
bus_rsp_o.data(ctrl_rx_avail_c) <= fifo.rx_avail;
bus_rsp_o.data(ctrl_busy_c) <= engine.busy or fifo.tx_avail; -- bus engine busy or TX FIFO not empty
bus_rsp_o.data(ctrl_sense_scl_c) <= io_con.scl_in_ff(1);
bus_rsp_o.data(ctrl_sense_sda_c) <= io_con.sda_in_ff(1);
bus_rsp_o.data(ctrl_tx_full_c) <= not fifo.tx_free;
bus_rsp_o.data(ctrl_rx_avail_c) <= fifo.rx_avail;
bus_rsp_o.data(ctrl_busy_c) <= engine.busy or fifo.tx_avail; -- bus engine busy or TX FIFO not empty
else -- RX FIFO
bus_rsp_o.data(8 downto 0) <= fifo.rx_rdata; -- ACK + data
end if;
Expand Down Expand Up @@ -378,7 +382,7 @@ begin
engine.state(1 downto 0) <= "00"; -- go back to IDLE
end if;

when others => -- "0---" OFFLINE: TWI deactivated, bus unclaimed
when others => -- "0--" OFFLINE: TWI deactivated, bus unclaimed
-- ------------------------------------------------------------
io_con.scl_out <= '1'; -- SCL driven by pull-up resistor
io_con.sda_out <= '1'; -- SDA driven by pull-up resistor
Expand Down
13 changes: 13 additions & 0 deletions sw/example/demo_twi/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ int main() {
" start - generate (repeated) START condition\n"
" stop - generate STOP condition\n"
" send - write/read single byte to/from bus\n"
" sense - show current SCL and SDA bus levels\n"
"Start a new transmission by generating a START condition. Next, transfer the 7-bit device address\n"
"and the R/W flag. After that, transfer your data to be written or send a 0xFF if you want to read\n"
"data from the bus. Finish the transmission by generating a STOP condition.\n");
Expand All @@ -110,6 +111,10 @@ int main() {
else if (!strcmp(buffer, "send")) {
send_twi();
}
else if (!strcmp(buffer, "sense")) {
neorv32_uart0_printf(" SCL: %u\n", neorv32_twi_sense_scl());
neorv32_uart0_printf(" SDA: %u\n", neorv32_twi_sense_sda());
}
else {
neorv32_uart0_printf("Invalid command. Type 'help' to see all commands.\n");
}
Expand Down Expand Up @@ -168,6 +173,14 @@ void set_clock(void) {
// print new clock frequency
uint32_t clock = neorv32_sysinfo_get_clk() / (4 * PRSC_LUT[prsc] * (1 + cdiv));
neorv32_uart0_printf("\nNew I2C clock: %u Hz\n", clock);

// check if bus lines are OK
if (neorv32_twi_sense_scl() != 1) {
neorv32_uart0_printf("WARNING! SCL bus line is not idle-high! Pull-up missing?\n");
}
if (neorv32_twi_sense_sda() != 1) {
neorv32_uart0_printf("WARNING! SDA bus line is not idle-high! Pull-up missing?\n");
}
}


Expand Down
38 changes: 22 additions & 16 deletions sw/lib/include/neorv32_twi.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,22 +36,24 @@ typedef volatile struct __attribute__((packed,aligned(4))) {

/** TWI control register bits */
enum NEORV32_TWI_CTRL_enum {
TWI_CTRL_EN = 0, /**< TWI control register(0) (r/w): TWI enable */
TWI_CTRL_PRSC0 = 1, /**< TWI control register(1) (r/w): Clock prescaler select bit 0 */
TWI_CTRL_PRSC1 = 2, /**< TWI control register(2) (r/w): Clock prescaler select bit 1 */
TWI_CTRL_PRSC2 = 3, /**< TWI control register(3) (r/w): Clock prescaler select bit 2 */
TWI_CTRL_CDIV0 = 4, /**< TWI control register(4) (r/w): Clock divider bit 0 */
TWI_CTRL_CDIV1 = 5, /**< TWI control register(5) (r/w): Clock divider bit 1 */
TWI_CTRL_CDIV2 = 6, /**< TWI control register(6) (r/w): Clock divider bit 2 */
TWI_CTRL_CDIV3 = 7, /**< TWI control register(7) (r/w): Clock divider bit 3 */
TWI_CTRL_CLKSTR = 8, /**< TWI control register(8) (r/w): Enable/allow clock stretching */

TWI_CTRL_FIFO_LSB = 15, /**< SPI control register(15) (r/-): log2(FIFO size), lsb */
TWI_CTRL_FIFO_MSB = 18, /**< SPI control register(18) (r/-): log2(FIFO size), msb */

TWI_CTRL_TX_FULL = 29, /**< TWI control register(29) (r/-): TX FIFO full */
TWI_CTRL_RX_AVAIL = 30, /**< TWI control register(30) (r/-): RX FIFO data available */
TWI_CTRL_BUSY = 31 /**< TWI control register(31) (r/-): Bus engine busy or TX FIFO not empty */
TWI_CTRL_EN = 0, /**< TWI control register(0) (r/w): TWI enable */
TWI_CTRL_PRSC0 = 1, /**< TWI control register(1) (r/w): Clock prescaler select bit 0 */
TWI_CTRL_PRSC1 = 2, /**< TWI control register(2) (r/w): Clock prescaler select bit 1 */
TWI_CTRL_PRSC2 = 3, /**< TWI control register(3) (r/w): Clock prescaler select bit 2 */
TWI_CTRL_CDIV0 = 4, /**< TWI control register(4) (r/w): Clock divider bit 0 */
TWI_CTRL_CDIV1 = 5, /**< TWI control register(5) (r/w): Clock divider bit 1 */
TWI_CTRL_CDIV2 = 6, /**< TWI control register(6) (r/w): Clock divider bit 2 */
TWI_CTRL_CDIV3 = 7, /**< TWI control register(7) (r/w): Clock divider bit 3 */
TWI_CTRL_CLKSTR = 8, /**< TWI control register(8) (r/w): Enable/allow clock stretching */

TWI_CTRL_FIFO_LSB = 15, /**< SPI control register(15) (r/-): log2(FIFO size), lsb */
TWI_CTRL_FIFO_MSB = 18, /**< SPI control register(18) (r/-): log2(FIFO size), msb */

TWI_CTRL_SENSE_SCL = 27, /**< TWI control register(27) (r/-): current state of the SCL bus line */
TWI_CTRL_SENSE_SDA = 28, /**< TWI control register(28) (r/-): current state of the SDA bus line */
TWI_CTRL_TX_FULL = 29, /**< TWI control register(29) (r/-): TX FIFO full */
TWI_CTRL_RX_AVAIL = 30, /**< TWI control register(30) (r/-): RX FIFO data available */
TWI_CTRL_BUSY = 31 /**< TWI control register(31) (r/-): Bus engine busy or TX FIFO not empty */
};

/** TWI command/data register bits */
Expand All @@ -64,6 +66,7 @@ enum NEORV32_TWI_DCMD_enum {
};
/**@}*/


/**********************************************************************//**
* @name TWI commands
**************************************************************************/
Expand All @@ -85,6 +88,9 @@ int neorv32_twi_get_fifo_depth(void);
void neorv32_twi_disable(void);
void neorv32_twi_enable(void);

int neorv32_twi_sense_scl(void);
int neorv32_twi_sense_sda(void);

int neorv32_twi_busy(void);
int neorv32_twi_get(uint8_t *data);

Expand Down
34 changes: 33 additions & 1 deletion sw/lib/source/neorv32_twi.c
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,39 @@ void neorv32_twi_enable(void) {


/**********************************************************************//**
* Check if TWI is busy (TWI bus engine busy or TX FIFO not empty).
* Get current state of SCL bus line.
*
* @return 1 if SCL is high, 0 if SCL is low.
**************************************************************************/
int neorv32_twi_sense_scl(void) {

if (NEORV32_TWI->CTRL & (1 << TWI_CTRL_SENSE_SCL)) {
return 1;
}
else {
return 0;
}
}


/**********************************************************************//**
* Get current state of SDA bus line.
*
* @return 1 if SDA is high, 0 if SDA is low.
**************************************************************************/
int neorv32_twi_sense_sda(void) {

if (NEORV32_TWI->CTRL & (1 << TWI_CTRL_SENSE_SDA)) {
return 1;
}
else {
return 0;
}
}


/**********************************************************************//**
* Check if TWI controller is busy (TWI bus engine busy or TX FIFO not empty).
*
* @return 0 if idle, 1 if busy
**************************************************************************/
Expand Down
12 changes: 12 additions & 0 deletions sw/svd/neorv32.svd
Original file line number Diff line number Diff line change
Expand Up @@ -1251,6 +1251,18 @@
<access>read-only</access>
<description>TX FIFO full</description>
</field>
<field>
<name>TWI_CTRL_SENSE_SCL</name>
<bitRange>[27:27]</bitRange>
<access>read-only</access>
<description>current state of the SCL bus line</description>
</field>
<field>
<name>TWI_CTRL_SENSE_SDA</name>
<bitRange>[28:28]</bitRange>
<access>read-only</access>
<description>current state of the SDA bus line</description>
</field>
<field>
<name>TWI_CTRL_RX_AVAIL</name>
<bitRange>[30:30]</bitRange>
Expand Down