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

add back TWI clock stretching option #867

Merged
merged 5 commits into from
Apr 2, 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 | Link |
|:----:|:-------:|:--------|:----:|
| 01.04.2024 | 1.9.7.7 | add back TWI clock stretching option | [#867](https://github.com/stnolting/neorv32/pull/867) |
| 26.03.2024 | 1.9.7.6 | :warning: rework TWI module; add optional & configurable command/data FIFO | [#865](https://github.com/stnolting/neorv32/pull/865) |
| 24.03.2024 | 1.9.7.5 | :warning: **interrupt system rework**: rework CPU's FIRQ system; `mip` CSR is now read-only ; :bug: fix DMA fence configuration flag | [#864](https://github.com/stnolting/neorv32/pull/864) |
| 23.03.2024 | 1.9.7.4 | :warning: **interrupt system rework**: rework TWI and XIRQ interrupts | [#860](https://github.com/stnolting/neorv32/pull/860) |
Expand Down
32 changes: 20 additions & 12 deletions docs/datasheet/soc_twi.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ for triggering operation: `CTRL` is the control and status register, `DCMD` is t
Key features:

* Programmable clock speed
* Optional clock stretching
* Generate START / repeated-START and STOP conditions
* Sending & receiving 8 data bits including ACK/NACK
* Generating a host-ACK (ACK send by the TWI controller)
Expand Down Expand Up @@ -74,6 +75,12 @@ _**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%.

.Clock Stretching
[NOTE]
An accessed peripheral can slow down/halt the controller's bus clock by using clock stretching (= actively keeping the
SCL line low). The controller will halt operation in this case. Clock stretching is enabled by setting the
`TWI_CTRL_CLKSTR` bit in the module's control register `CTRL`.


**TWI Transfers**

Expand Down Expand Up @@ -120,16 +127,17 @@ 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
.9+<| `0xfffff900` .9+<| `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
<|`14:8` - ^| 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
<|`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
.3+<| `0xfffff904` .3+<| `DCMD` <|`7:0` `TWI_DCMD_MSB : TWI_DCMD_LSB` ^| r/w <| RX/TX data byte
<|`8` `TWI_DCMD_ACK` ^| r/w <| write: ACK bit sent by controller; read: `1` = device NACK, `0` = device ACK
<|`10:9` `TWI_DCMD_CMD_HI : TWI_DCMD_CMD_LO` ^| r/w <| TWI operation (`00` = NOP, `01` = START conditions, `10` = STOP condition, `11` = data transmission)
.10+<| `0xfffff900` .10+<| `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
<|`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
.3+<| `0xfffff904` .3+<| `DCMD` <|`7:0` `TWI_DCMD_MSB : TWI_DCMD_LSB` ^| r/w <| RX/TX data byte
<|`8` `TWI_DCMD_ACK` ^| r/w <| write: ACK bit sent by controller; read: `1` = device NACK, `0` = device ACK
<|`10:9` `TWI_DCMD_CMD_HI : TWI_DCMD_CMD_LO` ^| r/w <| TWI operation (`00` = NOP, `01` = START conditions, `10` = STOP condition, `11` = data transmission)
|=======================
2 changes: 1 addition & 1 deletion rtl/core/neorv32_package.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ package neorv32_package is

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

Expand Down
47 changes: 28 additions & 19 deletions rtl/core/neorv32_twi.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ architecture neorv32_twi_rtl of neorv32_twi is
constant ctrl_cdiv1_c : natural := 5; -- r/w: clock divider bit 1
constant ctrl_cdiv2_c : natural := 6; -- r/w: clock divider bit 2
constant ctrl_cdiv3_c : natural := 7; -- r/w: clock divider bit 3
constant ctrl_clkstr_en_c : natural := 8; -- r/w: enable clock stretching
--
constant ctrl_fifo_size0_c : natural := 15; -- r/-: log2(fifo size), bit 0 (lsb)
constant ctrl_fifo_size1_c : natural := 16; -- r/-: log2(fifo size), bit 1
Expand All @@ -86,17 +87,18 @@ architecture neorv32_twi_rtl of neorv32_twi is
constant ctrl_busy_c : natural := 31; -- r/-: Set if TWI unit is busy

-- data/command register --
constant data_lsb_c : natural := 0; -- r/w: data byte LSB
constant data_msb_c : natural := 7; -- r/w: data byte MSB
constant data_ack_c : natural := 8; -- r/w: ACK/NACK/MACK
constant data_cmd_lo_c : natural := 9; -- -/w: operation command; 00=NOP, 01=START
constant data_cmd_hi_c : natural := 10; -- -/w: operation command; 10=STOP, 11=DATA
constant dcmd_lsb_c : natural := 0; -- r/w: data byte LSB
constant dcmd_msb_c : natural := 7; -- r/w: data byte MSB
constant dcmd_ack_c : natural := 8; -- r/w: ACK/NACK/MACK
constant dcmd_cmd_lo_c : natural := 9; -- -/w: operation command; 00=NOP, 01=START
constant dcmd_cmd_hi_c : natural := 10; -- -/w: operation command; 10=STOP, 11=DATA

-- control register --
type ctrl_t is record
enable : std_ulogic;
prsc : std_ulogic_vector(2 downto 0);
cdiv : std_ulogic_vector(3 downto 0);
clkstr : std_ulogic;
end record;
signal ctrl : ctrl_t;

Expand All @@ -116,6 +118,7 @@ architecture neorv32_twi_rtl of neorv32_twi is
type clk_gen_t is record
cnt : std_ulogic_vector(3 downto 0); -- clock divider
tick : std_ulogic; -- actual TWI clock tick
halt : std_ulogic; -- halt clock during clock stretching
phase_gen : std_ulogic_vector(3 downto 0); -- clock phase generator
phase_gen_ff : std_ulogic_vector(3 downto 0);
phase : std_ulogic_vector(3 downto 0);
Expand Down Expand Up @@ -165,12 +168,14 @@ begin
ctrl.enable <= bus_req_i.data(ctrl_en_c);
ctrl.prsc <= bus_req_i.data(ctrl_prsc2_c downto ctrl_prsc0_c);
ctrl.cdiv <= bus_req_i.data(ctrl_cdiv3_c downto ctrl_cdiv0_c);
ctrl.clkstr <= bus_req_i.data(ctrl_clkstr_en_c);
end if;
else -- read access
if (bus_req_i.addr(2) = '0') then -- control register
bus_rsp_o.data(ctrl_en_c) <= ctrl.enable;
bus_rsp_o.data(ctrl_prsc2_c downto ctrl_prsc0_c) <= ctrl.prsc;
bus_rsp_o.data(ctrl_cdiv3_c downto ctrl_cdiv0_c) <= ctrl.cdiv;
bus_rsp_o.data(ctrl_clkstr_en_c) <= ctrl.clkstr;
--
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));
--
Expand Down Expand Up @@ -219,7 +224,7 @@ begin
);

fifo.tx_we <= '1' when (bus_req_i.stb = '1') and (bus_req_i.rw = '1') and (bus_req_i.addr(2) = '1') else '0';
fifo.tx_wdata <= bus_req_i.data(data_cmd_hi_c downto data_lsb_c);
fifo.tx_wdata <= bus_req_i.data(dcmd_cmd_hi_c downto dcmd_lsb_c);
fifo.tx_re <= '1' when (engine.busy = '0') and (fifo.tx_avail = '1') and (clk_gen.tick = '1') else '0';


Expand Down Expand Up @@ -289,6 +294,9 @@ begin
end if;
end process clock_generator;

-- global clock generator enable --
clkgen_en_o <= ctrl.enable;

-- generate four non-overlapping clock phases --
phase_generator: process(rstn_i, clk_i)
begin
Expand All @@ -299,7 +307,7 @@ begin
clk_gen.phase_gen_ff <= clk_gen.phase_gen;
if (ctrl.enable = '0') or (engine.busy = '0') then -- disabled or idle
clk_gen.phase_gen <= "0001"; -- make sure to start with a new phase beginning
elsif (clk_gen.tick = '1') then
elsif (clk_gen.tick = '1') and (clk_gen.halt = '0') then -- clock tick and no clock stretching
clk_gen.phase_gen <= clk_gen.phase_gen(2 downto 0) & clk_gen.phase_gen(3); -- rotate left
end if;
end if;
Expand All @@ -311,23 +319,24 @@ begin
clk_gen.phase(2) <= clk_gen.phase_gen_ff(2) and (not clk_gen.phase_gen(2));
clk_gen.phase(3) <= clk_gen.phase_gen_ff(3) and (not clk_gen.phase_gen(3)); -- last step

-- global clock generator enable --
clkgen_en_o <= ctrl.enable;
-- Clock Stretching Detector --
-- controller wants to drive SCL high, but SCL is still pulled low by peripheral --
clk_gen.halt <= '1' when (io_con.scl_out = '1') and (io_con.scl_in_ff(1) = '0') and (ctrl.clkstr = '1') else '0';


-- TWI Bus Engine -------------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
twi_engine: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
io_con.sda_in_ff <= (others => '0');
io_con.scl_in_ff <= (others => '0');
io_con.sda_out <= '0';
io_con.scl_out <= '0';
engine.state <= (others => '0');
engine.bitcnt <= (others => '0');
engine.sreg <= (others => '0');
engine.done <= '0';
io_con.sda_in_ff <= (others => '0');
io_con.scl_in_ff <= (others => '0');
io_con.sda_out <= '0';
io_con.scl_out <= '0';
engine.state <= (others => '0');
engine.bitcnt <= (others => '0');
engine.sreg <= (others => '0');
engine.done <= '0';
elsif rising_edge(clk_i) then
-- input synchronizer --
io_con.sda_in_ff <= io_con.sda_in_ff(0) & io_con.sda_in;
Expand All @@ -343,9 +352,9 @@ begin
when "100" => -- IDLE: waiting for operation requests
-- ------------------------------------------------------------
engine.bitcnt <= (others => '0');
engine.sreg <= fifo.tx_rdata(data_msb_c downto data_lsb_c) & (not fifo.tx_rdata(data_ack_c)); -- data + HOST ACK
engine.sreg <= fifo.tx_rdata(dcmd_msb_c downto dcmd_lsb_c) & (not fifo.tx_rdata(dcmd_ack_c)); -- data + HOST ACK
if (fifo.tx_avail = '1') and (clk_gen.tick = '1') then -- trigger new operation on next TWI clock pulse
engine.state(1 downto 0) <= fifo.tx_rdata(data_cmd_hi_c downto data_cmd_lo_c);
engine.state(1 downto 0) <= fifo.tx_rdata(dcmd_cmd_hi_c downto dcmd_cmd_lo_c);
end if;

when "101" => -- START: generate (repeated) START condition
Expand Down
30 changes: 26 additions & 4 deletions sw/example/demo_twi/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,8 @@ int main() {
neorv32_uart0_printf("This program allows to create TWI transfers by hand.\n"
"Execute 'help' to see the help menu.\n\n");

// configure TWI, second slowest clock
neorv32_twi_setup(CLK_PRSC_2048, 15);
// configure TWI, second slowest clock, no clock stretching allowed
neorv32_twi_setup(CLK_PRSC_2048, 15, 0);

// Main menu
for (;;) {
Expand Down Expand Up @@ -147,13 +147,14 @@ int main() {


/**********************************************************************//**
* TWI clock speed menu
* TWI clock setup
**************************************************************************/
void set_clock(void) {

const uint32_t PRSC_LUT[8] = {2, 4, 8, 64, 128, 1024, 2048, 4096};
char terminal_buffer[2];

// clock prescaler
neorv32_uart0_printf("Select new clock prescaler (0..7; one hex char): ");
neorv32_uart0_scan(terminal_buffer, 2, 1); // 1 hex char plus '\0'
int prsc = (int)hexstr_to_uint(terminal_buffer, strlen(terminal_buffer));
Expand All @@ -163,12 +164,33 @@ void set_clock(void) {
return;
}

// clock divider
neorv32_uart0_printf("\nSelect new clock divider (0..15; one hex char): ");
neorv32_uart0_scan(terminal_buffer, 2, 1); // 1 hex char plus '\0'
int cdiv = (int)hexstr_to_uint(terminal_buffer, strlen(terminal_buffer));

if ((cdiv < 0) || (cdiv > 15)) { // invalid?
neorv32_uart0_printf("\nInvalid selection!\n");
return;
}

// clock stretching
neorv32_uart0_printf("\nEnable clock stretching (y/n)? ");
int clkstr = 0;
char tmp = neorv32_uart0_getc();
neorv32_uart0_putc(tmp);

if ((tmp != 'y') && (tmp != 'n')) { // invalid?
neorv32_uart0_printf("\nInvalid selection!\n");
return;
}

if (tmp == 'y') {
clkstr = 1;
}

// set new configuration
neorv32_twi_setup(prsc, cdiv);
neorv32_twi_setup(prsc, cdiv, clkstr);

// print new clock frequency
uint32_t clock = NEORV32_SYSINFO->CLK / (4 * PRSC_LUT[prsc] * (1 + cdiv));
Expand Down
4 changes: 2 additions & 2 deletions sw/example/processor_check/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -1275,8 +1275,8 @@ int main() {
if (NEORV32_SYSINFO->SOC & (1 << SYSINFO_SOC_IO_TWI)) {
cnt_test++;

// configure TWI with fastest clock
neorv32_twi_setup(CLK_PRSC_2, 0);
// configure TWI with fastest clock, no clock stretching
neorv32_twi_setup(CLK_PRSC_2, 0, 0);

// issue some TWI operations, after they are done the interrupt will be fired
neorv32_twi_generate_start_nonblocking();
Expand Down
3 changes: 2 additions & 1 deletion sw/lib/include/neorv32_twi.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ enum NEORV32_TWI_CTRL_enum {
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 */
Expand Down Expand Up @@ -100,7 +101,7 @@ enum NEORV32_TWI_DCMD_enum {
**************************************************************************/
/**@{*/
int neorv32_twi_available(void);
void neorv32_twi_setup(int prsc, int cdiv);
void neorv32_twi_setup(int prsc, int cdiv, int clkstr);
int neorv32_twi_get_fifo_depth(void);
void neorv32_twi_disable(void);
void neorv32_twi_enable(void);
Expand Down
10 changes: 6 additions & 4 deletions sw/lib/source/neorv32_twi.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,17 @@ int neorv32_twi_available(void) {
*
* @param[in] prsc Clock prescaler select (0..7). See #NEORV32_CLOCK_PRSC_enum.
* @param[in] cdiv Clock divider (0..15).
* @param[in] clkstr Enable (allow) clock stretching.
**************************************************************************/
void neorv32_twi_setup(int prsc, int cdiv) {
void neorv32_twi_setup(int prsc, int cdiv, int clkstr) {

NEORV32_TWI->CTRL = 0; // reset

uint32_t ctrl = 0;
ctrl |= ((uint32_t)( 1) << TWI_CTRL_EN);
ctrl |= ((uint32_t)(prsc & 0x07) << TWI_CTRL_PRSC0);
ctrl |= ((uint32_t)(cdiv & 0x0f) << TWI_CTRL_CDIV0);
ctrl |= ((uint32_t)( 0x1) << TWI_CTRL_EN);
ctrl |= ((uint32_t)(prsc & 0x7) << TWI_CTRL_PRSC0);
ctrl |= ((uint32_t)(cdiv & 0xf) << TWI_CTRL_CDIV0);
ctrl |= ((uint32_t)(clkstr & 0x1) << TWI_CTRL_CLKSTR);
NEORV32_TWI->CTRL = ctrl;
}

Expand Down
5 changes: 5 additions & 0 deletions sw/svd/neorv32.svd
Original file line number Diff line number Diff line change
Expand Up @@ -1214,6 +1214,11 @@
<bitRange>[7:4]</bitRange>
<description>TWI clock divider</description>
</field>
<field>
<name>TWI_CTRL_CLKSTR</name>
<bitRange>[8:8]</bitRange>
<description>Enable (allow) clock stretching</description>
</field>
<field>
<name>TWI_CTRL_FIFO</name>
<bitRange>[18:15]</bitRange>
Expand Down