diff --git a/CHANGELOG.md b/CHANGELOG.md index 38ea1ded6..850960ed2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ mimpid = 0x01040312 -> Version 01.04.03.12 -> v1.4.3.12 | Date | Version | Comment | Ticket | |:----:|:-------:|:--------|:------:| +| 14.12.2024 | 1.10.7.4 | :sparkles: add new module: I2C-compatible **Two-Wire Device Controller (TWD)** | [#1121](https://github.com/stnolting/neorv32/pull/1121) | | 14.12.2024 | 1.10.7.3 | :warning: rework TRNG (change HAL; remove interrupt) | [#1120](https://github.com/stnolting/neorv32/pull/1120) | | 12.12.2024 | 1.10.7.2 | add external memory configuration/initialization options to testbench | [#1119](https://github.com/stnolting/neorv32/pull/1119) | | 11.12.2024 | 1.10.7.1 | :test_tube: shrink bootloader's minimal ISA (`rv32e`) and RAM (256 bytes) requirements | [#1118](https://github.com/stnolting/neorv32/pull/1118) | diff --git a/README.md b/README.md index fec5536f2..ad032bfb4 100644 --- a/README.md +++ b/README.md @@ -162,7 +162,8 @@ allows booting application code via UART or from external SPI flash ([UART](https://stnolting.github.io/neorv32/#_primary_universal_asynchronous_receiver_and_transmitter_uart0), [SPI](https://stnolting.github.io/neorv32/#_serial_peripheral_interface_controller_spi) (SPI host), [SDI](https://stnolting.github.io/neorv32/#_serial_data_interface_controller_sdi) (SPI device), -[TWI/I²C](https://stnolting.github.io/neorv32/#_two_wire_serial_interface_controller_twi), +[TWI](https://stnolting.github.io/neorv32/#_two_wire_serial_interface_controller_twi) (I²C host), +[TWD](https://stnolting.github.io/neorv32/#_two_wire_serial_device_controller_twd) (I²C device), [ONEWIRE/1-Wire](https://stnolting.github.io/neorv32/#_one_wire_serial_interface_controller_onewire)) * general purpose IOs ([GPIO](https://stnolting.github.io/neorv32/#_general_purpose_input_and_output_port_gpio)) and [PWM](https://stnolting.github.io/neorv32/#_pulse_width_modulation_controller_pwm) diff --git a/docs/datasheet/soc.adoc b/docs/datasheet/soc.adoc index abe61f9d2..ae63d6d3b 100644 --- a/docs/datasheet/soc.adoc +++ b/docs/datasheet/soc.adoc @@ -28,7 +28,8 @@ image::neorv32_processor.png[align=center] <<_secondary_universal_asynchronous_receiver_and_transmitter_uart1,**UART1**>>) with optional hardware flow control (RTS/CTS) * _optional_ serial peripheral interface host controller (<<_serial_peripheral_interface_controller_spi,**SPI**>>) with 8 dedicated CS lines * _optional_ 8-bit serial data device interface (<<_serial_data_interface_controller_spi,**SDI**>>) -* _optional_ two wire serial interface controller (<<_two_wire_serial_interface_controller_twi,**TWI**>>), compatible to the I²C standard +* _optional_ two-wire serial interface controller (<<_two_wire_serial_interface_controller_twi,**TWI**>>), compatible to the I²C standard +* _optional_ two-wire serial device controller (<<_two_wire_serial_device_controller_twd,**TWD**>>), compatible to the I²C standard * _optional_ general purpose parallel IO port (<<_general_purpose_input_and_output_port_gpio,**GPIO**>>), 64xOut, 64xIn * _optional_ 32-bit external bus interface, Wishbone b4 / AXI4-Lite compatible (<<_processor_external_bus_interface_xbus,**XBUS**>>) * _optional_ watchdog timer (<<_watchdog_timer_wdt,**WDT**>>) @@ -70,7 +71,7 @@ bits/channels are hardwired to zero. .Tri-State Interfaces [NOTE] -Some interfaces (like the TWI and the 1-Wire bus) require explicit tri-state drivers in the final top module. +Some interfaces (like the TWI, the TWD and the 1-Wire bus) require explicit tri-state drivers in the final top module. .Input/Output Registers [NOTE] @@ -146,6 +147,11 @@ to all inputs and output so the synthesis tool can insert an explicit IO (bounda | `twi_sda_o` | 1 | out | - | serial data line output (pull low only) | `twi_scl_i` | 1 | in | `'H'` | serial clock line sense input | `twi_scl_o` | 1 | out | - | serial clock line output (pull low only) +5+^| **<<_two_wire_serial_device_controller_twd>>** +| `twd_sda_i` | 1 | in | `'H'` | serial data line sense input +| `twd_sda_o` | 1 | out | - | serial data line output (pull low only) +| `twd_scl_i` | 1 | in | `'H'` | serial clock line sense input +| `twd_scl_o` | 1 | out | - | serial clock line output (pull low only) 5+^| **<<_one_wire_serial_interface_controller_onewire>>** | `onewire_i` | 1 | in | `'H'` | 1-wire bus sense input | `onewire_o` | 1 | out | - | 1-wire bus output (pull low only) @@ -293,6 +299,8 @@ The generic type "`suv(x:y)`" is an abbreviation for "`std_ulogic_vector(x downt | `IO_SDI_FIFO` | natural | 1 | Depth of the <<_serial_data_interface_controller_sdi>> FIFO. Has to be a power of two, min 1, max 32768. | `IO_TWI_EN` | boolean | false | Implement the <<_two_wire_serial_interface_controller_twi>>. | `IO_TWI_FIFO` | natural | 1 | Depth of the <<_two_wire_serial_interface_controller_twi>> FIFO. Has to be a power of two, min 1, max 32768. +| `IO_TWD_EN` | boolean | false | Implement the <<_two_wire_serial_device_controller_twd>>. +| `IO_TWD_FIFO` | natural | 1 | Depth of the <<_two_wire_serial_device_controller_twd>> FIFO. Has to be a power of two, min 1, max 32768. | `IO_PWM_NUM_CH` | natural | 0 | Number of channels of the <<_pulse_width_modulation_controller_pwm>> to implement (0..16). | `IO_WDT_EN` | boolean | false | Implement the <<_watchdog_timer_wdt>>. | `IO_TRNG_EN` | boolean | false | Implement the <<_true_random_number_generator_trng>>. @@ -449,7 +457,7 @@ table (the channel number also corresponds to the according FIRQ priority: 0 = h [options="header",grid="rows"] |======================= | Channel | Source | Description -| 0 | _reserved_ | _hardwired to zero_ +| 0 | <<_two_wire_serial_device_controller_twd,TWD>> | TWD FIFO level interrupt | 1 | <<_custom_functions_subsystem_cfs,CFS>> | Custom functions subsystem (CFS) interrupt (user-defined) | 2 | <<_primary_universal_asynchronous_receiver_and_transmitter_uart0,UART0>> | UART0 RX FIFO level interrupt | 3 | <<_primary_universal_asynchronous_receiver_and_transmitter_uart0,UART0>> | UART0 TX FIFO level interrupt @@ -810,6 +818,8 @@ include::soc_sdi.adoc[] include::soc_twi.adoc[] +include::soc_twd.adoc[] + include::soc_onewire.adoc[] include::soc_pwm.adoc[] diff --git a/docs/datasheet/soc_sysinfo.adoc b/docs/datasheet/soc_sysinfo.adoc index e87d45b64..0c25a271a 100644 --- a/docs/datasheet/soc_sysinfo.adoc +++ b/docs/datasheet/soc_sysinfo.adoc @@ -86,7 +86,7 @@ Bit fields in this register are set to all-zero if the according memory system i | `10` | `SYSINFO_SOC_XIP_CACHE` | set if XIP cache is implemented (via top's `XIP_CACHE_EN` generic) | `11` | `SYSINFO_SOC_OCD_AUTH` | set if on-chip debugger authentication is implemented (via top's `OCD_AUTHENTICATION` generic) | `12` | `SYSINFO_SOC_IMEM_ROM` | set if processor-internal IMEM is implemented as pre-initialized ROM (via top's `BOOT_MODE_SELECT` generic; see <<_boot_configuration>>) -| `13` | - | _reserved_, read as zero +| `13` | `SYSINFO_SOC_IO_TWD` | set if TWD is implemented (via top's `IO_TWD_EN` generic) | `14` | `SYSINFO_SOC_IO_DMA` | set if direct memory access controller is implemented (via top's `IO_DMA_EN` generic) | `15` | `SYSINFO_SOC_IO_GPIO` | set if GPIO is implemented (via top's `IO_GPIO_EN` generic) | `16` | `SYSINFO_SOC_IO_MTIME` | set if MTIME is implemented (via top's `IO_MTIME_EN` generic) diff --git a/docs/datasheet/soc_twd.adoc b/docs/datasheet/soc_twd.adoc new file mode 100644 index 000000000..dd74f858f --- /dev/null +++ b/docs/datasheet/soc_twd.adoc @@ -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 +|======================= diff --git a/docs/datasheet/soc_twi.adoc b/docs/datasheet/soc_twi.adoc index 579825b77..d1e4b38cd 100644 --- a/docs/datasheet/soc_twi.adoc +++ b/docs/datasheet/soc_twi.adoc @@ -21,9 +21,14 @@ **Overview** -The NEORV32 TWI implements an I2C-compatible host controller to communicate with arbitrary I2C-devices. +The NEORV32 TWI implements a I²C-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. +.Host-Mode Only +[NOTE] +The NEORV32 TWI controller only supports **host mode**. Transmission are initiated by the processor's TWI controller +and not by an external I²C module. If you are looking for a _device-mode_ module (transactions +initiated by an external host) check out the <<_two_wire_serial_device_controller_twd>>. Key features: @@ -32,7 +37,7 @@ Key features: * 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) -* Configurable data/command FIFO to "program" large TWI sequences without further involvement of the CPU +* Configurable data/command FIFO to "program" large I²C 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`. @@ -42,7 +47,7 @@ for triggering operations: the control and status register `CTRL` and the comman 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`). +is shown below (here, `sda_io` and `scl_io` are the actual I²C bus lines, which are of type `std_logic`). .TWI VHDL Tristate Driver Example [source,VHDL] @@ -111,7 +116,7 @@ that have not been executed yet) or of the TWI bus engine is still processing an 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. +The current state of the I²C 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` diff --git a/docs/figures/neorv32_processor.png b/docs/figures/neorv32_processor.png index f5f1cc010..62cb96477 100644 Binary files a/docs/figures/neorv32_processor.png and b/docs/figures/neorv32_processor.png differ diff --git a/docs/figures/twd_sequences.png b/docs/figures/twd_sequences.png new file mode 100644 index 000000000..7471b485b Binary files /dev/null and b/docs/figures/twd_sequences.png differ diff --git a/docs/sources/twd_sequences.json b/docs/sources/twd_sequences.json new file mode 100644 index 000000000..4f2a3705b --- /dev/null +++ b/docs/sources/twd_sequences.json @@ -0,0 +1,34 @@ +{signal: [ + [ + "write byte", + {name: 'SDA', wave: '10.7..7..7..7..7..7..7..0..0..x|.', node: 'a.b.....................c..d..e', data: ['A6', 'A5', 'A4', 'A3', 'A2', 'A1', 'A0']}, + {name: 'SCL', wave: '1.0.10.10.10.10.10.10.10.10.10.|.'}, + {}, + {name: 'SDA', wave: 'x|.5..5..5..5..5..5..5..5..0..0.1', node: '...........................f..gh.i', data: ['D7', 'D6', 'D5', 'D4', 'D3', 'D2', 'D1', 'D0']}, + {name: 'SCL', wave: '0|..10.10.10.10.10.10.10.10.10.1.'} + ], + {}, + {}, + [ + "read byte", + {name: 'SDA', wave: '10.7..7..7..7..7..7..7..1..0..x|.', node: 'j.k.....................l..m..n', data: ['A6', 'A5', 'A4', 'A3', 'A2', 'A1', 'A0']}, + {name: 'SCL', wave: '1.0.10.10.10.10.10.10.10.10.10.|.'}, + {}, + {name: 'SDA', wave: 'x|.9..9..9..9..9..9..9..9..0..0.1', node: '...........................o..pq.r', data: ['D7', 'D6', 'D5', 'D4', 'D3', 'D2', 'D1', 'D0']}, + {name: 'SCL', wave: '0|..10.10.10.10.10.10.10.10.10.1.'} + ] + ], + edge: [ + 'a-b START', + 'c-d WRITE', + 'd-e ACK by TWD', + 'f-g ACK by TWD', + 'h-i STOP', + + 'j-k START', + 'l-m READ', + 'm-n ACK by TWD', + 'o-p ACK by HOST', + 'q-r STOP' + ] +} diff --git a/rtl/core/neorv32_package.vhd b/rtl/core/neorv32_package.vhd index 2f6916757..970a16211 100644 --- a/rtl/core/neorv32_package.vhd +++ b/rtl/core/neorv32_package.vhd @@ -29,7 +29,7 @@ package neorv32_package is -- Architecture Constants ----------------------------------------------------------------- -- ------------------------------------------------------------------------------------------- - constant hw_version_c : std_ulogic_vector(31 downto 0) := x"01100703"; -- hardware version + constant hw_version_c : std_ulogic_vector(31 downto 0) := x"01100704"; -- hardware version constant archid_c : natural := 19; -- official RISC-V architecture ID constant XLEN : natural := 32; -- native data path width @@ -72,7 +72,7 @@ package neorv32_package is --constant base_io_???_c : std_ulogic_vector(31 downto 0) := x"ffffe700"; -- reserved --constant base_io_???_c : std_ulogic_vector(31 downto 0) := x"ffffe800"; -- reserved --constant base_io_???_c : std_ulogic_vector(31 downto 0) := x"ffffe900"; -- reserved ---constant base_io_???_c : std_ulogic_vector(31 downto 0) := x"ffffea00"; -- reserved + constant base_io_twd_c : std_ulogic_vector(31 downto 0) := x"ffffea00"; constant base_io_cfs_c : std_ulogic_vector(31 downto 0) := x"ffffeb00"; constant base_io_slink_c : std_ulogic_vector(31 downto 0) := x"ffffec00"; constant base_io_dma_c : std_ulogic_vector(31 downto 0) := x"ffffed00"; @@ -806,6 +806,8 @@ package neorv32_package is IO_SDI_FIFO : natural range 1 to 2**15 := 1; IO_TWI_EN : boolean := false; IO_TWI_FIFO : natural range 1 to 2**15 := 1; + IO_TWD_EN : boolean := false; + IO_TWD_FIFO : natural range 1 to 2**15 := 1; IO_PWM_NUM_CH : natural range 0 to 16 := 0; IO_WDT_EN : boolean := false; IO_TRNG_EN : boolean := false; @@ -889,6 +891,11 @@ package neorv32_package is twi_sda_o : out std_ulogic; twi_scl_i : in std_ulogic := 'H'; twi_scl_o : out std_ulogic; + -- TWD (available if IO_TWD_EN = true) -- + twd_sda_i : in std_ulogic := 'H'; + twd_sda_o : out std_ulogic; + twd_scl_i : in std_ulogic := 'H'; + twd_scl_o : out std_ulogic; -- 1-Wire Interface (available if IO_ONEWIRE_EN = true) -- onewire_i : in std_ulogic := 'H'; onewire_o : out std_ulogic; diff --git a/rtl/core/neorv32_sysinfo.vhd b/rtl/core/neorv32_sysinfo.vhd index 06010acc9..0272d4c8d 100644 --- a/rtl/core/neorv32_sysinfo.vhd +++ b/rtl/core/neorv32_sysinfo.vhd @@ -49,6 +49,7 @@ entity neorv32_sysinfo is IO_SPI_EN : boolean; -- implement serial peripheral interface (SPI)? IO_SDI_EN : boolean; -- implement serial data interface (SDI)? IO_TWI_EN : boolean; -- implement two-wire interface (TWI)? + IO_TWD_EN : boolean; -- implement two-wire device (TWD)? IO_PWM_EN : boolean; -- implement pulse-width modulation controller (PWM)? IO_WDT_EN : boolean; -- implement watch dog timer (WDT)? IO_TRNG_EN : boolean; -- implement true random number generator (TRNG)? @@ -123,7 +124,7 @@ begin sysinfo(2)(10) <= '1' when xip_cache_en_c else '0'; -- execute in-place cache implemented? sysinfo(2)(11) <= '1' when ocd_auth_en_c else '0'; -- on-chip debugger authentication implemented? sysinfo(2)(12) <= '1' when int_imem_rom_c else '0'; -- processor-internal instruction memory implemented as pre-initialized ROM? - sysinfo(2)(13) <= '0'; -- reserved + sysinfo(2)(13) <= '1' when IO_TWD_EN else '0'; -- two-wire device (TWD) implemented? sysinfo(2)(14) <= '1' when IO_DMA_EN else '0'; -- direct memory access controller (DMA) implemented? sysinfo(2)(15) <= '1' when IO_GPIO_EN else '0'; -- general purpose input/output port unit (GPIO) implemented? sysinfo(2)(16) <= '1' when IO_MTIME_EN else '0'; -- machine system timer (MTIME) implemented? diff --git a/rtl/core/neorv32_top.vhd b/rtl/core/neorv32_top.vhd index d0d70ce43..9c6b6663c 100644 --- a/rtl/core/neorv32_top.vhd +++ b/rtl/core/neorv32_top.vhd @@ -56,7 +56,7 @@ entity neorv32_top is RISCV_ISA_Zknd : boolean := false; -- implement cryptography NIST AES decryption extension RISCV_ISA_Zkne : boolean := false; -- implement cryptography NIST AES encryption extension RISCV_ISA_Zknh : boolean := false; -- implement cryptography NIST hash extension - RISCV_ISA_Zksed : boolean := false; -- implement ShangMi block cypher extension + RISCV_ISA_Zksed : boolean := false; -- implement ShangMi block cipher extension RISCV_ISA_Zksh : boolean := false; -- implement ShangMi hash extension RISCV_ISA_Zmmul : boolean := false; -- implement multiply-only M sub-extension RISCV_ISA_Zxcfu : boolean := false; -- implement custom (instr.) functions unit @@ -116,21 +116,23 @@ entity neorv32_top is IO_GPIO_NUM : natural range 0 to 64 := 0; -- number of GPIO input/output pairs (0..64) IO_MTIME_EN : boolean := false; -- implement machine system timer (MTIME)? IO_UART0_EN : boolean := false; -- implement primary universal asynchronous receiver/transmitter (UART0)? - IO_UART0_RX_FIFO : natural range 1 to 2**15 := 1; -- RX fifo depth, has to be a power of two, min 1 - IO_UART0_TX_FIFO : natural range 1 to 2**15 := 1; -- TX fifo depth, has to be a power of two, min 1 + IO_UART0_RX_FIFO : natural range 1 to 2**15 := 1; -- RX FIFO depth, has to be a power of two, min 1 + IO_UART0_TX_FIFO : natural range 1 to 2**15 := 1; -- TX FIFO depth, has to be a power of two, min 1 IO_UART1_EN : boolean := false; -- implement secondary universal asynchronous receiver/transmitter (UART1)? - IO_UART1_RX_FIFO : natural range 1 to 2**15 := 1; -- RX fifo depth, has to be a power of two, min 1 - IO_UART1_TX_FIFO : natural range 1 to 2**15 := 1; -- TX fifo depth, has to be a power of two, min 1 + IO_UART1_RX_FIFO : natural range 1 to 2**15 := 1; -- RX FIFO depth, has to be a power of two, min 1 + IO_UART1_TX_FIFO : natural range 1 to 2**15 := 1; -- TX FIFO depth, has to be a power of two, min 1 IO_SPI_EN : boolean := false; -- implement serial peripheral interface (SPI)? - IO_SPI_FIFO : natural range 1 to 2**15 := 1; -- RTX fifo depth, has to be a power of two, min 1 + IO_SPI_FIFO : natural range 1 to 2**15 := 1; -- RTX FIFO depth, has to be a power of two, min 1 IO_SDI_EN : boolean := false; -- implement serial data interface (SDI)? - IO_SDI_FIFO : natural range 1 to 2**15 := 1; -- RTX fifo depth, has to be zero or a power of two, min 1 + IO_SDI_FIFO : natural range 1 to 2**15 := 1; -- RTX FIFO depth, has to be zero or a power of two, min 1 IO_TWI_EN : boolean := false; -- implement two-wire interface (TWI)? - IO_TWI_FIFO : natural range 1 to 2**15 := 1; -- RTX fifo depth, has to be zero or a power of two, min 1 + IO_TWI_FIFO : natural range 1 to 2**15 := 1; -- RTX FIFO depth, has to be zero or a power of two, min 1 + IO_TWD_EN : boolean := false; -- implement two-wire device (TWD)? + IO_TWD_FIFO : natural range 1 to 2**15 := 1; -- RTX FIFO depth, has to be zero or a power of two, min 1 IO_PWM_NUM_CH : natural range 0 to 16 := 0; -- number of PWM channels to implement (0..16) IO_WDT_EN : boolean := false; -- implement watch dog timer (WDT)? IO_TRNG_EN : boolean := false; -- implement true random number generator (TRNG)? - IO_TRNG_FIFO : natural range 1 to 2**15 := 1; -- data fifo depth, has to be a power of two, min 1 + IO_TRNG_FIFO : natural range 1 to 2**15 := 1; -- data FIFO depth, has to be a power of two, min 1 IO_CFS_EN : boolean := false; -- implement custom functions subsystem (CFS)? IO_CFS_CONFIG : std_ulogic_vector(31 downto 0) := x"00000000"; -- custom CFS configuration generic IO_CFS_IN_SIZE : natural := 32; -- size of CFS input conduit in bits @@ -139,11 +141,11 @@ entity neorv32_top is IO_NEOLED_TX_FIFO : natural range 1 to 2**15 := 1; -- NEOLED FIFO depth, has to be a power of two, min 1 IO_GPTMR_EN : boolean := false; -- implement general purpose timer (GPTMR)? IO_ONEWIRE_EN : boolean := false; -- implement 1-wire interface (ONEWIRE)? - IO_ONEWIRE_FIFO : natural range 1 to 2**15 := 1; -- RTX fifo depth, has to be zero or a power of two, min 1 + IO_ONEWIRE_FIFO : natural range 1 to 2**15 := 1; -- RTX FIFO depth, has to be zero or a power of two, min 1 IO_DMA_EN : boolean := false; -- implement direct memory access controller (DMA)? IO_SLINK_EN : boolean := false; -- implement stream link interface (SLINK)? - IO_SLINK_RX_FIFO : natural range 1 to 2**15 := 1; -- RX fifo depth, has to be a power of two, min 1 - IO_SLINK_TX_FIFO : natural range 1 to 2**15 := 1; -- TX fifo depth, has to be a power of two, min 1 + IO_SLINK_RX_FIFO : natural range 1 to 2**15 := 1; -- RX FIFO depth, has to be a power of two, min 1 + IO_SLINK_TX_FIFO : natural range 1 to 2**15 := 1; -- TX FIFO depth, has to be a power of two, min 1 IO_CRC_EN : boolean := false -- implement cyclic redundancy check unit (CRC)? ); port ( @@ -221,6 +223,12 @@ entity neorv32_top is twi_scl_i : in std_ulogic := 'H'; -- serial clock line sense input twi_scl_o : out std_ulogic; -- serial clock line output (pull low only) + -- TWD (available if IO_TWD_EN = true) -- + twd_sda_i : in std_ulogic := 'H'; -- serial data line sense input + twd_sda_o : out std_ulogic; -- serial data line output (pull low only) + twd_scl_i : in std_ulogic := 'H'; -- serial clock line sense input + twd_scl_o : out std_ulogic; -- serial clock line output (pull low only) + -- 1-Wire Interface (available if IO_ONEWIRE_EN = true) -- onewire_i : in std_ulogic := 'H'; -- 1-wire bus sense input onewire_o : out std_ulogic; -- 1-wire bus output (pull low only) @@ -286,11 +294,11 @@ architecture neorv32_top_rtl of neorv32_top is signal clk_gen : std_ulogic_vector(7 downto 0); -- scaled clock-enables -- type clk_gen_en_enum_t is ( - CG_CFS, CG_UART0, CG_UART1, CG_SPI, CG_TWI, CG_PWM, CG_WDT, CG_NEOLED, CG_GPTMR, CG_XIP, CG_ONEWIRE + CG_CFS, CG_UART0, CG_UART1, CG_SPI, CG_TWI, CG_TWD, CG_PWM, CG_WDT, CG_NEOLED, CG_GPTMR, CG_XIP, CG_ONEWIRE ); type clk_gen_en_t is array (clk_gen_en_enum_t) of std_ulogic; signal clk_gen_en : clk_gen_en_t; - signal clk_gen_en2 : std_ulogic_vector(10 downto 0); + signal clk_gen_en2 : std_ulogic_vector(11 downto 0); -- CPU status -- signal cpu_debug, cpu_sleep : std_ulogic; @@ -314,7 +322,7 @@ architecture neorv32_top_rtl of neorv32_top is type io_devices_enum_t is ( IODEV_OCD, IODEV_SYSINFO, IODEV_NEOLED, IODEV_GPIO, IODEV_WDT, IODEV_TRNG, IODEV_TWI, IODEV_SPI, IODEV_SDI, IODEV_UART1, IODEV_UART0, IODEV_MTIME, IODEV_XIRQ, IODEV_ONEWIRE, - IODEV_GPTMR, IODEV_PWM, IODEV_XIP, IODEV_CRC, IODEV_DMA, IODEV_SLINK, IODEV_CFS + IODEV_GPTMR, IODEV_PWM, IODEV_XIP, IODEV_CRC, IODEV_DMA, IODEV_SLINK, IODEV_CFS, IODEV_TWD ); type iodev_req_t is array (io_devices_enum_t) of bus_req_t; type iodev_rsp_t is array (io_devices_enum_t) of bus_rsp_t; @@ -323,7 +331,7 @@ architecture neorv32_top_rtl of neorv32_top is -- IRQs -- type firq_enum_t is ( - FIRQ_reserved, FIRQ_UART0_RX, FIRQ_UART0_TX, FIRQ_UART1_RX, FIRQ_UART1_TX, FIRQ_SPI, FIRQ_SDI, FIRQ_TWI, + FIRQ_TWD, FIRQ_UART0_RX, FIRQ_UART0_TX, FIRQ_UART1_RX, FIRQ_UART1_TX, FIRQ_SPI, FIRQ_SDI, FIRQ_TWI, FIRQ_CFS, FIRQ_NEOLED, FIRQ_XIRQ, FIRQ_GPTMR, FIRQ_ONEWIRE, FIRQ_DMA, FIRQ_SLINK_RX, FIRQ_SLINK_TX ); type firq_t is array (firq_enum_t) of std_ulogic; @@ -364,6 +372,7 @@ begin cond_sel_string_f(IO_SPI_EN, "SPI ", "") & cond_sel_string_f(IO_SDI_EN, "SDI ", "") & cond_sel_string_f(IO_TWI_EN, "TWI ", "") & + cond_sel_string_f(IO_TWD_EN, "TWD ", "") & cond_sel_string_f(io_pwm_en_c, "PWM ", "") & cond_sel_string_f(IO_WDT_EN, "WDT ", "") & cond_sel_string_f(IO_TRNG_EN, "TRNG ", "") & @@ -431,7 +440,7 @@ begin -- ------------------------------------------------------------------------------------------- neorv32_sys_clock_inst: entity neorv32.neorv32_sys_clock generic map ( - NUM_EN => 11 + NUM_EN => clk_gen_en2'length ) port map ( clk_i => clk_i, @@ -441,9 +450,9 @@ begin ); -- fresh clocks anyone? -- - clk_gen_en2 <= clk_gen_en(CG_WDT) & clk_gen_en(CG_UART0) & clk_gen_en(CG_UART1) & clk_gen_en(CG_SPI) & - clk_gen_en(CG_TWI) & clk_gen_en(CG_PWM) & clk_gen_en(CG_WDT) & clk_gen_en(CG_NEOLED) & - clk_gen_en(CG_GPTMR) & clk_gen_en(CG_XIP) & clk_gen_en(CG_ONEWIRE); + clk_gen_en2 <= clk_gen_en(CG_WDT) & clk_gen_en(CG_UART0) & clk_gen_en(CG_UART1) & clk_gen_en(CG_SPI) & + clk_gen_en(CG_TWI) & clk_gen_en(CG_TWD) & clk_gen_en(CG_PWM) & clk_gen_en(CG_WDT) & + clk_gen_en(CG_NEOLED) & clk_gen_en(CG_GPTMR) & clk_gen_en(CG_XIP) & clk_gen_en(CG_ONEWIRE); end generate; -- /generators @@ -544,7 +553,7 @@ begin ); -- fast interrupt requests (FIRQs) -- - cpu_firq(0) <= '0'; -- reserved + cpu_firq(0) <= firq(FIRQ_TWD); cpu_firq(1) <= firq(FIRQ_CFS); cpu_firq(2) <= firq(FIRQ_UART0_RX); cpu_firq(3) <= firq(FIRQ_UART0_TX); @@ -1023,17 +1032,17 @@ begin DEV_18_EN => IO_DMA_EN, DEV_18_BASE => base_io_dma_c, DEV_19_EN => IO_SLINK_EN, DEV_19_BASE => base_io_slink_c, DEV_20_EN => IO_CFS_EN, DEV_20_BASE => base_io_cfs_c, - DEV_21_EN => false, DEV_31_BASE => (others => '0'), -- reserved - DEV_22_EN => false, DEV_30_BASE => (others => '0'), -- reserved - DEV_23_EN => false, DEV_29_BASE => (others => '0'), -- reserved - DEV_24_EN => false, DEV_28_BASE => (others => '0'), -- reserved - DEV_25_EN => false, DEV_27_BASE => (others => '0'), -- reserved + DEV_21_EN => IO_TWD_EN, DEV_21_BASE => base_io_twd_c, + DEV_22_EN => false, DEV_22_BASE => (others => '0'), -- reserved + DEV_23_EN => false, DEV_23_BASE => (others => '0'), -- reserved + DEV_24_EN => false, DEV_24_BASE => (others => '0'), -- reserved + DEV_25_EN => false, DEV_25_BASE => (others => '0'), -- reserved DEV_26_EN => false, DEV_26_BASE => (others => '0'), -- reserved - DEV_27_EN => false, DEV_25_BASE => (others => '0'), -- reserved - DEV_28_EN => false, DEV_24_BASE => (others => '0'), -- reserved - DEV_29_EN => false, DEV_23_BASE => (others => '0'), -- reserved - DEV_30_EN => false, DEV_22_BASE => (others => '0'), -- reserved - DEV_31_EN => false, DEV_21_BASE => (others => '0') -- reserved + DEV_27_EN => false, DEV_27_BASE => (others => '0'), -- reserved + DEV_28_EN => false, DEV_28_BASE => (others => '0'), -- reserved + DEV_29_EN => false, DEV_29_BASE => (others => '0'), -- reserved + DEV_30_EN => false, DEV_30_BASE => (others => '0'), -- reserved + DEV_31_EN => false, DEV_31_BASE => (others => '0') -- reserved ) port map ( clk_i => clk_i, @@ -1061,7 +1070,7 @@ begin dev_18_req_o => iodev_req(IODEV_DMA), dev_18_rsp_i => iodev_rsp(IODEV_DMA), dev_19_req_o => iodev_req(IODEV_SLINK), dev_19_rsp_i => iodev_rsp(IODEV_SLINK), dev_20_req_o => iodev_req(IODEV_CFS), dev_20_rsp_i => iodev_rsp(IODEV_CFS), - dev_21_req_o => open, dev_21_rsp_i => rsp_terminate_c, -- reserved + dev_21_req_o => iodev_req(IODEV_TWD), dev_21_rsp_i => iodev_rsp(IODEV_TWD), dev_22_req_o => open, dev_22_rsp_i => rsp_terminate_c, -- reserved dev_23_req_o => open, dev_23_rsp_i => rsp_terminate_c, -- reserved dev_24_req_o => open, dev_24_rsp_i => rsp_terminate_c, -- reserved @@ -1355,6 +1364,39 @@ begin end generate; + -- Two-Wire Device (TWD) ------------------------------------------------------------------ + -- ------------------------------------------------------------------------------------------- + neorv32_twd_inst_true: + if IO_TWD_EN generate + neorv32_twd_inst: entity neorv32.neorv32_twd + generic map ( + TWD_FIFO => IO_TWD_FIFO + ) + port map ( + clk_i => clk_i, + rstn_i => rstn_sys, + bus_req_i => iodev_req(IODEV_TWD), + bus_rsp_o => iodev_rsp(IODEV_TWD), + clkgen_en_o => clk_gen_en(CG_TWD), + clkgen_i => clk_gen, + twd_sda_i => twd_sda_i, + twd_sda_o => twd_sda_o, + twd_scl_i => twd_scl_i, + twd_scl_o => twd_scl_o, + irq_o => firq(FIRQ_TWD) + ); + end generate; + + neorv32_twd_inst_false: + if not IO_TWD_EN generate + iodev_rsp(IODEV_TWD) <= rsp_terminate_c; + twd_sda_o <= '1'; + twd_scl_o <= '1'; + clk_gen_en(CG_TWD) <= '0'; + firq(FIRQ_TWD) <= '0'; + end generate; + + -- Pulse-Width Modulation Controller (PWM) ------------------------------------------------ -- ------------------------------------------------------------------------------------------- neorv32_pwm_inst_true: @@ -1611,6 +1653,7 @@ begin IO_SPI_EN => IO_SPI_EN, IO_SDI_EN => IO_SDI_EN, IO_TWI_EN => IO_TWI_EN, + IO_TWD_EN => IO_TWD_EN, IO_PWM_EN => io_pwm_en_c, IO_WDT_EN => IO_WDT_EN, IO_TRNG_EN => IO_TRNG_EN, diff --git a/rtl/core/neorv32_twd.vhd b/rtl/core/neorv32_twd.vhd new file mode 100644 index 000000000..a9d6a8815 --- /dev/null +++ b/rtl/core/neorv32_twd.vhd @@ -0,0 +1,441 @@ +-- ================================================================================ -- +-- NEORV32 SoC - Two-Wire Device (TWD) -- +-- -------------------------------------------------------------------------------- -- +-- The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 -- +-- Copyright (c) NEORV32 contributors. -- +-- Copyright (c) 2020 - 2024 Stephan Nolting. All rights reserved. -- +-- Licensed under the BSD-3-Clause license, see LICENSE for details. -- +-- SPDX-License-Identifier: BSD-3-Clause -- +-- ================================================================================ -- + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library neorv32; +use neorv32.neorv32_package.all; + +entity neorv32_twd is + generic ( + TWD_FIFO : natural range 1 to 2**15 -- RTX FIFO depth, has to be a power of two, min 1 + ); + port ( + clk_i : in std_ulogic; -- global clock line + rstn_i : in std_ulogic; -- global reset line, low-active, async + bus_req_i : in bus_req_t; -- bus request + bus_rsp_o : out bus_rsp_t; -- bus response + clkgen_en_o : out std_ulogic; -- enable clock generator + clkgen_i : in std_ulogic_vector(7 downto 0); + twd_sda_i : in std_ulogic; -- serial data line input + twd_sda_o : out std_ulogic; -- serial data line output + twd_scl_i : in std_ulogic; -- serial clock line input + twd_scl_o : out std_ulogic; -- serial clock line output + irq_o : out std_ulogic -- interrupt + ); +end neorv32_twd; + +architecture neorv32_twd_rtl of neorv32_twd is + + -- control register -- + constant ctrl_en_c : natural := 0; -- r/w: module enable (reset when zero) + constant ctrl_clr_rx_c : natural := 1; -- -/w: clear RX FIFO (flag auto-clears) + constant ctrl_clr_tx_c : natural := 2; -- -/w: clear TX FIFO (flag auto-clears) + constant ctrl_fsel_c : natural := 3; -- r/w: input filter / sample clock select + constant ctrl_dev_addr0_c : natural := 4; -- r/w: device address, bit 0 (LSB) + constant ctrl_dev_addr6_c : natural := 10; -- r/w: device address, bit 6 (MSB) + constant ctrl_irq_rx_avail_c : natural := 11; -- r/w: IRQ if RX FIFO data available + constant ctrl_irq_rx_full_c : natural := 12; -- r/w: IRQ if RX FIFO full + constant ctrl_irq_tx_empty_c : natural := 13; -- r/w: IRQ if TX FIFO empty + -- + 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_rx_avail_c : natural := 25; -- r/-: RX FIFO data available + constant ctrl_rx_full_c : natural := 26; -- r/-: RX FIFO full + constant ctrl_tx_empty_c : natural := 27; -- r/-: TX FIFO empty + constant ctrl_tx_full_c : natural := 28; -- r/-: TX FIFO full + constant ctrl_sense_scl_c : natural := 29; -- r/-: current state of the SCL bus line + constant ctrl_sense_sda_c : natural := 30; -- r/-: current state of the SDA bus line + constant ctrl_busy_c : natural := 31; -- r/-: bus engine is busy (transaction in progress) + + -- control register -- + type ctrl_t is record + enable : std_ulogic; + clr_rx : std_ulogic; + clr_tx : std_ulogic; + fsel : std_ulogic; + device_addr : std_ulogic_vector(6 downto 0); + irq_rx_avail : std_ulogic; + irq_rx_full : std_ulogic; + irq_tx_empty : std_ulogic; + end record; + signal ctrl : ctrl_t; + + -- bus sample logic -- + type smp_t is record + clk_en : std_ulogic; -- sample clock + valid : std_ulogic; -- valid sample + sda_sreg : std_ulogic_vector(2 downto 0); -- synchronizer + scl_sreg : std_ulogic_vector(2 downto 0); -- synchronizer + sda : std_ulogic; -- current SDA state + scl : std_ulogic; -- current SCL state + scl_rise : std_ulogic; -- SCL rising edge + scl_fall : std_ulogic; -- SCL falling edge + start : std_ulogic; -- start condition + stop : std_ulogic; -- stop condition + end record; + signal smp : smp_t; + + -- FIFO interface -- + type fifo_t is record + clr : std_ulogic; -- sync reset, high-active + we : std_ulogic; -- write enable + re : std_ulogic; -- read enable + wdata : std_ulogic_vector(7 downto 0); -- write data + rdata : std_ulogic_vector(7 downto 0); -- read data + avail : std_ulogic; -- data available? + free : std_ulogic; -- free entry available? + end record; + signal rx_fifo, tx_fifo : fifo_t; + + -- bus engine -- + type state_t is (S_IDLE, S_INIT, S_ADDR, S_RESP, S_RTX, S_ACK); + type engine_t is record + state : state_t; -- FSM state + cnt : unsigned(3 downto 0); -- bit counter + sreg : std_ulogic_vector(7 downto 0); -- shift register + cmd : std_ulogic; -- 0 = write, 1 = read + rdata : std_ulogic_vector(7 downto 0); -- read-access data + dout : std_ulogic; -- output bit + busy : std_ulogic; -- bus operation in progress + wr_we : std_ulogic; -- write write-enable + rd_re : std_ulogic; -- read read-enable + end record; + signal engine : engine_t; + +begin + + -- Bus Access ----------------------------------------------------------------------------- + -- ------------------------------------------------------------------------------------------- + bus_access: process(rstn_i, clk_i) + begin + if (rstn_i = '0') then + bus_rsp_o <= rsp_terminate_c; + ctrl.enable <= '0'; + ctrl.clr_rx <= '0'; + ctrl.clr_tx <= '0'; + ctrl.fsel <= '0'; + ctrl.device_addr <= (others => '0'); + ctrl.irq_rx_avail <= '0'; + ctrl.irq_rx_full <= '0'; + ctrl.irq_tx_empty <= '0'; + elsif rising_edge(clk_i) then + -- bus handshake defaults -- + bus_rsp_o.ack <= bus_req_i.stb; + bus_rsp_o.err <= '0'; + bus_rsp_o.data <= (others => '0'); + -- read/write access -- + ctrl.clr_rx <= '0'; -- auto-clear + ctrl.clr_tx <= '0'; -- auto-clear + if (bus_req_i.stb = '1') then + if (bus_req_i.rw = '1') then -- write access + if (bus_req_i.addr(2) = '0') then -- control register + ctrl.enable <= bus_req_i.data(ctrl_en_c); + ctrl.clr_rx <= bus_req_i.data(ctrl_clr_rx_c); + ctrl.clr_tx <= bus_req_i.data(ctrl_clr_tx_c); + ctrl.fsel <= bus_req_i.data(ctrl_fsel_c); + ctrl.device_addr <= bus_req_i.data(ctrl_dev_addr6_c downto ctrl_dev_addr0_c); + ctrl.irq_rx_avail <= bus_req_i.data(ctrl_irq_rx_avail_c); + ctrl.irq_rx_full <= bus_req_i.data(ctrl_irq_rx_full_c); + ctrl.irq_tx_empty <= bus_req_i.data(ctrl_irq_tx_empty_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_fsel_c) <= ctrl.fsel; + bus_rsp_o.data(ctrl_dev_addr6_c downto ctrl_dev_addr0_c) <= ctrl.device_addr; + bus_rsp_o.data(ctrl_irq_rx_avail_c) <= ctrl.irq_rx_avail; + bus_rsp_o.data(ctrl_irq_rx_full_c) <= ctrl.irq_rx_full; + bus_rsp_o.data(ctrl_irq_tx_empty_c) <= ctrl.irq_tx_empty; + -- + bus_rsp_o.data(ctrl_fifo_size3_c downto ctrl_fifo_size0_c) <= std_ulogic_vector(to_unsigned(index_size_f(TWD_FIFO), 4)); + bus_rsp_o.data(ctrl_rx_avail_c) <= rx_fifo.avail; + bus_rsp_o.data(ctrl_rx_full_c) <= not rx_fifo.free; + bus_rsp_o.data(ctrl_tx_empty_c) <= not tx_fifo.avail; + bus_rsp_o.data(ctrl_tx_full_c) <= not tx_fifo.free; + bus_rsp_o.data(ctrl_sense_scl_c) <= smp.scl; + bus_rsp_o.data(ctrl_sense_sda_c) <= smp.sda; + bus_rsp_o.data(ctrl_busy_c) <= engine.busy; + else -- RX FIFO + bus_rsp_o.data(7 downto 0) <= rx_fifo.rdata; + end if; + end if; + end if; + end if; + end process bus_access; + + -- enable SoC clock generator -- + clkgen_en_o <= ctrl.enable; + + + -- Data FIFO ("Ring Buffer") -------------------------------------------------------------- + -- ------------------------------------------------------------------------------------------- + + -- TX FIFO -- + tx_fifo_inst: entity neorv32.neorv32_fifo + generic map ( + FIFO_DEPTH => TWD_FIFO, + FIFO_WIDTH => 8, + FIFO_RSYNC => true, + FIFO_SAFE => true, + FULL_RESET => false + ) + port map ( + -- control -- + clk_i => clk_i, + rstn_i => rstn_i, + clear_i => tx_fifo.clr, + half_o => open, + -- write port -- + wdata_i => tx_fifo.wdata, + we_i => tx_fifo.we, + free_o => tx_fifo.free, + -- read port -- + re_i => tx_fifo.re, + rdata_o => tx_fifo.rdata, + avail_o => tx_fifo.avail + ); + + tx_fifo.clr <= '1' when (ctrl.enable = '0') or (ctrl.clr_tx = '1') else '0'; + tx_fifo.we <= '1' when (bus_req_i.stb = '1') and (bus_req_i.rw = '1') and (bus_req_i.addr(2) = '1') else '0'; + tx_fifo.wdata <= bus_req_i.data(7 downto 0); + tx_fifo.re <= engine.rd_re; + engine.rdata <= tx_fifo.rdata when (tx_fifo.avail = '1') else (others => '1'); -- read ones when TX FIFO is drained + + + -- RX FIFO -- + rx_fifo_inst: entity neorv32.neorv32_fifo + generic map ( + FIFO_DEPTH => TWD_FIFO, + FIFO_WIDTH => 8, + FIFO_RSYNC => true, + FIFO_SAFE => true, + FULL_RESET => false + ) + port map ( + -- control -- + clk_i => clk_i, + rstn_i => rstn_i, + clear_i => rx_fifo.clr, + half_o => open, + -- write port -- + wdata_i => rx_fifo.wdata, + we_i => rx_fifo.we, + free_o => rx_fifo.free, + -- read port -- + re_i => rx_fifo.re, + rdata_o => rx_fifo.rdata, + avail_o => rx_fifo.avail + ); + + rx_fifo.clr <= '1' when (ctrl.enable = '0') or (ctrl.clr_rx = '1') else '0'; + rx_fifo.wdata <= engine.sreg; + rx_fifo.we <= engine.wr_we; + rx_fifo.re <= '1' when (bus_req_i.stb = '1') and (bus_req_i.rw = '0') and (bus_req_i.addr(2) = '1') else '0'; + + + -- Interrupt Generator -- + irq_trigger: process(rstn_i, clk_i) + begin + if (rstn_i = '0') then + irq_o <= '0'; + elsif rising_edge(clk_i) then + irq_o <= ctrl.enable and ( + (ctrl.irq_rx_avail and rx_fifo.avail) or -- RX FIFO data available + (ctrl.irq_rx_full and (not rx_fifo.free)) or -- RX FIFO full + (ctrl.irq_tx_empty and (not tx_fifo.avail))); -- TX FIFO empty + end if; + end process irq_trigger; + + + -- Bus Sample Logic ----------------------------------------------------------------------- + -- ------------------------------------------------------------------------------------------- + synchronizer: process(rstn_i, clk_i) + begin + if (rstn_i = '0') then + smp.sda_sreg <= (others => '0'); + smp.scl_sreg <= (others => '0'); + smp.valid <= '0'; + elsif rising_edge(clk_i) then + -- input register -- + smp.sda_sreg(0) <= to_stdulogic(to_bit(twd_sda_i)); -- "to_bit" to avoid hardware-vs-simulation mismatch + smp.scl_sreg(0) <= to_stdulogic(to_bit(twd_scl_i)); + -- sample register -- + smp.valid <= '0'; + if (ctrl.enable = '1') then + if (smp.clk_en = '1') then + smp.valid <= '1'; -- valid sample + smp.sda_sreg(2 downto 1) <= smp.sda_sreg(1 downto 0); + smp.scl_sreg(2 downto 1) <= smp.scl_sreg(1 downto 0); + end if; + else + smp.sda_sreg(2 downto 1) <= (others => '1'); + smp.scl_sreg(2 downto 1) <= (others => '1'); + end if; + end if; + end process synchronizer; + + -- sample clock for input "filtering" -- + smp.clk_en <= clkgen_i(clk_div64_c) when (ctrl.fsel = '1') else clkgen_i(clk_div8_c); + + -- bus event detector (event signals are "single-shot") -- + bus_event: process(rstn_i, clk_i) + begin + if (rstn_i = '0') then + smp.sda <= '0'; + smp.scl <= '0'; + smp.scl_rise <= '0'; + smp.scl_fall <= '0'; + smp.start <= '0'; + smp.stop <= '0'; + elsif rising_edge(clk_i) then + smp.sda <= smp.sda_sreg(2) or smp.sda_sreg(1); + smp.scl <= smp.sda_sreg(2) or smp.sda_sreg(1); + smp.scl_rise <= smp.valid and (not smp.scl_sreg(2)) and ( smp.scl_sreg(1)); -- rising edge + smp.scl_fall <= smp.valid and ( smp.scl_sreg(2)) and (not smp.scl_sreg(1)); -- falling edge + smp.start <= smp.valid and smp.scl_sreg(2) and smp.scl_sreg(1) and ( smp.sda_sreg(2)) and (not smp.sda_sreg(1)); + smp.stop <= smp.valid and smp.scl_sreg(2) and smp.scl_sreg(1) and (not smp.sda_sreg(2)) and ( smp.sda_sreg(1)); + end if; + end process bus_event; + + + -- Bus Engine ----------------------------------------------------------------------------- + -- ------------------------------------------------------------------------------------------- + bus_engine: process(rstn_i, clk_i) + begin + if (rstn_i = '0') then + twd_sda_o <= '1'; + twd_scl_o <= '1'; + engine.state <= S_IDLE; + engine.cnt <= (others => '0'); + engine.sreg <= (others => '1'); + engine.cmd <= '0'; + engine.dout <= '0'; + engine.wr_we <= '0'; + engine.rd_re <= '0'; + elsif rising_edge(clk_i) then + -- keep bus idle by default -- + twd_sda_o <= '1'; + twd_scl_o <= '1'; + + -- defaults -- + engine.wr_we <= '0'; + engine.rd_re <= '0'; + + -- fsm -- + case engine.state is + + when S_IDLE => -- idle, wait for start condition + -- ------------------------------------------------------------ + if (ctrl.enable = '1') and (smp.start = '1') then + engine.state <= S_INIT; + end if; + + when S_INIT => -- (re-)initialize new transaction + -- ------------------------------------------------------------ + engine.cnt <= (others => '0'); + engine.sreg <= (others => '0'); + if (ctrl.enable = '0') or (smp.stop = '1') then -- disabled or stop-condition received? + engine.state <= S_IDLE; + else + engine.state <= S_ADDR; + end if; + + when S_ADDR => -- sample address + R/W bit and check if address match + -- ------------------------------------------------------------ + if (ctrl.enable = '0') or (smp.stop = '1') then -- disabled or stop-condition received? + engine.state <= S_IDLE; + elsif (smp.start = '1') then -- start-condition received? + engine.state <= S_INIT; + elsif (engine.cnt(3) = '1') and (smp.scl_fall = '1') then -- 8 bits received? + if (ctrl.device_addr = engine.sreg(7 downto 1)) then -- address match? + engine.state <= S_RESP; -- access device + else + engine.state <= S_IDLE; -- no match, go back to idle + end if; + end if; + -- sample bus on rising edge -- + if (smp.scl_rise = '1') then + engine.sreg <= engine.sreg(6 downto 0) & smp.sda; + engine.cnt <= engine.cnt + 1; + end if; + + when S_RESP => -- send device address match ACK + -- ------------------------------------------------------------ + twd_sda_o <= '0'; -- ACK + engine.cnt <= (others => '0'); + engine.cmd <= engine.sreg(0); + if (ctrl.enable = '0') then -- disabled? + engine.state <= S_IDLE; + elsif (smp.scl_fall = '1') then + engine.state <= S_RTX; + end if; + -- get FIFO TX data (required for read access only) -- + if (smp.scl_fall = '1') then + engine.sreg <= engine.rdata; -- FIFO TX data + engine.dout <= engine.rdata(7); -- FIFO TX data (first bit) + end if; + + when S_RTX => -- receive/transmit 8 data bits + -- ------------------------------------------------------------ + if (ctrl.enable = '0') or (smp.stop = '1') then -- disabled or stop-condition + engine.state <= S_IDLE; + elsif (smp.start = '1') then -- start-condition + engine.state <= S_INIT; -- restart transaction + elsif (engine.cnt(3) = '1') and (smp.scl_fall = '1') then -- 8 bits received? + engine.wr_we <= '1'; -- write byte to RX FIFO + engine.state <= S_ACK; + end if; + -- sample bus on rising edge -- + if (smp.scl_rise = '1') then + engine.sreg <= engine.sreg(6 downto 0) & smp.sda; + engine.cnt <= engine.cnt + 1; + end if; + -- update bus on falling edge -- + twd_sda_o <= engine.dout; + if (smp.scl_fall = '1') and (engine.cmd = '1') then + engine.dout <= engine.sreg(7); + end if; + + when S_ACK => -- receive/transmit ACK/NACK + -- ------------------------------------------------------------ + engine.cnt <= (others => '0'); + engine.sreg <= engine.rdata; -- FIFO TX data + engine.dout <= engine.rdata(7); -- FIFO TX data (first bit) + if (ctrl.enable = '0') then -- disabled? + engine.state <= S_IDLE; + elsif (smp.scl_fall = '1') then + engine.state <= S_RTX; + end if; + -- [READ] advance to next data byte if ACK is send by host -- + if (engine.cmd = '1') and (smp.scl_rise = '1') and (smp.sda = '0') then + engine.rd_re <= '1'; -- get next TX data byte + end if; + -- [WRITE] transmit ACK -- + if (engine.cmd = '0') then + twd_sda_o <= '0'; + end if; + + when others => -- undefined + -- ------------------------------------------------------------ + engine.state <= S_IDLE; + + end case; + end if; + end process bus_engine; + + -- transaction in progress -- + engine.busy <= '0' when (engine.state = S_IDLE) else '1'; + + +end neorv32_twd_rtl; diff --git a/rtl/file_list_soc.f b/rtl/file_list_soc.f index e10289f20..b0f1390b1 100644 --- a/rtl/file_list_soc.f +++ b/rtl/file_list_soc.f @@ -34,6 +34,7 @@ NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_uart.vhd NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_spi.vhd NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_twi.vhd +NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_twd.vhd NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_pwm.vhd NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_trng.vhd NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_neoled.vhd diff --git a/rtl/system_integration/neorv32_vivado_ip.tcl b/rtl/system_integration/neorv32_vivado_ip.tcl index 8aa7a96a9..a8238cb69 100644 --- a/rtl/system_integration/neorv32_vivado_ip.tcl +++ b/rtl/system_integration/neorv32_vivado_ip.tcl @@ -129,6 +129,7 @@ proc setup_ip_gui {} { set_property enablement_dependency {$IO_SPI_EN} [ipx::get_ports spi_* -of_objects [ipx::current_core]] set_property enablement_dependency {$IO_SDI_EN} [ipx::get_ports sdi_* -of_objects [ipx::current_core]] set_property enablement_dependency {$IO_TWI_EN} [ipx::get_ports twi_* -of_objects [ipx::current_core]] + set_property enablement_dependency {$IO_TWD_EN} [ipx::get_ports twd_* -of_objects [ipx::current_core]] set_property enablement_dependency {$IO_ONEWIRE_EN} [ipx::get_ports onewire_* -of_objects [ipx::current_core]] set_property enablement_dependency {$IO_PWM_EN} [ipx::get_ports pwm_o -of_objects [ipx::current_core]] set_property enablement_dependency {$IO_CFS_EN} [ipx::get_ports cfs_* -of_objects [ipx::current_core]] @@ -341,12 +342,18 @@ proc setup_ip_gui {} { { IO_SDI_FIFO {FIFO Depth} {Number of entries (use a power of two)} {$IO_SDI_EN} } } - set group [add_group $page {Two-Wire/I2C Interface (TWI)}] + set group [add_group $page {Two-Wire/I2C Host (TWI)}] add_params $group { { IO_TWI_EN {Enable TWI} } { IO_TWI_FIFO {FIFO Depth} {Number of entries (use a power of two)} {$IO_TWI_EN} } } + set group [add_group $page {Two-Wire/I2C Device (TWD)}] + add_params $group { + { IO_TWD_EN {Enable TWD} } + { IO_TWD_FIFO {FIFO Depth} {Number of entries (use a power of two)} {$IO_TWD_EN} } + } + set group [add_group $page {Pulse-Width Modulation Controller (PWM)}] add_params $group { { IO_PWM_EN {Enable PWM} } diff --git a/rtl/system_integration/neorv32_vivado_ip.vhd b/rtl/system_integration/neorv32_vivado_ip.vhd index 9e868f5e9..c06228390 100644 --- a/rtl/system_integration/neorv32_vivado_ip.vhd +++ b/rtl/system_integration/neorv32_vivado_ip.vhd @@ -117,6 +117,8 @@ entity neorv32_vivado_ip is IO_SDI_FIFO : natural range 1 to 2**15 := 1; IO_TWI_EN : boolean := false; IO_TWI_FIFO : natural range 1 to 2**15 := 1; + IO_TWD_EN : boolean := false; + IO_TWD_FIFO : natural range 1 to 2**15 := 1; IO_PWM_EN : boolean := false; IO_PWM_NUM_CH : natural range 1 to 16 := 1; -- variable-sized ports must be at least 0 downto 0; #974 IO_WDT_EN : boolean := false; @@ -232,6 +234,11 @@ entity neorv32_vivado_ip is twi_sda_o : out std_logic; twi_scl_i : in std_logic := '0'; twi_scl_o : out std_logic; + -- TWD (available if IO_TWD_EN = true) -- + twd_sda_i : in std_logic := '0'; + twd_sda_o : out std_logic; + twd_scl_i : in std_logic := '0'; + twd_scl_o : out std_logic; -- 1-Wire Interface (available if IO_ONEWIRE_EN = true) -- onewire_i : in std_logic := '0'; onewire_o : out std_logic; @@ -263,7 +270,7 @@ architecture neorv32_vivado_ip_rtl of neorv32_vivado_ip is -- AXI4-Lite bridge -- component xbus2axi4lite_bridge port ( - -- Global control + -- Global control clk : in std_logic; resetn : in std_logic; -- XBUS device interface -- @@ -315,6 +322,7 @@ architecture neorv32_vivado_ip_rtl of neorv32_vivado_ip is signal spi_csn_aux : std_ulogic_vector(7 downto 0); signal sdi_do_aux : std_ulogic; signal twi_sda_o_aux, twi_scl_o_aux : std_ulogic; + signal twd_sda_o_aux, twd_scl_o_aux : std_ulogic; signal onewire_o_aux : std_ulogic; signal cfs_out_aux : std_ulogic_vector(IO_CFS_OUT_SIZE-1 downto 0); signal neoled_aux : std_ulogic; @@ -435,6 +443,8 @@ begin IO_SDI_FIFO => IO_SDI_FIFO, IO_TWI_EN => IO_TWI_EN, IO_TWI_FIFO => IO_TWI_FIFO, + IO_TWD_EN => IO_TWD_EN, + IO_TWD_FIFO => IO_TWD_FIFO, IO_PWM_NUM_CH => num_pwm_c, IO_WDT_EN => IO_WDT_EN, IO_TRNG_EN => IO_TRNG_EN, @@ -517,6 +527,11 @@ begin twi_sda_o => twi_sda_o_aux, twi_scl_i => std_ulogic(twi_scl_i), twi_scl_o => twi_scl_o_aux, + -- TWD (available if IO_TWD_EN = true) -- + twd_sda_i => std_ulogic(twd_sda_i), + twd_sda_o => twd_sda_o_aux, + twd_scl_i => std_ulogic(twd_scl_i), + twd_scl_o => twd_scl_o_aux, -- 1-Wire Interface (available if IO_ONEWIRE_EN = true) -- onewire_i => std_ulogic(onewire_i), onewire_o => onewire_o_aux, @@ -566,6 +581,9 @@ begin twi_sda_o <= std_logic(twi_sda_o_aux); twi_scl_o <= std_logic(twi_scl_o_aux); + twd_sda_o <= std_logic(twd_sda_o_aux); + twd_scl_o <= std_logic(twd_scl_o_aux); + onewire_o <= std_logic(onewire_o_aux); cfs_out_o <= std_logic_vector(cfs_out_aux); diff --git a/sim/neorv32_tb.vhd b/sim/neorv32_tb.vhd index 667a2978a..c51053b4e 100644 --- a/sim/neorv32_tb.vhd +++ b/sim/neorv32_tb.vhd @@ -78,8 +78,9 @@ architecture neorv32_tb_rtl of neorv32_tb is -- IO connection -- signal uart0_txd, uart0_cts, uart1_txd, uart1_cts : std_ulogic; signal gpio : std_ulogic_vector(63 downto 0); - signal twi_scl, twi_sda : std_logic; + signal i2c_scl, i2c_sda : std_logic; signal twi_scl_i, twi_scl_o, twi_sda_i, twi_sda_o : std_ulogic; + signal twd_scl_i, twd_scl_o, twd_sda_i, twd_sda_o : std_ulogic; signal onewire : std_logic; signal onewire_i, onewire_o : std_ulogic; signal spi_csn : std_ulogic_vector(7 downto 0); @@ -196,6 +197,8 @@ begin IO_SDI_FIFO => 4, IO_TWI_EN => true, IO_TWI_FIFO => 4, + IO_TWD_EN => true, + IO_TWD_FIFO => 4, IO_PWM_NUM_CH => 8, IO_WDT_EN => true, IO_TRNG_EN => true, @@ -279,6 +282,11 @@ begin twi_sda_o => twi_sda_o, twi_scl_i => twi_scl_i, twi_scl_o => twi_scl_o, + -- TWD -- + twd_sda_i => twd_sda_i, + twd_sda_o => twd_sda_o, + twd_scl_i => twd_scl_i, + twd_scl_o => twd_scl_o, -- 1-Wire Interface -- onewire_i => onewire_i, onewire_o => onewire_o, @@ -300,28 +308,33 @@ begin ); - -- Two-Wire Bus --------------------------------------------------------------------------- + -- Two-Wire Bus - Tri-State Drivers (modules can only actively pull the signals low) ------ -- ------------------------------------------------------------------------------------------- - twi_sda <= '0' when (twi_sda_o = '0') else 'Z'; -- tristate driver: module can only pull the line low actively - twi_scl <= '0' when (twi_scl_o = '0') else 'Z'; -- tristate driver: module can only pull the line low actively - twi_sda_i <= std_ulogic(twi_sda); -- sense input - twi_scl_i <= std_ulogic(twi_scl); -- sense input + i2c_sda <= '0' when (twi_sda_o = '0') else 'Z'; + i2c_scl <= '0' when (twi_scl_o = '0') else 'Z'; + twi_sda_i <= std_ulogic(i2c_sda); -- sense input + twi_scl_i <= std_ulogic(i2c_scl); -- sense input - -- TWI bus termination -- - twi_scl <= 'H'; -- weak pull-up "resistor" - twi_sda <= 'H'; -- weak pull-up "resistor" + i2c_sda <= '0' when (twd_sda_o = '0') else 'Z'; + i2c_scl <= '0' when (twd_scl_o = '0') else 'Z'; + twd_sda_i <= std_ulogic(i2c_sda); -- sense input + twd_scl_i <= std_ulogic(i2c_scl); -- sense input + -- I2C bus termination with weak pull-ups -- + i2c_scl <= 'H'; + i2c_sda <= 'H'; - -- One-Wire Bus --------------------------------------------------------------------------- + + -- One-Wire Bus - Tri-State Driver (module can only actively pull the signals low) -------- -- ------------------------------------------------------------------------------------------- - onewire <= '0' when (onewire_o = '0') else 'Z'; -- tristate driver: module can only pull the line low actively + onewire <= '0' when (onewire_o = '0') else 'Z'; onewire_i <= std_ulogic(onewire); -- sense input - -- 1-Wire bus termination -- - onewire <= 'H'; -- weak pull-up "resistor" + -- 1-Wire bus termination with weak pull-up -- + onewire <= 'H'; - -- SP/SDI --------------------------------------------------------------------------- + -- SPI/SDI -------------------------------------------------------------------------------- -- ------------------------------------------------------------------------------------------- sdi_clk <= spi_clk after 40 ns; -- echo with propagation delay sdi_csn <= spi_csn(7) after 40 ns; @@ -455,7 +468,7 @@ begin end generate; - -- XBUS: Memory-Mapped IO ----------------------------------------------------------------- + -- XBUS: External Memory-Mapped IO -------------------------------------------------------- -- ------------------------------------------------------------------------------------------- xbus_mmio: entity work.xbus_memory generic map ( @@ -471,7 +484,7 @@ begin ); - -- XBUS: IRQ Triggers --------------------------------------------------------------------- + -- XBUS: External IRQ Trigger ------------------------------------------------------------- -- ------------------------------------------------------------------------------------------- xbus_irq_trigger: process(rst_gen, clk_gen) begin diff --git a/sw/example/processor_check/main.c b/sw/example/processor_check/main.c index 0e28d5e51..ea5ca4f24 100644 --- a/sw/example/processor_check/main.c +++ b/sw/example/processor_check/main.c @@ -1004,8 +1004,42 @@ int main() { // Fast interrupt channel 0 // ---------------------------------------------------------- neorv32_cpu_csr_write(CSR_MCAUSE, mcause_never_c); - PRINT_STANDARD("[%i] FIRQ0 (reserved) ", cnt_test); - PRINT_STANDARD("[n.a.]\n"); + PRINT_STANDARD("[%i] FIRQ0 (TWD) ", cnt_test); + + if (NEORV32_SYSINFO->SOC & (1 << SYSINFO_SOC_IO_TWD)) { + cnt_test++; + + // configure TWD and enable RX-available interrupt + neorv32_twd_setup(0b1101001, 0, 1, 0, 0); + + // configure TWI with third-fastest clock, no clock stretching + neorv32_twi_setup(CLK_PRSC_8, 1, 0); + + // enable fast interrupt + neorv32_cpu_csr_write(CSR_MIE, 1 << TWD_FIRQ_ENABLE); + + // program sequence: write data via TWI + neorv32_twi_generate_start_nonblocking(); + neorv32_twi_send_nonblocking(0b11010010, 0); // write-address + neorv32_twi_send_nonblocking(0x47, 0); + neorv32_twi_generate_stop_nonblocking(); + + // sleep until interrupt + neorv32_cpu_sleep(); + + neorv32_cpu_csr_write(CSR_MIE, 0); + + if ((neorv32_cpu_csr_read(CSR_MCAUSE) == TWD_TRAP_CODE) && // interrupt triggered + (neorv32_twd_get() == 0x47)) { // correct data written + test_ok(); + } + else { + test_fail(); + } + } + else { + PRINT_STANDARD("[n.a.]\n"); + } // ---------------------------------------------------------- @@ -1251,13 +1285,17 @@ int main() { if (NEORV32_SYSINFO->SOC & (1 << SYSINFO_SOC_IO_TWI)) { cnt_test++; - // configure TWI with fastest clock, no clock stretching - neorv32_twi_setup(CLK_PRSC_2, 0, 0); + // configure TWI with third-fastest clock, no clock stretching + neorv32_twi_setup(CLK_PRSC_8, 1, 0); - // issue some TWI operations, after they are done the interrupt will be fired + // configure TWD, no interrupts + neorv32_twd_setup(0b0010110, 0, 0, 0, 0); + neorv32_twd_put(0x8e); + + // program sequence: read data via TWI neorv32_twi_generate_start_nonblocking(); - neorv32_twi_send_nonblocking(0xA5, 0); - neorv32_twi_send_nonblocking(0x12, 0); + neorv32_twi_send_nonblocking(0b00101101, 0); // read-address + neorv32_twi_send_nonblocking(0xff, 1); neorv32_twi_generate_stop_nonblocking(); // enable TWI FIRQ @@ -1268,10 +1306,15 @@ int main() { neorv32_cpu_csr_write(CSR_MIE, 0); - tmp_a = NEORV32_TWI->CTRL; + // get TWI response + uint8_t twi_data_y; + int twi_ack_x = neorv32_twi_get(&twi_data_y); + neorv32_twi_get(&twi_data_y); + + if ((neorv32_cpu_csr_read(CSR_MCAUSE) == TWI_TRAP_CODE) && // interrupt triggered - (tmp_a & (1< + + +/**********************************************************************//** + * @name IO Device: Two-Wire Device Controller (TWD) + **************************************************************************/ +/**@{*/ +/** TWD module prototype */ +typedef volatile struct __attribute__((packed,aligned(4))) { + uint32_t CTRL; /**< offset 0: control register (#NEORV32_TWD_CTRL_enum) */ + uint32_t DATA; /**< offset 4: data register (#NEORV32_TWD_DATA_enum) */ +} neorv32_twd_t; + +/** TWD module hardware access (#neorv32_twd_t) */ +#define NEORV32_TWD ((neorv32_twd_t*) (NEORV32_TWD_BASE)) + +/** TWD control register bits */ +enum NEORV32_TWD_CTRL_enum { + TWD_CTRL_EN = 0, /**< TWD control register(0) (r/w): TWD enable */ + TWD_CTRL_CLR_RX = 1, /**< TWD control register(1) (-/w): Clear RX FIFO, flag auto-clears */ + TWD_CTRL_CLR_TX = 2, /**< TWD control register(2) (-/w): Clear TX FIFO, flag auto-clears */ + TWD_CTRL_FSEL = 3, /**< TWD control register(3) (r/w): Bus sample clock / filter select */ + TWD_CTRL_DEV_ADDR0 = 4, /**< TWD control register(4) (r/w): Device address (7-bit), LSB */ + TWD_CTRL_DEV_ADDR6 = 10, /**< TWD control register(10) (r/w): Device address (7-bit), MSB */ + TWD_CTRL_IRQ_RX_AVAIL = 11, /**< TWD control register(11) (r/w): IRQ if RX FIFO data available */ + TWD_CTRL_IRQ_RX_FULL = 12, /**< TWD control register(12) (r/w): IRQ if RX FIFO full */ + TWD_CTRL_IRQ_TX_EMPTY = 13, /**< TWD control register(13) (r/w): IRQ if TX FIFO empty */ + + TWD_CTRL_FIFO_LSB = 15, /**< TWD control register(15) (r/-): log2(FIFO size), LSB */ + TWD_CTRL_FIFO_MSB = 18, /**< TWD control register(18) (r/-): log2(FIFO size), MSB */ + + TWD_CTRL_RX_AVAIL = 25, /**< TWD control register(25) (r/-): RX FIFO data available */ + TWD_CTRL_RX_FULL = 26, /**< TWD control register(26) (r/-): RX FIFO full */ + TWD_CTRL_TX_EMPTY = 27, /**< TWD control register(27) (r/-): TX FIFO empty */ + TWD_CTRL_TX_FULL = 28, /**< TWD control register(28) (r/-): TX FIFO full */ + TWD_CTRL_SENSE_SCL = 29, /**< TWD control register(29) (r/-): current state of the SCL bus line */ + TWD_CTRL_SENSE_SDA = 30, /**< TWD control register(30) (r/-): current state of the SDA bus line */ + TWD_CTRL_BUSY = 31 /**< TWD control register(31) (r/-): bus engine is busy (transaction in progress) */ +}; + +/** TWD data register bits */ +enum NEORV32_TWD_DATA_enum { + TWD_DATA_LSB = 0, /**< TWD data register(0) (r/w): Receive/transmit data (8-bit) LSB */ + TWD_DATA_MSB = 7 /**< TWD data register(7) (r/w): Receive/transmit data (8-bit) MSB */ +}; +/**@}*/ + + +/**********************************************************************//** + * @name Prototypes + **************************************************************************/ +/**@{*/ +int neorv32_twd_available(void); +void neorv32_twd_setup(int device_addr, int fsel, int irq_rx_avail, int irq_rx_full, int irq_tx_empty); +int neorv32_twd_get_fifo_depth(void); +void neorv32_twd_disable(void); +void neorv32_twd_enable(void); +void neorv32_twd_clear_rx(void); +void neorv32_twd_clear_tx(void); +int neorv32_twd_sense_scl(void); +int neorv32_twd_sense_sda(void); +int neorv32_twd_busy(void); +int neorv32_twd_rx_available(void); +int neorv32_twd_rx_full(void); +int neorv32_twd_tx_empty(void); +int neorv32_twd_tx_full(void); +void neorv32_twd_put(uint8_t data); +uint8_t neorv32_twd_get(void); +/**@}*/ + + +#endif // neorv32_twd_h diff --git a/sw/lib/source/neorv32_rte.c b/sw/lib/source/neorv32_rte.c index edcf69e9d..ec5b535ad 100644 --- a/sw/lib/source/neorv32_rte.c +++ b/sw/lib/source/neorv32_rte.c @@ -650,6 +650,7 @@ void neorv32_rte_print_hw_config(void) { if (tmp & (1 << SYSINFO_SOC_IO_SPI)) { neorv32_uart0_printf("SPI "); } neorv32_uart0_printf("SYSINFO "); // always enabled if (tmp & (1 << SYSINFO_SOC_IO_TRNG)) { neorv32_uart0_printf("TRNG "); } + if (tmp & (1 << SYSINFO_SOC_IO_TWD)) { neorv32_uart0_printf("TWD "); } if (tmp & (1 << SYSINFO_SOC_IO_TWI)) { neorv32_uart0_printf("TWI "); } if (tmp & (1 << SYSINFO_SOC_IO_UART0)) { neorv32_uart0_printf("UART0 "); } if (tmp & (1 << SYSINFO_SOC_IO_UART1)) { neorv32_uart0_printf("UART1 "); } diff --git a/sw/lib/source/neorv32_twd.c b/sw/lib/source/neorv32_twd.c new file mode 100644 index 000000000..04537c441 --- /dev/null +++ b/sw/lib/source/neorv32_twd.c @@ -0,0 +1,244 @@ +// ================================================================================ // +// The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 // +// Copyright (c) NEORV32 contributors. // +// Copyright (c) 2020 - 2024 Stephan Nolting. All rights reserved. // +// Licensed under the BSD-3-Clause license, see LICENSE for details. // +// SPDX-License-Identifier: BSD-3-Clause // +// ================================================================================ // + +/** + * @file neorv32_twd.c + * @brief Two-Wire Device Controller (TWD) HW driver source file. + * + * @note These functions should only be used if the TWD unit was synthesized (IO_TWD_EN = true). + * + * @see https://stnolting.github.io/neorv32/sw/files.html + */ + +#include + + +/**********************************************************************//** + * Check if TWD unit was synthesized. + * + * @return 0 if TWD was not synthesized, 1 if TWD is available. + **************************************************************************/ +int neorv32_twd_available(void) { + + if (NEORV32_SYSINFO->SOC & (1 << SYSINFO_SOC_IO_TWD)) { + return 1; + } + else { + return 0; + } +} + + +/**********************************************************************//** + * Enable and configure TWD controller. The TWD control register bits are listed in #NEORV32_TWD_CTRL_enum. + * + * @param[in] device_addr 7-bit device address. + * @param[in] fsel Bus sample clock / filter select. + * @param[in] irq_rx_avail IRQ if RX FIFO data available. + * @param[in] irq_rx_full IRQ if RX FIFO full. + * @param[in] irq_tx_empty IRQ if TX FIFO empty. + **************************************************************************/ +void neorv32_twd_setup(int device_addr, int fsel, int irq_rx_avail, int irq_rx_full, int irq_tx_empty) { + + NEORV32_TWD->CTRL = 0; // reset + + uint32_t ctrl = 0; + ctrl |= ((uint32_t)( 0x01) << TWD_CTRL_EN); + ctrl |= ((uint32_t)(device_addr & 0x7f) << TWD_CTRL_DEV_ADDR0); + ctrl |= ((uint32_t)(fsel & 0x01) << TWD_CTRL_FSEL); + ctrl |= ((uint32_t)(irq_rx_avail & 0x01) << TWD_CTRL_IRQ_RX_AVAIL); + ctrl |= ((uint32_t)(irq_rx_full & 0x01) << TWD_CTRL_IRQ_RX_FULL); + ctrl |= ((uint32_t)(irq_tx_empty & 0x01) << TWD_CTRL_IRQ_TX_EMPTY); + NEORV32_TWD->CTRL = ctrl; +} + + +/**********************************************************************//** + * Get TWD FIFO depth. + * + * @return FIFO depth (number of entries), zero if no FIFO implemented + **************************************************************************/ +int neorv32_twd_get_fifo_depth(void) { + + uint32_t tmp = (NEORV32_TWD->CTRL >> TWD_CTRL_FIFO_LSB) & 0xf; + return (int)(1 << tmp); +} + + +/**********************************************************************//** + * Disable TWD controller. + **************************************************************************/ +void neorv32_twd_disable(void) { + + NEORV32_TWD->CTRL &= ~((uint32_t)(1 << TWD_CTRL_EN)); +} + + +/**********************************************************************//** + * Enable TWD controller. + **************************************************************************/ +void neorv32_twd_enable(void) { + + NEORV32_TWD->CTRL |= (uint32_t)(1 << TWD_CTRL_EN); +} + + +/**********************************************************************//** + * Clear TWD RX FIFO. + **************************************************************************/ +void neorv32_twd_clear_rx(void) { + + NEORV32_TWD->CTRL |= (uint32_t)(1 << TWD_CTRL_CLR_RX); +} + + +/**********************************************************************//** + * Clear TWD TX FIFO. + **************************************************************************/ +void neorv32_twd_clear_tx(void) { + + NEORV32_TWD->CTRL |= (uint32_t)(1 << TWD_CTRL_CLR_TX); +} + + +/**********************************************************************//** + * Get current state of SCL bus line. + * + * @return 1 if SCL is high, 0 if SCL is low. + **************************************************************************/ +int neorv32_twd_sense_scl(void) { + + if (NEORV32_TWD->CTRL & (1 << TWD_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_twd_sense_sda(void) { + + if (NEORV32_TWD->CTRL & (1 << TWD_CTRL_SENSE_SDA)) { + return 1; + } + else { + return 0; + } +} + + +/**********************************************************************//** + * Check if there is a TWD bus operation in progress. + * + * @return 0 if idle, 1 if busy. + **************************************************************************/ +int neorv32_twd_busy(void) { + + if (NEORV32_TWD->CTRL & (1 << TWD_CTRL_BUSY)) { + return 1; + } + else { + return 0; + } +} + + +/**********************************************************************//** + * Check if RX data available. + * + * @return 0 if no data available, 1 if data is available. + **************************************************************************/ +int neorv32_twd_rx_available(void) { + + if (NEORV32_TWD->CTRL & (1 << TWD_CTRL_RX_AVAIL)) { + return 1; + } + else { + return 0; + } +} + + +/**********************************************************************//** + * Check if RX FIFO is full. + * + * @return 0 if no RX FIFO is not full, 1 if RX FIFO is full. + **************************************************************************/ +int neorv32_twd_rx_full(void) { + + if (NEORV32_TWD->CTRL & (1 << TWD_CTRL_RX_FULL)) { + return 1; + } + else { + return 0; + } +} + + +/**********************************************************************//** + * Check if TX FIFO is empty. + * + * @return 0 if no TX FIFO is not empty, 1 if TX FIFO is empty. + **************************************************************************/ +int neorv32_twd_tx_empty(void) { + + if (NEORV32_TWD->CTRL & (1 << TWD_CTRL_TX_EMPTY)) { + return 1; + } + else { + return 0; + } +} + + +/**********************************************************************//** + * Check if TX FIFO is full. + * + * @return 0 if no TX FIFO is not full, 1 if TX FIFO is full. + **************************************************************************/ +int neorv32_twd_tx_full(void) { + + if (NEORV32_TWD->CTRL & (1 << TWD_CTRL_TX_FULL)) { + return 1; + } + else { + return 0; + } +} + + +/**********************************************************************//** + * Put data byte into TX FIFO. + * + * @warning This function is non-blocking. Check FIFO status before. + * + * @param[in] data Data byte to be stored in TX FIFO. + **************************************************************************/ +void neorv32_twd_put(uint8_t data) { + + NEORV32_TWD->DATA = data; +} + + +/**********************************************************************//** + * Get data byte from RX FIFO. + * + * @warning This function is non-blocking. Check FIFO status before. + * + * @return Data byte read from RX FIFO. + **************************************************************************/ +uint8_t neorv32_twd_get(void) { + + return NEORV32_TWD->DATA; +} diff --git a/sw/svd/neorv32.svd b/sw/svd/neorv32.svd index 9fca3aed9..b86d74605 100644 --- a/sw/svd/neorv32.svd +++ b/sw/svd/neorv32.svd @@ -1315,6 +1315,132 @@ + + + + TWD + Two-wire device controller + 0xFFFFEA00 + + TWD_FIRQ0 + + + 0 + 0x08 + registers + + + + + CTRL + Control register + 0x00 + + + TWD_CTRL_EN + [0:0] + TWD enable flag + + + TWD_CTRL_CLR_RX + [1:1] + Clear RX FIFO, flag auto-clears + + + TWD_CTRL_CLR_TX + [2:2] + Clear TX FIFO, flag auto-clears + + + TWD_CTRL_FSEL + [3:3] + Bus sample clock / filter select + + + TWD_CTRL_DEV_ADDR + [10:4] + Device address (7-bit) + + + TWD_CTRL_IRQ_RX_AVAIL + [11:11] + IRQ if RX FIFO data available + + + TWD_CTRL_IRQ_RX_FULL + [12:12] + IRQ if RX FIFO full + + + TWD_CTRL_IRQ_TX_EMPTY + [13:13] + IRQ if TX FIFO empty + + + TWD_CTRL_FIFO + [18:15] + read-only + log2(TWD FIFO size) + + + TWD_CTRL_RX_AVAIL + [25:25] + read-only + RX FIFO data available + + + TWD_CTRL_RX_FULL + [26:26] + read-only + RX FIFO full + + + TWD_CTRL_TX_EMPTY + [27:27] + read-only + TX FIFO empty + + + TWD_CTRL_TX_FULL + [28:28] + read-only + TX FIFO full + + + TWD_CTRL_SENSE_SCL + [29:29] + read-only + current state of the SCL bus line + + + TWD_CTRL_SENSE_SDA + [30:30] + read-only + current state of the SDA bus line + + + TWD_CTRL_BUSY + [31:31] + read-only + bus engine is busy (transaction in progress) + + + + + DATA + RX/TX data register + 0x04 + + + TWI_DATA + [7:0] + RX/TX data FIFO access + + + + + + @@ -1643,6 +1769,7 @@ SYSINFO_SOC_XIP_CACHE[10:10]Execute in place cache implemented SYSINFO_SOC_OCD_AUTH[11:11]On-chip debugger authentication implemented SYSINFO_SOC_IMEM_ROM[12:12]Processor-internal instruction memory implemented as pre-initialized ROM + SYSINFO_SOC_IO_TWD[13:13]Two-wire device implemented SYSINFO_SOC_IO_DMA[14:14]Direct memory access controller implemented SYSINFO_SOC_IO_GPIO[15:15]General purpose input/output port unit implemented SYSINFO_SOC_IO_MTIME[16:16]Machine system timer implemented