feat: add I2C support to the SoC topology and formal verification

- Updated  to include I2C1 in IRQ bindings.
- Modified  to increase IRQ group width and add I2C1 interrupt.
- Enhanced  to include I2C formal properties and adjust timeouts.
- Added I2C-related source files to the formal verification flow.
- Updated DMA wrapper in  to handle I2C DMA signals.
- Created a new testbench  for I2C functionality verification.
- Implemented I2C helper functions in  for testing.
- Added I2C tests in  to validate controller behavior and recovery.
- Updated existing tests to accommodate changes in DMA and IRQ handling.
This commit is contained in:
maksyuki committed 2026-08-09 12:55:42 +08:00
1 parent 0130825057
commit 8266791cfc
48 files changed
+3862 -1030

No files matched your search

+1 -2
View File
@@ -28,9 +28,8 @@ The SDRAM controller implementation in rtl/ip/ribp/memory/sdram_core.sv retains
## 4. EmbedFire
Several SPI/I2C-related RTL files retain the original attribution to EmbedFire and refer to source files such as sd_init.v, sd_write.v, sd_read.v, data_rw_ctrl.v, and i2c_ctrl.v:
Several SPI SD card related RTL files retain the original attribution to EmbedFire and refer to source files such as sd_init.v, sd_write.v, sd_read.v, and data_rw_ctrl.v:
- rtl/ip/ribp/serial/i2c_core.sv
- rtl/ip/ribp/storage/spisd_core.sv
- rtl/ip/ribp/storage/spisd_data.sv
- rtl/ip/ribp/storage/spisd_init.sv
+2 -2
View File
@@ -252,7 +252,7 @@ endif
benchmark-report coremark-report \
pin-map check-pin-map soc-topology check-soc-topology user-extensions check-user-extensions \
check-clock-reset-domains tech-cell-test rtl-lint check-rtl-lint \
formal formal-bus formal-rib-adapter formal-rib2apb formal-gpio formal-ws2812 formal-uart formal-timer formal-clean formal-doctor
formal formal-bus formal-rib-adapter formal-rib2apb formal-gpio formal-ws2812 formal-uart formal-i2c formal-timer formal-clean formal-doctor
.NOTPARALLEL: setup
help:
@@ -280,7 +280,7 @@ help:
' check-clock-reset-domains validate the root clock/reset and CDC inventory' \
' rtl-lint | check-rtl-lint run/check strict Verilator RTL lint warnings' \
' formal | formal-bus | formal-rib-adapter | formal-rib2apb run SBY protocol proofs' \
' formal-sysctrl | formal-pll-rcu | formal-gpio | formal-ws2812 | formal-uart | formal-timer | formal-clint run peripheral proofs' \
' formal-sysctrl | formal-pll-rcu | formal-gpio | formal-ws2812 | formal-uart | formal-i2c | formal-timer | formal-clint run peripheral proofs' \
' formal-doctor check the SBY, Yosys, sv2v, and Bitwuzla formal toolchain' \
' benchmark-report run the memory/DMA profile and write meta/performance.json' \
' coremark-report run the quick CoreMark profile and write meta/coremark.json' \
+1 -1
View File
@@ -7,6 +7,6 @@ The following third-party projects or authors are referenced in the RTL source h
- ETH Zurich and University of Bologna: notices are preserved in several RTL files under rtl/managed/clusterip/, rtl/managed/mpw/core/username7/ibex/, and in rtl/tech/tc_clk.sv.
- Ravenna / PicoRV32 in ASIC: notices are preserved in rtl/ip/ribp/peripheral/gpio.sv, rtl/ip/ribp/peripheral/timer.sv, and rtl/ip/ribp/serial/uart.sv.
- Hirosh Dabui: the original copyright notice is preserved in rtl/ip/ribp/memory/sdram_core.sv.
- EmbedFire: attribution is preserved in rtl/ip/ribp/serial/i2c_core.sv and the SPI SD card related files under rtl/ip/ribp/storage/.
- EmbedFire: attribution is preserved in the SPI SD card related files under rtl/ip/ribp/storage/.
These notices are included solely to preserve attribution for the retained third-party source material. They do not alter the licensing terms of the retroSoC repository itself, which is distributed under the Mulan PSL v2 license.
+1 -1
View File
@@ -28,7 +28,7 @@ under the [Mulan Permissive Software License, Version 2](LICENSE).
- Configurable GF180, SKY130, IHP130, and ICS55 implementation targets with
open-source CI coverage.
- A memory-mapped peripheral subsystem with GPIO, UART,
[dual general timers](docs/ip/timer.md), PWM, I2C, I2S, PS2,
[dual general timers](docs/ip/timer.md), PWM, [dual I2C controllers](docs/ip/i2c.md), I2S, PS2,
WS2812, SPI/QSPI, SDIO, PSRAM/OPI-PSRAM, SDRAM, DMA, LCD, RTC, watchdog, RNG, and CRC
support. Available interfaces depend on the selected SoC configuration.
- A standalone RISC-V runtime, HAL, board support, middleware, and `benchmark`, `bringup`,
+1 -1
View File
@@ -33,7 +33,7 @@ int main(void) {
return 1;
}
rs_booter();
if (rs_i2c0_init((uint8_t)(CPU_FREQ / 2 - 1)) != RS_OK) {
if (rs_i2c_init(RS_I2C_BUS_0, CPU_FREQ * UINT32_C(1000000), UINT32_C(400000)) != RS_OK) {
return 1;
}
qspi_dev_init();
+13 -6
View File
@@ -3,6 +3,13 @@
#include <retrosoc/hal/i2c.h>
#include <retrosoc/board/at24cxx.h>
static const rs_i2c_register_access_t at24c64_test_access = {
.address = AT24C64_DEV_ADDR,
.ten_bit_address = false,
.register_address = 0U,
.register_address_width = RS_I2C_REGISTER_ADDRESS_16_BIT,
};
void at24cxx_test(void) {
printf("AT24C64 wr/rd test\n");
// prepare ref data
@@ -11,14 +18,14 @@ void at24cxx_test(void) {
ref_data[i] = i;
}
// write AT24C64
if (rs_i2c0_write(AT24C64_DEV_ADDR, 0U, RS_I2C_DEVICE_ADDRESS_16BIT, I2C_TEST_NUM, ref_data,
RS_TIMEOUT_DEFAULT) != RS_OK) {
if (rs_i2c_register_write(RS_I2C_BUS_0, &at24c64_test_access, ref_data, I2C_TEST_NUM,
RS_TIMEOUT_DEFAULT) != RS_OK) {
printf("AT24C64 write timed out\n");
return;
}
// read AT24C64
if (rs_i2c0_read(AT24C64_DEV_ADDR, 0U, RS_I2C_DEVICE_ADDRESS_16BIT, I2C_TEST_NUM, rd_data,
RS_TIMEOUT_DEFAULT) != RS_OK) {
if (rs_i2c_register_read(RS_I2C_BUS_0, &at24c64_test_access, rd_data, I2C_TEST_NUM,
RS_TIMEOUT_DEFAULT) != RS_OK) {
printf("AT24C64 read timed out\n");
return;
}
@@ -29,8 +36,8 @@ void at24cxx_test(void) {
printf("test fail\n");
}
(void)rs_i2c0_write(AT24C64_DEV_ADDR, 0U, RS_I2C_DEVICE_ADDRESS_16BIT, I2C_TEST_NUM, ref_data,
RS_TIMEOUT_DEFAULT);
(void)rs_i2c_register_write(RS_I2C_BUS_0, &at24c64_test_access, ref_data, I2C_TEST_NUM,
RS_TIMEOUT_DEFAULT);
printf("AT24C64 wr/rd test done\n");
}
+11 -4
View File
@@ -40,19 +40,26 @@ static const uint8_t es8388_init_cfg[] = {
void es8388_init(void) {
const uint32_t init_cfg_len = sizeof(es8388_init_cfg) / sizeof(es8388_init_cfg[0]);
rs_i2c_register_access_t access = {
.address = ES8388_DEV_ADDR,
.ten_bit_address = false,
.register_address = 0U,
.register_address_width = RS_I2C_REGISTER_ADDRESS_8_BIT,
};
printf("[ES8388] init cfg len: %u\n", init_cfg_len);
for (uint32_t i = 0U; i < init_cfg_len; i += 2U) {
if (rs_i2c0_write(ES8388_DEV_ADDR, (uint16_t)es8388_init_cfg[i], RS_I2C_DEVICE_ADDRESS_8BIT,
1U, es8388_init_cfg + i + 1, RS_TIMEOUT_DEFAULT) != RS_OK) {
access.register_address = es8388_init_cfg[i];
if (rs_i2c_register_write(RS_I2C_BUS_0, &access, es8388_init_cfg + i + 1, 1U,
RS_TIMEOUT_DEFAULT) != RS_OK) {
printf("[ES8388] write failed at register %d\n", es8388_init_cfg[i]);
return;
}
}
uint8_t rxdata[60] = {0};
if (rs_i2c0_read(ES8388_DEV_ADDR, 0U, RS_I2C_DEVICE_ADDRESS_8BIT, 53U, rxdata,
RS_TIMEOUT_DEFAULT) != RS_OK) {
access.register_address = 0U;
if (rs_i2c_register_read(RS_I2C_BUS_0, &access, rxdata, 53U, RS_TIMEOUT_DEFAULT) != RS_OK) {
printf("[ES8388] read failed\n");
return;
}
+11 -4
View File
@@ -4,6 +4,13 @@
#include <retrosoc/hal/i2c.h>
#include <retrosoc/board/pcf8563b.h>
static const rs_i2c_register_access_t pcf8563b_time_access = {
.address = PCF8563B_DEV_ADDR,
.ten_bit_address = false,
.register_address = PCF8563B_SECOND_REG,
.register_address_width = RS_I2C_REGISTER_ADDRESS_8_BIT,
};
uint8_t pcf8563b_bin2bcd(uint8_t val) {
uint8_t bcdhigh = 0;
while (val >= 10) {
@@ -33,8 +40,8 @@ void pcf8563b_wr_reg(PCF8563B_info_t *info) {
*(wr_data + 4) = pcf8563b_bin2bcd(info->date.weekday);
*(wr_data + 5) = pcf8563b_bin2bcd(info->date.month);
*(wr_data + 6) = pcf8563b_bin2bcd(info->date.year);
if (rs_i2c0_write(PCF8563B_DEV_ADDR, PCF8563B_SECOND_REG, RS_I2C_DEVICE_ADDRESS_8BIT, 7U,
wr_data, RS_TIMEOUT_DEFAULT) != RS_OK) {
if (rs_i2c_register_write(RS_I2C_BUS_0, &pcf8563b_time_access, wr_data, 7U,
RS_TIMEOUT_DEFAULT) != RS_OK) {
printf("[PCF8563B] write failed\n");
}
}
@@ -43,8 +50,8 @@ PCF8563B_info_t pcf8563b_rd_reg(void) {
uint8_t rd_data[7] = {0};
PCF8563B_info_t info = {0};
if (rs_i2c0_read(PCF8563B_DEV_ADDR, PCF8563B_SECOND_REG, RS_I2C_DEVICE_ADDRESS_8BIT, 7U,
rd_data, RS_TIMEOUT_DEFAULT) != RS_OK) {
if (rs_i2c_register_read(RS_I2C_BUS_0, &pcf8563b_time_access, rd_data, 7U,
RS_TIMEOUT_DEFAULT) != RS_OK) {
printf("[PCF8563B] read failed\n");
return info;
}
-18
View File
@@ -43,15 +43,6 @@
#define reg_spisd_rxdata RS_SOC_REG32(RS_SOC_RIBP_SPISD_BASE, UINT32_C(0x10))
#define reg_spisd_status RS_SOC_REG32(RS_SOC_RIBP_SPISD_BASE, UINT32_C(0x14))
#define reg_spisd_sync RS_SOC_REG32(RS_SOC_RIBP_SPISD_BASE, UINT32_C(0x18))
// i2c0
#define reg_i2c0_clkdiv RS_SOC_REG32(RS_SOC_RIBP_I2C0_BASE, UINT32_C(0x00))
#define reg_i2c0_devaddr RS_SOC_REG32(RS_SOC_RIBP_I2C0_BASE, UINT32_C(0x04))
#define reg_i2c0_regaddr RS_SOC_REG32(RS_SOC_RIBP_I2C0_BASE, UINT32_C(0x08))
#define reg_i2c0_txdata RS_SOC_REG32(RS_SOC_RIBP_I2C0_BASE, UINT32_C(0x0C))
#define reg_i2c0_rxdata RS_SOC_REG32(RS_SOC_RIBP_I2C0_BASE, UINT32_C(0x10))
#define reg_i2c0_xfer RS_SOC_REG32(RS_SOC_RIBP_I2C0_BASE, UINT32_C(0x14))
#define reg_i2c0_cfg RS_SOC_REG32(RS_SOC_RIBP_I2C0_BASE, UINT32_C(0x18))
#define reg_i2c0_status RS_SOC_REG32(RS_SOC_RIBP_I2C0_BASE, UINT32_C(0x1C))
// i2s
#define reg_i2s_mode RS_SOC_REG32(RS_SOC_RIBP_I2S_BASE, UINT32_C(0x00))
#define reg_i2s_format RS_SOC_REG32(RS_SOC_RIBP_I2S_BASE, UINT32_C(0x04))
@@ -192,15 +183,6 @@
#define reg_dvp_recven RS_SOC_REG32(RS_SOC_RIBP_DVP_BASE, UINT32_C(0x00))
#define reg_dvp_rxdata RS_SOC_REG32(RS_SOC_RIBP_DVP_BASE, UINT32_C(0x04))
#define reg_dvp_status RS_SOC_REG32(RS_SOC_RIBP_DVP_BASE, UINT32_C(0x08))
// i2c1
#define reg_i2c1_clkdiv RS_SOC_REG32(RS_SOC_RIBP_I2C1_BASE, UINT32_C(0x00))
#define reg_i2c1_devaddr RS_SOC_REG32(RS_SOC_RIBP_I2C1_BASE, UINT32_C(0x04))
#define reg_i2c1_regaddr RS_SOC_REG32(RS_SOC_RIBP_I2C1_BASE, UINT32_C(0x08))
#define reg_i2c1_txdata RS_SOC_REG32(RS_SOC_RIBP_I2C1_BASE, UINT32_C(0x0C))
#define reg_i2c1_rxdata RS_SOC_REG32(RS_SOC_RIBP_I2C1_BASE, UINT32_C(0x10))
#define reg_i2c1_xfer RS_SOC_REG32(RS_SOC_RIBP_I2C1_BASE, UINT32_C(0x14))
#define reg_i2c1_cfg RS_SOC_REG32(RS_SOC_RIBP_I2C1_BASE, UINT32_C(0x18))
#define reg_i2c1_status RS_SOC_REG32(RS_SOC_RIBP_I2C1_BASE, UINT32_C(0x1C))
// archinfo
#define reg_archinfo_sys RS_SOC_REG32(RS_SOC_APB_ARCHINFO_BASE, UINT32_C(0x00))
#define reg_archinfo_idl RS_SOC_REG32(RS_SOC_APB_ARCHINFO_BASE, UINT32_C(0x04))
+4
View File
@@ -17,6 +17,10 @@ typedef struct {
#define RS_DMA_MODE_QSPI_RX UINT32_C(4)
#define RS_DMA_MODE_UART_TX UINT32_C(5)
#define RS_DMA_MODE_UART_RX UINT32_C(6)
#define RS_DMA_MODE_I2C0_TX UINT32_C(7)
#define RS_DMA_MODE_I2C0_RX UINT32_C(8)
#define RS_DMA_MODE_I2C1_TX UINT32_C(9)
#define RS_DMA_MODE_I2C1_RX UINT32_C(10)
rs_status_t rs_dma_config(uint32_t mode, uintptr_t source, uint32_t source_increment,
uintptr_t destination, uint32_t destination_increment,
+108 -9
View File
@@ -1,19 +1,118 @@
#ifndef RETROSOC_HAL_I2C_H
#define RETROSOC_HAL_I2C_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <retrosoc/core/status.h>
#define RS_I2C_XFER_WRITE 0x2U
#define RS_I2C_XFER_READ 0x3U
#define RS_I2C_DEVICE_ADDRESS_8BIT 0U
#define RS_I2C_DEVICE_ADDRESS_16BIT 1U
#define RS_I2C_DMA_MAX_BYTES 16U
rs_status_t rs_i2c0_init(uint8_t clock_divider);
rs_status_t rs_i2c0_write(uint8_t device_address, uint16_t register_address, uint8_t address_width,
uint8_t count, const uint8_t *data, rs_timeout_t timeout);
rs_status_t rs_i2c0_read(uint8_t device_address, uint16_t register_address, uint8_t address_width,
uint8_t count, uint8_t *data, rs_timeout_t timeout);
#define RS_I2C_ERROR_ADDR_NACK (UINT32_C(1) << 0U)
#define RS_I2C_ERROR_DATA_NACK (UINT32_C(1) << 1U)
#define RS_I2C_ERROR_ARB_LOST (UINT32_C(1) << 2U)
#define RS_I2C_ERROR_STRETCH_TO (UINT32_C(1) << 3U)
#define RS_I2C_ERROR_BUS_TO (UINT32_C(1) << 4U)
#define RS_I2C_ERROR_COMMAND_TO (UINT32_C(1) << 5U)
#define RS_I2C_ERROR_COMMAND (UINT32_C(1) << 6U)
#define RS_I2C_ERROR_RX_OVERFLOW (UINT32_C(1) << 7U)
#define RS_I2C_ERROR_CONFIG (UINT32_C(1) << 8U)
#define RS_I2C_ERROR_ABORTED (UINT32_C(1) << 9U)
#define RS_I2C_ERROR_RECOVERY (UINT32_C(1) << 10U)
#define RS_I2C_ERROR_ALL UINT32_C(0x7FF)
#define RS_I2C_INTR_DONE (UINT32_C(1) << 0U)
#define RS_I2C_INTR_CMD_WATERMARK (UINT32_C(1) << 1U)
#define RS_I2C_INTR_RX_WATERMARK (UINT32_C(1) << 2U)
#define RS_I2C_INTR_NACK (UINT32_C(1) << 3U)
#define RS_I2C_INTR_ARB_LOST (UINT32_C(1) << 4U)
#define RS_I2C_INTR_TIMEOUT (UINT32_C(1) << 5U)
#define RS_I2C_INTR_ERROR (UINT32_C(1) << 6U)
#define RS_I2C_INTR_RECOVERY_DONE (UINT32_C(1) << 7U)
#define RS_I2C_INTR_ALL UINT32_C(0xFF)
typedef enum {
RS_I2C_BUS_0 = 0,
RS_I2C_BUS_1 = 1,
} rs_i2c_bus_t;
typedef enum {
RS_I2C_REGISTER_ADDRESS_8_BIT = 1,
RS_I2C_REGISTER_ADDRESS_16_BIT = 2,
} rs_i2c_register_address_width_t;
typedef struct {
uint16_t scl_low_cycles;
uint16_t scl_high_cycles;
uint16_t start_hold_cycles;
uint16_t start_setup_cycles;
uint16_t data_hold_cycles;
uint16_t data_setup_cycles;
uint16_t stop_setup_cycles;
uint16_t bus_free_cycles;
} rs_i2c_timing_t;
typedef struct {
uint32_t source_clock_hz;
uint32_t bus_hz;
uint32_t stretch_timeout_us;
uint32_t bus_idle_timeout_us;
uint32_t command_timeout_us;
uint8_t scl_filter_cycles;
uint8_t sda_filter_cycles;
uint8_t command_watermark;
uint8_t rx_watermark;
} rs_i2c_config_t;
typedef struct {
uint16_t address;
bool ten_bit_address;
const uint8_t *tx_data;
size_t tx_length;
uint8_t *rx_data;
size_t rx_length;
} rs_i2c_transfer_t;
typedef struct {
uint16_t address;
bool ten_bit_address;
uint16_t register_address;
rs_i2c_register_address_width_t register_address_width;
} rs_i2c_register_access_t;
typedef struct {
uint32_t flags;
uint32_t command_level;
uint32_t rx_level;
uint32_t errors;
uint32_t interrupt_state;
} rs_i2c_status_t;
typedef struct {
uint32_t words[RS_I2C_DMA_MAX_BYTES];
} rs_i2c_dma_workspace_t;
rs_status_t rs_i2c_timing_calculate(uint32_t source_clock_hz, uint32_t bus_hz,
rs_i2c_timing_t *timing);
rs_status_t rs_i2c_configure(rs_i2c_bus_t bus, const rs_i2c_config_t *config, rs_timeout_t timeout);
rs_status_t rs_i2c_init(rs_i2c_bus_t bus, uint32_t source_clock_hz, uint32_t bus_hz);
rs_status_t rs_i2c_transfer(rs_i2c_bus_t bus, const rs_i2c_transfer_t *transfer,
rs_timeout_t timeout);
rs_status_t rs_i2c_register_write(rs_i2c_bus_t bus, const rs_i2c_register_access_t *access,
const uint8_t *data, size_t length, rs_timeout_t timeout);
rs_status_t rs_i2c_register_read(rs_i2c_bus_t bus, const rs_i2c_register_access_t *access,
uint8_t *data, size_t length, rs_timeout_t timeout);
rs_status_t rs_i2c_abort(rs_i2c_bus_t bus, rs_timeout_t timeout);
rs_status_t rs_i2c_recover(rs_i2c_bus_t bus, rs_timeout_t timeout);
rs_status_t rs_i2c_get_status(rs_i2c_bus_t bus, rs_i2c_status_t *status);
rs_status_t rs_i2c_irq_enable(rs_i2c_bus_t bus, uint32_t mask);
rs_status_t rs_i2c_irq_ack(rs_i2c_bus_t bus, uint32_t mask);
rs_status_t rs_i2c_irq_test(rs_i2c_bus_t bus, uint32_t mask);
rs_status_t rs_i2c_write_dma(rs_i2c_bus_t bus, uint16_t address, bool ten_bit_address,
const uint8_t *data, size_t length, rs_i2c_dma_workspace_t *workspace,
rs_timeout_t timeout);
rs_status_t rs_i2c_read_dma(rs_i2c_bus_t bus, uint16_t address, bool ten_bit_address, uint8_t *data,
size_t length, rs_i2c_dma_workspace_t *workspace, rs_timeout_t timeout);
#endif
+1 -1
View File
@@ -6,7 +6,7 @@
rs_status_t rs_dma_config(uint32_t mode, uintptr_t source, uint32_t source_increment,
uintptr_t destination, uint32_t destination_increment,
uint32_t transfer_words) {
if ((mode > RS_DMA_MODE_UART_RX) || (source == 0U) || (destination == 0U) ||
if ((mode > RS_DMA_MODE_I2C1_RX) || (source == 0U) || (destination == 0U) ||
(transfer_words == 0U) || (source_increment > 1U) || (destination_increment > 1U)) {
return RS_EINVAL;
}
+501 -36
View File
@@ -1,59 +1,524 @@
#include <limits.h>
#include <stddef.h>
#include <retrosoc/core/soc.h>
#include <retrosoc/core/wait.h>
#include <retrosoc/hal/dma.h>
#include <retrosoc/hal/gpio.h>
#include <retrosoc/hal/i2c.h>
static rs_status_t rs_i2c0_validate(uint8_t address_width, uint8_t count, const void *data) {
if ((address_width > RS_I2C_DEVICE_ADDRESS_16BIT) || ((count != 0U) && (data == NULL))) {
#define RS_I2C_CTRL_OFFSET UINT32_C(0x00)
#define RS_I2C_SCL_TIMING_OFFSET UINT32_C(0x04)
#define RS_I2C_START_TIMING_OFFSET UINT32_C(0x08)
#define RS_I2C_DATA_TIMING_OFFSET UINT32_C(0x0C)
#define RS_I2C_STOP_TIMING_OFFSET UINT32_C(0x10)
#define RS_I2C_FILTER_OFFSET UINT32_C(0x14)
#define RS_I2C_STRETCH_TIMEOUT_OFFSET UINT32_C(0x18)
#define RS_I2C_BUS_IDLE_TIMEOUT_OFFSET UINT32_C(0x1C)
#define RS_I2C_COMMAND_TIMEOUT_OFFSET UINT32_C(0x20)
#define RS_I2C_TARGET_ADDR_OFFSET UINT32_C(0x24)
#define RS_I2C_DATA_CMD_OFFSET UINT32_C(0x28)
#define RS_I2C_RXDATA_OFFSET UINT32_C(0x2C)
#define RS_I2C_STATUS_OFFSET UINT32_C(0x30)
#define RS_I2C_FIFO_LEVEL_OFFSET UINT32_C(0x34)
#define RS_I2C_COMMAND_OFFSET UINT32_C(0x38)
#define RS_I2C_CMD_WATERMARK_OFFSET UINT32_C(0x3C)
#define RS_I2C_RX_WATERMARK_OFFSET UINT32_C(0x40)
#define RS_I2C_ERROR_STATUS_OFFSET UINT32_C(0x44)
#define RS_I2C_INTR_STATE_OFFSET UINT32_C(0x48)
#define RS_I2C_INTR_ENABLE_OFFSET UINT32_C(0x4C)
#define RS_I2C_INTR_TEST_OFFSET UINT32_C(0x54)
#define RS_I2C_REG(bus, offset) RS_SOC_REG32(rs_i2c_base((bus)), (offset))
#define RS_I2C_CTRL_ENABLE UINT32_C(0x001)
#define RS_I2C_STATUS_ENABLE UINT32_C(0x001)
#define RS_I2C_STATUS_BUSY UINT32_C(0x002)
#define RS_I2C_STATUS_CMD_FULL UINT32_C(0x010)
#define RS_I2C_COMMAND_ABORT UINT32_C(0x001)
#define RS_I2C_COMMAND_RECOVER UINT32_C(0x002)
#define RS_I2C_COMMAND_FLUSH_ALL UINT32_C(0x00C)
#define RS_I2C_DATA_CMD_READ UINT32_C(0x100)
#define RS_I2C_DATA_CMD_RESTART UINT32_C(0x200)
#define RS_I2C_DATA_CMD_STOP UINT32_C(0x400)
#define RS_I2C_DATA_CMD_NACK_LAST UINT32_C(0x800)
#define RS_I2C_TARGET_TEN_BIT UINT32_C(0x400)
#define RS_I2C_FIFO_COMMAND_MASK UINT32_C(0x0FF)
#define RS_I2C_FIFO_RX_SHIFT 16U
#define RS_I2C_FIFO_RX_MASK UINT32_C(0x0FF)
#define RS_I2C_FIFO_DEPTH UINT32_C(16)
#define RS_I2C_TIMEOUT_MAX_CYCLES UINT32_C(0xFFFFFF)
static uintptr_t rs_i2c_base(rs_i2c_bus_t bus) {
return (bus == RS_I2C_BUS_0) ? (uintptr_t)RS_SOC_RIBP_I2C0_BASE
: (uintptr_t)RS_SOC_RIBP_I2C1_BASE;
}
static bool rs_i2c_bus_valid(rs_i2c_bus_t bus) {
return (bus == RS_I2C_BUS_0) || (bus == RS_I2C_BUS_1);
}
static bool rs_i2c_address_valid(uint16_t address, bool ten_bit_address) {
return ten_bit_address ? (address <= UINT16_C(0x03FF)) : (address <= UINT16_C(0x007F));
}
static rs_status_t rs_i2c_timeout_cycles(uint32_t source_clock_hz, uint32_t timeout_us,
uint32_t *cycles) {
uint64_t value;
if (cycles == NULL) {
return RS_EINVAL;
}
if (timeout_us == 0U) {
*cycles = 0U;
return RS_OK;
}
value =
(((uint64_t)source_clock_hz * (uint64_t)timeout_us) + UINT64_C(999999)) / UINT64_C(1000000);
if ((value == 0U) || (value > RS_I2C_TIMEOUT_MAX_CYCLES)) {
return RS_EINVAL;
}
*cycles = (uint32_t)value;
return RS_OK;
}
static rs_status_t rs_i2c_gpio_configure(rs_i2c_bus_t bus) {
const rs_gpio_config_t pin_config = {
.mode = (bus == RS_I2C_BUS_0) ? RS_GPIO_MODE_ALT0 : RS_GPIO_MODE_ALT1,
.pull = RS_GPIO_PULL_NONE,
.trigger = RS_GPIO_TRIGGER_NONE,
.output_high = true,
.open_drain = true,
.input_cmos = false,
.filter_enable = false,
.interrupt_enable = false,
};
const uint32_t scl_pin = (bus == RS_I2C_BUS_0) ? UINT32_C(7) : UINT32_C(3);
const uint32_t sda_pin = (bus == RS_I2C_BUS_0) ? UINT32_C(8) : UINT32_C(4);
rs_status_t status = rs_gpio_configure(scl_pin, &pin_config);
if (status == RS_OK) {
status = rs_gpio_configure(sda_pin, &pin_config);
}
return status;
}
static rs_status_t rs_i2c_wait_idle(rs_i2c_bus_t bus, rs_timeout_t timeout) {
while ((RS_I2C_REG(bus, RS_I2C_STATUS_OFFSET) & RS_I2C_STATUS_BUSY) != 0U) {
if (timeout == 0U) {
return RS_ETIMEOUT;
}
timeout--;
}
return RS_OK;
}
static rs_status_t rs_i2c_prepare(rs_i2c_bus_t bus, uint16_t address, bool ten_bit_address,
rs_timeout_t timeout) {
rs_status_t status;
if (!rs_i2c_bus_valid(bus) || !rs_i2c_address_valid(address, ten_bit_address)) {
return RS_EINVAL;
}
if ((RS_I2C_REG(bus, RS_I2C_STATUS_OFFSET) & RS_I2C_STATUS_ENABLE) == 0U) {
return RS_EIO;
}
status = rs_i2c_wait_idle(bus, timeout);
if (status != RS_OK) {
return status;
}
RS_I2C_REG(bus, RS_I2C_COMMAND_OFFSET) = RS_I2C_COMMAND_FLUSH_ALL;
RS_I2C_REG(bus, RS_I2C_ERROR_STATUS_OFFSET) = RS_I2C_ERROR_ALL;
RS_I2C_REG(bus, RS_I2C_INTR_STATE_OFFSET) = RS_I2C_INTR_ALL;
RS_I2C_REG(bus, RS_I2C_TARGET_ADDR_OFFSET) =
(uint32_t)address | (ten_bit_address ? RS_I2C_TARGET_TEN_BIT : 0U);
return RS_OK;
}
static uint32_t rs_i2c_command(const uint8_t *prefix, size_t prefix_length, const uint8_t *tx_data,
size_t tx_length, size_t rx_length, size_t index) {
const size_t write_length = prefix_length + tx_length;
const size_t total_length = write_length + rx_length;
uint32_t command;
if (index < write_length) {
command = (index < prefix_length) ? (uint32_t)prefix[index]
: (uint32_t)tx_data[index - prefix_length];
} else {
command = RS_I2C_DATA_CMD_READ;
if ((index == write_length) && (write_length != 0U)) {
command |= RS_I2C_DATA_CMD_RESTART;
}
}
if ((index + 1U) == total_length) {
command |= RS_I2C_DATA_CMD_STOP;
if (index >= write_length) {
command |= RS_I2C_DATA_CMD_NACK_LAST;
}
}
return command;
}
static rs_status_t rs_i2c_execute(rs_i2c_bus_t bus, uint16_t address, bool ten_bit_address,
const uint8_t *prefix, size_t prefix_length,
const uint8_t *tx_data, size_t tx_length, uint8_t *rx_data,
size_t rx_length, rs_timeout_t timeout) {
size_t command_index = 0U;
size_t rx_index = 0U;
size_t total_length;
rs_status_t status;
if ((prefix_length > (SIZE_MAX - tx_length)) ||
((prefix_length + tx_length) > (SIZE_MAX - rx_length))) {
return RS_EINVAL;
}
total_length = prefix_length + tx_length + rx_length;
if ((total_length == 0U) || ((prefix_length != 0U) && (prefix == NULL)) ||
((tx_length != 0U) && (tx_data == NULL)) || ((rx_length != 0U) && (rx_data == NULL))) {
return RS_EINVAL;
}
status = rs_i2c_prepare(bus, address, ten_bit_address, timeout);
if (status != RS_OK) {
return status;
}
while (timeout != 0U) {
uint32_t levels;
uint32_t errors;
errors = RS_I2C_REG(bus, RS_I2C_ERROR_STATUS_OFFSET);
if (errors != 0U) {
return RS_EIO;
}
levels = RS_I2C_REG(bus, RS_I2C_FIFO_LEVEL_OFFSET);
if ((command_index < total_length) &&
((levels & RS_I2C_FIFO_COMMAND_MASK) < RS_I2C_FIFO_DEPTH)) {
RS_I2C_REG(bus, RS_I2C_DATA_CMD_OFFSET) =
rs_i2c_command(prefix, prefix_length, tx_data, tx_length, rx_length, command_index);
command_index++;
}
if ((rx_index < rx_length) &&
(((levels >> RS_I2C_FIFO_RX_SHIFT) & RS_I2C_FIFO_RX_MASK) != 0U)) {
rx_data[rx_index] = (uint8_t)RS_I2C_REG(bus, RS_I2C_RXDATA_OFFSET);
rx_index++;
}
if (((RS_I2C_REG(bus, RS_I2C_INTR_STATE_OFFSET) & RS_I2C_INTR_DONE) != 0U) &&
(command_index == total_length) && (rx_index == rx_length)) {
RS_I2C_REG(bus, RS_I2C_INTR_STATE_OFFSET) = RS_I2C_INTR_DONE;
return RS_OK;
}
timeout--;
}
(void)rs_i2c_abort(bus, RS_TIMEOUT_DEFAULT);
return RS_ETIMEOUT;
}
static rs_status_t rs_i2c_wait_done(rs_i2c_bus_t bus, rs_timeout_t timeout) {
while (timeout != 0U) {
if (RS_I2C_REG(bus, RS_I2C_ERROR_STATUS_OFFSET) != 0U) {
return RS_EIO;
}
if ((RS_I2C_REG(bus, RS_I2C_INTR_STATE_OFFSET) & RS_I2C_INTR_DONE) != 0U) {
RS_I2C_REG(bus, RS_I2C_INTR_STATE_OFFSET) = RS_I2C_INTR_DONE;
return RS_OK;
}
timeout--;
}
return RS_ETIMEOUT;
}
rs_status_t rs_i2c_configure(rs_i2c_bus_t bus, const rs_i2c_config_t *config,
rs_timeout_t timeout) {
rs_i2c_timing_t timing;
uint32_t stretch_timeout;
uint32_t bus_idle_timeout;
uint32_t command_timeout;
rs_status_t status;
if (!rs_i2c_bus_valid(bus) || (config == NULL) || (config->scl_filter_cycles > 15U) ||
(config->sda_filter_cycles > 15U) || (config->command_watermark >= 16U) ||
(config->rx_watermark == 0U) || (config->rx_watermark > 16U)) {
return RS_EINVAL;
}
status = rs_i2c_timing_calculate(config->source_clock_hz, config->bus_hz, &timing);
if (status == RS_OK) {
status = rs_i2c_timeout_cycles(config->source_clock_hz, config->stretch_timeout_us,
&stretch_timeout);
}
if (status == RS_OK) {
status = rs_i2c_timeout_cycles(config->source_clock_hz, config->bus_idle_timeout_us,
&bus_idle_timeout);
}
if (status == RS_OK) {
status = rs_i2c_timeout_cycles(config->source_clock_hz, config->command_timeout_us,
&command_timeout);
}
if (status != RS_OK) {
return status;
}
status = rs_i2c_wait_idle(bus, timeout);
if (status != RS_OK) {
return status;
}
RS_I2C_REG(bus, RS_I2C_CTRL_OFFSET) = 0U;
status = rs_i2c_gpio_configure(bus);
if (status != RS_OK) {
return status;
}
RS_I2C_REG(bus, RS_I2C_COMMAND_OFFSET) = RS_I2C_COMMAND_FLUSH_ALL;
RS_I2C_REG(bus, RS_I2C_ERROR_STATUS_OFFSET) = RS_I2C_ERROR_ALL;
RS_I2C_REG(bus, RS_I2C_INTR_STATE_OFFSET) = RS_I2C_INTR_ALL;
RS_I2C_REG(bus, RS_I2C_SCL_TIMING_OFFSET) =
((uint32_t)timing.scl_high_cycles << 16U) | (uint32_t)timing.scl_low_cycles;
RS_I2C_REG(bus, RS_I2C_START_TIMING_OFFSET) =
((uint32_t)timing.start_setup_cycles << 16U) | (uint32_t)timing.start_hold_cycles;
RS_I2C_REG(bus, RS_I2C_DATA_TIMING_OFFSET) =
((uint32_t)timing.data_setup_cycles << 16U) | (uint32_t)timing.data_hold_cycles;
RS_I2C_REG(bus, RS_I2C_STOP_TIMING_OFFSET) =
((uint32_t)timing.bus_free_cycles << 16U) | (uint32_t)timing.stop_setup_cycles;
RS_I2C_REG(bus, RS_I2C_FILTER_OFFSET) =
((uint32_t)config->sda_filter_cycles << 8U) | (uint32_t)config->scl_filter_cycles;
RS_I2C_REG(bus, RS_I2C_STRETCH_TIMEOUT_OFFSET) = stretch_timeout;
RS_I2C_REG(bus, RS_I2C_BUS_IDLE_TIMEOUT_OFFSET) = bus_idle_timeout;
RS_I2C_REG(bus, RS_I2C_COMMAND_TIMEOUT_OFFSET) = command_timeout;
RS_I2C_REG(bus, RS_I2C_CMD_WATERMARK_OFFSET) = config->command_watermark;
RS_I2C_REG(bus, RS_I2C_RX_WATERMARK_OFFSET) = config->rx_watermark;
RS_I2C_REG(bus, RS_I2C_CTRL_OFFSET) = RS_I2C_CTRL_ENABLE;
return ((RS_I2C_REG(bus, RS_I2C_STATUS_OFFSET) & RS_I2C_STATUS_ENABLE) != 0U) ? RS_OK : RS_EIO;
}
rs_status_t rs_i2c_init(rs_i2c_bus_t bus, uint32_t source_clock_hz, uint32_t bus_hz) {
const rs_i2c_config_t config = {
.source_clock_hz = source_clock_hz,
.bus_hz = bus_hz,
.stretch_timeout_us = UINT32_C(25000),
.bus_idle_timeout_us = UINT32_C(25000),
.command_timeout_us = UINT32_C(100000),
.scl_filter_cycles = UINT8_C(2),
.sda_filter_cycles = UINT8_C(2),
.command_watermark = UINT8_C(4),
.rx_watermark = UINT8_C(8),
};
return rs_i2c_configure(bus, &config, RS_TIMEOUT_DEFAULT);
}
rs_status_t rs_i2c_transfer(rs_i2c_bus_t bus, const rs_i2c_transfer_t *transfer,
rs_timeout_t timeout) {
if (transfer == NULL) {
return RS_EINVAL;
}
return rs_i2c_execute(bus, transfer->address, transfer->ten_bit_address, NULL, 0U,
transfer->tx_data, transfer->tx_length, transfer->rx_data,
transfer->rx_length, timeout);
}
static rs_status_t rs_i2c_register_prefix(const rs_i2c_register_access_t *access, uint8_t prefix[2],
size_t *prefix_length) {
if ((access == NULL) || (prefix == NULL) || (prefix_length == NULL)) {
return RS_EINVAL;
}
switch (access->register_address_width) {
case RS_I2C_REGISTER_ADDRESS_8_BIT:
if (access->register_address > UINT8_MAX) {
return RS_EINVAL;
}
prefix[0] = (uint8_t)access->register_address;
*prefix_length = 1U;
break;
case RS_I2C_REGISTER_ADDRESS_16_BIT:
prefix[0] = (uint8_t)(access->register_address >> 8U);
prefix[1] = (uint8_t)access->register_address;
*prefix_length = 2U;
break;
default:
return RS_EINVAL;
}
return RS_OK;
}
rs_status_t rs_i2c0_init(uint8_t clock_divider) {
reg_i2c0_clkdiv = clock_divider;
return RS_OK;
}
rs_status_t rs_i2c0_write(uint8_t device_address, uint16_t register_address, uint8_t address_width,
uint8_t count, const uint8_t *data, rs_timeout_t timeout) {
rs_status_t status = rs_i2c0_validate(address_width, count, data);
rs_status_t rs_i2c_register_write(rs_i2c_bus_t bus, const rs_i2c_register_access_t *access,
const uint8_t *data, size_t length, rs_timeout_t timeout) {
uint8_t prefix[2];
size_t prefix_length;
rs_status_t status;
if ((data == NULL) || (length == 0U)) {
return RS_EINVAL;
}
status = rs_i2c_register_prefix(access, prefix, &prefix_length);
if (status != RS_OK) {
return status;
}
reg_i2c0_devaddr = device_address;
reg_i2c0_cfg = address_width;
for (uint8_t index = 0U; index < count; ++index) {
reg_i2c0_regaddr = (uint32_t)register_address + index;
reg_i2c0_txdata = data[index];
reg_i2c0_xfer = RS_I2C_XFER_WRITE;
status = rs_wait_mask(&reg_i2c0_status, UINT32_MAX, 1U, timeout);
if (status != RS_OK) {
return status;
}
}
return RS_OK;
return rs_i2c_execute(bus, access->address, access->ten_bit_address, prefix, prefix_length,
data, length, NULL, 0U, timeout);
}
rs_status_t rs_i2c0_read(uint8_t device_address, uint16_t register_address, uint8_t address_width,
uint8_t count, uint8_t *data, rs_timeout_t timeout) {
rs_status_t status = rs_i2c0_validate(address_width, count, data);
rs_status_t rs_i2c_register_read(rs_i2c_bus_t bus, const rs_i2c_register_access_t *access,
uint8_t *data, size_t length, rs_timeout_t timeout) {
uint8_t prefix[2];
size_t prefix_length;
rs_status_t status;
if ((data == NULL) || (length == 0U)) {
return RS_EINVAL;
}
status = rs_i2c_register_prefix(access, prefix, &prefix_length);
if (status != RS_OK) {
return status;
}
reg_i2c0_devaddr = device_address;
reg_i2c0_cfg = address_width;
for (uint8_t index = 0U; index < count; ++index) {
reg_i2c0_regaddr = (uint32_t)register_address + index;
reg_i2c0_xfer = RS_I2C_XFER_READ;
status = rs_wait_mask(&reg_i2c0_status, UINT32_MAX, 1U, timeout);
if (status != RS_OK) {
return status;
}
data[index] = (uint8_t)reg_i2c0_rxdata;
return rs_i2c_execute(bus, access->address, access->ten_bit_address, prefix, prefix_length,
NULL, 0U, data, length, timeout);
}
rs_status_t rs_i2c_abort(rs_i2c_bus_t bus, rs_timeout_t timeout) {
if (!rs_i2c_bus_valid(bus)) {
return RS_EINVAL;
}
if ((RS_I2C_REG(bus, RS_I2C_STATUS_OFFSET) & RS_I2C_STATUS_BUSY) == 0U) {
return RS_OK;
}
RS_I2C_REG(bus, RS_I2C_COMMAND_OFFSET) = RS_I2C_COMMAND_ABORT;
return rs_i2c_wait_idle(bus, timeout);
}
rs_status_t rs_i2c_recover(rs_i2c_bus_t bus, rs_timeout_t timeout) {
rs_status_t status;
if (!rs_i2c_bus_valid(bus)) {
return RS_EINVAL;
}
status = rs_i2c_wait_idle(bus, timeout);
if (status != RS_OK) {
return status;
}
RS_I2C_REG(bus, RS_I2C_ERROR_STATUS_OFFSET) = RS_I2C_ERROR_ALL;
RS_I2C_REG(bus, RS_I2C_INTR_STATE_OFFSET) = RS_I2C_INTR_ALL;
RS_I2C_REG(bus, RS_I2C_COMMAND_OFFSET) = RS_I2C_COMMAND_RECOVER;
while (timeout != 0U) {
if (RS_I2C_REG(bus, RS_I2C_ERROR_STATUS_OFFSET) != 0U) {
return RS_EIO;
}
if ((RS_I2C_REG(bus, RS_I2C_INTR_STATE_OFFSET) & RS_I2C_INTR_RECOVERY_DONE) != 0U) {
RS_I2C_REG(bus, RS_I2C_INTR_STATE_OFFSET) = RS_I2C_INTR_RECOVERY_DONE;
return RS_OK;
}
timeout--;
}
return RS_ETIMEOUT;
}
rs_status_t rs_i2c_get_status(rs_i2c_bus_t bus, rs_i2c_status_t *status) {
uint32_t levels;
if (!rs_i2c_bus_valid(bus) || (status == NULL)) {
return RS_EINVAL;
}
levels = RS_I2C_REG(bus, RS_I2C_FIFO_LEVEL_OFFSET);
status->flags = RS_I2C_REG(bus, RS_I2C_STATUS_OFFSET);
status->command_level = levels & RS_I2C_FIFO_COMMAND_MASK;
status->rx_level = (levels >> RS_I2C_FIFO_RX_SHIFT) & RS_I2C_FIFO_RX_MASK;
status->errors = RS_I2C_REG(bus, RS_I2C_ERROR_STATUS_OFFSET);
status->interrupt_state = RS_I2C_REG(bus, RS_I2C_INTR_STATE_OFFSET);
return RS_OK;
}
rs_status_t rs_i2c_irq_enable(rs_i2c_bus_t bus, uint32_t mask) {
if (!rs_i2c_bus_valid(bus) || ((mask & ~RS_I2C_INTR_ALL) != 0U)) {
return RS_EINVAL;
}
RS_I2C_REG(bus, RS_I2C_INTR_ENABLE_OFFSET) = mask;
return RS_OK;
}
rs_status_t rs_i2c_irq_ack(rs_i2c_bus_t bus, uint32_t mask) {
if (!rs_i2c_bus_valid(bus) || (mask == 0U) || ((mask & ~RS_I2C_INTR_ALL) != 0U)) {
return RS_EINVAL;
}
RS_I2C_REG(bus, RS_I2C_INTR_STATE_OFFSET) = mask;
return RS_OK;
}
rs_status_t rs_i2c_irq_test(rs_i2c_bus_t bus, uint32_t mask) {
if (!rs_i2c_bus_valid(bus) || (mask == 0U) || ((mask & ~RS_I2C_INTR_ALL) != 0U)) {
return RS_EINVAL;
}
RS_I2C_REG(bus, RS_I2C_INTR_TEST_OFFSET) = mask;
return RS_OK;
}
rs_status_t rs_i2c_write_dma(rs_i2c_bus_t bus, uint16_t address, bool ten_bit_address,
const uint8_t *data, size_t length, rs_i2c_dma_workspace_t *workspace,
rs_timeout_t timeout) {
rs_status_t status;
size_t index;
const uint32_t mode = (bus == RS_I2C_BUS_0) ? RS_DMA_MODE_I2C0_TX : RS_DMA_MODE_I2C1_TX;
if (!rs_i2c_bus_valid(bus) || (data == NULL) || (workspace == NULL) || (length == 0U) ||
(length > RS_I2C_DMA_MAX_BYTES)) {
return RS_EINVAL;
}
status = rs_i2c_prepare(bus, address, ten_bit_address, timeout);
if (status != RS_OK) {
return status;
}
for (index = 0U; index < length; index++) {
workspace->words[index] = (uint32_t)data[index];
}
workspace->words[length - 1U] |= RS_I2C_DATA_CMD_STOP;
status = rs_dma_config(mode, (uintptr_t)&workspace->words[0], 1U,
rs_i2c_base(bus) + RS_I2C_DATA_CMD_OFFSET, 0U, (uint32_t)length);
if (status == RS_OK) {
status = rs_dma_start();
}
if (status == RS_OK) {
status = rs_dma_wait(timeout);
}
if (status == RS_OK) {
status = rs_i2c_wait_done(bus, timeout);
}
return status;
}
rs_status_t rs_i2c_read_dma(rs_i2c_bus_t bus, uint16_t address, bool ten_bit_address, uint8_t *data,
size_t length, rs_i2c_dma_workspace_t *workspace,
rs_timeout_t timeout) {
rs_status_t status;
size_t index;
const uint32_t mode = (bus == RS_I2C_BUS_0) ? RS_DMA_MODE_I2C0_RX : RS_DMA_MODE_I2C1_RX;
if (!rs_i2c_bus_valid(bus) || (data == NULL) || (workspace == NULL) || (length == 0U) ||
(length > RS_I2C_DMA_MAX_BYTES)) {
return RS_EINVAL;
}
status = rs_i2c_prepare(bus, address, ten_bit_address, timeout);
if (status != RS_OK) {
return status;
}
status = rs_dma_config(mode, rs_i2c_base(bus) + RS_I2C_RXDATA_OFFSET, 0U,
(uintptr_t)&workspace->words[0], 1U, (uint32_t)length);
if (status != RS_OK) {
return status;
}
for (index = 0U; index < length; index++) {
uint32_t command = RS_I2C_DATA_CMD_READ;
workspace->words[index] = 0U;
if ((index + 1U) == length) {
command |= RS_I2C_DATA_CMD_STOP | RS_I2C_DATA_CMD_NACK_LAST;
}
RS_I2C_REG(bus, RS_I2C_DATA_CMD_OFFSET) = command;
}
status = rs_dma_start();
if (status == RS_OK) {
status = rs_dma_wait(timeout);
}
if (status == RS_OK) {
status = rs_i2c_wait_done(bus, timeout);
}
if (status == RS_OK) {
for (index = 0U; index < length; index++) {
data[index] = (uint8_t)workspace->words[index];
}
}
return status;
}
+89
View File
@@ -0,0 +1,89 @@
#include <limits.h>
#include <retrosoc/hal/i2c.h>
typedef struct {
uint32_t maximum_hz;
uint32_t low_ns;
uint32_t high_ns;
uint32_t start_hold_ns;
uint32_t start_setup_ns;
uint32_t data_setup_ns;
uint32_t stop_setup_ns;
uint32_t bus_free_ns;
} rs_i2c_mode_t;
static uint64_t rs_i2c_divide_ceil(uint64_t dividend, uint64_t divisor) {
return (dividend + divisor - UINT64_C(1)) / divisor;
}
static uint64_t rs_i2c_cycles_from_ns(uint32_t source_clock_hz, uint32_t period_ns) {
return rs_i2c_divide_ceil((uint64_t)source_clock_hz * (uint64_t)period_ns,
UINT64_C(1000000000));
}
static rs_i2c_mode_t rs_i2c_mode_for_rate(uint32_t bus_hz) {
rs_i2c_mode_t mode;
if (bus_hz <= UINT32_C(100000)) {
mode = (rs_i2c_mode_t){UINT32_C(100000), UINT32_C(4700), UINT32_C(4000), UINT32_C(4000),
UINT32_C(4700), UINT32_C(250), UINT32_C(4000), UINT32_C(4700)};
} else if (bus_hz <= UINT32_C(400000)) {
mode = (rs_i2c_mode_t){UINT32_C(400000), UINT32_C(1300), UINT32_C(600), UINT32_C(600),
UINT32_C(600), UINT32_C(100), UINT32_C(600), UINT32_C(1300)};
} else {
mode = (rs_i2c_mode_t){UINT32_C(1000000), UINT32_C(500), UINT32_C(260), UINT32_C(260),
UINT32_C(260), UINT32_C(50), UINT32_C(260), UINT32_C(500)};
}
return mode;
}
rs_status_t rs_i2c_timing_calculate(uint32_t source_clock_hz, uint32_t bus_hz,
rs_i2c_timing_t *timing) {
rs_i2c_mode_t mode;
uint64_t low_cycles;
uint64_t high_cycles;
uint64_t target_cycles;
uint64_t start_hold_cycles;
uint64_t start_setup_cycles;
uint64_t data_setup_cycles;
uint64_t stop_setup_cycles;
uint64_t bus_free_cycles;
if ((source_clock_hz == 0U) || (bus_hz == 0U) || (bus_hz > UINT32_C(1000000)) ||
(timing == NULL)) {
return RS_EINVAL;
}
mode = rs_i2c_mode_for_rate(bus_hz);
if (bus_hz > mode.maximum_hz) {
return RS_EINVAL;
}
low_cycles = rs_i2c_cycles_from_ns(source_clock_hz, mode.low_ns);
high_cycles = rs_i2c_cycles_from_ns(source_clock_hz, mode.high_ns);
target_cycles = rs_i2c_divide_ceil((uint64_t)source_clock_hz, (uint64_t)bus_hz);
if ((low_cycles + high_cycles) < target_cycles) {
low_cycles += target_cycles - (low_cycles + high_cycles);
}
start_hold_cycles = rs_i2c_cycles_from_ns(source_clock_hz, mode.start_hold_ns);
start_setup_cycles = rs_i2c_cycles_from_ns(source_clock_hz, mode.start_setup_ns);
data_setup_cycles = rs_i2c_cycles_from_ns(source_clock_hz, mode.data_setup_ns);
stop_setup_cycles = rs_i2c_cycles_from_ns(source_clock_hz, mode.stop_setup_ns);
bus_free_cycles = rs_i2c_cycles_from_ns(source_clock_hz, mode.bus_free_ns);
if ((low_cycles == 0U) || (high_cycles == 0U) || (start_hold_cycles == 0U) ||
(start_setup_cycles == 0U) || (data_setup_cycles == 0U) || (stop_setup_cycles == 0U) ||
(bus_free_cycles == 0U) || (low_cycles > UINT16_MAX) || (high_cycles > UINT16_MAX) ||
(start_hold_cycles > UINT16_MAX) || (start_setup_cycles > UINT16_MAX) ||
(data_setup_cycles > UINT16_MAX) || (stop_setup_cycles > UINT16_MAX) ||
(bus_free_cycles > UINT16_MAX)) {
return RS_EINVAL;
}
timing->scl_low_cycles = (uint16_t)low_cycles;
timing->scl_high_cycles = (uint16_t)high_cycles;
timing->start_hold_cycles = (uint16_t)start_hold_cycles;
timing->start_setup_cycles = (uint16_t)start_setup_cycles;
timing->data_hold_cycles = 0U;
timing->data_setup_cycles = (uint16_t)data_setup_cycles;
timing->stop_setup_cycles = (uint16_t)stop_setup_cycles;
timing->bus_free_cycles = (uint16_t)bus_free_cycles;
return RS_OK;
}
+4 -2
View File
@@ -103,7 +103,8 @@ void rs_app_info(void) {
(void *)(uintptr_t)RS_SOC_RIBP_TIM0_BASE, (void *)(uintptr_t)RS_SOC_RIBP_TIM1_BASE);
printf(" 1 x PSRAM @%p\n", (void *)&reg_psram_wait);
printf(" 1 x SPISD @%p\n", (void *)&reg_spisd_mode);
printf(" 1 x I2C0 @%p\n", (void *)&reg_i2c0_clkdiv);
printf(" 1 x I2C0 @%p\n",
(void *)(uintptr_t)RS_SOC_RIBP_I2C0_BASE);
printf(" 1 x I2S @%p\n", (void *)&reg_i2s_mode);
printf(" 1 x WS2812 @%p\n", (void *)&reg_ws2812_bit_cycles);
printf(" 1 x XPI @%p\n", (void *)&reg_xpi_cfgidx);
@@ -113,7 +114,8 @@ void rs_app_info(void) {
(void *)(uintptr_t)RS_SOC_RIBP_CLINT_BASE);
printf(" 1 x SDRAM @%p\n", (void *)&reg_sdram_clkdiv);
printf(" 1 x DVP @%p\n", (void *)&reg_dvp_recven);
printf(" 1 x I2C1 @%p\n", (void *)&reg_i2c1_clkdiv);
printf(" 1 x I2C1 @%p\n",
(void *)(uintptr_t)RS_SOC_RIBP_I2C1_BASE);
printf(" 1 x ARCHINFO @%p\n", (void *)&reg_archinfo_sys);
printf(" 1 x RNG @%p\n", (void *)&reg_rng_ctrl);
printf(" 1 x UART1(ADV) @%p\n", (void *)&reg_uart1_lcr);
+2
View File
@@ -30,6 +30,8 @@ the root README and subsystem guides.
filtering, interrupts, PDK capabilities, HAL, and verification contract.
- [ip/uart.md](ip/uart.md) defines the UART V2 framing, FIFO, error, interrupt,
DMA, HAL, and verification contracts.
- [ip/i2c.md](ip/i2c.md) defines the dual I2C V2 command, timing, error,
recovery, DMA, HAL, and verification contracts.
- [soc-integration-wiring.md](soc-integration-wiring.md) defines the generated
pin-map workflow and SoC integration boundary.
- [mini-soc-block-diagram.svg](mini-soc-block-diagram.svg) is the Mini SoC
+12 -3
View File
@@ -131,10 +131,11 @@ Verilator regressions.
`make CONFIG=configs/ci/ihp130.mk formal` proves selected
protocol invariants with SymbiYosys, Yosys, `sv2v`, and Bitwuzla. The current
targets are `bus`, `rib_adapter`, `rib2apb`, `sysctrl`, `pll_rcu`, `gpio`,
`ws2812`, `uart`, `timer`, and `clint`; each uses the SBY `prove` task for bounded model
`ws2812`, `uart`, `i2c`, `timer`, and `clint`; each uses the SBY `prove` task for bounded model
checking and k-induction, plus a `cover` task. The default depth is 20;
`ws2812` uses depth 120 and a 120-second per-task limit to cover a complete
serialized word and reset path. `sysctrl` checks register side effects, sticky terminal-test status, PLL request
serialized word and reset path. `i2c` uses depth 80 and a 300-second per-task
limit to reach a complete receive path and export its witness. `sysctrl` checks register side effects, sticky terminal-test status, PLL request
handling, and fault reporting. `pll_rcu` checks the clock-switch controller
state machine. `gpio` checks dual-window access isolation, fixed user-GPIO
ownership, locks, handoff, open-drain safety, and mux behavior. `rib_adapter`
@@ -142,12 +143,20 @@ checks both single-word compatibility adapters
under backpressure. `ws2812` checks RIBP response stability, FIFO bounds,
waveform idle safety, sticky error and interrupt propagation, abort, and
underflow preconditions. `uart` checks FIFO bounds, interrupt composition,
DMA gating, and idle/break line safety. Direct Common utility proofs, including `spill_register`,
DMA gating, and idle/break line safety. `i2c` checks FIFO bounds, interrupt
composition, valid command consumption, open-drain idle safety, and recovery
state consistency. Direct Common utility proofs, including `spill_register`,
are maintained in the Common repository.
`FORMAL_DEPTH` and `FORMAL_TIMEOUT` set the proof bound and per-task
wall-clock limit. Use `make CONFIG=<profile> formal-doctor` before a local
proof to verify that the required tools are available.
The I2C cover task stores the compact Yosys witness, constraints, and Verilog
testbench but disables direct VCD output. Expanding the 70-cycle receive cover
to VCD produces hundreds of MiB and can exceed the task limit on networked
filesystems; the witness remains available for an explicit trace conversion
when waveform inspection is required.
The locked Bitwuzla 0.9.1 CLI uses `--lang smt2`, while the selected Yosys
version invokes legacy Bitwuzla options. `scripts/bitwuzla_smt2.py` is the
single compatibility adapter used by SBY; it translates only solver startup
+1
View File
@@ -8,6 +8,7 @@ verification documentation for self-owned retroSoC peripheral IP.
- [clint.md](clint.md) defines the management-hart software and timer interrupt block.
- [gpio.md](gpio.md) defines the dual-window GPIO controller.
- [uart.md](uart.md) defines the RIBP UART V2 controller.
- [i2c.md](i2c.md) defines the dual RIBP I2C V2 controllers.
The corresponding RTL and HAL implementations remain the executable sources
of truth. Update the affected document whenever an IP interface or register ABI
+158
View File
@@ -0,0 +1,158 @@
# RIBP I2C V2
The Mini SoC provides two independent controller-mode I2C instances. I2C V2
implements 7-bit and 10-bit addressing, queued write and read commands,
repeated START, clock stretching, multi-controller arbitration detection,
programmable digital input filtering, bounded waits, bus recovery, interrupts,
and generic-DMA pacing. The register ABI is directly encoded in RTL and C; a
register generator is intentionally not used in this version.
The electrical and timing behavior follows the controller requirements in the
[NXP UM10204 I2C-bus specification](https://www.nxp.com/docs/en/user-guide/UM10204.pdf).
The FIFO command model and explicit error, timeout, and recovery reporting
follow common commercial controller practice without copying another IP ABI.
## Integration
| Property | I2C0 | I2C1 |
| --- | --- | --- |
| RIBP base address | `0x10006000` | `0x10011000` |
| Command / RX FIFO depth | 16 / 16 | 16 / 16 |
| GPIO alternate function | GPIO7/8 ALT0 | GPIO3/4 ALT1 |
| RIBP interrupt group bit | 7 | 12 |
| Management-core interrupt | 7 | 19 |
| DMA TX/RX modes | 7 / 8 | 9 / 10 |
Both instances and the generic DMA run in the SoC clock domain. Each pad uses
open-drain signaling: the controller output value is permanently zero and its
output enable means pull the line low. External pull-ups are required. The GPIO
HAL selects the corresponding alternate function and open-drain mode during
I2C configuration.
## Architecture
`ribp_i2c` composes four implementation blocks:
- `i2c_reg` owns the RIBP ABI, command and receive FIFOs, sticky error and
interrupt state, capability discovery, and DMA pacing.
- `i2c_filter` synchronizes SCL and SDA with the Common `cdc_sync` component
and changes each filtered value only after its programmed stability period.
- `i2c_core` executes START, address, data, ACK/NACK, repeated START, STOP, and
nine-clock recovery sequences. Common register components hold all protocol
state.
- `i2c_if` remains the ClusterIP boundary between controller logic and GPIO
alternate-function integration.
Software queues one 12-bit command per transferred byte. The engine owns the
bus across adjacent commands and enters a bounded hold state when the command
FIFO temporarily empties. A direction change or explicit `RESTART` emits a
repeated START without an intervening STOP. A read command pushes one byte into
the receive FIFO. The last read before STOP must set `NACK_LAST`.
## Register ABI
All registers are 32-bit and naturally aligned. Unmapped, unaligned,
direction-invalid, reserved-field, and semantically illegal accesses complete
with `resp_err`. RW configuration registers support byte strobes. Timing,
filter, and timeout registers can change only while the controller is disabled
and idle.
| Offset | Name | Access | Description |
| --- | --- | --- | --- |
| `0x000` | `CTRL` | RW | Bit 0 enables the controller after valid timing is installed. |
| `0x004` | `SCL_TIMING` | RW | SCL low cycles at 15:0 and high cycles at 31:16. |
| `0x008` | `START_TIMING` | RW | START hold cycles at 15:0 and setup cycles at 31:16. |
| `0x00C` | `DATA_TIMING` | RW | Data hold cycles at 15:0 and setup cycles at 31:16. |
| `0x010` | `STOP_TIMING` | RW | STOP setup cycles at 15:0 and bus-free cycles at 31:16. |
| `0x014` | `FILTER` | RW | SCL filter cycles at 3:0 and SDA filter cycles at 11:8. |
| `0x018` | `STRETCH_TIMEOUT` | RW | Maximum SCL-low stretch wait in SoC clocks; zero disables. |
| `0x01C` | `BUS_IDLE_TIMEOUT` | RW | Maximum bus-idle or bus-free wait; zero disables. |
| `0x020` | `COMMAND_TIMEOUT` | RW | Maximum active-command time; zero disables. |
| `0x024` | `TARGET_ADDR` | RW | Address at 9:0; bit 10 selects 10-bit addressing. |
| `0x028` | `DATA_CMD` | WO/push | Data at 7:0; READ, RESTART, STOP, NACK_LAST at bits 8 through 11. |
| `0x02C` | `RXDATA` | RO/pop | Pop one received byte; an empty read returns `resp_err`. |
| `0x030` | `STATUS` | RO | Enable, activity, FIFO, line, configuration, recovery, and DMA state. |
| `0x034` | `FIFO_LEVEL` | RO | Command count at 7:0 and receive count at 23:16. |
| `0x038` | `COMMAND` | WO | Bits 0 through 3 request abort, recovery, command flush, and RX flush. |
| `0x03C` | `CMD_WATERMARK` | RW | Command low-water threshold, 0 through 15; reset value 4. |
| `0x040` | `RX_WATERMARK` | RW | Receive high-water threshold, 1 through 16; reset value 8. |
| `0x044` | `ERROR_STATUS` | RW1C | Sticky protocol, timeout, overflow, configuration, abort, and recovery errors. |
| `0x048` | `INTR_STATE` | RW1C | Sticky raw interrupt state; active watermarks reassert after clear. |
| `0x04C` | `INTR_ENABLE` | RW | Interrupt enable mask. |
| `0x050` | `INTR_STATUS` | RO | `INTR_STATE & INTR_ENABLE`. |
| `0x054` | `INTR_TEST` | WO | Write-one interrupt injection. |
| `0x058` | `LINE_STATE` | RO | Filtered SCL, SDA, and bus-free state at bits 0 through 2. |
| `0x0F8` | `IP_VERSION` | RO | `0x00020000`, ABI 2.0. |
| `0x0FC` | `CAPABILITY` | RO | `0x007F1010`, feature mask and FIFO depths. |
`STATUS` bits 0 through 12 report enable, controller busy, external bus busy,
command empty/full, RX empty/full, valid configuration, recovery active, SCL,
SDA, TX DMA request, and RX DMA request.
`ERROR_STATUS` bits 0 through 10 report address NACK, data NACK, arbitration
loss, stretch timeout, bus timeout, command timeout, malformed command, RX
overflow, invalid configuration, abort, and failed recovery. Error and event
state is sticky, with a newly observed event taking priority over a concurrent
software clear.
`INTR_STATE` bits 0 through 7 report transfer done, command watermark, RX
watermark, NACK, arbitration loss, timeout, aggregate error, and recovery done.
The external interrupt is the reduction OR of enabled state.
## Protocol and failure behavior
For 7-bit transfers, the engine sends the target address and direction bit.
For 10-bit writes it sends the write header and low address byte. A 10-bit read
first sends that write-form address, issues a repeated START, then sends the
read header. Arbitration is checked whenever the controller releases SDA for a
transmitted one; observing low ends the transfer, releases both lines, flushes
pending commands, and records `ARB_LOST`.
The engine waits for synchronized SCL high, so target clock stretching is
honored on address, data, ACK, STOP, and recovery phases. Independent stretch,
bus, and active-command watchdogs prevent a faulty target or missing command
from holding the controller indefinitely. Software can abort an active
transfer. Recovery generates nine SCL pulses followed by STOP and reports
success only after both lines remain high for the programmed bus-free period.
## Timing, DMA, and software
`rs_i2c_timing_calculate()` converts the source clock and requested bus rate
into integer cycle constraints using 64-bit ceiling arithmetic. It supports
Standard-mode through 100 kHz, Fast-mode through 400 kHz, and Fast-mode Plus
through 1 MHz. It enforces the mode-specific minimum low, high, START, data,
STOP, and bus-free intervals rather than programming only a nominal divider.
The public HAL exposes structured configuration, 7-bit and 10-bit combined
transfers, abort, nine-clock recovery, status, interrupt control, and bounded
waits. Register-oriented devices use `rs_i2c_register_read()` and
`rs_i2c_register_write()` with an `rs_i2c_register_access_t` descriptor. The
descriptor explicitly selects I2C target address width and 8-bit or 16-bit
register addressing, while the API selects I2C0 or I2C1 independently. The
retired fixed-I2C0 compatibility API is not retained. All operations that can
wait take a timeout and return `rs_status_t`.
DMA moves one 32-bit word per command or received byte through the fixed
`DATA_CMD` and `RXDATA` addresses. The initial DMA API deliberately limits a
transfer to 16 bytes because one generic DMA channel cannot refill commands or
drain RX concurrently beyond the hardware FIFO proof boundary. Longer and
combined transfers use the bounded CPU streaming API. Removing this limit
requires scatter/gather or linked DMA descriptors and an end-to-end overflow
proof.
## Verification and current scope
The self-checking RTL test covers ABI discovery and access errors, 7-bit write,
7-bit combined write/repeated-START read, 10-bit read, clock stretching, address NACK,
arbitration loss, and nine-clock recovery. The SBY target proves FIFO bounds,
IRQ composition, valid command consumption, idle open-drain release, and
recovery/busy consistency; 80-cycle covers reach RIBP errors, TX and RX data
paths, DMA pacing, activity, recovery, interrupt, and error state. Deep cover
results retain a compact Yosys witness instead of automatically expanding a
hundreds-of-MiB VCD. Host C tests cover exact timing conversion and invalid
timing ranges.
Target/peripheral mode, SMBus/PMBus extensions, High-speed mode, wakeup from a
separate low-power clock, and multi-controller fairness policy are outside I2C
V2. Adding one requires a versioned ABI, explicit clock/reset and pad review,
and dedicated protocol verification.
+10 -2
View File
@@ -128,12 +128,16 @@ digraph mini_soc_block_diagram {
];
ribp_targets [
fillcolor="#F9E1E7",
label="RIBP target wrapper\nXPI/Flash, SDRAM, PSRAM, SPI-SD and DVP\nI2C and I2S"
label="RIBP target wrapper\nXPI/Flash, SDRAM, PSRAM, SPI-SD and DVP\nI2S"
];
uart0 [
fillcolor="#F9E1E7",
label="UART0 V2\n64-byte TX/RX FIFOs, fractional baud\nframing/errors, IRQ2, DMA-paced"
];
i2c_controllers [
fillcolor="#F9E1E7",
label="I2C0 / I2C1 V2\n16-entry command/RX FIFOs, Sm/Fm/Fm+\n7/10-bit, IRQ7/19, DMA-paced"
];
gpio_controller [
fillcolor="#F9E1E7",
label="GPIO V2\nuser + management RIBP windows\natomic I/O, filter, locks, IRQ18"
@@ -165,7 +169,7 @@ digraph mini_soc_block_diagram {
{ rank=same; user_core; dma; }
{ rank=same; fault; fabric; sram; }
{ rank=same; ribp_adapter; apb_adapter; }
{ rank=same; sysctrl; clint; ribp_targets; uart0; gpio_controller; timers; ws2812; apb_targets; }
{ rank=same; sysctrl; clint; ribp_targets; uart0; i2c_controllers; gpio_controller; timers; ws2812; apb_targets; }
{ rank=same; gpio_bridge; irq; }
ext_clock -> rcu [label="clock / reset"];
@@ -191,6 +195,7 @@ digraph mini_soc_block_diagram {
ribp_adapter -> clint [label="RIBP"];
ribp_adapter -> ribp_targets [label="RIBP"];
ribp_adapter -> uart0 [label="RIBP"];
ribp_adapter -> i2c_controllers [label="RIBP"];
ribp_adapter -> gpio_controller [label="RIBP\nuser/admin"];
ribp_adapter -> timers [label="RIBP"];
ribp_adapter -> ws2812 [label="RIBP"];
@@ -206,6 +211,9 @@ digraph mini_soc_block_diagram {
ws2812 -> irq [label="IRQ17"];
dma -> uart0 [label="TX/RX data", style=dashed];
uart0 -> irq [label="IRQ2"];
i2c_controllers -> gpio_controller [label="GPIO3/4 ALT1\nGPIO7/8 ALT0"];
dma -> i2c_controllers [label="DATA_CMD / RXDATA", style=dashed];
i2c_controllers -> irq [label="IRQ7 / IRQ19"];
apb_targets -> gpio_controller [label="user_gpio"];
ribp_targets -> ext_io [dir=both, label="device interfaces"];
uart0 -> ext_io [dir=both, label="UART pads"];
+253 -216
View File
@@ -4,19 +4,19 @@
<!-- Generated by graphviz version 2.43.0 (0)
-->
<!-- Title: mini_soc_block_diagram Pages: 1 -->
<svg width="2197pt" height="896pt"
viewBox="0.00 0.00 2197.00 896.48" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<svg width="2433pt" height="896pt"
viewBox="0.00 0.00 2433.00 896.48" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 892.48)">
<title>mini_soc_block_diagram</title>
<text text-anchor="middle" x="132" y="-870.08" font-family="Arial" font-size="18.00">retroSoC Mini SoC Architecture</text>
<g id="clust1" class="cluster">
<title>cluster_asic</title>
<path fill="transparent" stroke="#111827" d="M160,-85C160,-85 2169,-85 2169,-85 2175,-85 2181,-91 2181,-97 2181,-97 2181,-772.48 2181,-772.48 2181,-778.48 2175,-784.48 2169,-784.48 2169,-784.48 160,-784.48 160,-784.48 154,-784.48 148,-778.48 148,-772.48 148,-772.48 148,-97 148,-97 148,-91 154,-85 160,-85"/>
<path fill="transparent" stroke="#111827" d="M160,-85C160,-85 2405,-85 2405,-85 2411,-85 2417,-91 2417,-97 2417,-97 2417,-772.48 2417,-772.48 2417,-778.48 2411,-784.48 2405,-784.48 2405,-784.48 160,-784.48 160,-784.48 154,-784.48 148,-778.48 148,-772.48 148,-772.48 148,-97 148,-97 148,-91 154,-85 160,-85"/>
<text text-anchor="middle" x="196.5" y="-769.28" font-family="Arial" font-size="14.00" fill="#111827">retrosoc_asic</text>
</g>
<g id="clust2" class="cluster">
<title>cluster_soc</title>
<path fill="transparent" stroke="#94a3b8" stroke-dasharray="5,2" d="M390,-103C390,-103 2151,-103 2151,-103 2157,-103 2163,-109 2163,-115 2163,-115 2163,-729.48 2163,-729.48 2163,-735.48 2157,-741.48 2151,-741.48 2151,-741.48 390,-741.48 390,-741.48 384,-741.48 378,-735.48 378,-729.48 378,-729.48 378,-115 378,-115 378,-109 384,-103 390,-103"/>
<path fill="transparent" stroke="#94a3b8" stroke-dasharray="5,2" d="M390,-103C390,-103 2387,-103 2387,-103 2393,-103 2399,-109 2399,-115 2399,-115 2399,-729.48 2399,-729.48 2399,-735.48 2393,-741.48 2387,-741.48 2387,-741.48 390,-741.48 390,-741.48 384,-741.48 378,-735.48 378,-729.48 378,-729.48 378,-115 378,-115 378,-109 384,-103 390,-103"/>
<text text-anchor="middle" x="407.5" y="-727.88" font-family="Arial" font-size="12.00" fill="#334155">retrosoc</text>
</g>
<!-- ext_clock -->
@@ -44,46 +44,46 @@
<!-- ext_io -->
<g id="node2" class="node">
<title>ext_io</title>
<path fill="#f3f4f6" stroke="#4b5563" stroke-width="1.1" d="M1678,-45C1678,-45 1486,-45 1486,-45 1480,-45 1474,-39 1474,-33 1474,-33 1474,-12 1474,-12 1474,-6 1480,0 1486,0 1486,0 1678,0 1678,0 1684,0 1690,-6 1690,-12 1690,-12 1690,-33 1690,-33 1690,-39 1684,-45 1678,-45"/>
<text text-anchor="middle" x="1582" y="-31" font-family="Arial" font-size="10.00" fill="#111827">External I/O and memory</text>
<text text-anchor="middle" x="1582" y="-20" font-family="Arial" font-size="10.00" fill="#111827">GPIO, UART0, XPI Flash, SDRAM, PSRAM</text>
<text text-anchor="middle" x="1582" y="-9" font-family="Arial" font-size="10.00" fill="#111827">SD card, I2C, I2S, DVP and WS2812</text>
<path fill="#f3f4f6" stroke="#4b5563" stroke-width="1.1" d="M2045,-45C2045,-45 1853,-45 1853,-45 1847,-45 1841,-39 1841,-33 1841,-33 1841,-12 1841,-12 1841,-6 1847,0 1853,0 1853,0 2045,0 2045,0 2051,0 2057,-6 2057,-12 2057,-12 2057,-33 2057,-33 2057,-39 2051,-45 2045,-45"/>
<text text-anchor="middle" x="1949" y="-31" font-family="Arial" font-size="10.00" fill="#111827">External I/O and memory</text>
<text text-anchor="middle" x="1949" y="-20" font-family="Arial" font-size="10.00" fill="#111827">GPIO, UART0, XPI Flash, SDRAM, PSRAM</text>
<text text-anchor="middle" x="1949" y="-9" font-family="Arial" font-size="10.00" fill="#111827">SD card, I2C, I2S, DVP and WS2812</text>
</g>
<!-- ext_jtag -->
<g id="node3" class="node">
<title>ext_jtag</title>
<path fill="#f3f4f6" stroke="#4b5563" stroke-width="1.1" d="M777.5,-860.48C777.5,-860.48 646.5,-860.48 646.5,-860.48 640.5,-860.48 634.5,-854.48 634.5,-848.48 634.5,-848.48 634.5,-836.48 634.5,-836.48 634.5,-830.48 640.5,-824.48 646.5,-824.48 646.5,-824.48 777.5,-824.48 777.5,-824.48 783.5,-824.48 789.5,-830.48 789.5,-836.48 789.5,-836.48 789.5,-848.48 789.5,-848.48 789.5,-854.48 783.5,-860.48 777.5,-860.48"/>
<text text-anchor="middle" x="712" y="-845.48" font-family="Arial" font-size="10.00" fill="#111827">Management JTAG</text>
<text text-anchor="middle" x="712" y="-834.48" font-family="Arial" font-size="10.00" fill="#111827">TCK, TMS, TDI, TRST_n, TDO</text>
<path fill="#f3f4f6" stroke="#4b5563" stroke-width="1.1" d="M700.5,-860.48C700.5,-860.48 569.5,-860.48 569.5,-860.48 563.5,-860.48 557.5,-854.48 557.5,-848.48 557.5,-848.48 557.5,-836.48 557.5,-836.48 557.5,-830.48 563.5,-824.48 569.5,-824.48 569.5,-824.48 700.5,-824.48 700.5,-824.48 706.5,-824.48 712.5,-830.48 712.5,-836.48 712.5,-836.48 712.5,-848.48 712.5,-848.48 712.5,-854.48 706.5,-860.48 700.5,-860.48"/>
<text text-anchor="middle" x="635" y="-845.48" font-family="Arial" font-size="10.00" fill="#111827">Management JTAG</text>
<text text-anchor="middle" x="635" y="-834.48" font-family="Arial" font-size="10.00" fill="#111827">TCK, TMS, TDI, TRST_n, TDO</text>
</g>
<!-- jtag_dtm -->
<g id="node6" class="node">
<title>jtag_dtm</title>
<path fill="#e5e7eb" stroke="#4b5563" stroke-width="1.1" d="M787,-678.98C787,-678.98 637,-678.98 637,-678.98 631,-678.98 625,-672.98 625,-666.98 625,-666.98 625,-654.98 625,-654.98 625,-648.98 631,-642.98 637,-642.98 637,-642.98 787,-642.98 787,-642.98 793,-642.98 799,-648.98 799,-654.98 799,-654.98 799,-666.98 799,-666.98 799,-672.98 793,-678.98 787,-678.98"/>
<text text-anchor="middle" x="712" y="-663.98" font-family="Arial" font-size="10.00" fill="#111827">Hazard3 JTAG DTM</text>
<text text-anchor="middle" x="712" y="-652.98" font-family="Arial" font-size="10.00" fill="#111827">10 MHz asynchronous TCK domain</text>
<path fill="#e5e7eb" stroke="#4b5563" stroke-width="1.1" d="M710,-678.98C710,-678.98 560,-678.98 560,-678.98 554,-678.98 548,-672.98 548,-666.98 548,-666.98 548,-654.98 548,-654.98 548,-648.98 554,-642.98 560,-642.98 560,-642.98 710,-642.98 710,-642.98 716,-642.98 722,-648.98 722,-654.98 722,-654.98 722,-666.98 722,-666.98 722,-672.98 716,-678.98 710,-678.98"/>
<text text-anchor="middle" x="635" y="-663.98" font-family="Arial" font-size="10.00" fill="#111827">Hazard3 JTAG DTM</text>
<text text-anchor="middle" x="635" y="-652.98" font-family="Arial" font-size="10.00" fill="#111827">10 MHz asynchronous TCK domain</text>
</g>
<!-- ext_jtag&#45;&gt;jtag_dtm -->
<g id="edge2" class="edge">
<title>ext_jtag&#45;&gt;jtag_dtm</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M712,-824.27C712,-792.24 712,-723.39 712,-686.51"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="714.45,-686.14 712,-679.14 709.55,-686.14 714.45,-686.14"/>
<text text-anchor="middle" x="732" y="-795.08" font-family="Arial" font-size="8.00" fill="#374151">JTAG pads</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M635,-824.27C635,-792.24 635,-723.39 635,-686.51"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="637.45,-686.14 635,-679.14 632.55,-686.14 637.45,-686.14"/>
<text text-anchor="middle" x="655" y="-795.08" font-family="Arial" font-size="8.00" fill="#374151">JTAG pads</text>
</g>
<!-- fabric -->
<g id="node11" class="node">
<title>fabric</title>
<path fill="#dcecf8" stroke="#4b5563" stroke-width="1.1" d="M1083.66,-451.31C1083.66,-451.31 1015.98,-479.16 1015.98,-479.16 1010.43,-481.45 998.88,-483.73 992.88,-483.73 992.88,-483.73 837.12,-483.73 837.12,-483.73 831.12,-483.73 819.57,-481.45 814.02,-479.16 814.02,-479.16 746.34,-451.31 746.34,-451.31 740.79,-449.03 740.79,-444.46 746.34,-442.18 746.34,-442.18 814.02,-414.32 814.02,-414.32 819.57,-412.04 831.12,-409.76 837.12,-409.76 837.12,-409.76 992.88,-409.76 992.88,-409.76 998.88,-409.76 1010.43,-412.04 1015.98,-414.32 1015.98,-414.32 1083.66,-442.18 1083.66,-442.18 1089.21,-444.46 1089.21,-449.03 1083.66,-451.31"/>
<text text-anchor="middle" x="915" y="-455.24" font-family="Arial" font-size="10.00" fill="#111827">RIB fabric</text>
<text text-anchor="middle" x="915" y="-444.24" font-family="Arial" font-size="10.00" fill="#111827">3&#45;master round&#45;robin arbitration</text>
<text text-anchor="middle" x="915" y="-433.24" font-family="Arial" font-size="10.00" fill="#111827">INCR1 / INCR4, access policy, fault reporting</text>
<path fill="#dcecf8" stroke="#4b5563" stroke-width="1.1" d="M1327.66,-451.31C1327.66,-451.31 1259.98,-479.16 1259.98,-479.16 1254.43,-481.45 1242.88,-483.73 1236.88,-483.73 1236.88,-483.73 1081.12,-483.73 1081.12,-483.73 1075.12,-483.73 1063.57,-481.45 1058.02,-479.16 1058.02,-479.16 990.34,-451.31 990.34,-451.31 984.79,-449.03 984.79,-444.46 990.34,-442.18 990.34,-442.18 1058.02,-414.32 1058.02,-414.32 1063.57,-412.04 1075.12,-409.76 1081.12,-409.76 1081.12,-409.76 1236.88,-409.76 1236.88,-409.76 1242.88,-409.76 1254.43,-412.04 1259.98,-414.32 1259.98,-414.32 1327.66,-442.18 1327.66,-442.18 1333.21,-444.46 1333.21,-449.03 1327.66,-451.31"/>
<text text-anchor="middle" x="1159" y="-455.24" font-family="Arial" font-size="10.00" fill="#111827">RIB fabric</text>
<text text-anchor="middle" x="1159" y="-444.24" font-family="Arial" font-size="10.00" fill="#111827">3&#45;master round&#45;robin arbitration</text>
<text text-anchor="middle" x="1159" y="-433.24" font-family="Arial" font-size="10.00" fill="#111827">INCR1 / INCR4, access policy, fault reporting</text>
</g>
<!-- rcu&#45;&gt;fabric -->
<g id="edge3" class="edge">
<title>rcu&#45;&gt;fabric</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" stroke-dasharray="5,2" d="M362.03,-540.47C368.2,-539.24 372,-538.48 372,-538.48 372,-538.48 731,-483.48 731,-483.48 731,-483.48 755.75,-478.68 787.1,-472.59"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="787.92,-474.92 794.32,-471.18 786.98,-470.11 787.92,-474.92"/>
<text text-anchor="middle" x="607" y="-509.08" font-family="Arial" font-size="8.00" fill="#374151">system clock / reset</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" stroke-dasharray="5,2" d="M362.03,-540.47C368.2,-539.24 372,-538.48 372,-538.48 372,-538.48 939,-483.48 939,-483.48 939,-483.48 978.82,-477.02 1024.57,-469.58"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1025.15,-471.97 1031.66,-468.43 1024.36,-467.13 1025.15,-471.97"/>
<text text-anchor="middle" x="724" y="-509.08" font-family="Arial" font-size="8.00" fill="#374151">system clock / reset</text>
</g>
<!-- clint -->
<g id="node17" class="node">
@@ -96,273 +96,295 @@
<!-- rcu&#45;&gt;clint -->
<g id="edge8" class="edge">
<title>rcu&#45;&gt;clint</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" stroke-dasharray="5,2" d="M286.04,-538.33C353.25,-472.3 551,-278 551,-278 551,-278 590.26,-266.94 629.65,-255.85"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="630.4,-258.18 636.47,-253.93 629.07,-253.47 630.4,-258.18"/>
<text text-anchor="middle" x="464.5" y="-380.6" font-family="Arial" font-size="8.00" fill="#374151">1 MHz tick</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" stroke-dasharray="5,2" d="M293.54,-538.31C372.58,-480.32 588.63,-321.81 673.81,-259.32"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="675.27,-261.28 679.47,-255.17 672.37,-257.33 675.27,-261.28"/>
<text text-anchor="middle" x="525.5" y="-380.6" font-family="Arial" font-size="8.00" fill="#374151">1 MHz tick</text>
</g>
<!-- mgmt_core -->
<g id="node5" class="node">
<title>mgmt_core</title>
<path fill="#ddf2e3" stroke="#4b5563" stroke-width="1.1" d="M992,-683.48C992,-683.48 838,-683.48 838,-683.48 832,-683.48 826,-677.48 826,-671.48 826,-671.48 826,-650.48 826,-650.48 826,-644.48 832,-638.48 838,-638.48 838,-638.48 992,-638.48 992,-638.48 998,-638.48 1004,-644.48 1004,-650.48 1004,-650.48 1004,-671.48 1004,-671.48 1004,-677.48 998,-683.48 992,-683.48"/>
<text text-anchor="middle" x="915" y="-669.48" font-family="Arial" font-size="10.00" fill="#111827">Management core</text>
<text text-anchor="middle" x="915" y="-658.48" font-family="Arial" font-size="10.00" fill="#111827">Fixed Hazard3, branch predictor</text>
<text text-anchor="middle" x="915" y="-647.48" font-family="Arial" font-size="10.00" fill="#111827">JTAG Debug Module always present</text>
<path fill="#ddf2e3" stroke="#4b5563" stroke-width="1.1" d="M1295,-683.48C1295,-683.48 1141,-683.48 1141,-683.48 1135,-683.48 1129,-677.48 1129,-671.48 1129,-671.48 1129,-650.48 1129,-650.48 1129,-644.48 1135,-638.48 1141,-638.48 1141,-638.48 1295,-638.48 1295,-638.48 1301,-638.48 1307,-644.48 1307,-650.48 1307,-650.48 1307,-671.48 1307,-671.48 1307,-677.48 1301,-683.48 1295,-683.48"/>
<text text-anchor="middle" x="1218" y="-669.48" font-family="Arial" font-size="10.00" fill="#111827">Management core</text>
<text text-anchor="middle" x="1218" y="-658.48" font-family="Arial" font-size="10.00" fill="#111827">Fixed Hazard3, branch predictor</text>
<text text-anchor="middle" x="1218" y="-647.48" font-family="Arial" font-size="10.00" fill="#111827">JTAG Debug Module always present</text>
</g>
<!-- mgmt_adapter -->
<g id="node8" class="node">
<title>mgmt_adapter</title>
<path fill="#e5e7eb" stroke="#4b5563" stroke-width="1.1" d="M935.5,-578.98C935.5,-578.98 894.5,-578.98 894.5,-578.98 888.5,-578.98 882.5,-572.98 882.5,-566.98 882.5,-566.98 882.5,-554.98 882.5,-554.98 882.5,-548.98 888.5,-542.98 894.5,-542.98 894.5,-542.98 935.5,-542.98 935.5,-542.98 941.5,-542.98 947.5,-548.98 947.5,-554.98 947.5,-554.98 947.5,-566.98 947.5,-566.98 947.5,-572.98 941.5,-578.98 935.5,-578.98"/>
<text text-anchor="middle" x="915" y="-558.48" font-family="Arial" font-size="10.00" fill="#111827">RIBP2RIB</text>
<path fill="#e5e7eb" stroke="#4b5563" stroke-width="1.1" d="M1179.5,-578.98C1179.5,-578.98 1138.5,-578.98 1138.5,-578.98 1132.5,-578.98 1126.5,-572.98 1126.5,-566.98 1126.5,-566.98 1126.5,-554.98 1126.5,-554.98 1126.5,-548.98 1132.5,-542.98 1138.5,-542.98 1138.5,-542.98 1179.5,-542.98 1179.5,-542.98 1185.5,-542.98 1191.5,-548.98 1191.5,-554.98 1191.5,-554.98 1191.5,-566.98 1191.5,-566.98 1191.5,-572.98 1185.5,-578.98 1179.5,-578.98"/>
<text text-anchor="middle" x="1159" y="-558.48" font-family="Arial" font-size="10.00" fill="#111827">RIBP2RIB</text>
</g>
<!-- mgmt_core&#45;&gt;mgmt_adapter -->
<g id="edge9" class="edge">
<title>mgmt_core&#45;&gt;mgmt_adapter</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M915,-638.24C915,-622.92 915,-602.4 915,-586.51"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="917.45,-586.12 915,-579.12 912.55,-586.12 917.45,-586.12"/>
<text text-anchor="middle" x="924.5" y="-609.08" font-family="Arial" font-size="8.00" fill="#374151">RIBP</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1204.91,-638.24C1195.44,-622.5 1182.66,-601.28 1172.99,-585.22"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1175.02,-583.85 1169.31,-579.12 1170.83,-586.38 1175.02,-583.85"/>
<text text-anchor="middle" x="1199.5" y="-609.08" font-family="Arial" font-size="8.00" fill="#374151">RIBP</text>
</g>
<!-- debug_module -->
<g id="node7" class="node">
<title>debug_module</title>
<path fill="#fce7f3" stroke="#4b5563" stroke-width="1.1" d="M1282.5,-683.48C1282.5,-683.48 1161.5,-683.48 1161.5,-683.48 1155.5,-683.48 1149.5,-677.48 1149.5,-671.48 1149.5,-671.48 1149.5,-650.48 1149.5,-650.48 1149.5,-644.48 1155.5,-638.48 1161.5,-638.48 1161.5,-638.48 1282.5,-638.48 1282.5,-638.48 1288.5,-638.48 1294.5,-644.48 1294.5,-650.48 1294.5,-650.48 1294.5,-671.48 1294.5,-671.48 1294.5,-677.48 1288.5,-683.48 1282.5,-683.48"/>
<text text-anchor="middle" x="1222" y="-669.48" font-family="Arial" font-size="10.00" fill="#111827">Hazard3 Debug Module</text>
<text text-anchor="middle" x="1222" y="-658.48" font-family="Arial" font-size="10.00" fill="#111827">abstract commands, no SBA</text>
<text text-anchor="middle" x="1222" y="-647.48" font-family="Arial" font-size="10.00" fill="#111827">management&#45;hart&#45;only reset</text>
<path fill="#fce7f3" stroke="#4b5563" stroke-width="1.1" d="M971.5,-683.48C971.5,-683.48 850.5,-683.48 850.5,-683.48 844.5,-683.48 838.5,-677.48 838.5,-671.48 838.5,-671.48 838.5,-650.48 838.5,-650.48 838.5,-644.48 844.5,-638.48 850.5,-638.48 850.5,-638.48 971.5,-638.48 971.5,-638.48 977.5,-638.48 983.5,-644.48 983.5,-650.48 983.5,-650.48 983.5,-671.48 983.5,-671.48 983.5,-677.48 977.5,-683.48 971.5,-683.48"/>
<text text-anchor="middle" x="911" y="-669.48" font-family="Arial" font-size="10.00" fill="#111827">Hazard3 Debug Module</text>
<text text-anchor="middle" x="911" y="-658.48" font-family="Arial" font-size="10.00" fill="#111827">abstract commands, no SBA</text>
<text text-anchor="middle" x="911" y="-647.48" font-family="Arial" font-size="10.00" fill="#111827">management&#45;hart&#45;only reset</text>
</g>
<!-- jtag_dtm&#45;&gt;debug_module -->
<g id="edge4" class="edge">
<title>jtag_dtm&#45;&gt;debug_module</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M730.06,-679.13C759.11,-708.33 813,-762.48 813,-762.48 813,-762.48 1018,-762.48 1018,-762.48 1018,-762.48 1109.81,-716.8 1170.25,-686.73"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1171.55,-688.82 1176.73,-683.51 1169.37,-684.44 1171.55,-688.82"/>
<text text-anchor="middle" x="974" y="-795.08" font-family="Arial" font-size="8.00" fill="#374151">DMI, asynchronous CDC</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M722.33,-660.98C757.12,-660.98 796.91,-660.98 830.95,-660.98"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="831.12,-663.43 838.12,-660.98 831.12,-658.53 831.12,-663.43"/>
<text text-anchor="middle" x="780.25" y="-666.58" font-family="Arial" font-size="8.00" fill="#374151">DMI, asynchronous CDC</text>
</g>
<!-- debug_module&#45;&gt;mgmt_core -->
<g id="edge5" class="edge">
<title>debug_module&#45;&gt;mgmt_core</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1149.11,-660.98C1107.89,-660.98 1055.84,-660.98 1011.29,-660.98"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1011.21,-658.53 1004.21,-660.98 1011.21,-663.43 1011.21,-658.53"/>
<text text-anchor="middle" x="1076.75" y="-666.58" font-family="Arial" font-size="8.00" fill="#374151">halt, resume, abstract commands</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M983.75,-660.98C1024.99,-660.98 1077.09,-660.98 1121.69,-660.98"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1121.77,-663.43 1128.77,-660.98 1121.77,-658.53 1121.77,-663.43"/>
<text text-anchor="middle" x="1056.25" y="-666.58" font-family="Arial" font-size="8.00" fill="#374151">halt, resume, abstract commands</text>
</g>
<!-- debug_module&#45;&gt;mgmt_adapter -->
<g id="edge6" class="edge">
<title>debug_module&#45;&gt;mgmt_adapter</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" stroke-dasharray="5,2" d="M1149.23,-638.57C1072.31,-615.94 962,-583.48 962,-583.48 962,-583.48 958.87,-582.05 954.22,-579.93"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="955.03,-577.6 947.64,-576.92 952.99,-582.06 955.03,-577.6"/>
<text text-anchor="middle" x="1106" y="-609.08" font-family="Arial" font-size="8.00" fill="#374151">reset after bridge idle</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" stroke-dasharray="5,2" d="M965.42,-638.48C1012.02,-620.06 1078,-593.99 1119.63,-577.54"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1120.74,-579.74 1126.35,-574.89 1118.94,-575.18 1120.74,-579.74"/>
<text text-anchor="middle" x="1080" y="-609.08" font-family="Arial" font-size="8.00" fill="#374151">reset after bridge idle</text>
</g>
<!-- timers -->
<g id="node21" class="node">
<g id="node22" class="node">
<title>timers</title>
<path fill="#f9e1e7" stroke="#4b5563" stroke-width="1.1" d="M1923.5,-255C1923.5,-255 1742.5,-255 1742.5,-255 1736.5,-255 1730.5,-249 1730.5,-243 1730.5,-243 1730.5,-222 1730.5,-222 1730.5,-216 1736.5,-210 1742.5,-210 1742.5,-210 1923.5,-210 1923.5,-210 1929.5,-210 1935.5,-216 1935.5,-222 1935.5,-222 1935.5,-243 1935.5,-243 1935.5,-249 1929.5,-255 1923.5,-255"/>
<text text-anchor="middle" x="1833" y="-241" font-family="Arial" font-size="10.00" fill="#111827">General timers 0/1</text>
<text text-anchor="middle" x="1833" y="-230" font-family="Arial" font-size="10.00" fill="#111827">32&#45;bit counter, 16&#45;bit prescaler</text>
<text text-anchor="middle" x="1833" y="-219" font-family="Arial" font-size="10.00" fill="#111827">periodic / one&#45;shot, compare, debug freeze</text>
<path fill="#f9e1e7" stroke="#4b5563" stroke-width="1.1" d="M1001.5,-255C1001.5,-255 820.5,-255 820.5,-255 814.5,-255 808.5,-249 808.5,-243 808.5,-243 808.5,-222 808.5,-222 808.5,-216 814.5,-210 820.5,-210 820.5,-210 1001.5,-210 1001.5,-210 1007.5,-210 1013.5,-216 1013.5,-222 1013.5,-222 1013.5,-243 1013.5,-243 1013.5,-249 1007.5,-255 1001.5,-255"/>
<text text-anchor="middle" x="911" y="-241" font-family="Arial" font-size="10.00" fill="#111827">General timers 0/1</text>
<text text-anchor="middle" x="911" y="-230" font-family="Arial" font-size="10.00" fill="#111827">32&#45;bit counter, 16&#45;bit prescaler</text>
<text text-anchor="middle" x="911" y="-219" font-family="Arial" font-size="10.00" fill="#111827">periodic / one&#45;shot, compare, debug freeze</text>
</g>
<!-- debug_module&#45;&gt;timers -->
<g id="edge7" class="edge">
<title>debug_module&#45;&gt;timers</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" stroke-dasharray="5,2" d="M1294.55,-648.31C1456.04,-622.32 1831,-561.98 1831,-561.98 1831,-561.98 1831,-561.98 1831,-336 1831,-336 1831.85,-292.57 1832.44,-262.25"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1834.89,-262.28 1832.58,-255.24 1829.99,-262.19 1834.89,-262.28"/>
<text text-anchor="middle" x="1842" y="-444.84" font-family="Arial" font-size="8.00" fill="#374151">halted</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" stroke-dasharray="5,2" d="M911,-638.24C911,-609.36 911,-561.98 911,-561.98 911,-561.98 911,-561.98 911,-336 911,-336 911,-292.57 911,-262.25"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="913.45,-262.24 911,-255.24 908.55,-262.24 913.45,-262.24"/>
<text text-anchor="middle" x="922" y="-444.84" font-family="Arial" font-size="8.00" fill="#374151">halted</text>
</g>
<!-- mgmt_adapter&#45;&gt;fabric -->
<g id="edge10" class="edge">
<title>mgmt_adapter&#45;&gt;fabric</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M915,-542.94C915,-529.08 915,-508.91 915,-490.69"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="917.45,-490.57 915,-483.57 912.55,-490.57 917.45,-490.57"/>
<text text-anchor="middle" x="922" y="-509.08" font-family="Arial" font-size="8.00" fill="#374151">RIB</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1159,-542.94C1159,-529.08 1159,-508.91 1159,-490.69"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1161.45,-490.57 1159,-483.57 1156.55,-490.57 1161.45,-490.57"/>
<text text-anchor="middle" x="1166" y="-509.08" font-family="Arial" font-size="8.00" fill="#374151">RIB</text>
</g>
<!-- user_core -->
<g id="node9" class="node">
<title>user_core</title>
<path fill="#ddf2e3" stroke="#4b5563" stroke-width="1.1" d="M1063,-583.48C1063,-583.48 987,-583.48 987,-583.48 981,-583.48 975,-577.48 975,-571.48 975,-571.48 975,-550.48 975,-550.48 975,-544.48 981,-538.48 987,-538.48 987,-538.48 1063,-538.48 1063,-538.48 1069,-538.48 1075,-544.48 1075,-550.48 1075,-550.48 1075,-571.48 1075,-571.48 1075,-577.48 1069,-583.48 1063,-583.48"/>
<text text-anchor="middle" x="1025" y="-569.48" font-family="Arial" font-size="10.00" fill="#111827">Selected user core</text>
<text text-anchor="middle" x="1025" y="-558.48" font-family="Arial" font-size="10.00" fill="#111827">C0&#45;C5, one active</text>
<text text-anchor="middle" x="1025" y="-547.48" font-family="Arial" font-size="10.00" fill="#111827">PicoRV32 at C5</text>
<path fill="#ddf2e3" stroke="#4b5563" stroke-width="1.1" d="M1489,-583.48C1489,-583.48 1413,-583.48 1413,-583.48 1407,-583.48 1401,-577.48 1401,-571.48 1401,-571.48 1401,-550.48 1401,-550.48 1401,-544.48 1407,-538.48 1413,-538.48 1413,-538.48 1489,-538.48 1489,-538.48 1495,-538.48 1501,-544.48 1501,-550.48 1501,-550.48 1501,-571.48 1501,-571.48 1501,-577.48 1495,-583.48 1489,-583.48"/>
<text text-anchor="middle" x="1451" y="-569.48" font-family="Arial" font-size="10.00" fill="#111827">Selected user core</text>
<text text-anchor="middle" x="1451" y="-558.48" font-family="Arial" font-size="10.00" fill="#111827">C0&#45;C5, one active</text>
<text text-anchor="middle" x="1451" y="-547.48" font-family="Arial" font-size="10.00" fill="#111827">PicoRV32 at C5</text>
</g>
<!-- user_core&#45;&gt;fabric -->
<g id="edge11" class="edge">
<title>user_core&#45;&gt;fabric</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1003.79,-538.34C990.1,-524.37 971.85,-505.75 955.45,-489.02"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="957.08,-487.18 950.43,-483.89 953.58,-490.61 957.08,-487.18"/>
<text text-anchor="middle" x="986" y="-509.08" font-family="Arial" font-size="8.00" fill="#374151">RIB</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1438.39,-538.22C1429.81,-523.41 1420,-506.48 1420,-506.48 1420,-506.48 1348.85,-490.47 1280.85,-475.17"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1281.23,-472.74 1273.86,-473.59 1280.15,-477.52 1281.23,-472.74"/>
<text text-anchor="middle" x="1432" y="-509.08" font-family="Arial" font-size="8.00" fill="#374151">RIB</text>
</g>
<!-- dma -->
<g id="node10" class="node">
<title>dma</title>
<path fill="#ddf2e3" stroke="#4b5563" stroke-width="1.1" d="M1544,-578.98C1544,-578.98 1426,-578.98 1426,-578.98 1420,-578.98 1414,-572.98 1414,-566.98 1414,-566.98 1414,-554.98 1414,-554.98 1414,-548.98 1420,-542.98 1426,-542.98 1426,-542.98 1544,-542.98 1544,-542.98 1550,-542.98 1556,-548.98 1556,-554.98 1556,-554.98 1556,-566.98 1556,-566.98 1556,-572.98 1550,-578.98 1544,-578.98"/>
<text text-anchor="middle" x="1485" y="-563.98" font-family="Arial" font-size="10.00" fill="#111827">DMA</text>
<text text-anchor="middle" x="1485" y="-552.98" font-family="Arial" font-size="10.00" fill="#111827">RIB master and RIBP target</text>
<path fill="#ddf2e3" stroke="#4b5563" stroke-width="1.1" d="M2046,-578.98C2046,-578.98 1928,-578.98 1928,-578.98 1922,-578.98 1916,-572.98 1916,-566.98 1916,-566.98 1916,-554.98 1916,-554.98 1916,-548.98 1922,-542.98 1928,-542.98 1928,-542.98 2046,-542.98 2046,-542.98 2052,-542.98 2058,-548.98 2058,-554.98 2058,-554.98 2058,-566.98 2058,-566.98 2058,-572.98 2052,-578.98 2046,-578.98"/>
<text text-anchor="middle" x="1987" y="-563.98" font-family="Arial" font-size="10.00" fill="#111827">DMA</text>
<text text-anchor="middle" x="1987" y="-552.98" font-family="Arial" font-size="10.00" fill="#111827">RIB master and RIBP target</text>
</g>
<!-- dma&#45;&gt;fabric -->
<g id="edge12" class="edge">
<title>dma&#45;&gt;fabric</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1413.75,-545.15C1337.69,-529.32 1228,-506.48 1228,-506.48 1228,-506.48 1130.17,-488.12 1043.89,-471.93"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1044.24,-469.51 1036.91,-470.62 1043.34,-474.32 1044.24,-469.51"/>
<text text-anchor="middle" x="1278" y="-509.08" font-family="Arial" font-size="8.00" fill="#374151">RIB</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1915.77,-551.02C1755.21,-530.82 1379,-483.48 1379,-483.48 1379,-483.48 1339.18,-477.02 1293.43,-469.58"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1293.64,-467.13 1286.34,-468.43 1292.85,-471.97 1293.64,-467.13"/>
<text text-anchor="middle" x="1635" y="-509.08" font-family="Arial" font-size="8.00" fill="#374151">RIB</text>
</g>
<!-- uart0 -->
<g id="node19" class="node">
<title>uart0</title>
<path fill="#f9e1e7" stroke="#4b5563" stroke-width="1.1" d="M2135,-255C2135,-255 1975,-255 1975,-255 1969,-255 1963,-249 1963,-243 1963,-243 1963,-222 1963,-222 1963,-216 1969,-210 1975,-210 1975,-210 2135,-210 2135,-210 2141,-210 2147,-216 2147,-222 2147,-222 2147,-243 2147,-243 2147,-249 2141,-255 2135,-255"/>
<text text-anchor="middle" x="2055" y="-241" font-family="Arial" font-size="10.00" fill="#111827">UART0 V2</text>
<text text-anchor="middle" x="2055" y="-230" font-family="Arial" font-size="10.00" fill="#111827">64&#45;byte TX/RX FIFOs, fractional baud</text>
<text text-anchor="middle" x="2055" y="-219" font-family="Arial" font-size="10.00" fill="#111827">framing/errors, IRQ2, DMA&#45;paced</text>
<path fill="#f9e1e7" stroke="#4b5563" stroke-width="1.1" d="M2371,-255C2371,-255 2211,-255 2211,-255 2205,-255 2199,-249 2199,-243 2199,-243 2199,-222 2199,-222 2199,-216 2205,-210 2211,-210 2211,-210 2371,-210 2371,-210 2377,-210 2383,-216 2383,-222 2383,-222 2383,-243 2383,-243 2383,-249 2377,-255 2371,-255"/>
<text text-anchor="middle" x="2291" y="-241" font-family="Arial" font-size="10.00" fill="#111827">UART0 V2</text>
<text text-anchor="middle" x="2291" y="-230" font-family="Arial" font-size="10.00" fill="#111827">64&#45;byte TX/RX FIFOs, fractional baud</text>
<text text-anchor="middle" x="2291" y="-219" font-family="Arial" font-size="10.00" fill="#111827">framing/errors, IRQ2, DMA&#45;paced</text>
</g>
<!-- dma&#45;&gt;uart0 -->
<g id="edge33" class="edge">
<g id="edge34" class="edge">
<title>dma&#45;&gt;uart0</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" stroke-dasharray="5,2" d="M1512.85,-542.86C1586.53,-497.56 1781,-378 1781,-378 1781,-378 1929.1,-299.9 2007.53,-258.53"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="2008.93,-260.57 2013.98,-255.13 2006.64,-256.23 2008.93,-260.57"/>
<text text-anchor="middle" x="1802.5" y="-380.6" font-family="Arial" font-size="8.00" fill="#374151">TX/RX data</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" stroke-dasharray="5,2" d="M2002.92,-542.89C2052.59,-489.55 2205.03,-325.83 2265.7,-260.67"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="2267.62,-262.2 2270.6,-255.41 2264.04,-258.86 2267.62,-262.2"/>
<text text-anchor="middle" x="2174.5" y="-380.6" font-family="Arial" font-size="8.00" fill="#374151">TX/RX data</text>
</g>
<!-- i2c_controllers -->
<g id="node20" class="node">
<title>i2c_controllers</title>
<path fill="#f9e1e7" stroke="#4b5563" stroke-width="1.1" d="M1974.5,-255C1974.5,-255 1789.5,-255 1789.5,-255 1783.5,-255 1777.5,-249 1777.5,-243 1777.5,-243 1777.5,-222 1777.5,-222 1777.5,-216 1783.5,-210 1789.5,-210 1789.5,-210 1974.5,-210 1974.5,-210 1980.5,-210 1986.5,-216 1986.5,-222 1986.5,-222 1986.5,-243 1986.5,-243 1986.5,-249 1980.5,-255 1974.5,-255"/>
<text text-anchor="middle" x="1882" y="-241" font-family="Arial" font-size="10.00" fill="#111827">I2C0 / I2C1 V2</text>
<text text-anchor="middle" x="1882" y="-230" font-family="Arial" font-size="10.00" fill="#111827">16&#45;entry command/RX FIFOs, Sm/Fm/Fm+</text>
<text text-anchor="middle" x="1882" y="-219" font-family="Arial" font-size="10.00" fill="#111827">7/10&#45;bit, IRQ7/19, DMA&#45;paced</text>
</g>
<!-- dma&#45;&gt;i2c_controllers -->
<g id="edge37" class="edge">
<title>dma&#45;&gt;i2c_controllers</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" stroke-dasharray="5,2" d="M1981.5,-542.89C1964.48,-489.98 1912.54,-328.45 1891.25,-262.26"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1893.52,-261.32 1889.05,-255.41 1888.86,-262.82 1893.52,-261.32"/>
<text text-anchor="middle" x="1971" y="-380.6" font-family="Arial" font-size="8.00" fill="#374151">DATA_CMD / RXDATA</text>
</g>
<!-- ws2812 -->
<g id="node22" class="node">
<g id="node23" class="node">
<title>ws2812</title>
<path fill="#f9e1e7" stroke="#4b5563" stroke-width="1.1" d="M1478,-255C1478,-255 1344,-255 1344,-255 1338,-255 1332,-249 1332,-243 1332,-243 1332,-222 1332,-222 1332,-216 1338,-210 1344,-210 1344,-210 1478,-210 1478,-210 1484,-210 1490,-216 1490,-222 1490,-222 1490,-243 1490,-243 1490,-249 1484,-255 1478,-255"/>
<text text-anchor="middle" x="1411" y="-241" font-family="Arial" font-size="10.00" fill="#111827">WS2812 TX</text>
<text text-anchor="middle" x="1411" y="-230" font-family="Arial" font-size="10.00" fill="#111827">24&#45;bit MSB&#45;first serializer</text>
<text text-anchor="middle" x="1411" y="-219" font-family="Arial" font-size="10.00" fill="#111827">16&#45;word FIFO, IRQ17, DMA&#45;fed</text>
<path fill="#f9e1e7" stroke="#4b5563" stroke-width="1.1" d="M2160,-255C2160,-255 2026,-255 2026,-255 2020,-255 2014,-249 2014,-243 2014,-243 2014,-222 2014,-222 2014,-216 2020,-210 2026,-210 2026,-210 2160,-210 2160,-210 2166,-210 2172,-216 2172,-222 2172,-222 2172,-243 2172,-243 2172,-249 2166,-255 2160,-255"/>
<text text-anchor="middle" x="2093" y="-241" font-family="Arial" font-size="10.00" fill="#111827">WS2812 TX</text>
<text text-anchor="middle" x="2093" y="-230" font-family="Arial" font-size="10.00" fill="#111827">24&#45;bit MSB&#45;first serializer</text>
<text text-anchor="middle" x="2093" y="-219" font-family="Arial" font-size="10.00" fill="#111827">16&#45;word FIFO, IRQ17, DMA&#45;fed</text>
</g>
<!-- dma&#45;&gt;ws2812 -->
<g id="edge31" class="edge">
<g id="edge32" class="edge">
<title>dma&#45;&gt;ws2812</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" stroke-dasharray="5,2" d="M1484.69,-542.77C1483.61,-481.55 1480,-278 1480,-278 1480,-278 1466.13,-269.06 1450.83,-259.19"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1451.9,-256.96 1444.69,-255.23 1449.25,-261.08 1451.9,-256.96"/>
<text text-anchor="middle" x="1508.5" y="-380.6" font-family="Arial" font-size="8.00" fill="#374151">TXDATA writes</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" stroke-dasharray="5,2" d="M1992.55,-542.89C2009.73,-489.98 2062.17,-328.45 2083.66,-262.26"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="2086.05,-262.83 2085.89,-255.41 2081.39,-261.31 2086.05,-262.83"/>
<text text-anchor="middle" x="2071.5" y="-380.6" font-family="Arial" font-size="8.00" fill="#374151">TXDATA writes</text>
</g>
<!-- fault -->
<g id="node12" class="node">
<title>fault</title>
<path fill="#fce7f3" stroke="#4b5563" stroke-width="1.1" d="M1295.5,-464.74C1295.5,-464.74 1186.5,-464.74 1186.5,-464.74 1180.5,-464.74 1174.5,-458.74 1174.5,-452.74 1174.5,-452.74 1174.5,-440.74 1174.5,-440.74 1174.5,-434.74 1180.5,-428.74 1186.5,-428.74 1186.5,-428.74 1295.5,-428.74 1295.5,-428.74 1301.5,-428.74 1307.5,-434.74 1307.5,-440.74 1307.5,-440.74 1307.5,-452.74 1307.5,-452.74 1307.5,-458.74 1301.5,-464.74 1295.5,-464.74"/>
<text text-anchor="middle" x="1241" y="-449.74" font-family="Arial" font-size="10.00" fill="#111827">Error slave</text>
<text text-anchor="middle" x="1241" y="-438.74" font-family="Arial" font-size="10.00" fill="#111827">access and protocol faults</text>
<path fill="#fce7f3" stroke="#4b5563" stroke-width="1.1" d="M1662.5,-464.74C1662.5,-464.74 1553.5,-464.74 1553.5,-464.74 1547.5,-464.74 1541.5,-458.74 1541.5,-452.74 1541.5,-452.74 1541.5,-440.74 1541.5,-440.74 1541.5,-434.74 1547.5,-428.74 1553.5,-428.74 1553.5,-428.74 1662.5,-428.74 1662.5,-428.74 1668.5,-428.74 1674.5,-434.74 1674.5,-440.74 1674.5,-440.74 1674.5,-452.74 1674.5,-452.74 1674.5,-458.74 1668.5,-464.74 1662.5,-464.74"/>
<text text-anchor="middle" x="1608" y="-449.74" font-family="Arial" font-size="10.00" fill="#111827">Error slave</text>
<text text-anchor="middle" x="1608" y="-438.74" font-family="Arial" font-size="10.00" fill="#111827">access and protocol faults</text>
</g>
<!-- fabric&#45;&gt;fault -->
<g id="edge13" class="edge">
<title>fabric&#45;&gt;fault</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1101.98,-446.74C1124.93,-446.74 1147.24,-446.74 1167.02,-446.74"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1101.69,-444.29 1094.69,-446.74 1101.69,-449.19 1101.69,-444.29"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1167.19,-449.19 1174.19,-446.74 1167.19,-444.29 1167.19,-449.19"/>
<text text-anchor="middle" x="1134.56" y="-452.34" font-family="Arial" font-size="8.00" fill="#374151">invalid / denied</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1272.37,-478.99C1320.41,-492.66 1365.5,-505.48 1365.5,-505.48 1365.5,-505.48 1418.5,-505.48 1418.5,-505.48 1418.5,-505.48 1528,-483.48 1528,-483.48 1528,-483.48 1544.11,-476.08 1561.88,-467.93"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1272.98,-476.62 1265.58,-477.06 1271.64,-481.33 1272.98,-476.62"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1563.13,-470.05 1568.47,-464.9 1561.08,-465.59 1563.13,-470.05"/>
<text text-anchor="middle" x="1392" y="-509.08" font-family="Arial" font-size="8.00" fill="#374151">invalid / denied</text>
</g>
<!-- sram -->
<g id="node13" class="node">
<title>sram</title>
<path fill="#fff3c9" stroke="#4b5563" stroke-width="1.1" d="M1445,-464.74C1445,-464.74 1347,-464.74 1347,-464.74 1341,-464.74 1335,-458.74 1335,-452.74 1335,-452.74 1335,-440.74 1335,-440.74 1335,-434.74 1341,-428.74 1347,-428.74 1347,-428.74 1445,-428.74 1445,-428.74 1451,-428.74 1457,-434.74 1457,-440.74 1457,-440.74 1457,-452.74 1457,-452.74 1457,-458.74 1451,-464.74 1445,-464.74"/>
<text text-anchor="middle" x="1396" y="-449.74" font-family="Arial" font-size="10.00" fill="#111827">On&#45;chip SRAM</text>
<text text-anchor="middle" x="1396" y="-438.74" font-family="Arial" font-size="10.00" fill="#111827">optional RIB2RAM path</text>
<path fill="#fff3c9" stroke="#4b5563" stroke-width="1.1" d="M1502,-464.74C1502,-464.74 1404,-464.74 1404,-464.74 1398,-464.74 1392,-458.74 1392,-452.74 1392,-452.74 1392,-440.74 1392,-440.74 1392,-434.74 1398,-428.74 1404,-428.74 1404,-428.74 1502,-428.74 1502,-428.74 1508,-428.74 1514,-434.74 1514,-440.74 1514,-440.74 1514,-452.74 1514,-452.74 1514,-458.74 1508,-464.74 1502,-464.74"/>
<text text-anchor="middle" x="1453" y="-449.74" font-family="Arial" font-size="10.00" fill="#111827">On&#45;chip SRAM</text>
<text text-anchor="middle" x="1453" y="-438.74" font-family="Arial" font-size="10.00" fill="#111827">optional RIB2RAM path</text>
</g>
<!-- fabric&#45;&gt;sram -->
<g id="edge14" class="edge">
<title>fabric&#45;&gt;sram</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1036.29,-471.06C1116.9,-487.22 1208,-505.48 1208,-505.48 1208,-505.48 1222,-505.48 1222,-505.48 1222,-505.48 1321,-483.48 1321,-483.48 1321,-483.48 1335.84,-476.21 1352.32,-468.14"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1353.73,-470.18 1358.94,-464.9 1351.58,-465.78 1353.73,-470.18"/>
<text text-anchor="middle" x="1215" y="-509.08" font-family="Arial" font-size="8.00" fill="#374151">RIB</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1338.91,-446.74C1354.95,-446.74 1370.44,-446.74 1384.56,-446.74"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1384.81,-449.19 1391.81,-446.74 1384.81,-444.29 1384.81,-449.19"/>
<text text-anchor="middle" x="1365.31" y="-452.34" font-family="Arial" font-size="8.00" fill="#374151">RIB</text>
</g>
<!-- ribp_adapter -->
<g id="node14" class="node">
<title>ribp_adapter</title>
<path fill="#e5e7eb" stroke="#4b5563" stroke-width="1.1" d="M1253.5,-355C1253.5,-355 1212.5,-355 1212.5,-355 1206.5,-355 1200.5,-349 1200.5,-343 1200.5,-343 1200.5,-331 1200.5,-331 1200.5,-325 1206.5,-319 1212.5,-319 1212.5,-319 1253.5,-319 1253.5,-319 1259.5,-319 1265.5,-325 1265.5,-331 1265.5,-331 1265.5,-343 1265.5,-343 1265.5,-349 1259.5,-355 1253.5,-355"/>
<text text-anchor="middle" x="1233" y="-334.5" font-family="Arial" font-size="10.00" fill="#111827">RIB2RIBP</text>
<path fill="#e5e7eb" stroke="#4b5563" stroke-width="1.1" d="M1442.5,-355C1442.5,-355 1401.5,-355 1401.5,-355 1395.5,-355 1389.5,-349 1389.5,-343 1389.5,-343 1389.5,-331 1389.5,-331 1389.5,-325 1395.5,-319 1401.5,-319 1401.5,-319 1442.5,-319 1442.5,-319 1448.5,-319 1454.5,-325 1454.5,-331 1454.5,-331 1454.5,-343 1454.5,-343 1454.5,-349 1448.5,-355 1442.5,-355"/>
<text text-anchor="middle" x="1422" y="-334.5" font-family="Arial" font-size="10.00" fill="#111827">RIB2RIBP</text>
</g>
<!-- fabric&#45;&gt;ribp_adapter -->
<g id="edge15" class="edge">
<title>fabric&#45;&gt;ribp_adapter</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1012.57,-412.68C1073.53,-392.03 1148.71,-366.56 1193.65,-351.33"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1194.49,-353.64 1200.33,-349.07 1192.91,-349 1194.49,-353.64"/>
<text text-anchor="middle" x="1118" y="-380.6" font-family="Arial" font-size="8.00" fill="#374151">RIB</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1246.58,-409.87C1292.52,-391.04 1346.56,-368.91 1382.39,-354.23"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1383.76,-356.31 1389.31,-351.39 1381.9,-351.78 1383.76,-356.31"/>
<text text-anchor="middle" x="1328" y="-380.6" font-family="Arial" font-size="8.00" fill="#374151">RIB</text>
</g>
<!-- apb_adapter -->
<g id="node15" class="node">
<title>apb_adapter</title>
<path fill="#e5e7eb" stroke="#4b5563" stroke-width="1.1" d="M662,-355C662,-355 592,-355 592,-355 586,-355 580,-349 580,-343 580,-343 580,-331 580,-331 580,-325 586,-319 592,-319 592,-319 662,-319 662,-319 668,-319 674,-325 674,-331 674,-331 674,-343 674,-343 674,-349 668,-355 662,-355"/>
<text text-anchor="middle" x="627" y="-340" font-family="Arial" font-size="10.00" fill="#111827">RIB2APB</text>
<text text-anchor="middle" x="627" y="-329" font-family="Arial" font-size="10.00" fill="#111827">direct APB bridge</text>
<path fill="#e5e7eb" stroke="#4b5563" stroke-width="1.1" d="M1107,-355C1107,-355 1037,-355 1037,-355 1031,-355 1025,-349 1025,-343 1025,-343 1025,-331 1025,-331 1025,-325 1031,-319 1037,-319 1037,-319 1107,-319 1107,-319 1113,-319 1119,-325 1119,-331 1119,-331 1119,-343 1119,-343 1119,-349 1113,-355 1107,-355"/>
<text text-anchor="middle" x="1072" y="-340" font-family="Arial" font-size="10.00" fill="#111827">RIB2APB</text>
<text text-anchor="middle" x="1072" y="-329" font-family="Arial" font-size="10.00" fill="#111827">direct APB bridge</text>
</g>
<!-- fabric&#45;&gt;apb_adapter -->
<g id="edge16" class="edge">
<title>fabric&#45;&gt;apb_adapter</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M846.72,-409.89C815.78,-393.64 786,-378 786,-378 786,-378 727.32,-363.24 681.26,-351.65"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="681.66,-349.22 674.27,-349.89 680.47,-353.98 681.66,-349.22"/>
<text text-anchor="middle" x="808" y="-380.6" font-family="Arial" font-size="8.00" fill="#374151">RIB</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1130.03,-409.87C1116.97,-393.69 1101.93,-375.07 1090.44,-360.83"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1092.05,-358.93 1085.74,-355.02 1088.24,-362.01 1092.05,-358.93"/>
<text text-anchor="middle" x="1117" y="-380.6" font-family="Arial" font-size="8.00" fill="#374151">RIB</text>
</g>
<!-- sysctrl -->
<g id="node16" class="node">
<title>sysctrl</title>
<path fill="#f9e1e7" stroke="#4b5563" stroke-width="1.1" d="M1691.5,-255C1691.5,-255 1528.5,-255 1528.5,-255 1522.5,-255 1516.5,-249 1516.5,-243 1516.5,-243 1516.5,-222 1516.5,-222 1516.5,-216 1522.5,-210 1528.5,-210 1528.5,-210 1691.5,-210 1691.5,-210 1697.5,-210 1703.5,-216 1703.5,-222 1703.5,-222 1703.5,-243 1703.5,-243 1703.5,-249 1697.5,-255 1691.5,-255"/>
<text text-anchor="middle" x="1610" y="-241" font-family="Arial" font-size="10.00" fill="#111827">SYSCTRL</text>
<text text-anchor="middle" x="1610" y="-230" font-family="Arial" font-size="10.00" fill="#111827">PLL, user&#45;core select/reset/bus enable</text>
<text text-anchor="middle" x="1610" y="-219" font-family="Arial" font-size="10.00" fill="#111827">fault and performance control</text>
<path fill="#f9e1e7" stroke="#4b5563" stroke-width="1.1" d="M1738.5,-255C1738.5,-255 1575.5,-255 1575.5,-255 1569.5,-255 1563.5,-249 1563.5,-243 1563.5,-243 1563.5,-222 1563.5,-222 1563.5,-216 1569.5,-210 1575.5,-210 1575.5,-210 1738.5,-210 1738.5,-210 1744.5,-210 1750.5,-216 1750.5,-222 1750.5,-222 1750.5,-243 1750.5,-243 1750.5,-249 1744.5,-255 1738.5,-255"/>
<text text-anchor="middle" x="1657" y="-241" font-family="Arial" font-size="10.00" fill="#111827">SYSCTRL</text>
<text text-anchor="middle" x="1657" y="-230" font-family="Arial" font-size="10.00" fill="#111827">PLL, user&#45;core select/reset/bus enable</text>
<text text-anchor="middle" x="1657" y="-219" font-family="Arial" font-size="10.00" fill="#111827">fault and performance control</text>
</g>
<!-- ribp_adapter&#45;&gt;sysctrl -->
<g id="edge17" class="edge">
<title>ribp_adapter&#45;&gt;sysctrl</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1265.56,-320C1300.48,-302.83 1351,-278 1351,-278 1351,-278 1504,-255 1504,-255 1504,-255 1505.94,-254.61 1509.28,-253.93"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1509.79,-256.33 1516.16,-252.53 1508.81,-251.52 1509.79,-256.33"/>
<text text-anchor="middle" x="1360.5" y="-285.1" font-family="Arial" font-size="8.00" fill="#374151">RIBP</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1454.67,-324.54C1488.35,-312.72 1536,-296 1536,-296 1536,-296 1575.31,-275.69 1609.12,-258.23"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1610.26,-260.4 1615.36,-255.01 1608.01,-256.04 1610.26,-260.4"/>
<text text-anchor="middle" x="1577.5" y="-285.1" font-family="Arial" font-size="8.00" fill="#374151">RIBP</text>
</g>
<!-- ribp_adapter&#45;&gt;clint -->
<g id="edge18" class="edge">
<title>ribp_adapter&#45;&gt;clint</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1200.18,-333.65C1080.57,-325.07 675,-296 675,-296 675,-296 675,-278 675,-278 675,-278 681.01,-270.13 687.98,-261.01"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="690.1,-262.28 692.4,-255.23 686.2,-259.3 690.1,-262.28"/>
<text text-anchor="middle" x="684.5" y="-285.1" font-family="Arial" font-size="8.00" fill="#374151">RIBP</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1389.25,-333.22C1282.44,-324.15 951,-296 951,-296 951,-296 859.3,-272.32 788.79,-254.11"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="789,-251.63 781.61,-252.25 787.77,-256.37 789,-251.63"/>
<text text-anchor="middle" x="960.5" y="-285.1" font-family="Arial" font-size="8.00" fill="#374151">RIBP</text>
</g>
<!-- ribp_targets -->
<g id="node18" class="node">
<title>ribp_targets</title>
<path fill="#f9e1e7" stroke="#4b5563" stroke-width="1.1" d="M1292.5,-255C1292.5,-255 1087.5,-255 1087.5,-255 1081.5,-255 1075.5,-249 1075.5,-243 1075.5,-243 1075.5,-222 1075.5,-222 1075.5,-216 1081.5,-210 1087.5,-210 1087.5,-210 1292.5,-210 1292.5,-210 1298.5,-210 1304.5,-216 1304.5,-222 1304.5,-222 1304.5,-243 1304.5,-243 1304.5,-249 1298.5,-255 1292.5,-255"/>
<text text-anchor="middle" x="1190" y="-241" font-family="Arial" font-size="10.00" fill="#111827">RIBP target wrapper</text>
<text text-anchor="middle" x="1190" y="-230" font-family="Arial" font-size="10.00" fill="#111827">XPI/Flash, SDRAM, PSRAM, SPI&#45;SD and DVP</text>
<text text-anchor="middle" x="1190" y="-219" font-family="Arial" font-size="10.00" fill="#111827">I2C and I2S</text>
<path fill="#f9e1e7" stroke="#4b5563" stroke-width="1.1" d="M1524.5,-255C1524.5,-255 1319.5,-255 1319.5,-255 1313.5,-255 1307.5,-249 1307.5,-243 1307.5,-243 1307.5,-222 1307.5,-222 1307.5,-216 1313.5,-210 1319.5,-210 1319.5,-210 1524.5,-210 1524.5,-210 1530.5,-210 1536.5,-216 1536.5,-222 1536.5,-222 1536.5,-243 1536.5,-243 1536.5,-249 1530.5,-255 1524.5,-255"/>
<text text-anchor="middle" x="1422" y="-241" font-family="Arial" font-size="10.00" fill="#111827">RIBP target wrapper</text>
<text text-anchor="middle" x="1422" y="-230" font-family="Arial" font-size="10.00" fill="#111827">XPI/Flash, SDRAM, PSRAM, SPI&#45;SD and DVP</text>
<text text-anchor="middle" x="1422" y="-219" font-family="Arial" font-size="10.00" fill="#111827">I2S</text>
</g>
<!-- ribp_adapter&#45;&gt;ribp_targets -->
<g id="edge19" class="edge">
<title>ribp_adapter&#45;&gt;ribp_targets</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1225.71,-318.61C1219.18,-303.05 1209.48,-279.94 1201.84,-261.72"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1204.1,-260.77 1199.13,-255.26 1199.58,-262.66 1204.1,-260.77"/>
<text text-anchor="middle" x="1224.5" y="-285.1" font-family="Arial" font-size="8.00" fill="#374151">RIBP</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1422,-318.61C1422,-303.32 1422,-280.75 1422,-262.69"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1424.45,-262.26 1422,-255.26 1419.55,-262.26 1424.45,-262.26"/>
<text text-anchor="middle" x="1431.5" y="-285.1" font-family="Arial" font-size="8.00" fill="#374151">RIBP</text>
</g>
<!-- ribp_adapter&#45;&gt;uart0 -->
<g id="edge20" class="edge">
<title>ribp_adapter&#45;&gt;uart0</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1265.81,-331C1358.23,-316.94 1614,-278 1614,-278 1614,-278 1949,-255 1949,-255 1949,-255 1951.57,-254.48 1955.91,-253.6"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1956.48,-255.98 1962.86,-252.19 1955.51,-251.18 1956.48,-255.98"/>
<text text-anchor="middle" x="1623.5" y="-285.1" font-family="Arial" font-size="8.00" fill="#374151">RIBP</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1454.82,-332.96C1555.64,-323.63 1854,-296 1854,-296 1854,-296 2186,-255 2186,-255 2186,-255 2188.16,-254.56 2191.87,-253.8"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="2192.51,-256.17 2198.88,-252.36 2191.53,-251.37 2192.51,-256.17"/>
<text text-anchor="middle" x="1999.5" y="-285.1" font-family="Arial" font-size="8.00" fill="#374151">RIBP</text>
</g>
<!-- ribp_adapter&#45;&gt;i2c_controllers -->
<g id="edge21" class="edge">
<title>ribp_adapter&#45;&gt;i2c_controllers</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1454.71,-328.71C1521.23,-313.89 1674.71,-279.69 1778.43,-256.58"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1779.2,-258.92 1785.5,-255 1778.13,-254.13 1779.2,-258.92"/>
<text text-anchor="middle" x="1688.5" y="-285.1" font-family="Arial" font-size="8.00" fill="#374151">RIBP</text>
</g>
<!-- gpio_controller -->
<g id="node20" class="node">
<g id="node21" class="node">
<title>gpio_controller</title>
<path fill="#f9e1e7" stroke="#4b5563" stroke-width="1.1" d="M971,-255C971,-255 821,-255 821,-255 815,-255 809,-249 809,-243 809,-243 809,-222 809,-222 809,-216 815,-210 821,-210 821,-210 971,-210 971,-210 977,-210 983,-216 983,-222 983,-222 983,-243 983,-243 983,-249 977,-255 971,-255"/>
<text text-anchor="middle" x="896" y="-241" font-family="Arial" font-size="10.00" fill="#111827">GPIO V2</text>
<text text-anchor="middle" x="896" y="-230" font-family="Arial" font-size="10.00" fill="#111827">user + management RIBP windows</text>
<text text-anchor="middle" x="896" y="-219" font-family="Arial" font-size="10.00" fill="#111827">atomic I/O, filter, locks, IRQ18</text>
<path fill="#f9e1e7" stroke="#4b5563" stroke-width="1.1" d="M1203,-255C1203,-255 1053,-255 1053,-255 1047,-255 1041,-249 1041,-243 1041,-243 1041,-222 1041,-222 1041,-216 1047,-210 1053,-210 1053,-210 1203,-210 1203,-210 1209,-210 1215,-216 1215,-222 1215,-222 1215,-243 1215,-243 1215,-249 1209,-255 1203,-255"/>
<text text-anchor="middle" x="1128" y="-241" font-family="Arial" font-size="10.00" fill="#111827">GPIO V2</text>
<text text-anchor="middle" x="1128" y="-230" font-family="Arial" font-size="10.00" fill="#111827">user + management RIBP windows</text>
<text text-anchor="middle" x="1128" y="-219" font-family="Arial" font-size="10.00" fill="#111827">atomic I/O, filter, locks, IRQ18</text>
</g>
<!-- ribp_adapter&#45;&gt;gpio_controller -->
<g id="edge21" class="edge">
<g id="edge22" class="edge">
<title>ribp_adapter&#45;&gt;gpio_controller</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1200.46,-333.56C1084.74,-324.87 700,-296 700,-296 700,-296 700,-278 700,-278 700,-278 751.58,-266.29 801.82,-254.88"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="802.61,-257.22 808.89,-253.28 801.52,-252.44 802.61,-257.22"/>
<text text-anchor="middle" x="720" y="-289.6" font-family="Arial" font-size="8.00" fill="#374151">RIBP</text>
<text text-anchor="middle" x="720" y="-280.6" font-family="Arial" font-size="8.00" fill="#374151">user/admin</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1389.41,-324.64C1343.13,-308.5 1257.25,-278.56 1196.57,-257.41"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1197.09,-254.99 1189.68,-255 1195.48,-259.62 1197.09,-254.99"/>
<text text-anchor="middle" x="1321" y="-289.6" font-family="Arial" font-size="8.00" fill="#374151">RIBP</text>
<text text-anchor="middle" x="1321" y="-280.6" font-family="Arial" font-size="8.00" fill="#374151">user/admin</text>
</g>
<!-- ribp_adapter&#45;&gt;timers -->
<g id="edge22" class="edge">
<g id="edge23" class="edge">
<title>ribp_adapter&#45;&gt;timers</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1265.59,-326.62C1306.67,-314.8 1372,-296 1372,-296 1372,-296 1406,-278 1406,-278 1406,-278 1717,-255 1717,-255 1717,-255 1719.33,-254.57 1723.33,-253.83"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1723.87,-256.22 1730.31,-252.53 1722.98,-251.4 1723.87,-256.22"/>
<text text-anchor="middle" x="1415.5" y="-285.1" font-family="Arial" font-size="8.00" fill="#374151">RIBP</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1389.26,-329.43C1316.31,-314.8 1137.66,-278.96 1020.55,-255.47"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1020.97,-253.06 1013.63,-254.09 1020.01,-257.86 1020.97,-253.06"/>
<text text-anchor="middle" x="1221.5" y="-285.1" font-family="Arial" font-size="8.00" fill="#374151">RIBP</text>
</g>
<!-- ribp_adapter&#45;&gt;ws2812 -->
<g id="edge23" class="edge">
<g id="edge24" class="edge">
<title>ribp_adapter&#45;&gt;ws2812</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1250.38,-318.91C1267.79,-301.8 1292,-278 1292,-278 1292,-278 1318.5,-268.09 1346.5,-257.62"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1347.54,-259.84 1353.24,-255.1 1345.83,-255.26 1347.54,-259.84"/>
<text text-anchor="middle" x="1301.5" y="-285.1" font-family="Arial" font-size="8.00" fill="#374151">RIBP</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1454.73,-331.41C1577,-314.28 2000,-255 2000,-255 2000,-255 2002.58,-254.4 2006.88,-253.41"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="2007.45,-255.79 2013.72,-251.83 2006.35,-251.02 2007.45,-255.79"/>
<text text-anchor="middle" x="1838.5" y="-285.1" font-family="Arial" font-size="8.00" fill="#374151">RIBP</text>
</g>
<!-- apb_targets -->
<g id="node23" class="node">
<g id="node24" class="node">
<title>apb_targets</title>
<path fill="#e6f4ea" stroke="#4b5563" stroke-width="1.1" d="M597.5,-255C597.5,-255 406.5,-255 406.5,-255 400.5,-255 394.5,-249 394.5,-243 394.5,-243 394.5,-222 394.5,-222 394.5,-216 400.5,-210 406.5,-210 406.5,-210 597.5,-210 597.5,-210 603.5,-210 609.5,-216 609.5,-222 609.5,-222 609.5,-243 609.5,-243 609.5,-249 603.5,-255 597.5,-255"/>
<text text-anchor="middle" x="502" y="-241" font-family="Arial" font-size="10.00" fill="#111827">APB target wrapper</text>
@@ -370,112 +392,127 @@
<text text-anchor="middle" x="502" y="-219" font-family="Arial" font-size="10.00" fill="#111827">watchdog, CRC, advanced timer and user IP</text>
</g>
<!-- apb_adapter&#45;&gt;apb_targets -->
<g id="edge24" class="edge">
<g id="edge25" class="edge">
<title>apb_adapter&#45;&gt;apb_targets</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M606.08,-318.84C586.47,-302.77 556.88,-278.5 534.25,-259.95"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="535.63,-257.9 528.66,-255.36 532.52,-261.69 535.63,-257.9"/>
<text text-anchor="middle" x="583" y="-285.1" font-family="Arial" font-size="8.00" fill="#374151">APB</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1024.87,-327.52C936.06,-311.55 741.64,-276.59 616.52,-254.09"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="616.82,-251.66 609.5,-252.83 615.96,-256.48 616.82,-251.66"/>
<text text-anchor="middle" x="846" y="-285.1" font-family="Arial" font-size="8.00" fill="#374151">APB</text>
</g>
<!-- irq -->
<g id="node25" class="node">
<g id="node26" class="node">
<title>irq</title>
<path fill="#fce7f3" stroke="#4b5563" stroke-width="1.1" d="M1515,-155C1515,-155 1309,-155 1309,-155 1303,-155 1297,-149 1297,-143 1297,-143 1297,-131 1297,-131 1297,-125 1303,-119 1309,-119 1309,-119 1515,-119 1515,-119 1521,-119 1527,-125 1527,-131 1527,-131 1527,-143 1527,-143 1527,-149 1521,-155 1515,-155"/>
<text text-anchor="middle" x="1412" y="-140" font-family="Arial" font-size="10.00" fill="#111827">Interrupt aggregation</text>
<text text-anchor="middle" x="1412" y="-129" font-family="Arial" font-size="10.00" fill="#111827">RIBP and APB target IRQs to management core</text>
<path fill="#fce7f3" stroke="#4b5563" stroke-width="1.1" d="M1502,-155C1502,-155 1296,-155 1296,-155 1290,-155 1284,-149 1284,-143 1284,-143 1284,-131 1284,-131 1284,-125 1290,-119 1296,-119 1296,-119 1502,-119 1502,-119 1508,-119 1514,-125 1514,-131 1514,-131 1514,-143 1514,-143 1514,-149 1508,-155 1502,-155"/>
<text text-anchor="middle" x="1399" y="-140" font-family="Arial" font-size="10.00" fill="#111827">Interrupt aggregation</text>
<text text-anchor="middle" x="1399" y="-129" font-family="Arial" font-size="10.00" fill="#111827">RIBP and APB target IRQs to management core</text>
</g>
<!-- clint&#45;&gt;irq -->
<g id="edge29" class="edge">
<g id="edge30" class="edge">
<title>clint&#45;&gt;irq</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M781.63,-213.55C790.22,-211.43 796,-210 796,-210 796,-210 960,-178 960,-178 960,-178 1284,-155 1284,-155 1284,-155 1286.1,-154.72 1289.77,-154.23"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1290.12,-156.66 1296.74,-153.31 1289.48,-151.8 1290.12,-156.66"/>
<text text-anchor="middle" x="998.5" y="-180.6" font-family="Arial" font-size="8.00" fill="#374151">machine IRQ0 / IRQ1</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M781.56,-213.36C789.62,-211.34 795,-210 795,-210 795,-210 1100.51,-173.58 1276.97,-152.55"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1277.31,-154.97 1283.97,-151.71 1276.73,-150.11 1277.31,-154.97"/>
<text text-anchor="middle" x="1098.5" y="-180.6" font-family="Arial" font-size="8.00" fill="#374151">machine IRQ0 / IRQ1</text>
</g>
<!-- ribp_targets&#45;&gt;ext_io -->
<g id="edge36" class="edge">
<g id="edge40" class="edge">
<title>ribp_targets&#45;&gt;ext_io</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1311.9,-211.18C1316.39,-210.44 1319,-210 1319,-210 1319,-210 1564,-187 1564,-187 1564,-187 1573.65,-99.32 1578.82,-52.38"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1311.2,-208.82 1304.7,-212.38 1312.01,-213.65 1311.2,-208.82"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1581.28,-52.44 1579.61,-45.21 1576.41,-51.9 1581.28,-52.44"/>
<text text-anchor="middle" x="1605" y="-135.1" font-family="Arial" font-size="8.00" fill="#374151">device interfaces</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1543.73,-211.05C1547.71,-210.39 1550,-210 1550,-210 1550,-210 1931,-187 1931,-187 1931,-187 1940.65,-99.32 1945.82,-52.38"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1543.28,-208.64 1536.78,-212.22 1544.09,-213.48 1543.28,-208.64"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1948.28,-52.44 1946.61,-45.21 1943.41,-51.9 1948.28,-52.44"/>
<text text-anchor="middle" x="1972" y="-135.1" font-family="Arial" font-size="8.00" fill="#374151">device interfaces</text>
</g>
<!-- ribp_targets&#45;&gt;gpio_controller -->
<g id="edge25" class="edge">
<g id="edge26" class="edge">
<title>ribp_targets&#45;&gt;gpio_controller</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1075.39,-232.5C1047.19,-232.5 1017.32,-232.5 990.21,-232.5"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="990.02,-230.05 983.02,-232.5 990.02,-234.95 990.02,-230.05"/>
<text text-anchor="middle" x="1029.25" y="-238.1" font-family="Arial" font-size="8.00" fill="#374151">alternate functions</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1307.39,-232.5C1279.19,-232.5 1249.32,-232.5 1222.21,-232.5"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1222.02,-230.05 1215.02,-232.5 1222.02,-234.95 1222.02,-230.05"/>
<text text-anchor="middle" x="1261.25" y="-238.1" font-family="Arial" font-size="8.00" fill="#374151">alternate functions</text>
</g>
<!-- uart0&#45;&gt;ext_io -->
<g id="edge37" class="edge">
<g id="edge41" class="edge">
<title>uart0&#45;&gt;ext_io</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1999.17,-206.95C1908.15,-166.92 1728.85,-88.08 1637.83,-48.05"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1998.33,-209.25 2005.72,-209.83 2000.3,-204.77 1998.33,-209.25"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1638.56,-45.7 1631.17,-45.12 1636.59,-50.18 1638.56,-45.7"/>
<text text-anchor="middle" x="1901" y="-135.1" font-family="Arial" font-size="8.00" fill="#374151">UART pads</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M2249,-205.95C2183.15,-165.91 2056.38,-88.81 1990.71,-48.87"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="2248.11,-208.28 2255.37,-209.83 2250.66,-204.1 2248.11,-208.28"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1991.81,-46.67 1984.55,-45.12 1989.26,-50.85 1991.81,-46.67"/>
<text text-anchor="middle" x="2186" y="-135.1" font-family="Arial" font-size="8.00" fill="#374151">UART pads</text>
</g>
<!-- uart0&#45;&gt;irq -->
<g id="edge34" class="edge">
<g id="edge35" class="edge">
<title>uart0&#45;&gt;irq</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1962.86,-212.81C1954.44,-211.1 1949,-210 1949,-210 1949,-210 1692.67,-175.63 1534.2,-154.38"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1534.46,-151.95 1527.2,-153.45 1533.81,-156.8 1534.46,-151.95"/>
<text text-anchor="middle" x="1784.5" y="-180.6" font-family="Arial" font-size="8.00" fill="#374151">IRQ2</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M2198.88,-212.64C2191.02,-211.03 2186,-210 2186,-210 2186,-210 1879,-178 1879,-178 1879,-178 1663.63,-160.05 1521.42,-148.2"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1521.46,-145.75 1514.28,-147.61 1521.05,-150.63 1521.46,-145.75"/>
<text text-anchor="middle" x="1970.5" y="-180.6" font-family="Arial" font-size="8.00" fill="#374151">IRQ2</text>
</g>
<!-- i2c_controllers&#45;&gt;gpio_controller -->
<g id="edge36" class="edge">
<title>i2c_controllers&#45;&gt;gpio_controller</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1777.08,-252.51C1769.06,-254.04 1764,-255 1764,-255 1764,-255 1530,-287 1530,-287 1530,-287 1478,-287 1478,-287 1478,-287 1326.14,-263.35 1222.48,-247.21"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1222.71,-244.77 1215.42,-246.11 1221.96,-249.61 1222.71,-244.77"/>
<text text-anchor="middle" x="1504" y="-289.6" font-family="Arial" font-size="8.00" fill="#374151">GPIO3/4 ALT1</text>
<text text-anchor="middle" x="1504" y="-280.6" font-family="Arial" font-size="8.00" fill="#374151">GPIO7/8 ALT0</text>
</g>
<!-- i2c_controllers&#45;&gt;irq -->
<g id="edge38" class="edge">
<title>i2c_controllers&#45;&gt;irq</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1777.08,-212.38C1769.06,-210.92 1764,-210 1764,-210 1764,-210 1609,-187 1609,-187 1609,-187 1538.37,-170.52 1479.03,-156.67"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1479.23,-154.21 1471.86,-155 1478.12,-158.98 1479.23,-154.21"/>
<text text-anchor="middle" x="1633.5" y="-180.6" font-family="Arial" font-size="8.00" fill="#374151">IRQ7 / IRQ19</text>
</g>
<!-- gpio_bridge -->
<g id="node24" class="node">
<g id="node25" class="node">
<title>gpio_bridge</title>
<path fill="#e5e7eb" stroke="#4b5563" stroke-width="1.1" d="M1258.5,-155C1258.5,-155 1123.5,-155 1123.5,-155 1117.5,-155 1111.5,-149 1111.5,-143 1111.5,-143 1111.5,-131 1111.5,-131 1111.5,-125 1117.5,-119 1123.5,-119 1123.5,-119 1258.5,-119 1258.5,-119 1264.5,-119 1270.5,-125 1270.5,-131 1270.5,-131 1270.5,-143 1270.5,-143 1270.5,-149 1264.5,-155 1258.5,-155"/>
<text text-anchor="middle" x="1191" y="-140" font-family="Arial" font-size="10.00" fill="#111827">GPIO pad bridge</text>
<text text-anchor="middle" x="1191" y="-129" font-family="Arial" font-size="10.00" fill="#111827">PDK capability&#45;adapted controls</text>
<path fill="#e5e7eb" stroke="#4b5563" stroke-width="1.1" d="M1882.5,-155C1882.5,-155 1747.5,-155 1747.5,-155 1741.5,-155 1735.5,-149 1735.5,-143 1735.5,-143 1735.5,-131 1735.5,-131 1735.5,-125 1741.5,-119 1747.5,-119 1747.5,-119 1882.5,-119 1882.5,-119 1888.5,-119 1894.5,-125 1894.5,-131 1894.5,-131 1894.5,-143 1894.5,-143 1894.5,-149 1888.5,-155 1882.5,-155"/>
<text text-anchor="middle" x="1815" y="-140" font-family="Arial" font-size="10.00" fill="#111827">GPIO pad bridge</text>
<text text-anchor="middle" x="1815" y="-129" font-family="Arial" font-size="10.00" fill="#111827">PDK capability&#45;adapted controls</text>
</g>
<!-- gpio_controller&#45;&gt;gpio_bridge -->
<g id="edge26" class="edge">
<g id="edge27" class="edge">
<title>gpio_controller&#45;&gt;gpio_bridge</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M964.05,-209.93C1014.27,-194.02 1082,-172.55 1130.36,-157.22"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1131.12,-159.55 1137.06,-155.1 1129.64,-154.88 1131.12,-159.55"/>
<text text-anchor="middle" x="1081" y="-180.6" font-family="Arial" font-size="8.00" fill="#374151">pad control</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1215.38,-220.18C1255.37,-215 1294,-210 1294,-210 1294,-210 1721,-187 1721,-187 1721,-187 1750.1,-171.83 1775.91,-158.37"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1777.31,-160.41 1782.39,-155 1775.05,-156.06 1777.31,-160.41"/>
<text text-anchor="middle" x="1756" y="-180.6" font-family="Arial" font-size="8.00" fill="#374151">pad control</text>
</g>
<!-- gpio_controller&#45;&gt;irq -->
<g id="edge27" class="edge">
<g id="edge28" class="edge">
<title>gpio_controller&#45;&gt;irq</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M983.24,-215.69C1073.8,-199.28 1215.3,-173.64 1310.73,-156.35"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1311.35,-158.73 1317.8,-155.07 1310.48,-153.91 1311.35,-158.73"/>
<text text-anchor="middle" x="1196" y="-180.6" font-family="Arial" font-size="8.00" fill="#374151">IRQ18</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1190.51,-209.93C1236.46,-194.08 1298.35,-172.72 1342.75,-157.41"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1343.63,-159.7 1349.45,-155.1 1342.03,-155.06 1343.63,-159.7"/>
<text text-anchor="middle" x="1291" y="-180.6" font-family="Arial" font-size="8.00" fill="#374151">IRQ18</text>
</g>
<!-- timers&#45;&gt;irq -->
<g id="edge28" class="edge">
<g id="edge29" class="edge">
<title>timers&#45;&gt;irq</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1730.31,-212.47C1722.16,-210.96 1717,-210 1717,-210 1717,-210 1466,-187 1466,-187 1466,-187 1450.5,-172.93 1436.11,-159.88"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1437.72,-158.03 1430.89,-155.14 1434.42,-161.66 1437.72,-158.03"/>
<text text-anchor="middle" x="1488.5" y="-180.6" font-family="Arial" font-size="8.00" fill="#374151">IRQ3 / IRQ4</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1013.6,-211.84C1098.98,-195.48 1219.64,-172.37 1303.1,-156.37"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1303.64,-158.77 1310.05,-155.04 1302.71,-153.95 1303.64,-158.77"/>
<text text-anchor="middle" x="1206.5" y="-180.6" font-family="Arial" font-size="8.00" fill="#374151">IRQ3 / IRQ4</text>
</g>
<!-- ws2812&#45;&gt;gpio_bridge -->
<g id="edge30" class="edge">
<g id="edge31" class="edge">
<title>ws2812&#45;&gt;gpio_bridge</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1360.25,-209.93C1323.34,-194.24 1273.74,-173.16 1237.78,-157.88"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1238.63,-155.58 1231.23,-155.1 1236.71,-160.09 1238.63,-155.58"/>
<text text-anchor="middle" x="1323.5" y="-180.6" font-family="Arial" font-size="8.00" fill="#374151">GPIO2 ALT1</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M2013.72,-213.17C2005.45,-211.26 2000,-210 2000,-210 2000,-210 1832,-187 1832,-187 1832,-187 1827.62,-174.38 1823.31,-161.94"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1825.55,-160.95 1820.95,-155.14 1820.93,-162.55 1825.55,-160.95"/>
<text text-anchor="middle" x="1854.5" y="-180.6" font-family="Arial" font-size="8.00" fill="#374151">GPIO2 ALT1</text>
</g>
<!-- ws2812&#45;&gt;irq -->
<g id="edge32" class="edge">
<g id="edge33" class="edge">
<title>ws2812&#45;&gt;irq</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1410.51,-209.77C1410.26,-198.48 1410,-187 1410,-187 1410,-187 1410,-178 1410,-178 1410,-178 1410.37,-170.7 1410.78,-162.33"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1413.24,-162.31 1411.14,-155.2 1408.34,-162.06 1413.24,-162.31"/>
<text text-anchor="middle" x="1422" y="-180.6" font-family="Arial" font-size="8.00" fill="#374151">IRQ17</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M2013.72,-213.17C2005.45,-211.26 2000,-210 2000,-210 2000,-210 1691,-187 1691,-187 1691,-187 1660,-178 1660,-178 1660,-178 1586.26,-166.7 1517.16,-156.11"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1517.36,-153.66 1510.07,-155.02 1516.61,-158.5 1517.36,-153.66"/>
<text text-anchor="middle" x="1703" y="-180.6" font-family="Arial" font-size="8.00" fill="#374151">IRQ17</text>
</g>
<!-- apb_targets&#45;&gt;gpio_controller -->
<g id="edge35" class="edge">
<g id="edge39" class="edge">
<title>apb_targets&#45;&gt;gpio_controller</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M609.73,-254.58C675.1,-267.97 746,-282.5 746,-282.5 746,-282.5 782,-282.5 782,-282.5 782,-282.5 809.78,-270.32 837.91,-257.98"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="839.24,-260.07 844.67,-255.01 837.27,-255.58 839.24,-260.07"/>
<text text-anchor="middle" x="764" y="-285.1" font-family="Arial" font-size="8.00" fill="#374151">user_gpio</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M609.59,-252.51C617.81,-254.04 623,-255 623,-255 623,-255 976,-282.5 976,-282.5 976,-282.5 1012,-282.5 1012,-282.5 1012,-282.5 1040.5,-270.21 1069.25,-257.82"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1070.31,-260.03 1075.77,-255.01 1068.37,-255.53 1070.31,-260.03"/>
<text text-anchor="middle" x="994" y="-285.1" font-family="Arial" font-size="8.00" fill="#374151">user_gpio</text>
</g>
<!-- gpio_bridge&#45;&gt;ext_io -->
<g id="edge38" class="edge">
<g id="edge42" class="edge">
<title>gpio_bridge&#45;&gt;ext_io</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1228.68,-115.34C1254.46,-101.2 1284,-85 1284,-85 1284,-85 1386.02,-63.94 1470.68,-46.47"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1227.19,-113.36 1222.23,-118.88 1229.54,-117.66 1227.19,-113.36"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1471.41,-48.82 1477.77,-45.01 1470.42,-44.03 1471.41,-48.82"/>
<text text-anchor="middle" x="1383" y="-70.6" font-family="Arial" font-size="8.00" fill="#374151">pad control</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1839,-113.95C1860.61,-94.09 1889,-68 1889,-68 1889,-68 1900.85,-59.21 1914.01,-49.45"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1837.17,-112.3 1833.67,-118.84 1840.49,-115.91 1837.17,-112.3"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1915.54,-51.37 1919.7,-45.23 1912.62,-47.43 1915.54,-51.37"/>
<text text-anchor="middle" x="1909" y="-70.6" font-family="Arial" font-size="8.00" fill="#374151">pad control</text>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 43 KiB

+10
View File
@@ -26,6 +26,16 @@ SoC wrappers use the existing clusterIP interfaces for protocol boundaries.
`apb4_if_bridge` only adapts `apb4_pure_if` to `apb4_if`, and `gpio_pad_bridge`
only exposes the pad-side subset of `gpio_if`; neither module contains state.
## I2C alternate functions
The two I2C V2 controllers reuse GPIO pads rather than dedicated package pins.
I2C0 uses GPIO7 for SCL and GPIO8 for SDA on ALT0. I2C1 uses GPIO3 for SCL and
GPIO4 for SDA on ALT1. Both controller outputs are constant zero; the GPIO
alternate-function output enable pulls a line low and releases it for high.
Board-level pull-ups are therefore required. I2C0 uses management-core IRQ7
and generic-DMA modes 7/8; I2C1 uses IRQ19 and DMA modes 9/10. See
[i2c.md](ip/i2c.md) for the register and transfer contract.
## WS2812 output
The single-channel WS2812 transmitter occupies RIBP address `0x10008000` and
+75 -67
View File
@@ -34,6 +34,10 @@ interface dma_hw_trg_if ();
logic qspi_rx_proc;
logic uart_tx_proc;
logic uart_rx_proc;
logic i2c0_tx_proc;
logic i2c0_rx_proc;
logic i2c1_tx_proc;
logic i2c1_rx_proc;
modport dut(
input i2s_tx_proc,
@@ -41,7 +45,11 @@ interface dma_hw_trg_if ();
input qspi_tx_proc,
input qspi_rx_proc,
input uart_tx_proc,
input uart_rx_proc
input uart_rx_proc,
input i2c0_tx_proc,
input i2c0_rx_proc,
input i2c1_tx_proc,
input i2c1_rx_proc
);
endinterface
@@ -49,12 +57,12 @@ endinterface
module ribp_dma (
// verilog_format: off
input logic clk_i,
input logic rst_n_i,
output logic dma_xfer_done_o,
input logic clk_i,
input logic rst_n_i,
output logic dma_xfer_done_o,
dma_hw_trg_if.dut hw_trg,
ribp_if.slave ribp,
rib_if.master rib
ribp_if.slave ribp,
rib_if.master rib
// verilog_format: on
);
@@ -64,7 +72,7 @@ module ribp_dma (
logic [31:0] s_ribp_rdata_d, s_ribp_rdata_q;
logic s_dma_mode_en;
logic [2:0] s_dma_mode_d, s_dma_mode_q;
logic [3:0] s_dma_mode_d, s_dma_mode_q;
logic s_dma_srcaddr_en;
logic [31:0] s_dma_srcaddr_d, s_dma_srcaddr_q;
logic s_dma_srcincr_en;
@@ -97,13 +105,13 @@ module ribp_dma (
assign s_dma_mode_en = s_ribp_wr_hdshk && ribp.addr[7:0] == `RIBP_DMA_MODE;
assign s_dma_mode_d = ribp.wdata[2:0];
dffer #(3) u_dma_mode_dffer (
clk_i,
rst_n_i,
s_dma_mode_en,
s_dma_mode_d,
s_dma_mode_q
assign s_dma_mode_d = ribp.wdata[3:0];
dffer #(4) u_dma_mode_dffer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_dma_mode_en),
.dat_i (s_dma_mode_d),
.dat_o (s_dma_mode_q)
);
@@ -116,22 +124,22 @@ module ribp_dma (
if (ribp.wstrb[3]) s_dma_srcaddr_d[31:24] = ribp.wdata[31:24];
end
dffer #(32) u_dma_srcaddr_dffer (
clk_i,
rst_n_i,
s_dma_srcaddr_en,
s_dma_srcaddr_d,
s_dma_srcaddr_q
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_dma_srcaddr_en),
.dat_i (s_dma_srcaddr_d),
.dat_o (s_dma_srcaddr_q)
);
assign s_dma_srcincr_en = s_ribp_wr_hdshk && ribp.addr[7:0] == `RIBP_DMA_SRCINCR;
assign s_dma_srcincr_d = ribp.wdata[0];
dffer #(1) u_dma_srcincr_dffer (
clk_i,
rst_n_i,
s_dma_srcincr_en,
s_dma_srcincr_d,
s_dma_srcincr_q
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_dma_srcincr_en),
.dat_i (s_dma_srcincr_d),
.dat_o (s_dma_srcincr_q)
);
@@ -144,21 +152,21 @@ module ribp_dma (
if (ribp.wstrb[3]) s_dma_dstaddr_d[31:24] = ribp.wdata[31:24];
end
dffer #(32) u_dma_dstaddr_dffer (
clk_i,
rst_n_i,
s_dma_dstaddr_en,
s_dma_dstaddr_d,
s_dma_dstaddr_q
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_dma_dstaddr_en),
.dat_i (s_dma_dstaddr_d),
.dat_o (s_dma_dstaddr_q)
);
assign s_dma_dstincr_en = s_ribp_wr_hdshk && ribp.addr[7:0] == `RIBP_DMA_DSTINCR;
assign s_dma_dstincr_d = ribp.wdata[0];
dffer #(1) u_dma_dstincr_dffer (
clk_i,
rst_n_i,
s_dma_dstincr_en,
s_dma_dstincr_d,
s_dma_dstincr_q
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_dma_dstincr_en),
.dat_i (s_dma_dstincr_d),
.dat_o (s_dma_dstincr_q)
);
assign s_dma_xferlen_en = s_ribp_wr_hdshk && ribp.addr[7:0] == `RIBP_DMA_XFERLEN;
@@ -170,11 +178,11 @@ module ribp_dma (
if (ribp.wstrb[3]) s_dma_xferlen_d[31:24] = ribp.wdata[31:24];
end
dffer #(32) u_dma_xferlen_dffer (
clk_i,
rst_n_i,
s_dma_xferlen_en,
s_dma_xferlen_d,
s_dma_xferlen_q
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_dma_xferlen_en),
.dat_i (s_dma_xferlen_d),
.dat_o (s_dma_xferlen_q)
);
@@ -192,10 +200,10 @@ module ribp_dma (
end
end
dffr #(1) u_dma_status_dffr (
clk_i,
rst_n_i,
s_dma_status_d,
s_dma_status_q
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_dma_status_d),
.dat_o (s_dma_status_q)
);
always_comb begin
@@ -208,42 +216,42 @@ module ribp_dma (
end
end
dffr #(1) u_dma_error_status_dffr (
clk_i,
rst_n_i,
s_dma_error_status_d,
s_dma_error_status_q
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_dma_error_status_d),
.dat_o (s_dma_error_status_q)
);
assign s_dma_error_code_d = s_xfer_error_code;
dffer #(3) u_dma_error_code_dffer (
clk_i,
rst_n_i,
s_xfer_error,
s_dma_error_code_d,
s_dma_error_code_q
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_xfer_error),
.dat_i (s_dma_error_code_d),
.dat_o (s_dma_error_code_q)
);
assign s_dma_error_addr_d = s_xfer_error_addr;
dffer #(32) u_dma_error_addr_dffer (
clk_i,
rst_n_i,
s_xfer_error,
s_dma_error_addr_d,
s_dma_error_addr_q
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_xfer_error),
.dat_i (s_dma_error_addr_d),
.dat_o (s_dma_error_addr_q)
);
assign s_ribp_ready_d = ribp.valid && (~s_ribp_ready_q);
dffr #(1) u_ribp_ready_dffr (
clk_i,
rst_n_i,
s_ribp_ready_d,
s_ribp_ready_q
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_ribp_ready_d),
.dat_o (s_ribp_ready_q)
);
assign s_ribp_rdata_en = s_ribp_rd_hdshk;
always_comb begin
s_ribp_rdata_d = s_ribp_rdata_q;
unique case (ribp.addr[7:0])
`RIBP_DMA_MODE: s_ribp_rdata_d = {29'd0, s_dma_mode_q};
`RIBP_DMA_MODE: s_ribp_rdata_d = {28'd0, s_dma_mode_q};
`RIBP_DMA_SRCADDR: s_ribp_rdata_d = s_dma_srcaddr_q;
`RIBP_DMA_SRCINCR: s_ribp_rdata_d = {31'd0, s_dma_srcincr_q};
`RIBP_DMA_DSTADDR: s_ribp_rdata_d = s_dma_dstaddr_q;
@@ -257,11 +265,11 @@ module ribp_dma (
endcase
end
dffer #(32) u_ribp_rdata_dffer (
clk_i,
rst_n_i,
s_ribp_rdata_en,
s_ribp_rdata_d,
s_ribp_rdata_q
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_ribp_rdata_en),
.dat_i (s_ribp_rdata_d),
.dat_o (s_ribp_rdata_q)
);
+57 -49
View File
@@ -13,34 +13,38 @@
module dma_core (
// verilog_format: off
input logic clk_i,
input logic rst_n_i,
input logic [2:0] mode_i,
input logic [31:0] srcaddr_i,
input logic srcincr_i,
input logic [31:0] dstaddr_i,
input logic dstincr_i,
input logic [31:0] xferlen_i,
input logic start_i,
input logic stop_i,
input logic reset_i,
output logic done_o,
output logic error_o,
output logic [2:0] error_code_o,
output logic [31:0] error_addr_o,
output logic [1:0] fsm_o,
dma_hw_trg_if.dut hw_trg,
rib_if.master rib
input logic clk_i,
input logic rst_n_i,
input logic [3:0] mode_i,
input logic [31:0] srcaddr_i,
input logic srcincr_i,
input logic [31:0] dstaddr_i,
input logic dstincr_i,
input logic [31:0] xferlen_i,
input logic start_i,
input logic stop_i,
input logic reset_i,
output logic done_o,
output logic error_o,
output logic [2:0] error_code_o,
output logic [31:0] error_addr_o,
output logic [1:0] fsm_o,
dma_hw_trg_if.dut hw_trg,
rib_if.master rib
// verilog_format: on
);
localparam logic [2:0] SFT_TRG = 3'd0;
localparam logic [2:0] HWT_I2S_TX_TRG = 3'd1;
localparam logic [2:0] HWT_I2S_RX_TRG = 3'd2;
localparam logic [2:0] HWT_QSPI_TX_TRG = 3'd3;
localparam logic [2:0] HWT_QSPI_RX_TRG = 3'd4;
localparam logic [2:0] HWT_UART_TX_TRG = 3'd5;
localparam logic [2:0] HWT_UART_RX_TRG = 3'd6;
localparam logic [3:0] SFT_TRG = 4'd0;
localparam logic [3:0] HWT_I2S_TX_TRG = 4'd1;
localparam logic [3:0] HWT_I2S_RX_TRG = 4'd2;
localparam logic [3:0] HWT_QSPI_TX_TRG = 4'd3;
localparam logic [3:0] HWT_QSPI_RX_TRG = 4'd4;
localparam logic [3:0] HWT_UART_TX_TRG = 4'd5;
localparam logic [3:0] HWT_UART_RX_TRG = 4'd6;
localparam logic [3:0] HWT_I2C0_TX_TRG = 4'd7;
localparam logic [3:0] HWT_I2C0_RX_TRG = 4'd8;
localparam logic [3:0] HWT_I2C1_TX_TRG = 4'd9;
localparam logic [3:0] HWT_I2C1_RX_TRG = 4'd10;
localparam logic [2:0] FSM_IDLE = 3'd0;
localparam logic [2:0] FSM_RD_CMD = 3'd1;
@@ -89,6 +93,8 @@ module dma_core (
HWT_I2S_RX_TRG: s_read_trigger = hw_trg.i2s_rx_proc;
HWT_QSPI_RX_TRG: s_read_trigger = hw_trg.qspi_rx_proc;
HWT_UART_RX_TRG: s_read_trigger = hw_trg.uart_rx_proc;
HWT_I2C0_RX_TRG: s_read_trigger = hw_trg.i2c0_rx_proc;
HWT_I2C1_RX_TRG: s_read_trigger = hw_trg.i2c1_rx_proc;
default: s_read_trigger = 1'b1;
endcase
end
@@ -99,6 +105,8 @@ module dma_core (
HWT_I2S_TX_TRG: s_write_trigger = hw_trg.i2s_tx_proc;
HWT_QSPI_TX_TRG: s_write_trigger = hw_trg.qspi_tx_proc;
HWT_UART_TX_TRG: s_write_trigger = hw_trg.uart_tx_proc;
HWT_I2C0_TX_TRG: s_write_trigger = hw_trg.i2c0_tx_proc;
HWT_I2C1_TX_TRG: s_write_trigger = hw_trg.i2c1_tx_proc;
default: s_write_trigger = 1'b1;
endcase
end
@@ -231,34 +239,34 @@ module dma_core (
end
dffr #(3) u_fsm_dffr (
clk_i,
rst_n_i,
s_fsm_d,
s_fsm_q
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_fsm_d),
.dat_o (s_fsm_q)
);
dffr #(32) u_xfer_cnt_dffr (
clk_i,
rst_n_i,
s_xfer_cnt_d,
s_xfer_cnt_q
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_xfer_cnt_d),
.dat_o (s_xfer_cnt_q)
);
dffr #(32) u_src_addr_dffr (
clk_i,
rst_n_i,
s_src_addr_d,
s_src_addr_q
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_src_addr_d),
.dat_o (s_src_addr_q)
);
dffr #(32) u_dst_addr_dffr (
clk_i,
rst_n_i,
s_dst_addr_d,
s_dst_addr_q
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_dst_addr_d),
.dat_o (s_dst_addr_q)
);
dffr #(2) u_wr_beat_dffr (
clk_i,
rst_n_i,
s_wr_beat_d,
s_wr_beat_q
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_wr_beat_d),
.dat_o (s_wr_beat_q)
);
always_comb begin
@@ -267,10 +275,10 @@ module dma_core (
if (reset_i) s_ctrl_stop_d = 1'b0;
end
dffr #(1) u_ctrl_stop_dffr (
clk_i,
rst_n_i,
s_ctrl_stop_d,
s_ctrl_stop_q
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_ctrl_stop_d),
.dat_o (s_ctrl_stop_q)
);
endmodule
-222
View File
@@ -1,222 +0,0 @@
// Copyright (c) 2023-2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
`ifndef RIBP_I2C_DEF_SV
`define RIBP_I2C_DEF_SV
// verilog_format: off
`define RIBP_I2C_CLKDIV 8'h00
`define RIBP_I2C_DEVADDR 8'h04
`define RIBP_I2C_REGADDR 8'h08
`define RIBP_I2C_TXDATA 8'h0C
`define RIBP_I2C_RXDATA 8'h10
`define RIBP_I2C_XFER 8'h14
`define RIBP_I2C_CFG 8'h18
`define RIBP_I2C_STATUS 8'h1C
// verilog_format: on
`endif
module ribp_i2c (
// verilog_format: off
input logic clk_i,
input logic rst_n_i,
ribp_if.slave ribp,
i2c_if.dut i2c
// verilog_format: on
);
logic s_ribp_wr_hdshk, s_ribp_rd_hdshk;
logic s_ribp_ready_d, s_ribp_ready_q;
logic s_ribp_rdata_en;
logic [31:0] s_ribp_rdata_d, s_ribp_rdata_q;
//2^7=128
logic s_i2c_clkdiv_en;
logic [6:0] s_i2c_clkdiv_d, s_i2c_clkdiv_q;
logic s_i2c_devaddr_en;
logic [6:0] s_i2c_devaddr_d, s_i2c_devaddr_q;
logic s_i2c_regaddr_en;
logic [15:0] s_i2c_regaddr_d, s_i2c_regaddr_q;
logic s_i2c_txdata_en;
logic [7:0] s_i2c_txdata_d, s_i2c_txdata_q;
logic [7:0] s_i2c_rxdata;
logic [1:0] s_i2c_xfer_d, s_i2c_xfer_q;
logic s_i2c_cfg_en;
logic s_i2c_cfg_d, s_i2c_cfg_q;
logic s_i2c_status_d, s_i2c_status_q;
logic s_bit_rdwr, s_bit_start;
logic s_bit_end, s_bit_end_re;
logic s_bit_extn_addr;
logic s_oper_clk_pos;
assign s_bit_rdwr = s_i2c_xfer_q[0];
assign s_bit_start = s_i2c_xfer_q[1];
assign s_bit_extn_addr = s_i2c_cfg_q;
assign s_ribp_wr_hdshk = ribp.valid && (~s_ribp_ready_q) && (|ribp.wstrb);
assign s_ribp_rd_hdshk = ribp.valid && (~s_ribp_ready_q) && (~(|ribp.wstrb));
assign ribp.ready = s_ribp_ready_q;
assign ribp.resp_err = 1'b0;
assign ribp.rdata = s_ribp_rdata_q;
assign s_i2c_clkdiv_en = s_ribp_wr_hdshk && ribp.addr[7:0] == `RIBP_I2C_CLKDIV;
assign s_i2c_clkdiv_d = ribp.wdata[6:0];
dffer #(7) u_i2c_clkdiv_dffer (
clk_i,
rst_n_i,
s_i2c_clkdiv_en,
s_i2c_clkdiv_d,
s_i2c_clkdiv_q
);
assign s_i2c_devaddr_en = s_ribp_wr_hdshk && ribp.addr[7:0] == `RIBP_I2C_DEVADDR;
assign s_i2c_devaddr_d = ribp.wdata[6:0];
dffer #(7) u_i2c_devaddr_dffer (
clk_i,
rst_n_i,
s_i2c_devaddr_en,
s_i2c_devaddr_d,
s_i2c_devaddr_q
);
assign s_i2c_regaddr_en = s_ribp_wr_hdshk && ribp.addr[7:0] == `RIBP_I2C_REGADDR;
always_comb begin
s_i2c_regaddr_d = s_i2c_regaddr_q;
if (ribp.wstrb[0]) s_i2c_regaddr_d[7:0] = ribp.wdata[7:0];
if (ribp.wstrb[1]) s_i2c_regaddr_d[15:8] = ribp.wdata[15:8];
end
dffer #(16) u_i2c_regaddr_dffer (
clk_i,
rst_n_i,
s_i2c_regaddr_en,
s_i2c_regaddr_d,
s_i2c_regaddr_q
);
assign s_i2c_txdata_en = s_ribp_wr_hdshk && ribp.addr[7:0] == `RIBP_I2C_TXDATA;
assign s_i2c_txdata_d = ribp.wdata[7:0];
dffer #(8) u_i2c_txdata_dffer (
clk_i,
rst_n_i,
s_i2c_txdata_en,
s_i2c_txdata_d,
s_i2c_txdata_q
);
always_comb begin
s_i2c_xfer_d = s_i2c_xfer_q;
if (s_ribp_wr_hdshk && ribp.addr[7:0] == `RIBP_I2C_XFER) begin
s_i2c_xfer_d = ribp.wdata[1:0];
end else if (s_oper_clk_pos && (|s_i2c_xfer_q)) begin
s_i2c_xfer_d = '0;
end
end
dffr #(2) u_i2c_xfer_dffr (
clk_i,
rst_n_i,
s_i2c_xfer_d,
s_i2c_xfer_q
);
assign s_i2c_cfg_en = s_ribp_wr_hdshk && ribp.addr[7:0] == `RIBP_I2C_CFG;
assign s_i2c_cfg_d = ribp.wdata[0];
dffer #(1) u_i2c_cfg_dffer (
clk_i,
rst_n_i,
s_i2c_cfg_en,
s_i2c_cfg_d,
s_i2c_cfg_q
);
edge_det_sync_re #(1) u_i2c_end_edge_det_sync_re (
clk_i,
rst_n_i,
s_bit_end,
s_bit_end_re
);
always_comb begin
s_i2c_status_d = s_i2c_status_q;
if (s_bit_end_re) s_i2c_status_d = 1'b1;
else if (s_ribp_rd_hdshk && ribp.addr[7:0] == `RIBP_I2C_STATUS) begin
s_i2c_status_d = 1'b0;
end
end
dffr #(1) u_i2c_status_dffr (
clk_i,
rst_n_i,
s_i2c_status_d,
s_i2c_status_q
);
assign s_ribp_ready_d = ribp.valid && (~s_ribp_ready_q);
dffr #(1) u_ribp_ready_dffr (
clk_i,
rst_n_i,
s_ribp_ready_d,
s_ribp_ready_q
);
assign s_ribp_rdata_en = s_ribp_rd_hdshk;
always_comb begin
s_ribp_rdata_d = s_ribp_rdata_q;
unique case (ribp.addr[7:0])
`RIBP_I2C_CLKDIV: s_ribp_rdata_d = {25'd0, s_i2c_clkdiv_q};
`RIBP_I2C_DEVADDR: s_ribp_rdata_d = {25'd0, s_i2c_devaddr_q};
`RIBP_I2C_REGADDR: s_ribp_rdata_d = {16'd0, s_i2c_regaddr_q};
`RIBP_I2C_TXDATA: s_ribp_rdata_d = {24'd0, s_i2c_txdata_q};
`RIBP_I2C_RXDATA: s_ribp_rdata_d = {24'd0, s_i2c_rxdata};
`RIBP_I2C_XFER: s_ribp_rdata_d = {30'd0, s_i2c_xfer_q};
`RIBP_I2C_CFG: s_ribp_rdata_d = {31'd0, s_i2c_cfg_q};
`RIBP_I2C_STATUS: s_ribp_rdata_d = {31'd0, s_i2c_status_q};
default: s_ribp_rdata_d = s_ribp_rdata_q;
endcase
end
dffer #(32) u_ribp_rdata_dffer (
clk_i,
rst_n_i,
s_ribp_rdata_en,
s_ribp_rdata_d,
s_ribp_rdata_q
);
assign i2c.scl_oe_o = 1'b1;
assign i2c.irq_o = 1'b0;
i2c_core u_i2c_core (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.clk_div_i (s_i2c_clkdiv_q),
.dev_addr_i (s_i2c_devaddr_q),
.rdwr_i (s_bit_rdwr),
.start_i (s_bit_start),
.end_o (s_bit_end),
.extn_addr_i (s_bit_extn_addr),
.reg_addr_i (s_i2c_regaddr_q),
.wr_data_i (s_i2c_txdata_q),
.rd_data_o (s_i2c_rxdata),
.oper_clk_pos_o(s_oper_clk_pos),
.scl_o (i2c.scl_o),
.sda_oe_o (i2c.sda_oe_o),
.sda_o (i2c.sda_o),
.sda_i (i2c.sda_i)
);
endmodule
File diff suppressed because it is too large. Load diff
+98
View File
@@ -0,0 +1,98 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
module i2c_filter (
// verilog_format: off
input logic clk_i,
input logic rst_n_i,
input logic [3:0] scl_filter_cycles_i,
input logic [3:0] sda_filter_cycles_i,
input logic scl_async_i,
input logic sda_async_i,
output logic scl_o,
output logic sda_o
// verilog_format: on
);
logic [1:0] s_line_sync;
logic [3:0] s_scl_count_d, s_scl_count_q;
logic [3:0] s_sda_count_d, s_sda_count_q;
logic s_scl_d, s_scl_q;
logic s_sda_d, s_sda_q;
cdc_sync #(
.STAGE (2),
.DATA_WIDTH(2)
) u_line_cdc_sync (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i ({sda_async_i, scl_async_i}),
.dat_o (s_line_sync)
);
always_comb begin
s_scl_d = s_scl_q;
s_scl_count_d = s_scl_count_q;
if (s_line_sync[0] == s_scl_q) begin
s_scl_count_d = '0;
end else if ((scl_filter_cycles_i == 4'd0) ||
(s_scl_count_q >= (scl_filter_cycles_i - 1'b1))) begin
s_scl_d = s_line_sync[0];
s_scl_count_d = '0;
end else begin
s_scl_count_d = s_scl_count_q + 1'b1;
end
end
always_comb begin
s_sda_d = s_sda_q;
s_sda_count_d = s_sda_count_q;
if (s_line_sync[1] == s_sda_q) begin
s_sda_count_d = '0;
end else if ((sda_filter_cycles_i == 4'd0) ||
(s_sda_count_q >= (sda_filter_cycles_i - 1'b1))) begin
s_sda_d = s_line_sync[1];
s_sda_count_d = '0;
end else begin
s_sda_count_d = s_sda_count_q + 1'b1;
end
end
dffrc #(
.DATA_WIDTH(1),
.RESET_VAL (1'b1)
) u_scl_dffrc (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_scl_d),
.dat_o (s_scl_q)
);
dffrc #(
.DATA_WIDTH(1),
.RESET_VAL (1'b1)
) u_sda_dffrc (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_sda_d),
.dat_o (s_sda_q)
);
dffr #(4) u_scl_count_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_scl_count_d),
.dat_o (s_scl_count_q)
);
dffr #(4) u_sda_count_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_sda_count_d),
.dat_o (s_sda_count_q)
);
assign scl_o = s_scl_q;
assign sda_o = s_sda_q;
endmodule
+718
View File
@@ -0,0 +1,718 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
`include "ribp_i2c_define.svh"
module i2c_reg #(
parameter int CMD_FIFO_DEPTH = 16,
parameter int RX_FIFO_DEPTH = 16,
parameter int CMD_FIFO_LOG_DEPTH = $clog2(CMD_FIFO_DEPTH),
parameter int RX_FIFO_LOG_DEPTH = $clog2(RX_FIFO_DEPTH)
) (
// verilog_format: off
input logic clk_i,
input logic rst_n_i,
ribp_if.slave ribp,
output logic enable_o,
output logic [15:0] scl_low_cycles_o,
output logic [15:0] scl_high_cycles_o,
output logic [15:0] start_hold_cycles_o,
output logic [15:0] start_setup_cycles_o,
output logic [15:0] data_hold_cycles_o,
output logic [15:0] data_setup_cycles_o,
output logic [15:0] stop_setup_cycles_o,
output logic [15:0] bus_free_cycles_o,
output logic [ 3:0] scl_filter_cycles_o,
output logic [ 3:0] sda_filter_cycles_o,
output logic [23:0] stretch_timeout_o,
output logic [23:0] bus_idle_timeout_o,
output logic [23:0] command_timeout_o,
output logic [ 9:0] target_addr_o,
output logic ten_bit_o,
output logic abort_o,
output logic recover_o,
output logic cmd_valid_o,
output logic [11:0] cmd_data_o,
input logic cmd_pop_i,
input logic core_cmd_flush_i,
input logic rx_push_i,
input logic [ 7:0] rx_data_i,
output logic rx_full_o,
input logic busy_i,
input logic recovery_active_i,
input logic scl_i,
input logic sda_i,
input logic done_event_i,
input logic addr_nack_event_i,
input logic data_nack_event_i,
input logic arb_lost_event_i,
input logic stretch_timeout_event_i,
input logic bus_timeout_event_i,
input logic command_timeout_event_i,
input logic command_error_event_i,
input logic rx_overflow_event_i,
input logic aborted_event_i,
input logic recovery_done_event_i,
input logic recovery_failed_event_i,
output logic dma_tx_stall_o,
output logic dma_rx_stall_o,
output logic irq_o
// verilog_format: on
);
localparam logic [31:0] IP_VERSION = 32'h0002_0000;
localparam logic [7:0] CMD_DEPTH_INFO = 8'(CMD_FIFO_DEPTH);
localparam logic [7:0] RX_DEPTH_INFO = 8'(RX_FIFO_DEPTH);
localparam logic [31:0] CAPABILITY = {9'd0, 7'h7F, RX_DEPTH_INFO, CMD_DEPTH_INFO};
localparam logic [31:0] CMD_WATERMARK_RESET = (CMD_FIFO_DEPTH > 4) ? 32'd4 : CMD_FIFO_DEPTH - 1;
localparam logic [31:0] RX_WATERMARK_RESET = (RX_FIFO_DEPTH > 8) ? 32'd8 : RX_FIFO_DEPTH;
logic s_req;
logic s_write;
logic s_req_accept;
logic s_access_error;
logic s_ribp_ready_d, s_ribp_ready_q;
logic s_ribp_resp_err_d, s_ribp_resp_err_q;
logic [31:0] s_ribp_rdata_d, s_ribp_rdata_q;
logic s_enable_en;
logic s_enable_d, s_enable_q;
logic s_scl_timing_en;
logic [31:0] s_scl_timing_d, s_scl_timing_q;
logic s_start_timing_en;
logic [31:0] s_start_timing_d, s_start_timing_q;
logic s_data_timing_en;
logic [31:0] s_data_timing_d, s_data_timing_q;
logic s_stop_timing_en;
logic [31:0] s_stop_timing_d, s_stop_timing_q;
logic s_filter_en;
logic [11:0] s_filter_d, s_filter_q;
logic s_stretch_timeout_en;
logic [23:0] s_stretch_timeout_d, s_stretch_timeout_q;
logic s_bus_idle_timeout_en;
logic [23:0] s_bus_idle_timeout_d, s_bus_idle_timeout_q;
logic s_command_timeout_en;
logic [23:0] s_command_timeout_d, s_command_timeout_q;
logic s_target_addr_en;
logic [10:0] s_target_addr_d, s_target_addr_q;
logic s_cmd_watermark_en;
logic [7:0] s_cmd_watermark_d, s_cmd_watermark_q;
logic s_rx_watermark_en;
logic [7:0] s_rx_watermark_d, s_rx_watermark_q;
logic s_intr_enable_en;
logic [7:0] s_intr_enable_d, s_intr_enable_q;
logic s_cmd_push;
logic s_cmd_flush_cmd;
logic s_cmd_empty;
logic s_cmd_full;
logic [ 11:0] s_cmd_pop_data;
logic [CMD_FIFO_LOG_DEPTH:0] s_cmd_count;
logic s_rx_pop;
logic s_rx_flush_cmd;
logic s_rx_empty;
logic s_rx_full;
logic [ 7:0] s_rx_pop_data;
logic [ RX_FIFO_LOG_DEPTH:0] s_rx_count;
logic [10:0] s_error_status_d, s_error_status_q;
logic [7:0] s_intr_state_d, s_intr_state_q;
logic [10:0] s_error_clear;
logic [ 7:0] s_intr_clear;
logic [ 7:0] s_intr_test;
logic s_config_error_event;
logic s_sw_command_error_event;
logic s_timeout_event;
logic s_error_event;
logic s_cmd_watermark_active;
logic s_rx_watermark_active;
logic s_tx_dma_req;
logic s_rx_dma_req;
logic s_config_valid;
logic [31:0] s_status;
logic [31:0] s_fifo_level;
logic [31:0] s_line_state;
logic [31:0] s_merge_value;
function automatic logic [31:0] merge_wstrb(input logic [31:0] current, input logic [31:0] value,
input logic [3:0] strobe);
logic [31:0] merged;
integer byte_index;
begin
merged = current;
for (byte_index = 0; byte_index < 4; byte_index = byte_index + 1) begin
if (strobe[byte_index]) begin
merged[byte_index*8+:8] = value[byte_index*8+:8];
end
end
return merged;
end
endfunction
initial begin
if ((CMD_FIFO_DEPTH > 255) || (RX_FIFO_DEPTH > 255)) begin
$fatal(1, "i2c_reg: FIFO depths must fit CAPABILITY fields");
end
end
assign s_req = ribp.valid && !s_ribp_ready_q;
assign s_write = |ribp.wstrb;
assign ribp.ready = s_ribp_ready_q;
assign ribp.resp_err = s_ribp_resp_err_q;
assign ribp.rdata = s_ribp_rdata_q;
assign enable_o = s_enable_q;
assign scl_low_cycles_o = s_scl_timing_q[15:0];
assign scl_high_cycles_o = s_scl_timing_q[31:16];
assign start_hold_cycles_o = s_start_timing_q[15:0];
assign start_setup_cycles_o = s_start_timing_q[31:16];
assign data_hold_cycles_o = s_data_timing_q[15:0];
assign data_setup_cycles_o = s_data_timing_q[31:16];
assign stop_setup_cycles_o = s_stop_timing_q[15:0];
assign bus_free_cycles_o = s_stop_timing_q[31:16];
assign scl_filter_cycles_o = s_filter_q[3:0];
assign sda_filter_cycles_o = s_filter_q[11:8];
assign stretch_timeout_o = s_stretch_timeout_q;
assign bus_idle_timeout_o = s_bus_idle_timeout_q;
assign command_timeout_o = s_command_timeout_q;
assign target_addr_o = s_target_addr_q[9:0];
assign ten_bit_o = s_target_addr_q[10];
assign cmd_valid_o = !s_cmd_empty;
assign cmd_data_o = s_cmd_pop_data;
assign rx_full_o = s_rx_full;
assign s_config_valid = (s_scl_timing_q[15:0] != 16'd0) &&
(s_scl_timing_q[31:16] != 16'd0) &&
(s_start_timing_q[15:0] != 16'd0) &&
(s_start_timing_q[31:16] != 16'd0) &&
(s_data_timing_q[15:0] < s_scl_timing_q[15:0]) &&
(s_data_timing_q[31:16] != 16'd0) &&
(s_data_timing_q[31:16] <= s_scl_timing_q[15:0]) &&
(s_stop_timing_q[15:0] != 16'd0) &&
(s_stop_timing_q[31:16] != 16'd0) &&
(s_target_addr_q[10] || (s_target_addr_q[9:7] == 3'd0));
assign s_cmd_watermark_active = s_enable_q && (8'(s_cmd_count) <= s_cmd_watermark_q);
assign s_rx_watermark_active = s_enable_q && (8'(s_rx_count) >= s_rx_watermark_q);
assign s_tx_dma_req = s_enable_q && s_config_valid && !s_cmd_full;
assign s_rx_dma_req = s_enable_q && !s_rx_empty;
assign dma_tx_stall_o = !s_tx_dma_req;
assign dma_rx_stall_o = !s_rx_dma_req;
assign irq_o = |(s_intr_state_q & s_intr_enable_q);
always_comb begin
s_status = '0;
s_status[`I2C_STATUS_ENABLE] = s_enable_q;
s_status[`I2C_STATUS_BUSY] = busy_i;
s_status[`I2C_STATUS_BUS_BUSY] = !(scl_i && sda_i);
s_status[`I2C_STATUS_CMD_EMPTY] = s_cmd_empty;
s_status[`I2C_STATUS_CMD_FULL] = s_cmd_full;
s_status[`I2C_STATUS_RX_EMPTY] = s_rx_empty;
s_status[`I2C_STATUS_RX_FULL] = s_rx_full;
s_status[`I2C_STATUS_CONFIG_VALID] = s_config_valid;
s_status[`I2C_STATUS_RECOVERY_ACTIVE] = recovery_active_i;
s_status[`I2C_STATUS_SCL] = scl_i;
s_status[`I2C_STATUS_SDA] = sda_i;
s_status[`I2C_STATUS_TX_DMA_REQ] = s_tx_dma_req;
s_status[`I2C_STATUS_RX_DMA_REQ] = s_rx_dma_req;
s_fifo_level = '0;
s_fifo_level[7:0] = 8'(s_cmd_count);
s_fifo_level[23:16] = 8'(s_rx_count);
s_line_state = '0;
s_line_state[`I2C_LINE_SCL] = scl_i;
s_line_state[`I2C_LINE_SDA] = sda_i;
s_line_state[`I2C_LINE_BUS_FREE] = scl_i && sda_i;
end
always_comb begin
s_req_accept = s_req;
s_access_error = 1'b0;
s_enable_en = 1'b0;
s_scl_timing_en = 1'b0;
s_start_timing_en = 1'b0;
s_data_timing_en = 1'b0;
s_stop_timing_en = 1'b0;
s_filter_en = 1'b0;
s_stretch_timeout_en = 1'b0;
s_bus_idle_timeout_en = 1'b0;
s_command_timeout_en = 1'b0;
s_target_addr_en = 1'b0;
s_cmd_watermark_en = 1'b0;
s_rx_watermark_en = 1'b0;
s_intr_enable_en = 1'b0;
s_enable_d = s_enable_q;
s_scl_timing_d = s_scl_timing_q;
s_start_timing_d = s_start_timing_q;
s_data_timing_d = s_data_timing_q;
s_stop_timing_d = s_stop_timing_q;
s_filter_d = s_filter_q;
s_stretch_timeout_d = s_stretch_timeout_q;
s_bus_idle_timeout_d = s_bus_idle_timeout_q;
s_command_timeout_d = s_command_timeout_q;
s_target_addr_d = s_target_addr_q;
s_cmd_watermark_d = s_cmd_watermark_q;
s_rx_watermark_d = s_rx_watermark_q;
s_intr_enable_d = s_intr_enable_q;
s_cmd_push = 1'b0;
s_rx_pop = 1'b0;
s_cmd_flush_cmd = 1'b0;
s_rx_flush_cmd = 1'b0;
abort_o = 1'b0;
recover_o = 1'b0;
s_error_clear = '0;
s_intr_clear = '0;
s_intr_test = '0;
s_config_error_event = 1'b0;
s_sw_command_error_event = 1'b0;
s_ribp_rdata_d = '0;
s_merge_value = '0;
if (s_req) begin
if ((ribp.addr[11:8] != 4'd0) || (ribp.addr[1:0] != 2'b00)) begin
s_access_error = 1'b1;
end else if (s_write) begin
unique case (ribp.addr[7:0])
`RIBP_I2C_CTRL: begin
s_merge_value = merge_wstrb({31'd0, s_enable_q}, ribp.wdata, ribp.wstrb);
if ((s_merge_value[31:1] != 31'd0) ||
(s_merge_value[0] && !s_config_valid) ||
(!s_merge_value[0] && (busy_i || recovery_active_i))) begin
s_access_error = 1'b1;
s_config_error_event = 1'b1;
end else begin
s_enable_en = 1'b1;
s_enable_d = s_merge_value[0];
end
end
`RIBP_I2C_SCL_TIMING: begin
if (s_enable_q || busy_i || recovery_active_i) begin
s_access_error = 1'b1;
s_config_error_event = 1'b1;
end else begin
s_scl_timing_en = 1'b1;
s_scl_timing_d = merge_wstrb(s_scl_timing_q, ribp.wdata, ribp.wstrb);
end
end
`RIBP_I2C_START_TIMING: begin
if (s_enable_q || busy_i || recovery_active_i) begin
s_access_error = 1'b1;
s_config_error_event = 1'b1;
end else begin
s_start_timing_en = 1'b1;
s_start_timing_d = merge_wstrb(s_start_timing_q, ribp.wdata, ribp.wstrb);
end
end
`RIBP_I2C_DATA_TIMING: begin
if (s_enable_q || busy_i || recovery_active_i) begin
s_access_error = 1'b1;
s_config_error_event = 1'b1;
end else begin
s_data_timing_en = 1'b1;
s_data_timing_d = merge_wstrb(s_data_timing_q, ribp.wdata, ribp.wstrb);
end
end
`RIBP_I2C_STOP_TIMING: begin
if (s_enable_q || busy_i || recovery_active_i) begin
s_access_error = 1'b1;
s_config_error_event = 1'b1;
end else begin
s_stop_timing_en = 1'b1;
s_stop_timing_d = merge_wstrb(s_stop_timing_q, ribp.wdata, ribp.wstrb);
end
end
`RIBP_I2C_FILTER: begin
s_merge_value = merge_wstrb({20'd0, s_filter_q}, ribp.wdata, ribp.wstrb);
if (s_enable_q || busy_i || recovery_active_i ||
(s_merge_value[31:12] != 20'd0) || (s_merge_value[7:4] != 4'd0)) begin
s_access_error = 1'b1;
s_config_error_event = 1'b1;
end else begin
s_filter_en = 1'b1;
s_filter_d = s_merge_value[11:0];
end
end
`RIBP_I2C_STRETCH_TIMEOUT: begin
s_merge_value = merge_wstrb({8'd0, s_stretch_timeout_q}, ribp.wdata, ribp.wstrb);
if (s_enable_q || busy_i || recovery_active_i || (s_merge_value[31:24] != 8'd0)) begin
s_access_error = 1'b1;
s_config_error_event = 1'b1;
end else begin
s_stretch_timeout_en = 1'b1;
s_stretch_timeout_d = s_merge_value[23:0];
end
end
`RIBP_I2C_BUS_IDLE_TIMEOUT: begin
s_merge_value = merge_wstrb({8'd0, s_bus_idle_timeout_q}, ribp.wdata, ribp.wstrb);
if (s_enable_q || busy_i || recovery_active_i || (s_merge_value[31:24] != 8'd0)) begin
s_access_error = 1'b1;
s_config_error_event = 1'b1;
end else begin
s_bus_idle_timeout_en = 1'b1;
s_bus_idle_timeout_d = s_merge_value[23:0];
end
end
`RIBP_I2C_COMMAND_TIMEOUT: begin
s_merge_value = merge_wstrb({8'd0, s_command_timeout_q}, ribp.wdata, ribp.wstrb);
if (s_enable_q || busy_i || recovery_active_i || (s_merge_value[31:24] != 8'd0)) begin
s_access_error = 1'b1;
s_config_error_event = 1'b1;
end else begin
s_command_timeout_en = 1'b1;
s_command_timeout_d = s_merge_value[23:0];
end
end
`RIBP_I2C_TARGET_ADDR: begin
s_merge_value = merge_wstrb({21'd0, s_target_addr_q}, ribp.wdata, ribp.wstrb);
if (busy_i || recovery_active_i || !s_cmd_empty ||
(s_merge_value[31:11] != 21'd0) ||
(!s_merge_value[10] && (s_merge_value[9:7] != 3'd0))) begin
s_access_error = 1'b1;
s_config_error_event = 1'b1;
end else begin
s_target_addr_en = 1'b1;
s_target_addr_d = s_merge_value[10:0];
end
end
`RIBP_I2C_DATA_CMD: begin
if ((ribp.wstrb != 4'hF) || (ribp.wdata[31:12] != 20'd0) ||
(!ribp.wdata[`I2C_DATA_CMD_READ] &&
ribp.wdata[`I2C_DATA_CMD_NACK_LAST]) ||
(ribp.wdata[`I2C_DATA_CMD_READ] && ribp.wdata[`I2C_DATA_CMD_STOP] &&
!ribp.wdata[`I2C_DATA_CMD_NACK_LAST])) begin
s_access_error = 1'b1;
s_sw_command_error_event = 1'b1;
end else if (s_cmd_full && s_enable_q && s_config_valid) begin
s_req_accept = 1'b0;
end else if (s_cmd_full) begin
s_access_error = 1'b1;
s_sw_command_error_event = 1'b1;
end else begin
s_cmd_push = 1'b1;
end
end
`RIBP_I2C_COMMAND: begin
if (!ribp.wstrb[0] || (ribp.wdata[31:4] != 28'd0) || (ribp.wdata[3:0] == 4'd0)) begin
s_access_error = 1'b1;
s_sw_command_error_event = 1'b1;
end else if (ribp.wdata[`I2C_COMMAND_ABORT]) begin
if ((ribp.wdata[3:0] != 4'b0001) || (!busy_i && !recovery_active_i)) begin
s_access_error = 1'b1;
s_sw_command_error_event = 1'b1;
end else begin
abort_o = 1'b1;
end
end else if (ribp.wdata[`I2C_COMMAND_RECOVER]) begin
if ((ribp.wdata[3:0] != 4'b0010) || !s_enable_q || !s_config_valid ||
busy_i || recovery_active_i || !s_cmd_empty) begin
s_access_error = 1'b1;
s_sw_command_error_event = 1'b1;
end else begin
recover_o = 1'b1;
end
end else if (busy_i || recovery_active_i || (ribp.wdata[1:0] != 2'd0)) begin
s_access_error = 1'b1;
s_sw_command_error_event = 1'b1;
end else begin
s_cmd_flush_cmd = ribp.wdata[`I2C_COMMAND_CMD_FLUSH];
s_rx_flush_cmd = ribp.wdata[`I2C_COMMAND_RX_FLUSH];
end
end
`RIBP_I2C_CMD_WATERMARK: begin
s_merge_value = merge_wstrb({24'd0, s_cmd_watermark_q}, ribp.wdata, ribp.wstrb);
if ((s_merge_value[31:8] != 24'd0) || (s_merge_value[7:0] >= CMD_DEPTH_INFO)) begin
s_access_error = 1'b1;
s_sw_command_error_event = 1'b1;
end else begin
s_cmd_watermark_en = 1'b1;
s_cmd_watermark_d = s_merge_value[7:0];
end
end
`RIBP_I2C_RX_WATERMARK: begin
s_merge_value = merge_wstrb({24'd0, s_rx_watermark_q}, ribp.wdata, ribp.wstrb);
if ((s_merge_value[31:8] != 24'd0) || (s_merge_value[7:0] == 8'd0) ||
(s_merge_value[7:0] > RX_DEPTH_INFO)) begin
s_access_error = 1'b1;
s_sw_command_error_event = 1'b1;
end else begin
s_rx_watermark_en = 1'b1;
s_rx_watermark_d = s_merge_value[7:0];
end
end
`RIBP_I2C_ERROR_STATUS: begin
if (!ribp.wstrb[0] || (ribp.wdata[31:11] != 21'd0)) begin
s_access_error = 1'b1;
end else begin
s_error_clear = ribp.wdata[10:0];
end
end
`RIBP_I2C_INTR_STATE: begin
if (!ribp.wstrb[0] || (ribp.wdata[31:8] != 24'd0)) begin
s_access_error = 1'b1;
end else begin
s_intr_clear = ribp.wdata[7:0];
end
end
`RIBP_I2C_INTR_ENABLE: begin
s_merge_value = merge_wstrb({24'd0, s_intr_enable_q}, ribp.wdata, ribp.wstrb);
if (s_merge_value[31:8] != 24'd0) begin
s_access_error = 1'b1;
end else begin
s_intr_enable_en = 1'b1;
s_intr_enable_d = s_merge_value[7:0];
end
end
`RIBP_I2C_INTR_TEST: begin
if (!ribp.wstrb[0] || (ribp.wdata[31:8] != 24'd0)) begin
s_access_error = 1'b1;
end else begin
s_intr_test = ribp.wdata[7:0];
end
end
default: s_access_error = 1'b1;
endcase
end else begin
unique case (ribp.addr[7:0])
`RIBP_I2C_CTRL: s_ribp_rdata_d = {31'd0, s_enable_q};
`RIBP_I2C_SCL_TIMING: s_ribp_rdata_d = s_scl_timing_q;
`RIBP_I2C_START_TIMING: s_ribp_rdata_d = s_start_timing_q;
`RIBP_I2C_DATA_TIMING: s_ribp_rdata_d = s_data_timing_q;
`RIBP_I2C_STOP_TIMING: s_ribp_rdata_d = s_stop_timing_q;
`RIBP_I2C_FILTER: s_ribp_rdata_d = {20'd0, s_filter_q};
`RIBP_I2C_STRETCH_TIMEOUT: s_ribp_rdata_d = {8'd0, s_stretch_timeout_q};
`RIBP_I2C_BUS_IDLE_TIMEOUT: s_ribp_rdata_d = {8'd0, s_bus_idle_timeout_q};
`RIBP_I2C_COMMAND_TIMEOUT: s_ribp_rdata_d = {8'd0, s_command_timeout_q};
`RIBP_I2C_TARGET_ADDR: s_ribp_rdata_d = {21'd0, s_target_addr_q};
`RIBP_I2C_RXDATA: begin
if (s_rx_empty) begin
s_access_error = 1'b1;
s_sw_command_error_event = 1'b1;
end else begin
s_ribp_rdata_d = {24'd0, s_rx_pop_data};
s_rx_pop = 1'b1;
end
end
`RIBP_I2C_STATUS: s_ribp_rdata_d = s_status;
`RIBP_I2C_FIFO_LEVEL: s_ribp_rdata_d = s_fifo_level;
`RIBP_I2C_CMD_WATERMARK: s_ribp_rdata_d = {24'd0, s_cmd_watermark_q};
`RIBP_I2C_RX_WATERMARK: s_ribp_rdata_d = {24'd0, s_rx_watermark_q};
`RIBP_I2C_ERROR_STATUS: s_ribp_rdata_d = {21'd0, s_error_status_q};
`RIBP_I2C_INTR_STATE: s_ribp_rdata_d = {24'd0, s_intr_state_q};
`RIBP_I2C_INTR_ENABLE: s_ribp_rdata_d = {24'd0, s_intr_enable_q};
`RIBP_I2C_INTR_STATUS: s_ribp_rdata_d = {24'd0, (s_intr_state_q & s_intr_enable_q)};
`RIBP_I2C_LINE_STATE: s_ribp_rdata_d = s_line_state;
`RIBP_I2C_IP_VERSION: s_ribp_rdata_d = IP_VERSION;
`RIBP_I2C_CAPABILITY: s_ribp_rdata_d = CAPABILITY;
default: begin
s_access_error = 1'b1;
s_ribp_rdata_d = '0;
end
endcase
end
end
end
assign s_ribp_ready_d = s_req_accept;
assign s_ribp_resp_err_d = s_access_error;
dffr #(1) u_ribp_ready_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_ribp_ready_d),
.dat_o (s_ribp_ready_q)
);
dffer #(1) u_ribp_resp_err_dffer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_req_accept),
.dat_i (s_ribp_resp_err_d),
.dat_o (s_ribp_resp_err_q)
);
dffer #(32) u_ribp_rdata_dffer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_req_accept),
.dat_i (s_ribp_rdata_d),
.dat_o (s_ribp_rdata_q)
);
dffer #(1) u_enable_dffer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_enable_en),
.dat_i (s_enable_d),
.dat_o (s_enable_q)
);
dffer #(32) u_scl_timing_dffer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_scl_timing_en),
.dat_i (s_scl_timing_d),
.dat_o (s_scl_timing_q)
);
dffer #(32) u_start_timing_dffer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_start_timing_en),
.dat_i (s_start_timing_d),
.dat_o (s_start_timing_q)
);
dffer #(32) u_data_timing_dffer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_data_timing_en),
.dat_i (s_data_timing_d),
.dat_o (s_data_timing_q)
);
dffer #(32) u_stop_timing_dffer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_stop_timing_en),
.dat_i (s_stop_timing_d),
.dat_o (s_stop_timing_q)
);
dfferc #(
.DATA_WIDTH(12),
.RESET_VAL (12'h202)
) u_filter_dfferc (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_filter_en),
.dat_i (s_filter_d),
.dat_o (s_filter_q)
);
dffer #(24) u_stretch_timeout_dffer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_stretch_timeout_en),
.dat_i (s_stretch_timeout_d),
.dat_o (s_stretch_timeout_q)
);
dffer #(24) u_bus_idle_timeout_dffer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_bus_idle_timeout_en),
.dat_i (s_bus_idle_timeout_d),
.dat_o (s_bus_idle_timeout_q)
);
dffer #(24) u_command_timeout_dffer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_command_timeout_en),
.dat_i (s_command_timeout_d),
.dat_o (s_command_timeout_q)
);
dffer #(11) u_target_addr_dffer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_target_addr_en),
.dat_i (s_target_addr_d),
.dat_o (s_target_addr_q)
);
dfferc #(
.DATA_WIDTH(8),
.RESET_VAL (8'(CMD_WATERMARK_RESET))
) u_cmd_watermark_dfferc (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_cmd_watermark_en),
.dat_i (s_cmd_watermark_d),
.dat_o (s_cmd_watermark_q)
);
dfferc #(
.DATA_WIDTH(8),
.RESET_VAL (8'(RX_WATERMARK_RESET))
) u_rx_watermark_dfferc (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_rx_watermark_en),
.dat_i (s_rx_watermark_d),
.dat_o (s_rx_watermark_q)
);
dffer #(8) u_intr_enable_dffer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_intr_enable_en),
.dat_i (s_intr_enable_d),
.dat_o (s_intr_enable_q)
);
fifo #(
.DATA_WIDTH (12),
.BUFFER_DEPTH (CMD_FIFO_DEPTH),
.LOG_BUFFER_DEPTH(CMD_FIFO_LOG_DEPTH)
) u_cmd_fifo (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.flush_i(s_cmd_flush_cmd || core_cmd_flush_i),
.push_i (s_cmd_push),
.full_o (s_cmd_full),
.dat_i (ribp.wdata[11:0]),
.pop_i (cmd_pop_i),
.empty_o(s_cmd_empty),
.dat_o (s_cmd_pop_data),
.cnt_o (s_cmd_count)
);
fifo #(
.DATA_WIDTH (8),
.BUFFER_DEPTH (RX_FIFO_DEPTH),
.LOG_BUFFER_DEPTH(RX_FIFO_LOG_DEPTH)
) u_rx_fifo (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.flush_i(s_rx_flush_cmd),
.push_i (rx_push_i && !s_rx_full),
.full_o (s_rx_full),
.dat_i (rx_data_i),
.pop_i (s_rx_pop),
.empty_o(s_rx_empty),
.dat_o (s_rx_pop_data),
.cnt_o (s_rx_count)
);
assign s_timeout_event = stretch_timeout_event_i || bus_timeout_event_i ||
command_timeout_event_i;
assign s_error_event = addr_nack_event_i || data_nack_event_i || arb_lost_event_i ||
s_timeout_event || command_error_event_i ||
s_sw_command_error_event || rx_overflow_event_i ||
s_config_error_event || aborted_event_i || recovery_failed_event_i;
always_comb begin
s_error_status_d = s_error_status_q & ~s_error_clear;
if (addr_nack_event_i) s_error_status_d[`I2C_ERROR_ADDR_NACK] = 1'b1;
if (data_nack_event_i) s_error_status_d[`I2C_ERROR_DATA_NACK] = 1'b1;
if (arb_lost_event_i) s_error_status_d[`I2C_ERROR_ARB_LOST] = 1'b1;
if (stretch_timeout_event_i) s_error_status_d[`I2C_ERROR_STRETCH_TIMEOUT] = 1'b1;
if (bus_timeout_event_i) s_error_status_d[`I2C_ERROR_BUS_TIMEOUT] = 1'b1;
if (command_timeout_event_i) s_error_status_d[`I2C_ERROR_COMMAND_TIMEOUT] = 1'b1;
if (command_error_event_i || s_sw_command_error_event)
s_error_status_d[`I2C_ERROR_COMMAND] = 1'b1;
if (rx_overflow_event_i) s_error_status_d[`I2C_ERROR_RX_OVERFLOW] = 1'b1;
if (s_config_error_event) s_error_status_d[`I2C_ERROR_CONFIG] = 1'b1;
if (aborted_event_i) s_error_status_d[`I2C_ERROR_ABORTED] = 1'b1;
if (recovery_failed_event_i) s_error_status_d[`I2C_ERROR_RECOVERY_FAILED] = 1'b1;
end
dffr #(11) u_error_status_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_error_status_d),
.dat_o (s_error_status_q)
);
always_comb begin
s_intr_state_d = (s_intr_state_q & ~s_intr_clear) | s_intr_test;
if (done_event_i) s_intr_state_d[`I2C_INTR_DONE] = 1'b1;
if (s_cmd_watermark_active) s_intr_state_d[`I2C_INTR_CMD_WATERMARK] = 1'b1;
if (s_rx_watermark_active) s_intr_state_d[`I2C_INTR_RX_WATERMARK] = 1'b1;
if (addr_nack_event_i || data_nack_event_i) s_intr_state_d[`I2C_INTR_NACK] = 1'b1;
if (arb_lost_event_i) s_intr_state_d[`I2C_INTR_ARB_LOST] = 1'b1;
if (s_timeout_event) s_intr_state_d[`I2C_INTR_TIMEOUT] = 1'b1;
if (s_error_event) s_intr_state_d[`I2C_INTR_ERROR] = 1'b1;
if (recovery_done_event_i) s_intr_state_d[`I2C_INTR_RECOVERY_DONE] = 1'b1;
end
dffr #(8) u_intr_state_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_intr_state_d),
.dat_o (s_intr_state_q)
);
endmodule
+177
View File
@@ -0,0 +1,177 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
module ribp_i2c #(
parameter int CMD_FIFO_DEPTH = 16,
parameter int RX_FIFO_DEPTH = 16
) (
// verilog_format: off
input logic clk_i,
input logic rst_n_i,
output logic dma_tx_stall_o,
output logic dma_rx_stall_o,
ribp_if.slave ribp,
i2c_if.dut i2c
// verilog_format: on
);
logic s_enable;
logic [15:0] s_scl_low_cycles;
logic [15:0] s_scl_high_cycles;
logic [15:0] s_start_hold_cycles;
logic [15:0] s_start_setup_cycles;
logic [15:0] s_data_hold_cycles;
logic [15:0] s_data_setup_cycles;
logic [15:0] s_stop_setup_cycles;
logic [15:0] s_bus_free_cycles;
logic [ 3:0] s_scl_filter_cycles;
logic [ 3:0] s_sda_filter_cycles;
logic [23:0] s_stretch_timeout;
logic [23:0] s_bus_idle_timeout;
logic [23:0] s_command_timeout;
logic [ 9:0] s_target_addr;
logic s_ten_bit;
logic s_abort;
logic s_recover;
logic s_cmd_valid;
logic [11:0] s_cmd_data;
logic s_cmd_pop;
logic s_rx_push;
logic [ 7:0] s_rx_data;
logic s_rx_full;
logic s_busy;
logic s_recovery_active;
logic s_scl_filtered;
logic s_sda_filtered;
logic s_scl_pull_low;
logic s_sda_pull_low;
logic s_done_event;
logic s_addr_nack_event;
logic s_data_nack_event;
logic s_arb_lost_event;
logic s_stretch_timeout_event;
logic s_bus_timeout_event;
logic s_command_timeout_event;
logic s_command_error_event;
logic s_rx_overflow_event;
logic s_aborted_event;
logic s_recovery_done_event;
logic s_recovery_failed_event;
logic s_core_cmd_flush;
assign i2c.scl_o = 1'b0;
assign i2c.sda_o = 1'b0;
assign i2c.scl_oe_o = s_scl_pull_low;
assign i2c.sda_oe_o = s_sda_pull_low;
i2c_filter u_i2c_filter (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.scl_filter_cycles_i(s_scl_filter_cycles),
.sda_filter_cycles_i(s_sda_filter_cycles),
.scl_async_i (i2c.scl_i),
.sda_async_i (i2c.sda_i),
.scl_o (s_scl_filtered),
.sda_o (s_sda_filtered)
);
i2c_reg #(
.CMD_FIFO_DEPTH(CMD_FIFO_DEPTH),
.RX_FIFO_DEPTH (RX_FIFO_DEPTH)
) u_i2c_reg (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.ribp (ribp),
.enable_o (s_enable),
.scl_low_cycles_o (s_scl_low_cycles),
.scl_high_cycles_o (s_scl_high_cycles),
.start_hold_cycles_o (s_start_hold_cycles),
.start_setup_cycles_o (s_start_setup_cycles),
.data_hold_cycles_o (s_data_hold_cycles),
.data_setup_cycles_o (s_data_setup_cycles),
.stop_setup_cycles_o (s_stop_setup_cycles),
.bus_free_cycles_o (s_bus_free_cycles),
.scl_filter_cycles_o (s_scl_filter_cycles),
.sda_filter_cycles_o (s_sda_filter_cycles),
.stretch_timeout_o (s_stretch_timeout),
.bus_idle_timeout_o (s_bus_idle_timeout),
.command_timeout_o (s_command_timeout),
.target_addr_o (s_target_addr),
.ten_bit_o (s_ten_bit),
.abort_o (s_abort),
.recover_o (s_recover),
.cmd_valid_o (s_cmd_valid),
.cmd_data_o (s_cmd_data),
.cmd_pop_i (s_cmd_pop),
.core_cmd_flush_i (s_core_cmd_flush),
.rx_push_i (s_rx_push),
.rx_data_i (s_rx_data),
.rx_full_o (s_rx_full),
.busy_i (s_busy),
.recovery_active_i (s_recovery_active),
.scl_i (s_scl_filtered),
.sda_i (s_sda_filtered),
.done_event_i (s_done_event),
.addr_nack_event_i (s_addr_nack_event),
.data_nack_event_i (s_data_nack_event),
.arb_lost_event_i (s_arb_lost_event),
.stretch_timeout_event_i(s_stretch_timeout_event),
.bus_timeout_event_i (s_bus_timeout_event),
.command_timeout_event_i(s_command_timeout_event),
.command_error_event_i (s_command_error_event),
.rx_overflow_event_i (s_rx_overflow_event),
.aborted_event_i (s_aborted_event),
.recovery_done_event_i (s_recovery_done_event),
.recovery_failed_event_i(s_recovery_failed_event),
.dma_tx_stall_o (dma_tx_stall_o),
.dma_rx_stall_o (dma_rx_stall_o),
.irq_o (i2c.irq_o)
);
i2c_core u_i2c_core (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.enable_i (s_enable),
.scl_low_cycles_i (s_scl_low_cycles),
.scl_high_cycles_i (s_scl_high_cycles),
.start_hold_cycles_i (s_start_hold_cycles),
.start_setup_cycles_i (s_start_setup_cycles),
.data_hold_cycles_i (s_data_hold_cycles),
.data_setup_cycles_i (s_data_setup_cycles),
.stop_setup_cycles_i (s_stop_setup_cycles),
.bus_free_cycles_i (s_bus_free_cycles),
.stretch_timeout_i (s_stretch_timeout),
.bus_idle_timeout_i (s_bus_idle_timeout),
.command_timeout_i (s_command_timeout),
.target_addr_i (s_target_addr),
.ten_bit_i (s_ten_bit),
.abort_i (s_abort),
.recover_i (s_recover),
.cmd_valid_i (s_cmd_valid),
.cmd_data_i (s_cmd_data),
.cmd_pop_o (s_cmd_pop),
.cmd_flush_o (s_core_cmd_flush),
.rx_full_i (s_rx_full),
.rx_push_o (s_rx_push),
.rx_data_o (s_rx_data),
.scl_i (s_scl_filtered),
.sda_i (s_sda_filtered),
.scl_pull_low_o (s_scl_pull_low),
.sda_pull_low_o (s_sda_pull_low),
.busy_o (s_busy),
.recovery_active_o (s_recovery_active),
.done_event_o (s_done_event),
.addr_nack_event_o (s_addr_nack_event),
.data_nack_event_o (s_data_nack_event),
.arb_lost_event_o (s_arb_lost_event),
.stretch_timeout_event_o(s_stretch_timeout_event),
.bus_timeout_event_o (s_bus_timeout_event),
.command_timeout_event_o(s_command_timeout_event),
.command_error_event_o (s_command_error_event),
.rx_overflow_event_o (s_rx_overflow_event),
.aborted_event_o (s_aborted_event),
.recovery_done_event_o (s_recovery_done_event),
.recovery_failed_event_o(s_recovery_failed_event)
);
endmodule
+86
View File
@@ -0,0 +1,86 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
`ifndef RETROSOC_RIBP_I2C_DEFINE_SVH
`define RETROSOC_RIBP_I2C_DEFINE_SVH
// verilog_format: off
`define RIBP_I2C_CTRL 8'h00
`define RIBP_I2C_SCL_TIMING 8'h04
`define RIBP_I2C_START_TIMING 8'h08
`define RIBP_I2C_DATA_TIMING 8'h0C
`define RIBP_I2C_STOP_TIMING 8'h10
`define RIBP_I2C_FILTER 8'h14
`define RIBP_I2C_STRETCH_TIMEOUT 8'h18
`define RIBP_I2C_BUS_IDLE_TIMEOUT 8'h1C
`define RIBP_I2C_COMMAND_TIMEOUT 8'h20
`define RIBP_I2C_TARGET_ADDR 8'h24
`define RIBP_I2C_DATA_CMD 8'h28
`define RIBP_I2C_RXDATA 8'h2C
`define RIBP_I2C_STATUS 8'h30
`define RIBP_I2C_FIFO_LEVEL 8'h34
`define RIBP_I2C_COMMAND 8'h38
`define RIBP_I2C_CMD_WATERMARK 8'h3C
`define RIBP_I2C_RX_WATERMARK 8'h40
`define RIBP_I2C_ERROR_STATUS 8'h44
`define RIBP_I2C_INTR_STATE 8'h48
`define RIBP_I2C_INTR_ENABLE 8'h4C
`define RIBP_I2C_INTR_STATUS 8'h50
`define RIBP_I2C_INTR_TEST 8'h54
`define RIBP_I2C_LINE_STATE 8'h58
`define RIBP_I2C_IP_VERSION 8'hF8
`define RIBP_I2C_CAPABILITY 8'hFC
`define I2C_CTRL_ENABLE 0
`define I2C_DATA_CMD_READ 8
`define I2C_DATA_CMD_RESTART 9
`define I2C_DATA_CMD_STOP 10
`define I2C_DATA_CMD_NACK_LAST 11
`define I2C_COMMAND_ABORT 0
`define I2C_COMMAND_RECOVER 1
`define I2C_COMMAND_CMD_FLUSH 2
`define I2C_COMMAND_RX_FLUSH 3
`define I2C_STATUS_ENABLE 0
`define I2C_STATUS_BUSY 1
`define I2C_STATUS_BUS_BUSY 2
`define I2C_STATUS_CMD_EMPTY 3
`define I2C_STATUS_CMD_FULL 4
`define I2C_STATUS_RX_EMPTY 5
`define I2C_STATUS_RX_FULL 6
`define I2C_STATUS_CONFIG_VALID 7
`define I2C_STATUS_RECOVERY_ACTIVE 8
`define I2C_STATUS_SCL 9
`define I2C_STATUS_SDA 10
`define I2C_STATUS_TX_DMA_REQ 11
`define I2C_STATUS_RX_DMA_REQ 12
`define I2C_ERROR_ADDR_NACK 0
`define I2C_ERROR_DATA_NACK 1
`define I2C_ERROR_ARB_LOST 2
`define I2C_ERROR_STRETCH_TIMEOUT 3
`define I2C_ERROR_BUS_TIMEOUT 4
`define I2C_ERROR_COMMAND_TIMEOUT 5
`define I2C_ERROR_COMMAND 6
`define I2C_ERROR_RX_OVERFLOW 7
`define I2C_ERROR_CONFIG 8
`define I2C_ERROR_ABORTED 9
`define I2C_ERROR_RECOVERY_FAILED 10
`define I2C_INTR_DONE 0
`define I2C_INTR_CMD_WATERMARK 1
`define I2C_INTR_RX_WATERMARK 2
`define I2C_INTR_NACK 3
`define I2C_INTR_ARB_LOST 4
`define I2C_INTR_TIMEOUT 5
`define I2C_INTR_ERROR 6
`define I2C_INTR_RECOVERY_DONE 7
`define I2C_LINE_SCL 0
`define I2C_LINE_SDA 1
`define I2C_LINE_BUS_FREE 2
// verilog_format: on
`endif
+3 -1
View File
@@ -11,8 +11,10 @@
/ip/ribp/storage/spisd_data.sv
/ip/ribp/storage/spisd.sv
/ip/ribp/storage/sdio.sv
/ip/ribp/serial/i2c_filter.sv
/ip/ribp/serial/i2c_core.sv
/ip/ribp/serial/i2c.sv
/ip/ribp/serial/i2c_reg.sv
/ip/ribp/serial/ribp_i2c.sv
/ip/ribp/serial/i2s_reg.sv
/ip/ribp/serial/i2s_clkgen.sv
/ip/ribp/serial/i2s_recv.sv
@@ -121,6 +121,19 @@ def source_files(target: str) -> list[Path]:
SERIAL / "ribp_uart.sv",
SCRIPT_DIR / "uart_formal.sv",
]
if target == "i2c":
return [
COMMON_RTL / "interface/ribp_if.sv",
COMMON_RTL / "utils/register.sv",
COMMON_RTL / "utils/fifo.sv",
COMMON_RTL / "cdc/cdc_sync.sv",
ROOT / "rtl/managed/clusterip/i2c/rtl/i2c_if.sv",
SERIAL / "i2c_filter.sv",
SERIAL / "i2c_core.sv",
SERIAL / "i2c_reg.sv",
SERIAL / "ribp_i2c.sv",
SCRIPT_DIR / "i2c_formal.sv",
]
if target == "timer":
return [
COMMON_RTL / "interface/ribp_if.sv",
@@ -184,6 +197,7 @@ def parse_args() -> argparse.Namespace:
"gpio",
"ws2812",
"uart",
"i2c",
"timer",
"clint",
),
+16 -6
View File
@@ -14,12 +14,21 @@ sys.path.insert(0, str(ROOT))
from scripts.setup_helpers import atomic_write # noqa: E402
def render(top: str, design: Path, properties: Path, solver: str, mode: str, depth: int) -> str:
def render(
top: str,
design: Path,
properties: Path,
solver: str,
mode: str,
depth: int,
vcd: bool = True,
) -> str:
options = ["[options]", f"mode {mode}", f"depth {depth}"]
if not vcd:
options.append("vcd off")
return "\n".join(
(
"[options]",
f"mode {mode}",
f"depth {depth}",
tuple(options)
+ (
"",
"[engines]",
f"smtbmc --presat --nounroll {solver}",
@@ -55,6 +64,7 @@ def parse_args() -> argparse.Namespace:
parser.add_argument("--solver", required=True)
parser.add_argument("--mode", choices=("prove", "cover"), required=True)
parser.add_argument("--depth", type=positive_int, required=True)
parser.add_argument("--no-vcd", action="store_false", dest="vcd")
parser.add_argument("--output", type=Path, required=True)
return parser.parse_args()
@@ -63,7 +73,7 @@ def main() -> int:
args = parse_args()
atomic_write(
args.output.resolve(),
render(args.top, args.input, args.properties, args.solver, args.mode, args.depth),
render(args.top, args.input, args.properties, args.solver, args.mode, args.depth, args.vcd),
)
return 0
+95
View File
@@ -0,0 +1,95 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
module i2c_formal_design (
// verilog_format: off
input logic clk_i,
output logic rst_n_i,
output logic f_past_valid,
output logic rib_valid,
output logic [31:0] rib_addr,
output logic [31:0] rib_wdata,
output logic [ 3:0] rib_wstrb,
output logic rib_ready,
output logic rib_resp_err,
output logic [ 4:0] command_count,
output logic [ 4:0] rx_count,
output logic [10:0] error_status,
output logic [ 7:0] intr_state,
output logic [ 7:0] intr_enable,
output logic command_valid,
output logic command_pop,
output logic rx_full,
output logic rx_push,
output logic busy,
output logic recovery_active,
output logic scl_o,
output logic sda_o,
output logic scl_oe,
output logic sda_oe,
output logic tx_dma_stall,
output logic rx_dma_stall,
output logic irq
// verilog_format: on
);
ribp_if ribp ();
i2c_if i2c ();
(* anyseq *)logic f_rib_valid;
(* anyseq *)logic [31:0] f_rib_addr;
(* anyseq *)logic [31:0] f_rib_wdata;
(* anyseq *)logic [ 3:0] f_rib_wstrb;
(* anyseq *)logic f_scl;
(* anyseq *)logic f_sda;
assign ribp.valid = f_rib_valid;
assign ribp.addr = f_rib_addr;
assign ribp.wdata = f_rib_wdata;
assign ribp.wstrb = f_rib_wstrb;
assign i2c.scl_i = f_scl;
assign i2c.sda_i = f_sda;
assign rib_valid = ribp.valid;
assign rib_addr = ribp.addr;
assign rib_wdata = ribp.wdata;
assign rib_wstrb = ribp.wstrb;
assign rib_ready = ribp.ready;
assign rib_resp_err = ribp.resp_err;
assign command_count = u_dut.u_i2c_reg.s_cmd_count;
assign rx_count = u_dut.u_i2c_reg.s_rx_count;
assign error_status = u_dut.u_i2c_reg.s_error_status_q;
assign intr_state = u_dut.u_i2c_reg.s_intr_state_q;
assign intr_enable = u_dut.u_i2c_reg.s_intr_enable_q;
assign command_valid = u_dut.s_cmd_valid;
assign command_pop = u_dut.s_cmd_pop;
assign rx_full = u_dut.s_rx_full;
assign rx_push = u_dut.s_rx_push;
assign busy = u_dut.s_busy;
assign recovery_active = u_dut.s_recovery_active;
assign scl_o = i2c.scl_o;
assign sda_o = i2c.sda_o;
assign scl_oe = i2c.scl_oe_o;
assign sda_oe = i2c.sda_oe_o;
assign irq = i2c.irq_o;
ribp_i2c u_dut (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.dma_tx_stall_o(tx_dma_stall),
.dma_rx_stall_o(rx_dma_stall),
.ribp (ribp),
.i2c (i2c)
);
initial begin
rst_n_i = 1'b0;
f_past_valid = 1'b0;
end
always_ff @(posedge clk_i) begin
rst_n_i <= 1'b1;
f_past_valid <= 1'b1;
end
endmodule
+97
View File
@@ -0,0 +1,97 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
module i2c_formal;
(* anyseq, gclk *) logic clk_i;
wire rst_n_i;
wire f_past_valid;
wire rib_valid;
wire [31:0] rib_addr;
wire [31:0] rib_wdata;
wire [ 3:0] rib_wstrb;
wire rib_ready;
wire rib_resp_err;
wire [ 4:0] command_count;
wire [ 4:0] rx_count;
wire [10:0] error_status;
wire [ 7:0] intr_state;
wire [ 7:0] intr_enable;
wire command_valid;
wire command_pop;
wire rx_full;
wire rx_push;
wire busy;
wire recovery_active;
wire scl_o;
wire sda_o;
wire scl_oe;
wire sda_oe;
wire tx_dma_stall;
wire rx_dma_stall;
wire irq;
i2c_formal_design u_design (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.f_past_valid (f_past_valid),
.rib_valid (rib_valid),
.rib_addr (rib_addr),
.rib_wdata (rib_wdata),
.rib_wstrb (rib_wstrb),
.rib_ready (rib_ready),
.rib_resp_err (rib_resp_err),
.command_count (command_count),
.rx_count (rx_count),
.error_status (error_status),
.intr_state (intr_state),
.intr_enable (intr_enable),
.command_valid (command_valid),
.command_pop (command_pop),
.rx_full (rx_full),
.rx_push (rx_push),
.busy (busy),
.recovery_active(recovery_active),
.scl_o (scl_o),
.sda_o (sda_o),
.scl_oe (scl_oe),
.sda_oe (sda_oe),
.tx_dma_stall (tx_dma_stall),
.rx_dma_stall (rx_dma_stall),
.irq (irq)
);
always @(posedge clk_i) begin
if (f_past_valid && $past(rst_n_i && rib_valid && !rib_ready)) begin
assume (rib_valid);
assume (rib_addr == $past(rib_addr));
assume (rib_wdata == $past(rib_wdata));
assume (rib_wstrb == $past(rib_wstrb));
end
if (rst_n_i) begin
assert (command_count <= 5'd16);
assert (rx_count <= 5'd16);
assert (!scl_o);
assert (!sda_o);
assert (irq == |(intr_state & intr_enable));
if (command_pop) assert (command_valid);
if (!busy) begin
assert (!scl_oe);
assert (!sda_oe);
end
if (recovery_active) assert (busy);
cover (rib_ready && rib_resp_err);
cover (command_count != 5'd0);
cover (rx_count != 5'd0);
cover (busy && scl_oe);
cover (busy && sda_oe);
cover (recovery_active);
cover (irq);
cover (error_status != 11'd0);
cover (!tx_dma_stall);
cover (!rx_dma_stall);
end
end
endmodule
@@ -46,6 +46,7 @@ COMPATIBILITY_IRQ_BINDINGS = (
("reserved", "apb", 6, 16, "1'b0"),
("ws2812", "ribp", 10, 17, "ws2812.irq_o"),
("gpio", "ribp", 11, 18, "gpio.irq_o"),
("i2c1", "ribp", 12, 19, "i2c1.irq_o"),
)
+3 -2
View File
@@ -3,7 +3,7 @@
"gpio_pins": 32,
"irq_vector_width": 32,
"irq_groups": [
{"name": "ribp", "width": 12, "top_signal": "s_ribp_irq"},
{"name": "ribp", "width": 13, "top_signal": "s_ribp_irq"},
{"name": "apb", "width": 7, "top_signal": "s_apb_irq"}
],
"interrupts": [
@@ -25,7 +25,8 @@
{"name": "advanced_timer", "description": "APB advanced timer interrupt", "group": "apb", "group_bit": 5, "core_bit": 15, "signal": "u_tmr_if.irq_o"},
{"name": "reserved", "description": "Reserved interrupt slot", "group": "apb", "group_bit": 6, "core_bit": 16, "signal": "1'b0"},
{"name": "ws2812", "description": "WS2812 transmitter interrupt", "group": "ribp", "group_bit": 10, "core_bit": 17, "signal": "ws2812.irq_o"},
{"name": "gpio", "description": "RIBP GPIO combined interrupt", "group": "ribp", "group_bit": 11, "core_bit": 18, "signal": "gpio.irq_o"}
{"name": "gpio", "description": "RIBP GPIO combined interrupt", "group": "ribp", "group_bit": 11, "core_bit": 18, "signal": "gpio.irq_o"},
{"name": "i2c1", "description": "RIBP I2C1 interrupt", "group": "ribp", "group_bit": 12, "core_bit": 19, "signal": "i2c1.irq_o"}
],
"ribp_targets": [
{"slot": 0, "name": "uart0", "interface": "u_uart_ribp_if", "regions": ["RIBP_UART0"]},
+18 -6
View File
@@ -6,10 +6,12 @@ FORMAL_SOLVER_DIR := $(FORMAL_DIR)/bin
FORMAL_SOLVER_WRAPPER := $(FORMAL_SOLVER_DIR)/bitwuzla
FORMAL_DEPTH ?= 20
FORMAL_WS2812_DEPTH ?= 120
FORMAL_I2C_DEPTH ?= 80
FORMAL_CLINT_DEPTH ?= 32
FORMAL_TIMEOUT ?= 60
FORMAL_WS2812_TIMEOUT ?= 120
FORMAL_TARGETS := bus rib_adapter rib2apb sysctrl pll_rcu gpio ws2812 uart timer clint
FORMAL_I2C_TIMEOUT ?= 300
FORMAL_TARGETS := bus rib_adapter rib2apb sysctrl pll_rcu gpio ws2812 uart i2c timer clint
FORMAL_FILELIST_GENERATOR := $(RTL_PATH)/formal/generate_formal_filelist.py
FORMAL_SBY_GENERATOR := $(RTL_PATH)/formal/generate_sby_config.py
FORMAL_RESULT_GENERATOR := $(RTL_PATH)/formal/formal_results.py
@@ -29,6 +31,8 @@ FORMAL_SOURCE_FILES := $(RTL_PATH)/formal/bus_formal.sv \
$(RTL_PATH)/formal/ws2812_formal_props.sv \
$(RTL_PATH)/formal/uart_formal.sv \
$(RTL_PATH)/formal/uart_formal_props.sv \
$(RTL_PATH)/formal/i2c_formal.sv \
$(RTL_PATH)/formal/i2c_formal_props.sv \
$(RTL_PATH)/formal/timer_formal.sv \
$(RTL_PATH)/formal/timer_formal_props.sv \
$(RTL_PATH)/formal/clint_formal.sv \
@@ -59,6 +63,11 @@ FORMAL_SOURCE_FILES := $(RTL_PATH)/formal/bus_formal.sv \
$(ROOT_PATH)/rtl/ip/ribp/serial/uart_reg.sv \
$(ROOT_PATH)/rtl/ip/ribp/serial/ribp_uart.sv \
$(ROOT_PATH)/rtl/managed/clusterip/uart/rtl/uart_if.sv \
$(ROOT_PATH)/rtl/ip/ribp/serial/i2c_filter.sv \
$(ROOT_PATH)/rtl/ip/ribp/serial/i2c_core.sv \
$(ROOT_PATH)/rtl/ip/ribp/serial/i2c_reg.sv \
$(ROOT_PATH)/rtl/ip/ribp/serial/ribp_i2c.sv \
$(ROOT_PATH)/rtl/managed/clusterip/i2c/rtl/i2c_if.sv \
$(ROOT_PATH)/rtl/ip/ribp/peripheral/timer_core.sv \
$(ROOT_PATH)/rtl/ip/ribp/peripheral/timer_define.svh \
$(ROOT_PATH)/rtl/ip/ribp/peripheral/timer_reg.sv \
@@ -107,13 +116,14 @@ $(FORMAL_DIR)/%/prove.sby: $(FORMAL_DIR)/%/design.v $(RTL_PATH)/formal/%_formal_
$(FORMAL_SBY_GENERATOR)
python3 $(FORMAL_SBY_GENERATOR) --top $*_formal --input $< \
--properties $(RTL_PATH)/formal/$*_formal_props.sv --solver $(FORMAL_SOLVER) \
--mode prove --depth $(if $(filter ws2812,$*),$(FORMAL_WS2812_DEPTH),$(if $(filter clint,$*),$(FORMAL_CLINT_DEPTH),$(FORMAL_DEPTH))) --output $@
--mode prove --depth $(if $(filter ws2812,$*),$(FORMAL_WS2812_DEPTH),$(if $(filter i2c,$*),$(FORMAL_I2C_DEPTH),$(if $(filter clint,$*),$(FORMAL_CLINT_DEPTH),$(FORMAL_DEPTH)))) --output $@
$(FORMAL_DIR)/%/cover.sby: $(FORMAL_DIR)/%/design.v $(RTL_PATH)/formal/%_formal_props.sv \
$(FORMAL_SBY_GENERATOR)
python3 $(FORMAL_SBY_GENERATOR) --top $*_formal --input $< \
--properties $(RTL_PATH)/formal/$*_formal_props.sv --solver $(FORMAL_SOLVER) \
--mode cover --depth $(if $(filter ws2812,$*),$(FORMAL_WS2812_DEPTH),$(if $(filter clint,$*),$(FORMAL_CLINT_DEPTH),$(FORMAL_DEPTH))) --output $@
--mode cover --depth $(if $(filter ws2812,$*),$(FORMAL_WS2812_DEPTH),$(if $(filter i2c,$*),$(FORMAL_I2C_DEPTH),$(if $(filter clint,$*),$(FORMAL_CLINT_DEPTH),$(FORMAL_DEPTH)))) \
$(if $(filter i2c,$*),--no-vcd) --output $@
$(FORMAL_SOLVER_WRAPPER): $(ROOT_PATH)/scripts/bitwuzla_smt2.py
@mkdir -p $(@D)
@@ -125,7 +135,7 @@ $(FORMAL_DIR)/%/prove.stamp: $(FORMAL_DIR)/%/prove.sby $(FORMAL_SOLVER_WRAPPER)
--log $(@D)/prove.log --result $(@D)/result-prove.json \
--env RETROSOC_BITWUZLA=$(FORMAL_BITWUZLA) \
--env PATH=$(FORMAL_SOLVER_DIR):$(PATH) \
-- timeout --foreground --kill-after=5s $(if $(filter ws2812,$*),$(FORMAL_WS2812_TIMEOUT),$(FORMAL_TIMEOUT))s $(FORMAL_SBY) \
-- timeout --foreground --kill-after=5s $(if $(filter ws2812,$*),$(FORMAL_WS2812_TIMEOUT),$(if $(filter i2c,$*),$(FORMAL_I2C_TIMEOUT),$(FORMAL_TIMEOUT)))s $(FORMAL_SBY) \
-f -d $(@D)/prove $<
@test "$$(awk '{print $$1}' $(@D)/prove/status)" = PASS
@touch $@
@@ -135,7 +145,7 @@ $(FORMAL_DIR)/%/cover.stamp: $(FORMAL_DIR)/%/cover.sby $(FORMAL_SOLVER_WRAPPER)
--log $(@D)/cover.log --result $(@D)/result-cover.json \
--env RETROSOC_BITWUZLA=$(FORMAL_BITWUZLA) \
--env PATH=$(FORMAL_SOLVER_DIR):$(PATH) \
-- timeout --foreground --kill-after=5s $(if $(filter ws2812,$*),$(FORMAL_WS2812_TIMEOUT),$(FORMAL_TIMEOUT))s $(FORMAL_SBY) \
-- timeout --foreground --kill-after=5s $(if $(filter ws2812,$*),$(FORMAL_WS2812_TIMEOUT),$(if $(filter i2c,$*),$(FORMAL_I2C_TIMEOUT),$(FORMAL_TIMEOUT)))s $(FORMAL_SBY) \
-f -d $(@D)/cover $<
@test "$$(awk '{print $$1}' $(@D)/cover/status)" = PASS
@touch $@
@@ -165,6 +175,8 @@ formal-ws2812: $(FORMAL_DIR)/ws2812/.stamp | manifest
formal-uart: $(FORMAL_DIR)/uart/.stamp | manifest
formal-i2c: $(FORMAL_DIR)/i2c/.stamp | manifest
formal-timer: $(FORMAL_DIR)/timer/.stamp | manifest
formal-clint: $(FORMAL_DIR)/clint/.stamp | manifest
@@ -175,4 +187,4 @@ formal-doctor:
formal-clean:
python3 $(ROOT_PATH)/scripts/clean.py --root $(ROOT_PATH) --path $(FORMAL_DIR)
.PHONY: formal formal-bus formal-rib-adapter formal-rib2apb formal-sysctrl formal-pll-rcu formal-gpio formal-ws2812 formal-uart formal-timer formal-clint formal-doctor formal-clean
.PHONY: formal formal-bus formal-rib-adapter formal-rib2apb formal-sysctrl formal-pll-rcu formal-gpio formal-ws2812 formal-uart formal-i2c formal-timer formal-clint formal-doctor formal-clean
+1
View File
@@ -81,6 +81,7 @@ CRT_SRCS := $(ROOT_PATH)/crt/arch/riscv/startup.S \
$(ROOT_PATH)/crt/src/hal/advanced_timer.c \
$(ROOT_PATH)/crt/src/hal/hpuart.c \
$(ROOT_PATH)/crt/src/hal/ps2.c \
$(ROOT_PATH)/crt/src/hal/i2c_math.c \
$(ROOT_PATH)/crt/src/hal/i2c.c \
$(ROOT_PATH)/crt/src/hal/ws2812_math.c \
$(ROOT_PATH)/crt/src/hal/ws2812.c \
+18 -8
View File
@@ -59,6 +59,8 @@ module ip_ribp_wrapper (
logic s_dma_i2s_tx_stall, s_dma_i2s_rx_stall;
logic s_dma_xpi_tx_stall, s_dma_xpi_rx_stall;
logic s_dma_uart_tx_stall, s_dma_uart_rx_stall;
logic s_dma_i2c0_tx_stall, s_dma_i2c0_rx_stall;
logic s_dma_i2c1_tx_stall, s_dma_i2c1_rx_stall;
logic s_dma_xfer_done;
logic s_tim0_irq, s_tim1_irq;
@@ -96,6 +98,10 @@ module ip_ribp_wrapper (
assign u_dma_hw_trg_if.qspi_rx_proc = ~s_dma_xpi_rx_stall;
assign u_dma_hw_trg_if.uart_tx_proc = ~s_dma_uart_tx_stall;
assign u_dma_hw_trg_if.uart_rx_proc = ~s_dma_uart_rx_stall;
assign u_dma_hw_trg_if.i2c0_tx_proc = ~s_dma_i2c0_tx_stall;
assign u_dma_hw_trg_if.i2c0_rx_proc = ~s_dma_i2c0_rx_stall;
assign u_dma_hw_trg_if.i2c1_tx_proc = ~s_dma_i2c1_tx_stall;
assign u_dma_hw_trg_if.i2c1_rx_proc = ~s_dma_i2c1_rx_stall;
// Uses ClusterIP common ribp_if and register.sv dffr through generated bindings.
`include "ribp_routes.svh"
@@ -159,10 +165,12 @@ module ip_ribp_wrapper (
);
ribp_i2c u_rib_i2c0 (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.ribp (u_i2c0_ribp_if),
.i2c (i2c0)
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.dma_tx_stall_o(s_dma_i2c0_tx_stall),
.dma_rx_stall_o(s_dma_i2c0_rx_stall),
.ribp (u_i2c0_ribp_if),
.i2c (i2c0)
);
ribp_i2s u_rib_i2s (
@@ -251,10 +259,12 @@ module ip_ribp_wrapper (
);
ribp_i2c u_rib_i2c1 (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.ribp (u_i2c1_ribp_if),
.i2c (i2c1)
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.dma_tx_stall_o(s_dma_i2c1_tx_stall),
.dma_rx_stall_o(s_dma_i2c1_rx_stall),
.ribp (u_i2c1_ribp_if),
.i2c (i2c1)
);
endmodule
+1
View File
@@ -21,6 +21,7 @@ TEST_SOURCES = (
"crt/src/lib/stdlib.c",
"crt/src/lib/string.c",
"crt/src/hal/gpio_math.c",
"crt/src/hal/i2c_math.c",
"crt/src/hal/ws2812_math.c",
"crt/src/hal/uart_math.c",
"crt/src/hal/timer_math.c",
+28 -4
View File
@@ -4,6 +4,7 @@
#include <retrosoc/core/status.h>
#include <retrosoc/core/wait.h>
#include <retrosoc/hal/gpio.h>
#include <retrosoc/hal/i2c.h>
#include <retrosoc/hal/lcd.h>
#include <retrosoc/hal/spisd.h>
#include <retrosoc/hal/timer.h>
@@ -211,6 +212,30 @@ static int test_uart_helpers(void) {
return 0;
}
static int test_i2c_helpers(void) {
rs_i2c_timing_t timing;
if ((rs_i2c_timing_calculate(72000000U, 400000U, &timing) != RS_OK) ||
(timing.scl_low_cycles != 136U) || (timing.scl_high_cycles != 44U) ||
(timing.start_hold_cycles != 44U) || (timing.start_setup_cycles != 44U) ||
(timing.data_setup_cycles != 8U) || (timing.stop_setup_cycles != 44U) ||
(timing.bus_free_cycles != 94U)) {
return 1;
}
if ((rs_i2c_timing_calculate(72000000U, 1000000U, &timing) != RS_OK) ||
(timing.scl_low_cycles != 53U) || (timing.scl_high_cycles != 19U) ||
(timing.data_setup_cycles != 4U)) {
return 2;
}
if ((rs_i2c_timing_calculate(0U, 400000U, &timing) != RS_EINVAL) ||
(rs_i2c_timing_calculate(72000000U, 0U, &timing) != RS_EINVAL) ||
(rs_i2c_timing_calculate(72000000U, 1000001U, &timing) != RS_EINVAL) ||
(rs_i2c_timing_calculate(72000000U, 400000U, NULL) != RS_EINVAL)) {
return 3;
}
return 0;
}
static int test_gpio_helpers(void) {
rs_gpio_filter_timing_t timing;
@@ -271,10 +296,9 @@ static int test_video_parser(void) {
int main(void) {
const int results[] = {
test_string_helpers(), test_formatter(), test_compiler_helpers(),
test_wait_helper(), test_ws2812_helpers(), test_timer_helpers(),
test_uart_helpers(), test_gpio_helpers(), test_wav_parser(),
test_video_parser(),
test_string_helpers(), test_formatter(), test_compiler_helpers(), test_wait_helper(),
test_ws2812_helpers(), test_timer_helpers(), test_uart_helpers(), test_i2c_helpers(),
test_gpio_helpers(), test_wav_parser(), test_video_parser(),
};
for (size_t index = 0U; index < (sizeof(results) / sizeof(results[0])); ++index) {
+5 -1
View File
@@ -7,7 +7,7 @@ module dma_error_tb;
logic clk_i = 1'b0;
logic rst_n_i = 1'b0;
logic [ 2:0] mode_i = 3'd0;
logic [ 3:0] mode_i = 4'd0;
logic [31:0] srcaddr_i = 32'h3800_1000;
logic srcincr_i = 1'b1;
logic [31:0] dstaddr_i = 32'h4000_1000;
@@ -145,6 +145,10 @@ module dma_error_tb;
hw_trg.qspi_rx_proc = 1'b0;
hw_trg.uart_tx_proc = 1'b0;
hw_trg.uart_rx_proc = 1'b0;
hw_trg.i2c0_tx_proc = 1'b0;
hw_trg.i2c0_rx_proc = 1'b0;
hw_trg.i2c1_tx_proc = 1'b0;
hw_trg.i2c1_rx_proc = 1'b0;
repeat (2) @(posedge clk_i);
rst_n_i = 1'b1;
+314
View File
@@ -0,0 +1,314 @@
`timescale 1ns / 1ps
`include "ribp_i2c_define.svh"
module i2c_tb;
logic clk_i = 1'b0;
logic rst_n_i = 1'b0;
logic dma_tx_stall;
logic dma_rx_stall;
logic slave_scl_low = 1'b0;
logic slave_sda_low = 1'b0;
logic scl_line;
logic sda_line;
ribp_if ribp ();
i2c_if i2c ();
always #5 clk_i = ~clk_i;
assign scl_line = (i2c.scl_oe_o || slave_scl_low) ? 1'b0 : 1'b1;
assign sda_line = (i2c.sda_oe_o || slave_sda_low) ? 1'b0 : 1'b1;
assign i2c.scl_i = scl_line;
assign i2c.sda_i = sda_line;
ribp_i2c u_dut (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.dma_tx_stall_o(dma_tx_stall),
.dma_rx_stall_o(dma_rx_stall),
.ribp (ribp),
.i2c (i2c)
);
task automatic ribp_write(input logic [31:0] address, input logic [31:0] data,
input logic expected_error);
begin
@(negedge clk_i);
ribp.addr = address;
ribp.wdata = data;
ribp.wstrb = 4'hF;
ribp.valid = 1'b1;
while (!ribp.ready) begin
@(posedge clk_i);
#1;
end
if (ribp.resp_err !== expected_error) begin
$fatal(1, "I2C write %h error=%b expected=%b", address, ribp.resp_err, expected_error);
end
@(negedge clk_i);
ribp.valid = 1'b0;
ribp.wstrb = '0;
end
endtask
task automatic ribp_read(input logic [31:0] address, input logic expected_error,
output logic [31:0] data);
begin
@(negedge clk_i);
ribp.addr = address;
ribp.wdata = '0;
ribp.wstrb = '0;
ribp.valid = 1'b1;
while (!ribp.ready) begin
@(posedge clk_i);
#1;
end
if (ribp.resp_err !== expected_error) begin
$fatal(1, "I2C read %h error=%b expected=%b", address, ribp.resp_err, expected_error);
end
data = ribp.rdata;
@(negedge clk_i);
ribp.valid = 1'b0;
end
endtask
task automatic wait_start;
begin
@(negedge sda_line);
while (!scl_line) begin
@(negedge sda_line);
end
end
endtask
task automatic receive_byte(output logic [7:0] data, input integer stretch_cycles);
integer bit_index;
begin
for (bit_index = 7; bit_index >= 0; bit_index = bit_index - 1) begin
@(posedge scl_line);
data[bit_index] = sda_line;
end
@(negedge scl_line);
slave_sda_low = 1'b1;
if (stretch_cycles != 0) begin
slave_scl_low = 1'b1;
repeat (stretch_cycles) @(posedge clk_i);
slave_scl_low = 1'b0;
end
@(posedge scl_line);
@(negedge scl_line);
slave_sda_low = 1'b0;
end
endtask
task automatic send_byte(input logic [7:0] data, output logic nack);
integer bit_index;
begin
for (bit_index = 7; bit_index >= 0; bit_index = bit_index - 1) begin
slave_sda_low = !data[bit_index];
@(posedge scl_line);
@(negedge scl_line);
end
slave_sda_low = 1'b0;
@(posedge scl_line);
nack = sda_line;
@(negedge scl_line);
end
endtask
task automatic wait_done;
logic [31:0] value;
logic [31:0] error_value;
logic [31:0] status_value;
integer attempts;
begin
value = '0;
for (attempts = 0; attempts < 4000; attempts = attempts + 1) begin
ribp_read(`RIBP_I2C_INTR_STATE, 1'b0, value);
if (value[`I2C_INTR_DONE]) begin
attempts = 4000;
end
end
if (!value[`I2C_INTR_DONE]) begin
ribp_read(`RIBP_I2C_ERROR_STATUS, 1'b0, error_value);
ribp_read(`RIBP_I2C_STATUS, 1'b0, status_value);
$fatal(1, "I2C transaction did not complete: error=%h status=%h fsm=%0d", error_value,
status_value, u_dut.u_i2c_core.s_fsm_q);
end
ribp_write(`RIBP_I2C_INTR_STATE, 32'hFF, 1'b0);
end
endtask
task automatic clear_state;
begin
ribp_write(`RIBP_I2C_ERROR_STATUS, 32'h7FF, 1'b0);
ribp_write(`RIBP_I2C_INTR_STATE, 32'hFF, 1'b0);
ribp_write(`RIBP_I2C_COMMAND, 32'hC, 1'b0);
end
endtask
initial begin
logic [31:0] value;
logic [ 7:0] byte_value;
logic nack;
integer pulse_count;
ribp.valid = 1'b0;
ribp.addr = '0;
ribp.wdata = '0;
ribp.wstrb = '0;
repeat (4) @(posedge clk_i);
rst_n_i = 1'b1;
repeat (4) @(posedge clk_i);
ribp_read(`RIBP_I2C_IP_VERSION, 1'b0, value);
if (value != 32'h0002_0000) $fatal(1, "I2C version mismatch");
ribp_read(`RIBP_I2C_CAPABILITY, 1'b0, value);
if (value != 32'h007F_1010) $fatal(1, "I2C capability mismatch: %h", value);
ribp_read(32'h2, 1'b1, value);
ribp_write(`RIBP_I2C_SCL_TIMING, 32'h0008_0008, 1'b0);
ribp_write(`RIBP_I2C_START_TIMING, 32'h0002_0002, 1'b0);
ribp_write(`RIBP_I2C_DATA_TIMING, 32'h0001_0000, 1'b0);
ribp_write(`RIBP_I2C_STOP_TIMING, 32'h0002_0002, 1'b0);
ribp_write(`RIBP_I2C_FILTER, 0, 1'b0);
ribp_write(`RIBP_I2C_STRETCH_TIMEOUT, 200, 1'b0);
ribp_write(`RIBP_I2C_BUS_IDLE_TIMEOUT, 200, 1'b0);
ribp_write(`RIBP_I2C_COMMAND_TIMEOUT, 2000, 1'b0);
ribp_write(`RIBP_I2C_TARGET_ADDR, 7'h50, 1'b0);
ribp_write(`RIBP_I2C_CTRL, 1, 1'b0);
fork
begin
wait_start();
receive_byte(byte_value, 0);
if (byte_value != 8'hA0) $fatal(1, "write address mismatch: %h", byte_value);
receive_byte(byte_value, 0);
if (byte_value != 8'hA5) $fatal(1, "write data mismatch: %h", byte_value);
end
begin
ribp_write(`RIBP_I2C_DATA_CMD, 32'h4A5, 1'b0);
wait_done();
end
join
clear_state();
ribp_write(`RIBP_I2C_TARGET_ADDR, 32'h0000_06AA, 1'b0);
fork
begin
wait_start();
receive_byte(byte_value, 0);
if (byte_value != 8'hF4) $fatal(1, "10-bit write header mismatch: %h", byte_value);
receive_byte(byte_value, 0);
if (byte_value != 8'hAA) $fatal(1, "10-bit low address mismatch: %h", byte_value);
wait_start();
receive_byte(byte_value, 0);
if (byte_value != 8'hF5) $fatal(1, "10-bit read header mismatch: %h", byte_value);
send_byte(8'hC3, nack);
if (!nack) $fatal(1, "final 10-bit read byte was not NACKed");
end
begin
ribp_write(`RIBP_I2C_DATA_CMD, 32'hD00, 1'b0);
wait_done();
ribp_read(`RIBP_I2C_RXDATA, 1'b0, value);
if (value[7:0] != 8'hC3) $fatal(1, "10-bit read data mismatch: %h", value);
end
join
clear_state();
ribp_write(`RIBP_I2C_TARGET_ADDR, 7'h50, 1'b0);
fork
begin
wait_start();
receive_byte(byte_value, 0);
if (byte_value != 8'hA0) $fatal(1, "combined write address mismatch");
receive_byte(byte_value, 0);
if (byte_value != 8'h12) $fatal(1, "combined register byte mismatch");
wait_start();
receive_byte(byte_value, 0);
if (byte_value != 8'hA1) $fatal(1, "combined read address mismatch");
send_byte(8'h5A, nack);
if (!nack) $fatal(1, "final read byte was not NACKed");
end
begin
ribp_write(`RIBP_I2C_DATA_CMD, 32'h012, 1'b0);
ribp_write(`RIBP_I2C_DATA_CMD, 32'hF00, 1'b0);
wait_done();
ribp_read(`RIBP_I2C_RXDATA, 1'b0, value);
if (value[7:0] != 8'h5A) $fatal(1, "read data mismatch: %h", value);
end
join
clear_state();
fork
begin
wait_start();
receive_byte(byte_value, 12);
receive_byte(byte_value, 0);
end
begin
ribp_write(`RIBP_I2C_DATA_CMD, 32'h45C, 1'b0);
wait_done();
end
join
clear_state();
ribp_write(`RIBP_I2C_TARGET_ADDR, 7'h51, 1'b0);
ribp_write(`RIBP_I2C_DATA_CMD, 32'h455, 1'b0);
repeat (300) @(posedge clk_i);
ribp_read(`RIBP_I2C_ERROR_STATUS, 1'b0, value);
if (!value[`I2C_ERROR_ADDR_NACK]) $fatal(1, "address NACK was not reported");
clear_state();
ribp_write(`RIBP_I2C_TARGET_ADDR, 7'h7F, 1'b0);
fork
begin
wait_start();
@(negedge scl_line);
#1 slave_sda_low = 1'b1;
@(posedge scl_line);
repeat (4) @(posedge clk_i);
slave_sda_low = 1'b0;
end
begin
ribp_write(`RIBP_I2C_DATA_CMD, 32'h400, 1'b0);
end
join
repeat (20) @(posedge clk_i);
ribp_read(`RIBP_I2C_ERROR_STATUS, 1'b0, value);
if (!value[`I2C_ERROR_ARB_LOST]) $fatal(1, "arbitration loss was not reported");
clear_state();
slave_sda_low = 1'b1;
fork
begin
pulse_count = 0;
while (pulse_count < 9) begin
@(posedge scl_line);
pulse_count = pulse_count + 1;
end
slave_sda_low = 1'b0;
end
begin
ribp_write(`RIBP_I2C_COMMAND, 32'h2, 1'b0);
end
join
repeat (80) @(posedge clk_i);
ribp_read(`RIBP_I2C_INTR_STATE, 1'b0, value);
if (!value[`I2C_INTR_RECOVERY_DONE] || (pulse_count != 9)) begin
$fatal(1, "bus recovery failed: intr=%h pulses=%0d", value, pulse_count);
end
if (i2c.scl_o !== 1'b0 || i2c.sda_o !== 1'b0) begin
$fatal(1, "I2C pads are not open-drain constants");
end
$display("I2C V2 transfer, error, stretch, arbitration, and recovery test passed");
$finish;
end
initial begin
repeat (50000) @(posedge clk_i);
$fatal(1, "I2C test timeout");
end
endmodule
+5 -1
View File
@@ -127,7 +127,7 @@ module ws2812_dma_tb;
dma_core u_dma_core (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.mode_i (3'd0),
.mode_i (4'd0),
.srcaddr_i (SOURCE_BASE + 32'd4),
.srcincr_i (1'b1),
.dstaddr_i (WS2812_TXDATA),
@@ -219,6 +219,10 @@ module ws2812_dma_tb;
hw_trg.qspi_rx_proc = 1'b0;
hw_trg.uart_tx_proc = 1'b0;
hw_trg.uart_rx_proc = 1'b0;
hw_trg.i2c0_tx_proc = 1'b0;
hw_trg.i2c0_rx_proc = 1'b0;
hw_trg.i2c1_tx_proc = 1'b0;
hw_trg.i2c1_rx_proc = 1'b0;
repeat (3) @(posedge clk_i);
rst_n_i = 1'b1;
+66
View File
@@ -0,0 +1,66 @@
"""Directed I2C V2 controller, FIFO, error, timeout, and recovery tests."""
from __future__ import annotations
import shutil
import subprocess
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
def test_i2c_v2_controller_and_recovery_contract(tmp_path: Path) -> None:
iverilog = shutil.which("iverilog")
vvp = shutil.which("vvp")
if iverilog is None or vvp is None:
return
common = ROOT / "rtl/managed/clusterip/common/rtl"
serial = ROOT / "rtl/ip/ribp/serial"
cluster_i2c = ROOT / "rtl/managed/clusterip/i2c/rtl"
source_list = tmp_path / "i2c.fl"
source_list.write_text(
"\n".join(
[
"+define+SV_ASSRT_DISABLE",
f"+incdir+{common}",
f"+incdir+{common / 'interface'}",
f"+incdir+{common / 'utils'}",
f"+incdir+{common / 'cdc'}",
f"+incdir+{serial}",
str(common / "interface/ribp_if.sv"),
str(common / "utils/register.sv"),
str(common / "utils/fifo.sv"),
str(common / "cdc/cdc_sync.sv"),
str(cluster_i2c / "i2c_if.sv"),
str(serial / "i2c_filter.sv"),
str(serial / "i2c_core.sv"),
str(serial / "i2c_reg.sv"),
str(serial / "ribp_i2c.sv"),
str(ROOT / "tests/rtl/i2c_tb.sv"),
"",
]
),
encoding="utf-8",
)
converted = tmp_path / "i2c_tb.v"
subprocess.run(
[
sys.executable,
str(ROOT / "rtl/mini/script/convt_sv2v.py"),
"-f",
str(source_list),
"--output",
str(converted),
],
check=True,
)
simulation = tmp_path / "i2c_tb"
subprocess.run(
[iverilog, "-g2012", "-s", "i2c_tb", "-o", str(simulation), str(converted)],
check=True,
)
result = subprocess.run([vvp, str(simulation)], text=True, capture_output=True, check=True)
assert "I2C V2 transfer, error, stretch, arbitration, and recovery test passed" in result.stdout
+11
View File
@@ -205,6 +205,7 @@ def test_formal_filelists_are_scoped_to_the_protocol_duts(tmp_path: Path) -> Non
pll_rcu_filelist = tmp_path / "pll_rcu.fl"
gpio_filelist = tmp_path / "gpio.fl"
ws2812_filelist = tmp_path / "ws2812.fl"
i2c_filelist = tmp_path / "i2c.fl"
clint_filelist = tmp_path / "clint.fl"
assert generate_formal_filelist("bus", bus_filelist, memory_map, topology, user_extensions)
assert generate_formal_filelist(
@@ -225,6 +226,7 @@ def test_formal_filelists_are_scoped_to_the_protocol_duts(tmp_path: Path) -> Non
assert generate_formal_filelist(
"ws2812", ws2812_filelist, memory_map, topology, user_extensions
)
assert generate_formal_filelist("i2c", i2c_filelist, memory_map, topology, user_extensions)
assert generate_formal_filelist(
"clint", clint_filelist, memory_map, topology, user_extensions
)
@@ -236,6 +238,7 @@ def test_formal_filelists_are_scoped_to_the_protocol_duts(tmp_path: Path) -> Non
pll_rcu = parse_filelists([pll_rcu_filelist], require_files=False)
gpio = parse_filelists([gpio_filelist], require_files=False)
ws2812 = parse_filelists([ws2812_filelist], require_files=False)
i2c = parse_filelists([i2c_filelist], require_files=False)
clint = parse_filelists([clint_filelist], require_files=False)
assert "+define+SV_ASSRT_DISABLE" in bus.defines
assert ROOT / "rtl/mini/top/bus.sv" in bus.files
@@ -262,6 +265,9 @@ def test_formal_filelists_are_scoped_to_the_protocol_duts(tmp_path: Path) -> Non
assert ROOT / "rtl/ip/ribp/serial/ribp_ws2812.sv" in ws2812.files
assert ROOT / "rtl/managed/clusterip/common/rtl/utils/fifo.sv" in ws2812.files
assert ROOT / "rtl/mini/formal/ws2812_formal.sv" in ws2812.files
assert ROOT / "rtl/ip/ribp/serial/ribp_i2c.sv" in i2c.files
assert ROOT / "rtl/ip/ribp/serial/i2c_filter.sv" in i2c.files
assert ROOT / "rtl/mini/formal/i2c_formal.sv" in i2c.files
assert ROOT / "rtl/ip/ribp/peripheral/ribp_clint.sv" in clint.files
assert ROOT / "rtl/mini/formal/clint_formal.sv" in clint.files
@@ -284,6 +290,9 @@ def test_sby_config_uses_prove_and_cover_with_bitwuzla(tmp_path: Path) -> None:
properties = tmp_path / "properties.v"
prove_config = render_sby_config("bus_formal", design, properties, "bitwuzla", "prove", 20)
cover_config = render_sby_config("bus_formal", design, properties, "bitwuzla", "cover", 20)
compact_cover_config = render_sby_config(
"i2c_formal", design, properties, "bitwuzla", "cover", 80, vcd=False
)
assert "mode prove" in prove_config
assert "mode cover" in cover_config
@@ -291,6 +300,8 @@ def test_sby_config_uses_prove_and_cover_with_bitwuzla(tmp_path: Path) -> None:
assert "smtbmc --presat --nounroll bitwuzla" in prove_config
assert f"design.v {design.resolve()}" in prove_config
assert f"properties.v {properties.resolve()}" in prove_config
assert "vcd off" not in cover_config
assert "vcd off" in compact_cover_config
def test_bitwuzla_wrapper_translates_yosys_legacy_arguments() -> None:
+6 -4
View File
@@ -97,12 +97,13 @@ def test_topology_generates_complete_rib_apb_and_gpio_bindings(tmp_path: Path) -
assert ".apb_rib(u_apb_rib_if)" in bus_fabric
assert ".rib(u_rib_if)" in bus_fabric
assert "`define SOC_IRQ_VECTOR_WIDTH 32" in irq_config
assert "`define SOC_USER_IRQ_MASK 32'h0007FFFC" in irq_config
assert "`define SOC_IRQ_RIBP_WIDTH 12" in irq_config
assert "`define SOC_USER_IRQ_MASK 32'h000FFFFC" in irq_config
assert "`define SOC_IRQ_RIBP_WIDTH 13" in irq_config
assert "`define SOC_IRQ_APB_WIDTH 7" in irq_config
assert "assign irq_o[0] = u_clint_if.software_irq_o[0];" in rib_irq
assert "assign irq_o[10] = ws2812.irq_o;" in rib_irq
assert "assign irq_o[11] = gpio.irq_o;" in rib_irq
assert "assign irq_o[12] = i2c1.irq_o;" in rib_irq
assert "assign irq_o[5] = u_tmr_if.irq_o;" in apb_irq
assert "s_irq[16] = s_apb_irq[6];" in irq_wiring
assert "irq_i[31] == 1'b0" in irq_sva
@@ -123,7 +124,7 @@ def test_topology_preserves_default_irq_compatibility_mapping() -> None:
)
for interrupt in document["interrupts"]
]
assert mappings[:19] == [
assert mappings[:20] == [
("clint_software", "ribp", 0, 0, "u_clint_if.software_irq_o[0]"),
("clint_timer", "ribp", 1, 1, "u_clint_if.timer_irq_o[0]"),
("uart0", "ribp", 2, 2, "uart.irq_o"),
@@ -143,8 +144,9 @@ def test_topology_preserves_default_irq_compatibility_mapping() -> None:
("reserved", "apb", 6, 16, "1'b0"),
("ws2812", "ribp", 10, 17, "ws2812.irq_o"),
("gpio", "ribp", 11, 18, "gpio.irq_o"),
("i2c1", "ribp", 12, 19, "i2c1.irq_o"),
]
assert all(mapping[3] >= 19 for mapping in mappings[19:])
assert all(mapping[3] >= 20 for mapping in mappings[20:])
def test_topology_always_adds_the_user_apb_target(tmp_path: Path) -> None: