feat: add WS2812 support and related tests

- Replace onewire interface with ws2812 interface in retrosoc module.
- Include ws2812_math.c in C test sources.
- Add ws2812 header in test_runtime.c and implement ws2812 helper tests.
- Update ribp_topology_tb and soc_irq_topology_tb to handle ws2812 interface.
- Create ws2812_dma_tb and ws2812_tb for testing WS2812 DMA and waveform functionality.
- Enhance test_script_tools.py to include ws2812 in formal filelists.
- Modify test_soc_topology.py to ensure proper IRQ mapping for ws2812.
- Introduce test_ws2812.py for WS2812 register, waveform, and streaming regression tests.
This commit is contained in:
maksyuki committed 2026-08-07 18:18:51 +08:00
1 parent 17d486d2ef
commit 1eb342245c
46 files changed
+2635 -616

No files matched your search

+2 -2
View File
@@ -233,7 +233,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-clean formal-doctor
formal formal-bus formal-rib-adapter formal-rib2apb formal-ws2812 formal-clean formal-doctor
.NOTPARALLEL: setup
help:
@@ -261,7 +261,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-user run SBY peripheral-control proofs' \
' formal-sysctrl | formal-pll-rcu | formal-gpio-user | formal-ws2812 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
@@ -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, timers, PWM, I2C, I2S, PS2,
1-Wire, SPI/QSPI, SDIO, PSRAM/OPI-PSRAM, SDRAM, DMA, LCD, RTC, watchdog, RNG, and CRC
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`,
`coremark`, `debug`, and `shell` applications.
+2 -2
View File
@@ -9,7 +9,7 @@
#include <retrosoc/hal/i2c.h>
#include <retrosoc/hal/i2s.h>
#include <retrosoc/hal/lcd.h>
#include <retrosoc/hal/onewire.h>
#include <retrosoc/hal/ws2812.h>
#include <retrosoc/hal/ps2.h>
#include <retrosoc/hal/pwm.h>
#include <retrosoc/hal/qspi.h>
@@ -56,7 +56,7 @@ int main(void) {
rs_shell_init();
(void)rs_shell_register("app", "app info", true, rs_app_info_command);
(void)rs_shell_register("arch", "archinfo test", true, ip_archinfo_test);
(void)rs_shell_register("1wire", "1wire test", true, ip_1wire_test);
(void)rs_shell_register("ws2812", "ws2812 test", true, ip_ws2812_test);
(void)rs_shell_register("tim", "timer test", true, ip_tim_test);
(void)rs_shell_register("gpio", "gpio test", true, ip_gpio_test);
(void)rs_shell_register("pwm", "pwm test", true, ip_pwm_test);
+17 -8
View File
@@ -88,14 +88,23 @@
#define reg_i2s_txdata RS_SOC_REG32(RS_SOC_RIBP_I2S_BASE, UINT32_C(0x14))
#define reg_i2s_rxdata RS_SOC_REG32(RS_SOC_RIBP_I2S_BASE, UINT32_C(0x18))
#define reg_i2s_status RS_SOC_REG32(RS_SOC_RIBP_I2S_BASE, UINT32_C(0x1C))
// onewire
#define reg_onewire_clkdiv RS_SOC_REG32(RS_SOC_RIBP_ONEWIRE_BASE, UINT32_C(0x00))
#define reg_onewire_zerocnt RS_SOC_REG32(RS_SOC_RIBP_ONEWIRE_BASE, UINT32_C(0x04))
#define reg_onewire_onecnt RS_SOC_REG32(RS_SOC_RIBP_ONEWIRE_BASE, UINT32_C(0x08))
#define reg_onewire_rstcnt RS_SOC_REG32(RS_SOC_RIBP_ONEWIRE_BASE, UINT32_C(0x0C))
#define reg_onewire_txdata RS_SOC_REG32(RS_SOC_RIBP_ONEWIRE_BASE, UINT32_C(0x10))
#define reg_onewire_ctrl RS_SOC_REG32(RS_SOC_RIBP_ONEWIRE_BASE, UINT32_C(0x14))
#define reg_onewire_status RS_SOC_REG32(RS_SOC_RIBP_ONEWIRE_BASE, UINT32_C(0x18))
// ws2812
#define reg_ws2812_bit_cycles RS_SOC_REG32(RS_SOC_RIBP_WS2812_BASE, UINT32_C(0x00))
#define reg_ws2812_t0h_cycles RS_SOC_REG32(RS_SOC_RIBP_WS2812_BASE, UINT32_C(0x04))
#define reg_ws2812_t1h_cycles RS_SOC_REG32(RS_SOC_RIBP_WS2812_BASE, UINT32_C(0x08))
#define reg_ws2812_reset_cycles RS_SOC_REG32(RS_SOC_RIBP_WS2812_BASE, UINT32_C(0x0C))
#define reg_ws2812_txdata RS_SOC_REG32(RS_SOC_RIBP_WS2812_BASE, UINT32_C(0x10))
#define reg_ws2812_ctrl RS_SOC_REG32(RS_SOC_RIBP_WS2812_BASE, UINT32_C(0x14))
#define reg_ws2812_status RS_SOC_REG32(RS_SOC_RIBP_WS2812_BASE, UINT32_C(0x18))
#define reg_ws2812_frame_words RS_SOC_REG32(RS_SOC_RIBP_WS2812_BASE, UINT32_C(0x1C))
#define reg_ws2812_fifo_level RS_SOC_REG32(RS_SOC_RIBP_WS2812_BASE, UINT32_C(0x20))
#define reg_ws2812_fifo_watermark RS_SOC_REG32(RS_SOC_RIBP_WS2812_BASE, UINT32_C(0x24))
#define reg_ws2812_remaining_words RS_SOC_REG32(RS_SOC_RIBP_WS2812_BASE, UINT32_C(0x28))
#define reg_ws2812_error_status RS_SOC_REG32(RS_SOC_RIBP_WS2812_BASE, UINT32_C(0x2C))
#define reg_ws2812_intr_state RS_SOC_REG32(RS_SOC_RIBP_WS2812_BASE, UINT32_C(0x30))
#define reg_ws2812_intr_enable RS_SOC_REG32(RS_SOC_RIBP_WS2812_BASE, UINT32_C(0x34))
#define reg_ws2812_intr_test RS_SOC_REG32(RS_SOC_RIBP_WS2812_BASE, UINT32_C(0x38))
#define reg_ws2812_ip_info RS_SOC_REG32(RS_SOC_RIBP_WS2812_BASE, UINT32_C(0x3C))
// xpi
#define reg_xpi_cfgidx RS_SOC_REG32(RS_SOC_RIBP_XPI_BASE, UINT32_C(0x00))
#define reg_xpi_accmd RS_SOC_REG32(RS_SOC_RIBP_XPI_BASE, UINT32_C(0x04))
-5
View File
@@ -1,5 +0,0 @@
#ifndef RETROSOC_1WIRE_H
#define RETROSOC_1WIRE_H
void ip_1wire_test(int argc, char **argv);
#endif
+67
View File
@@ -0,0 +1,67 @@
#ifndef RETROSOC_HAL_WS2812_H
#define RETROSOC_HAL_WS2812_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <retrosoc/core/status.h>
#define RS_WS2812_DEFAULT_BIT_PERIOD_NS UINT32_C(1250)
#define RS_WS2812_DEFAULT_T0H_NS UINT32_C(350)
#define RS_WS2812_DEFAULT_T1H_NS UINT32_C(700)
#define RS_WS2812_DEFAULT_RESET_NS UINT32_C(300000)
#define RS_WS2812_ERROR_CONFIG UINT32_C(0x01)
#define RS_WS2812_ERROR_UNDERFLOW UINT32_C(0x02)
#define RS_WS2812_ERROR_COMMAND UINT32_C(0x04)
#define RS_WS2812_INTR_DONE UINT32_C(0x01)
#define RS_WS2812_INTR_FIFO_LOW UINT32_C(0x02)
#define RS_WS2812_INTR_ERROR UINT32_C(0x04)
#define RS_WS2812_INTR_ABORTED UINT32_C(0x08)
#define RS_WS2812_INTR_ALL UINT32_C(0x0F)
typedef struct {
uint32_t source_clock_hz;
uint32_t bit_period_ns;
uint32_t t0h_ns;
uint32_t t1h_ns;
uint32_t reset_ns;
uint32_t fifo_watermark;
} rs_ws2812_config_t;
typedef struct {
uint16_t bit_cycles;
uint16_t t0h_cycles;
uint16_t t1h_cycles;
uint32_t reset_cycles;
} rs_ws2812_timing_t;
typedef struct {
uint32_t fifo_level;
uint32_t remaining_words;
uint32_t error_status;
uint32_t interrupt_state;
bool busy;
bool fifo_empty;
bool fifo_full;
bool config_valid;
bool reset_active;
} rs_ws2812_status_t;
uint32_t rs_ws2812_pack_grb(uint8_t red, uint8_t green, uint8_t blue);
rs_status_t rs_ws2812_timing_from_ns(const rs_ws2812_config_t *config, rs_ws2812_timing_t *timing);
rs_status_t rs_ws2812_init(const rs_ws2812_config_t *config);
rs_status_t rs_ws2812_get_status(rs_ws2812_status_t *status);
rs_status_t rs_ws2812_push(uint32_t pixel, rs_timeout_t timeout);
rs_status_t rs_ws2812_start(uint32_t pixel_count);
rs_status_t rs_ws2812_wait(rs_timeout_t timeout);
rs_status_t rs_ws2812_abort(rs_timeout_t timeout);
rs_status_t rs_ws2812_irq_enable(uint32_t mask);
rs_status_t rs_ws2812_irq_ack(uint32_t mask);
rs_status_t rs_ws2812_write(const uint32_t *pixels, size_t pixel_count, rs_timeout_t timeout);
rs_status_t rs_ws2812_write_dma(const uint32_t *pixels, size_t pixel_count, rs_timeout_t timeout);
void ip_ws2812_test(int argc, char **argv);
#endif
-42
View File
@@ -1,42 +0,0 @@
#include <retrosoc/core/soc.h>
#include <retrosoc/core/wait.h>
#include <retrosoc/lib/printf.h>
#include <retrosoc/hal/onewire.h>
#include <retrosoc/hal/timer.h>
void ip_1wire_test(int argc, char **argv) {
(void)argc;
(void)argv;
printf("1-wire test\n");
reg_onewire_clkdiv = (uint8_t)(CPU_FREQ / 0.8); // 72M 90
reg_onewire_zerocnt = (uint8_t)(CPU_FREQ / 3); // 72M 24
reg_onewire_onecnt = (uint8_t)(CPU_FREQ / 1.6); // 72M 45
reg_onewire_rstcnt = (uint8_t)(CPU_FREQ / 12); // 72M 6
for (uint32_t num = 0U; num < 6U; ++num) {
for (uint32_t i = 0U; i < 256U; ++i) {
// clear fifo
reg_onewire_ctrl = (uint8_t)0b01;
// write data(GRB888 format)
reg_onewire_txdata = (uint32_t)i;
reg_onewire_txdata = (uint32_t)(i << 8);
reg_onewire_txdata = (uint32_t)(i << 16);
reg_onewire_txdata = (uint32_t)((i << 8) | (i << 16));
reg_onewire_ctrl = (uint8_t)0b10;
if (rs_wait_value(&reg_onewire_status, 5U, RS_TIMEOUT_DEFAULT) != RS_OK) {
printf("1-wire transfer timed out\n");
return;
}
delay_ms(5);
}
}
reg_onewire_ctrl = (uint8_t)0b01;
reg_onewire_txdata = (uint32_t)0;
reg_onewire_txdata = (uint32_t)0;
reg_onewire_txdata = (uint32_t)0;
reg_onewire_txdata = (uint32_t)0;
reg_onewire_ctrl = (uint8_t)0b10;
printf("1-wire test done\n");
}
+270
View File
@@ -0,0 +1,270 @@
#include <stddef.h>
#include <stdint.h>
#include <retrosoc/core/soc.h>
#include <retrosoc/hal/dma.h>
#include <retrosoc/hal/ws2812.h>
#include <retrosoc/lib/printf.h>
#define RS_WS2812_CTRL_START UINT32_C(0x01)
#define RS_WS2812_CTRL_ABORT UINT32_C(0x02)
#define RS_WS2812_CTRL_FIFO_FLUSH UINT32_C(0x04)
#define RS_WS2812_STATUS_BUSY UINT32_C(0x01)
#define RS_WS2812_STATUS_FIFO_EMPTY UINT32_C(0x02)
#define RS_WS2812_STATUS_FIFO_FULL UINT32_C(0x04)
#define RS_WS2812_STATUS_CONFIG_VALID UINT32_C(0x08)
#define RS_WS2812_STATUS_RESET_ACTIVE UINT32_C(0x10)
#define RS_WS2812_IP_FIFO_DEPTH_SHIFT 16U
#define RS_WS2812_IP_FIFO_DEPTH_MASK UINT32_C(0xFF)
#define RS_WS2812_PIXEL_MASK UINT32_C(0x00FFFFFF)
#define RS_WS2812_DMA_SOFTWARE_MODE UINT32_C(0)
#define RS_WS2812_GPIO_MASK (UINT32_C(1) << 2U)
static uint32_t rs_ws2812_fifo_depth(void) {
return (reg_ws2812_ip_info >> RS_WS2812_IP_FIFO_DEPTH_SHIFT) & RS_WS2812_IP_FIFO_DEPTH_MASK;
}
static rs_status_t rs_ws2812_prepare(const uint32_t *pixels, size_t pixel_count,
size_t *preloaded) {
uint32_t fifo_depth;
size_t preload_count;
if ((pixels == NULL) || (pixel_count == 0U) || (pixel_count > UINT32_MAX) ||
(preloaded == NULL)) {
return RS_EINVAL;
}
for (size_t index = 0U; index < pixel_count; ++index) {
if ((pixels[index] & ~RS_WS2812_PIXEL_MASK) != 0U) {
return RS_EINVAL;
}
}
if ((reg_ws2812_status & RS_WS2812_STATUS_BUSY) != 0U) {
return RS_EIO;
}
fifo_depth = rs_ws2812_fifo_depth();
if (fifo_depth == 0U) {
return RS_EIO;
}
reg_ws2812_ctrl = RS_WS2812_CTRL_FIFO_FLUSH;
reg_ws2812_error_status =
RS_WS2812_ERROR_CONFIG | RS_WS2812_ERROR_UNDERFLOW | RS_WS2812_ERROR_COMMAND;
reg_ws2812_intr_state = RS_WS2812_INTR_ALL;
preload_count = pixel_count < (size_t)fifo_depth ? pixel_count : (size_t)fifo_depth;
for (size_t index = 0U; index < preload_count; ++index) {
reg_ws2812_txdata = pixels[index];
}
*preloaded = preload_count;
return RS_OK;
}
rs_status_t rs_ws2812_init(const rs_ws2812_config_t *config) {
rs_ws2812_timing_t timing;
uint32_t fifo_depth;
if (rs_ws2812_timing_from_ns(config, &timing) != RS_OK) {
return RS_EINVAL;
}
fifo_depth = rs_ws2812_fifo_depth();
if ((fifo_depth == 0U) || (config->fifo_watermark >= fifo_depth)) {
return RS_EINVAL;
}
if ((reg_ws2812_status & RS_WS2812_STATUS_BUSY) != 0U) {
return RS_EIO;
}
reg_ws2812_ctrl = RS_WS2812_CTRL_FIFO_FLUSH;
reg_ws2812_error_status =
RS_WS2812_ERROR_CONFIG | RS_WS2812_ERROR_UNDERFLOW | RS_WS2812_ERROR_COMMAND;
reg_ws2812_intr_state = RS_WS2812_INTR_ALL;
reg_ws2812_bit_cycles = timing.bit_cycles;
reg_ws2812_t0h_cycles = timing.t0h_cycles;
reg_ws2812_t1h_cycles = timing.t1h_cycles;
reg_ws2812_reset_cycles = timing.reset_cycles;
reg_ws2812_fifo_watermark = config->fifo_watermark;
return ((reg_ws2812_status & RS_WS2812_STATUS_CONFIG_VALID) != 0U) ? RS_OK : RS_EIO;
}
rs_status_t rs_ws2812_get_status(rs_ws2812_status_t *status) {
uint32_t value;
if (status == NULL) {
return RS_EINVAL;
}
value = reg_ws2812_status;
status->fifo_level = reg_ws2812_fifo_level;
status->remaining_words = reg_ws2812_remaining_words;
status->error_status = reg_ws2812_error_status;
status->interrupt_state = reg_ws2812_intr_state;
status->busy = (value & RS_WS2812_STATUS_BUSY) != 0U;
status->fifo_empty = (value & RS_WS2812_STATUS_FIFO_EMPTY) != 0U;
status->fifo_full = (value & RS_WS2812_STATUS_FIFO_FULL) != 0U;
status->config_valid = (value & RS_WS2812_STATUS_CONFIG_VALID) != 0U;
status->reset_active = (value & RS_WS2812_STATUS_RESET_ACTIVE) != 0U;
return RS_OK;
}
rs_status_t rs_ws2812_push(uint32_t pixel, rs_timeout_t timeout) {
uint32_t status;
if ((pixel & ~RS_WS2812_PIXEL_MASK) != 0U) {
return RS_EINVAL;
}
while (timeout-- != 0U) {
status = reg_ws2812_status;
if ((reg_ws2812_error_status & RS_WS2812_ERROR_UNDERFLOW) != 0U) {
return RS_EIO;
}
if ((status & RS_WS2812_STATUS_FIFO_FULL) == 0U) {
reg_ws2812_txdata = pixel;
return RS_OK;
}
if ((status & RS_WS2812_STATUS_BUSY) == 0U) {
return RS_ENOSPC;
}
}
return RS_ETIMEOUT;
}
rs_status_t rs_ws2812_start(uint32_t pixel_count) {
if ((pixel_count == 0U) || ((reg_ws2812_status & RS_WS2812_STATUS_BUSY) != 0U)) {
return RS_EINVAL;
}
reg_ws2812_frame_words = pixel_count;
reg_ws2812_ctrl = RS_WS2812_CTRL_START;
if ((reg_ws2812_error_status & (RS_WS2812_ERROR_CONFIG | RS_WS2812_ERROR_COMMAND)) != 0U) {
return RS_EIO;
}
return ((reg_ws2812_status & RS_WS2812_STATUS_BUSY) != 0U) ? RS_OK : RS_EIO;
}
rs_status_t rs_ws2812_wait(rs_timeout_t timeout) {
while (timeout-- != 0U) {
const uint32_t interrupt_state = reg_ws2812_intr_state;
if ((interrupt_state & RS_WS2812_INTR_ERROR) != 0U) {
return RS_EIO;
}
if ((interrupt_state & RS_WS2812_INTR_ABORTED) != 0U) {
reg_ws2812_intr_state = RS_WS2812_INTR_ABORTED;
return RS_EIO;
}
if ((interrupt_state & RS_WS2812_INTR_DONE) != 0U) {
reg_ws2812_intr_state = RS_WS2812_INTR_DONE;
return RS_OK;
}
}
return RS_ETIMEOUT;
}
rs_status_t rs_ws2812_abort(rs_timeout_t timeout) {
if ((reg_ws2812_status & RS_WS2812_STATUS_BUSY) == 0U) {
return RS_OK;
}
reg_ws2812_ctrl = RS_WS2812_CTRL_ABORT;
while (timeout-- != 0U) {
if ((reg_ws2812_status & RS_WS2812_STATUS_BUSY) == 0U) {
reg_ws2812_intr_state = RS_WS2812_INTR_ABORTED;
return RS_OK;
}
}
return RS_ETIMEOUT;
}
rs_status_t rs_ws2812_irq_enable(uint32_t mask) {
if ((mask & ~RS_WS2812_INTR_ALL) != 0U) {
return RS_EINVAL;
}
reg_ws2812_intr_enable = mask;
return RS_OK;
}
rs_status_t rs_ws2812_irq_ack(uint32_t mask) {
if ((mask & ~RS_WS2812_INTR_ALL) != 0U) {
return RS_EINVAL;
}
reg_ws2812_intr_state = mask;
return RS_OK;
}
rs_status_t rs_ws2812_write(const uint32_t *pixels, size_t pixel_count, rs_timeout_t timeout) {
size_t preloaded;
rs_status_t status;
status = rs_ws2812_prepare(pixels, pixel_count, &preloaded);
if (status != RS_OK) {
return status;
}
status = rs_ws2812_start((uint32_t)pixel_count);
if (status != RS_OK) {
return status;
}
for (size_t index = preloaded; index < pixel_count; ++index) {
status = rs_ws2812_push(pixels[index], timeout);
if (status != RS_OK) {
(void)rs_ws2812_abort(timeout);
return status;
}
}
return rs_ws2812_wait(timeout);
}
rs_status_t rs_ws2812_write_dma(const uint32_t *pixels, size_t pixel_count, rs_timeout_t timeout) {
size_t preloaded;
size_t dma_words;
rs_status_t status;
status = rs_ws2812_prepare(pixels, pixel_count, &preloaded);
if (status != RS_OK) {
return status;
}
dma_words = pixel_count - preloaded;
if (dma_words != 0U) {
status = rs_dma_config(RS_WS2812_DMA_SOFTWARE_MODE, (uintptr_t)&pixels[preloaded], 1U,
(uintptr_t)&reg_ws2812_txdata, 0U, (uint32_t)dma_words);
if (status != RS_OK) {
return status;
}
}
status = rs_ws2812_start((uint32_t)pixel_count);
if (status != RS_OK) {
return status;
}
if (dma_words != 0U) {
status = rs_dma_start();
if (status == RS_OK) {
status = rs_dma_wait(timeout);
}
if (status != RS_OK) {
(void)rs_ws2812_abort(timeout);
return status;
}
}
return rs_ws2812_wait(timeout);
}
void ip_ws2812_test(int argc, char **argv) {
const rs_ws2812_config_t config = {
CPU_FREQ * UINT32_C(1000000), RS_WS2812_DEFAULT_BIT_PERIOD_NS, RS_WS2812_DEFAULT_T0H_NS,
RS_WS2812_DEFAULT_T1H_NS, RS_WS2812_DEFAULT_RESET_NS, 4U,
};
const uint32_t pixels[] = {
rs_ws2812_pack_grb(0x20U, 0x00U, 0x00U),
rs_ws2812_pack_grb(0x00U, 0x20U, 0x00U),
rs_ws2812_pack_grb(0x00U, 0x00U, 0x20U),
rs_ws2812_pack_grb(0x10U, 0x10U, 0x10U),
};
(void)argc;
(void)argv;
printf("ws2812 test\n");
reg_gpio_user_sel &= ~RS_WS2812_GPIO_MASK;
reg_gpio_pinmux |= RS_WS2812_GPIO_MASK;
reg_gpio_iofcfg |= RS_WS2812_GPIO_MASK;
if ((rs_ws2812_init(&config) != RS_OK) ||
(rs_ws2812_write(pixels, sizeof(pixels) / sizeof(pixels[0]), RS_TIMEOUT_DEFAULT) !=
RS_OK)) {
printf("ws2812 transfer failed\n");
return;
}
printf("ws2812 test passed\n");
}
+76
View File
@@ -0,0 +1,76 @@
#include <stddef.h>
#include <stdint.h>
#include <retrosoc/hal/ws2812.h>
#define RS_NANOSECONDS_PER_SECOND UINT64_C(1000000000)
static rs_status_t rs_ws2812_cycles_nearest(uint32_t frequency_hz, uint32_t duration_ns,
uint32_t maximum, uint32_t *cycles) {
uint64_t product;
uint64_t rounded;
if ((frequency_hz == 0U) || (duration_ns == 0U) || (cycles == NULL)) {
return RS_EINVAL;
}
product = (uint64_t)frequency_hz * (uint64_t)duration_ns;
if (product > (UINT64_MAX - (RS_NANOSECONDS_PER_SECOND / 2U))) {
return RS_EINVAL;
}
rounded = (product + (RS_NANOSECONDS_PER_SECOND / 2U)) / RS_NANOSECONDS_PER_SECOND;
if ((rounded == 0U) || (rounded > maximum)) {
return RS_EINVAL;
}
*cycles = (uint32_t)rounded;
return RS_OK;
}
static rs_status_t rs_ws2812_cycles_ceil(uint32_t frequency_hz, uint32_t duration_ns,
uint32_t *cycles) {
uint64_t product;
uint64_t rounded;
if ((frequency_hz == 0U) || (duration_ns == 0U) || (cycles == NULL)) {
return RS_EINVAL;
}
product = (uint64_t)frequency_hz * (uint64_t)duration_ns;
if (product > (UINT64_MAX - (RS_NANOSECONDS_PER_SECOND - 1U))) {
return RS_EINVAL;
}
rounded = (product + (RS_NANOSECONDS_PER_SECOND - 1U)) / RS_NANOSECONDS_PER_SECOND;
if ((rounded == 0U) || (rounded > UINT32_MAX)) {
return RS_EINVAL;
}
*cycles = (uint32_t)rounded;
return RS_OK;
}
uint32_t rs_ws2812_pack_grb(uint8_t red, uint8_t green, uint8_t blue) {
return ((uint32_t)green << 16U) | ((uint32_t)red << 8U) | (uint32_t)blue;
}
rs_status_t rs_ws2812_timing_from_ns(const rs_ws2812_config_t *config, rs_ws2812_timing_t *timing) {
uint32_t bit_cycles;
uint32_t t0h_cycles;
uint32_t t1h_cycles;
uint32_t reset_cycles;
if ((config == NULL) || (timing == NULL) ||
(rs_ws2812_cycles_nearest(config->source_clock_hz, config->bit_period_ns, UINT16_MAX,
&bit_cycles) != RS_OK) ||
(rs_ws2812_cycles_nearest(config->source_clock_hz, config->t0h_ns, UINT16_MAX,
&t0h_cycles) != RS_OK) ||
(rs_ws2812_cycles_nearest(config->source_clock_hz, config->t1h_ns, UINT16_MAX,
&t1h_cycles) != RS_OK) ||
(rs_ws2812_cycles_ceil(config->source_clock_hz, config->reset_ns, &reset_cycles) !=
RS_OK) ||
(t0h_cycles >= t1h_cycles) || (t1h_cycles >= bit_cycles)) {
return RS_EINVAL;
}
timing->bit_cycles = (uint16_t)bit_cycles;
timing->t0h_cycles = (uint16_t)t0h_cycles;
timing->t1h_cycles = (uint16_t)t1h_cycles;
timing->reset_cycles = reset_cycles;
return RS_OK;
}
+1 -1
View File
@@ -103,7 +103,7 @@ void rs_app_info(void) {
printf(" 1 x SPISD @%p\n", (void *)&reg_spisd_mode);
printf(" 1 x I2C0 @%p\n", (void *)&reg_i2c0_clkdiv);
printf(" 1 x I2S @%p\n", (void *)&reg_i2s_mode);
printf(" 1 x ONEWIRE @%p\n", (void *)&reg_onewire_clkdiv);
printf(" 1 x WS2812 @%p\n", (void *)&reg_ws2812_bit_cycles);
printf(" 1 x XPI @%p\n", (void *)&reg_xpi_cfgidx);
printf(" 1 x DMA @%p\n", (void *)&reg_dma_mode);
printf(" 1 x SYSCTRL @%p\n", (void *)&reg_sysctrl_coresel);
+2
View File
@@ -23,6 +23,8 @@ the root README and subsystem guides.
and its commercial reference points.
- [rib-interconnect.md](rib-interconnect.md) defines the RIB v1 linear-burst
contract, compatibility boundaries, target status, and verification.
- [ws2812.md](ws2812.md) defines the WS2812 transmitter register ABI,
timing, FIFO, interrupt, DMA, and integration 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
+8 -5
View File
@@ -130,14 +130,17 @@ 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`, and
`gpio_user`; each uses
the SBY `prove` task for bounded model checking and k-induction, plus a
`cover` task, at depth 20. `sysctrl` checks register side effects, sticky terminal-test status, PLL request
targets are `bus`, `rib_adapter`, `rib2apb`, `sysctrl`, `pll_rcu`,
`gpio_user`, and `ws2812`; 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
handling, and fault reporting. `pll_rcu` checks the clock-switch controller
state machine. `gpio_user` checks fixed user-GPIO ownership, lock, handoff,
and mux safety. `rib_adapter` checks both single-word compatibility adapters
under backpressure. Direct Common utility proofs, including `spill_register`,
under backpressure. `ws2812` checks RIBP response stability, FIFO bounds,
waveform idle safety, sticky error and interrupt propagation, abort, and
underflow preconditions. 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
+11 -3
View File
@@ -43,7 +43,7 @@ digraph mini_soc_block_diagram {
];
ext_io [
fillcolor="#F3F4F6",
label="External I/O and memory\nGPIO, UART0, XPI Flash, SDRAM, PSRAM\nSD card, I2C, I2S, DVP and serial I/O"
label="External I/O and memory\nGPIO, UART0, XPI Flash, SDRAM, PSRAM\nSD card, I2C, I2S, DVP and WS2812"
];
ext_jtag [
fillcolor="#F3F4F6",
@@ -124,7 +124,11 @@ digraph mini_soc_block_diagram {
];
ribp_targets [
fillcolor="#F9E1E7",
label="RIBP target wrapper\nXPI/Flash, SDRAM, PSRAM, SPI-SD, DVP\nGPIO, UART0, timers, I2C, I2S and 1-Wire"
label="RIBP target wrapper\nXPI/Flash, SDRAM, PSRAM, SPI-SD and DVP\nGPIO, UART0, timers, I2C and I2S"
];
ws2812 [
fillcolor="#F9E1E7",
label="WS2812 TX\n24-bit MSB-first serializer\n16-word FIFO, IRQ17, DMA-fed"
];
apb_targets [
fillcolor="#E6F4EA",
@@ -145,7 +149,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; ribp_targets; apb_targets; }
{ rank=same; sysctrl; ribp_targets; ws2812; apb_targets; }
{ rank=same; gpio_bridge; irq; }
ext_clock -> rcu [label="clock / reset"];
@@ -167,9 +171,13 @@ digraph mini_soc_block_diagram {
fabric -> apb_adapter [label="RIB"];
ribp_adapter -> sysctrl [label="RIBP"];
ribp_adapter -> ribp_targets [label="RIBP"];
ribp_adapter -> ws2812 [label="RIBP"];
apb_adapter -> apb_targets [label="APB"];
ribp_targets -> gpio_bridge [label="GPIO alternate functions"];
ws2812 -> gpio_bridge [label="GPIO2 ALT1"];
dma -> ws2812 [label="TXDATA writes", style=dashed];
ws2812 -> irq [label="IRQ17"];
apb_targets -> gpio_bridge [label="user_gpio"];
ribp_targets -> ext_io [dir=both, label="device interfaces"];
gpio_bridge -> ext_io [dir=both, label="pad control"];
+176 -140
View File
@@ -4,19 +4,19 @@
<!-- Generated by graphviz version 2.43.0 (0)
-->
<!-- Title: mini_soc_block_diagram Pages: 1 -->
<svg width="1203pt" height="887pt"
viewBox="0.00 0.00 1203.00 887.48" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<svg width="1315pt" height="887pt"
viewBox="0.00 0.00 1315.00 887.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 883.48)">
<title>mini_soc_block_diagram</title>
<text text-anchor="middle" x="132" y="-861.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 1175,-85 1175,-85 1181,-85 1187,-91 1187,-97 1187,-97 1187,-763.48 1187,-763.48 1187,-769.48 1181,-775.48 1175,-775.48 1175,-775.48 160,-775.48 160,-775.48 154,-775.48 148,-769.48 148,-763.48 148,-763.48 148,-97 148,-97 148,-91 154,-85 160,-85"/>
<path fill="transparent" stroke="#111827" d="M160,-85C160,-85 1287,-85 1287,-85 1293,-85 1299,-91 1299,-97 1299,-97 1299,-763.48 1299,-763.48 1299,-769.48 1293,-775.48 1287,-775.48 1287,-775.48 160,-775.48 160,-775.48 154,-775.48 148,-769.48 148,-763.48 148,-763.48 148,-97 148,-97 148,-91 154,-85 160,-85"/>
<text text-anchor="middle" x="196.5" y="-760.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 1157,-103 1157,-103 1163,-103 1169,-109 1169,-115 1169,-115 1169,-720.48 1169,-720.48 1169,-726.48 1163,-732.48 1157,-732.48 1157,-732.48 390,-732.48 390,-732.48 384,-732.48 378,-726.48 378,-720.48 378,-720.48 378,-115 378,-115 378,-109 384,-103 390,-103"/>
<path fill="transparent" stroke="#94a3b8" stroke-dasharray="5,2" d="M390,-103C390,-103 1269,-103 1269,-103 1275,-103 1281,-109 1281,-115 1281,-115 1281,-720.48 1281,-720.48 1281,-726.48 1275,-732.48 1269,-732.48 1269,-732.48 390,-732.48 390,-732.48 384,-732.48 378,-726.48 378,-720.48 378,-720.48 378,-115 378,-115 378,-109 384,-103 390,-103"/>
<text text-anchor="middle" x="407.5" y="-718.88" font-family="Arial" font-size="12.00" fill="#334155">retrosoc</text>
</g>
<!-- ext_clock -->
@@ -44,277 +44,313 @@
<!-- ext_io -->
<g id="node2" class="node">
<title>ext_io</title>
<path fill="#f3f4f6" stroke="#4b5563" stroke-width="1.1" d="M997,-45C997,-45 805,-45 805,-45 799,-45 793,-39 793,-33 793,-33 793,-12 793,-12 793,-6 799,0 805,0 805,0 997,0 997,0 1003,0 1009,-6 1009,-12 1009,-12 1009,-33 1009,-33 1009,-39 1003,-45 997,-45"/>
<text text-anchor="middle" x="901" y="-31" font-family="Arial" font-size="10.00" fill="#111827">External I/O and memory</text>
<text text-anchor="middle" x="901" y="-20" font-family="Arial" font-size="10.00" fill="#111827">GPIO, UART0, XPI Flash, SDRAM, PSRAM</text>
<text text-anchor="middle" x="901" y="-9" font-family="Arial" font-size="10.00" fill="#111827">SD card, I2C, I2S, DVP and serial I/O</text>
<path fill="#f3f4f6" stroke="#4b5563" stroke-width="1.1" d="M903,-45C903,-45 711,-45 711,-45 705,-45 699,-39 699,-33 699,-33 699,-12 699,-12 699,-6 705,0 711,0 711,0 903,0 903,0 909,0 915,-6 915,-12 915,-12 915,-33 915,-33 915,-39 909,-45 903,-45"/>
<text text-anchor="middle" x="807" y="-31" font-family="Arial" font-size="10.00" fill="#111827">External I/O and memory</text>
<text text-anchor="middle" x="807" y="-20" font-family="Arial" font-size="10.00" fill="#111827">GPIO, UART0, XPI Flash, SDRAM, PSRAM</text>
<text text-anchor="middle" x="807" 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="M546.5,-851.48C546.5,-851.48 415.5,-851.48 415.5,-851.48 409.5,-851.48 403.5,-845.48 403.5,-839.48 403.5,-839.48 403.5,-827.48 403.5,-827.48 403.5,-821.48 409.5,-815.48 415.5,-815.48 415.5,-815.48 546.5,-815.48 546.5,-815.48 552.5,-815.48 558.5,-821.48 558.5,-827.48 558.5,-827.48 558.5,-839.48 558.5,-839.48 558.5,-845.48 552.5,-851.48 546.5,-851.48"/>
<text text-anchor="middle" x="481" y="-836.48" font-family="Arial" font-size="10.00" fill="#111827">Management JTAG</text>
<text text-anchor="middle" x="481" y="-825.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="M569.5,-851.48C569.5,-851.48 438.5,-851.48 438.5,-851.48 432.5,-851.48 426.5,-845.48 426.5,-839.48 426.5,-839.48 426.5,-827.48 426.5,-827.48 426.5,-821.48 432.5,-815.48 438.5,-815.48 438.5,-815.48 569.5,-815.48 569.5,-815.48 575.5,-815.48 581.5,-821.48 581.5,-827.48 581.5,-827.48 581.5,-839.48 581.5,-839.48 581.5,-845.48 575.5,-851.48 569.5,-851.48"/>
<text text-anchor="middle" x="504" y="-836.48" font-family="Arial" font-size="10.00" fill="#111827">Management JTAG</text>
<text text-anchor="middle" x="504" y="-825.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="M556,-669.98C556,-669.98 406,-669.98 406,-669.98 400,-669.98 394,-663.98 394,-657.98 394,-657.98 394,-645.98 394,-645.98 394,-639.98 400,-633.98 406,-633.98 406,-633.98 556,-633.98 556,-633.98 562,-633.98 568,-639.98 568,-645.98 568,-645.98 568,-657.98 568,-657.98 568,-663.98 562,-669.98 556,-669.98"/>
<text text-anchor="middle" x="481" y="-654.98" font-family="Arial" font-size="10.00" fill="#111827">Hazard3 JTAG DTM</text>
<text text-anchor="middle" x="481" y="-643.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="M579,-669.98C579,-669.98 429,-669.98 429,-669.98 423,-669.98 417,-663.98 417,-657.98 417,-657.98 417,-645.98 417,-645.98 417,-639.98 423,-633.98 429,-633.98 429,-633.98 579,-633.98 579,-633.98 585,-633.98 591,-639.98 591,-645.98 591,-645.98 591,-657.98 591,-657.98 591,-663.98 585,-669.98 579,-669.98"/>
<text text-anchor="middle" x="504" y="-654.98" font-family="Arial" font-size="10.00" fill="#111827">Hazard3 JTAG DTM</text>
<text text-anchor="middle" x="504" y="-643.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="M481,-815.27C481,-783.24 481,-714.39 481,-677.51"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="483.45,-677.14 481,-670.14 478.55,-677.14 483.45,-677.14"/>
<text text-anchor="middle" x="501" y="-786.08" font-family="Arial" font-size="8.00" fill="#374151">JTAG pads</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M504,-815.27C504,-783.24 504,-714.39 504,-677.51"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="506.45,-677.14 504,-670.14 501.55,-677.14 506.45,-677.14"/>
<text text-anchor="middle" x="524" y="-786.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="M774.66,-442.31C774.66,-442.31 706.98,-470.16 706.98,-470.16 701.43,-472.45 689.88,-474.73 683.88,-474.73 683.88,-474.73 528.12,-474.73 528.12,-474.73 522.12,-474.73 510.57,-472.45 505.02,-470.16 505.02,-470.16 437.34,-442.31 437.34,-442.31 431.79,-440.03 431.79,-435.46 437.34,-433.18 437.34,-433.18 505.02,-405.32 505.02,-405.32 510.57,-403.04 522.12,-400.76 528.12,-400.76 528.12,-400.76 683.88,-400.76 683.88,-400.76 689.88,-400.76 701.43,-403.04 706.98,-405.32 706.98,-405.32 774.66,-433.18 774.66,-433.18 780.21,-435.46 780.21,-440.03 774.66,-442.31"/>
<text text-anchor="middle" x="606" y="-446.24" font-family="Arial" font-size="10.00" fill="#111827">RIB fabric</text>
<text text-anchor="middle" x="606" y="-435.24" font-family="Arial" font-size="10.00" fill="#111827">3&#45;master round&#45;robin arbitration</text>
<text text-anchor="middle" x="606" y="-424.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="M853.66,-442.31C853.66,-442.31 785.98,-470.16 785.98,-470.16 780.43,-472.45 768.88,-474.73 762.88,-474.73 762.88,-474.73 607.12,-474.73 607.12,-474.73 601.12,-474.73 589.57,-472.45 584.02,-470.16 584.02,-470.16 516.34,-442.31 516.34,-442.31 510.79,-440.03 510.79,-435.46 516.34,-433.18 516.34,-433.18 584.02,-405.32 584.02,-405.32 589.57,-403.04 601.12,-400.76 607.12,-400.76 607.12,-400.76 762.88,-400.76 762.88,-400.76 768.88,-400.76 780.43,-403.04 785.98,-405.32 785.98,-405.32 853.66,-433.18 853.66,-433.18 859.21,-435.46 859.21,-440.03 853.66,-442.31"/>
<text text-anchor="middle" x="685" y="-446.24" font-family="Arial" font-size="10.00" fill="#111827">RIB fabric</text>
<text text-anchor="middle" x="685" y="-435.24" font-family="Arial" font-size="10.00" fill="#111827">3&#45;master round&#45;robin arbitration</text>
<text text-anchor="middle" x="685" y="-424.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="M329.54,-529.48C377.9,-513.6 444.73,-491.67 500.57,-473.34"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="501.44,-475.64 507.33,-471.13 499.91,-470.98 501.44,-475.64"/>
<text text-anchor="middle" x="459" y="-500.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="M344.67,-529.48C408.63,-512.43 498.81,-488.38 570.28,-469.33"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="571,-471.67 577.13,-467.5 569.74,-466.94 571,-471.67"/>
<text text-anchor="middle" x="496" y="-500.08" font-family="Arial" font-size="8.00" fill="#374151">system clock / reset</text>
</g>
<!-- mgmt_core -->
<g id="node5" class="node">
<title>mgmt_core</title>
<path fill="#ddf2e3" stroke="#4b5563" stroke-width="1.1" d="M1141,-674.48C1141,-674.48 987,-674.48 987,-674.48 981,-674.48 975,-668.48 975,-662.48 975,-662.48 975,-641.48 975,-641.48 975,-635.48 981,-629.48 987,-629.48 987,-629.48 1141,-629.48 1141,-629.48 1147,-629.48 1153,-635.48 1153,-641.48 1153,-641.48 1153,-662.48 1153,-662.48 1153,-668.48 1147,-674.48 1141,-674.48"/>
<text text-anchor="middle" x="1064" y="-660.48" font-family="Arial" font-size="10.00" fill="#111827">Management core</text>
<text text-anchor="middle" x="1064" y="-649.48" font-family="Arial" font-size="10.00" fill="#111827">Fixed Hazard3, branch predictor</text>
<text text-anchor="middle" x="1064" y="-638.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="M1164,-674.48C1164,-674.48 1010,-674.48 1010,-674.48 1004,-674.48 998,-668.48 998,-662.48 998,-662.48 998,-641.48 998,-641.48 998,-635.48 1004,-629.48 1010,-629.48 1010,-629.48 1164,-629.48 1164,-629.48 1170,-629.48 1176,-635.48 1176,-641.48 1176,-641.48 1176,-662.48 1176,-662.48 1176,-668.48 1170,-674.48 1164,-674.48"/>
<text text-anchor="middle" x="1087" y="-660.48" font-family="Arial" font-size="10.00" fill="#111827">Management core</text>
<text text-anchor="middle" x="1087" y="-649.48" font-family="Arial" font-size="10.00" fill="#111827">Fixed Hazard3, branch predictor</text>
<text text-anchor="middle" x="1087" y="-638.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="M777.5,-569.98C777.5,-569.98 736.5,-569.98 736.5,-569.98 730.5,-569.98 724.5,-563.98 724.5,-557.98 724.5,-557.98 724.5,-545.98 724.5,-545.98 724.5,-539.98 730.5,-533.98 736.5,-533.98 736.5,-533.98 777.5,-533.98 777.5,-533.98 783.5,-533.98 789.5,-539.98 789.5,-545.98 789.5,-545.98 789.5,-557.98 789.5,-557.98 789.5,-563.98 783.5,-569.98 777.5,-569.98"/>
<text text-anchor="middle" x="757" y="-549.48" font-family="Arial" font-size="10.00" fill="#111827">RIBP2RIB</text>
<path fill="#e5e7eb" stroke="#4b5563" stroke-width="1.1" d="M809.5,-569.98C809.5,-569.98 768.5,-569.98 768.5,-569.98 762.5,-569.98 756.5,-563.98 756.5,-557.98 756.5,-557.98 756.5,-545.98 756.5,-545.98 756.5,-539.98 762.5,-533.98 768.5,-533.98 768.5,-533.98 809.5,-533.98 809.5,-533.98 815.5,-533.98 821.5,-539.98 821.5,-545.98 821.5,-545.98 821.5,-557.98 821.5,-557.98 821.5,-563.98 815.5,-569.98 809.5,-569.98"/>
<text text-anchor="middle" x="789" y="-549.48" font-family="Arial" font-size="10.00" fill="#111827">RIBP2RIB</text>
</g>
<!-- mgmt_core&#45;&gt;mgmt_adapter -->
<g id="edge7" class="edge">
<title>mgmt_core&#45;&gt;mgmt_adapter</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M996.63,-629.48C935.43,-609.94 847.24,-581.79 796.67,-565.65"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="797.21,-563.25 789.79,-563.45 795.72,-567.92 797.21,-563.25"/>
<text text-anchor="middle" x="928.5" y="-600.08" font-family="Arial" font-size="8.00" fill="#374151">RIBP</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1021.6,-629.48C962.53,-610.05 877.54,-582.1 828.32,-565.92"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="829.04,-563.57 821.62,-563.71 827.51,-568.23 829.04,-563.57"/>
<text text-anchor="middle" x="955.5" y="-600.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="M817.5,-674.48C817.5,-674.48 696.5,-674.48 696.5,-674.48 690.5,-674.48 684.5,-668.48 684.5,-662.48 684.5,-662.48 684.5,-641.48 684.5,-641.48 684.5,-635.48 690.5,-629.48 696.5,-629.48 696.5,-629.48 817.5,-629.48 817.5,-629.48 823.5,-629.48 829.5,-635.48 829.5,-641.48 829.5,-641.48 829.5,-662.48 829.5,-662.48 829.5,-668.48 823.5,-674.48 817.5,-674.48"/>
<text text-anchor="middle" x="757" y="-660.48" font-family="Arial" font-size="10.00" fill="#111827">Hazard3 Debug Module</text>
<text text-anchor="middle" x="757" y="-649.48" font-family="Arial" font-size="10.00" fill="#111827">abstract commands, no SBA</text>
<text text-anchor="middle" x="757" y="-638.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="M840.5,-674.48C840.5,-674.48 719.5,-674.48 719.5,-674.48 713.5,-674.48 707.5,-668.48 707.5,-662.48 707.5,-662.48 707.5,-641.48 707.5,-641.48 707.5,-635.48 713.5,-629.48 719.5,-629.48 719.5,-629.48 840.5,-629.48 840.5,-629.48 846.5,-629.48 852.5,-635.48 852.5,-641.48 852.5,-641.48 852.5,-662.48 852.5,-662.48 852.5,-668.48 846.5,-674.48 840.5,-674.48"/>
<text text-anchor="middle" x="780" y="-660.48" font-family="Arial" font-size="10.00" fill="#111827">Hazard3 Debug Module</text>
<text text-anchor="middle" x="780" y="-649.48" font-family="Arial" font-size="10.00" fill="#111827">abstract commands, no SBA</text>
<text text-anchor="middle" x="780" y="-638.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="M568.33,-651.98C603.12,-651.98 642.91,-651.98 676.95,-651.98"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="677.12,-654.43 684.12,-651.98 677.12,-649.53 677.12,-654.43"/>
<text text-anchor="middle" x="626.25" y="-657.58" font-family="Arial" font-size="8.00" fill="#374151">DMI, asynchronous CDC</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M591.33,-651.98C626.12,-651.98 665.91,-651.98 699.95,-651.98"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="700.12,-654.43 707.12,-651.98 700.12,-649.53 700.12,-654.43"/>
<text text-anchor="middle" x="649.25" y="-657.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="M829.75,-651.98C870.99,-651.98 923.09,-651.98 967.69,-651.98"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="967.77,-654.43 974.77,-651.98 967.77,-649.53 967.77,-654.43"/>
<text text-anchor="middle" x="902.25" y="-657.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="M852.75,-651.98C893.99,-651.98 946.09,-651.98 990.69,-651.98"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="990.77,-654.43 997.77,-651.98 990.77,-649.53 990.77,-654.43"/>
<text text-anchor="middle" x="925.25" y="-657.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="M757,-629.24C757,-613.92 757,-593.4 757,-577.51"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="759.45,-577.12 757,-570.12 754.55,-577.12 759.45,-577.12"/>
<text text-anchor="middle" x="795" y="-600.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="M782,-629.24C783.4,-613.92 785.29,-593.4 786.75,-577.51"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="789.23,-577.31 787.43,-570.12 784.35,-576.86 789.23,-577.31"/>
<text text-anchor="middle" x="822" y="-600.08" font-family="Arial" font-size="8.00" fill="#374151">reset after bridge idle</text>
</g>
<!-- mgmt_adapter&#45;&gt;fabric -->
<g id="edge8" class="edge">
<title>mgmt_adapter&#45;&gt;fabric</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M734.07,-533.94C714.43,-519.34 685.38,-497.75 659.9,-478.81"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="661.27,-476.77 654.19,-474.57 658.35,-480.71 661.27,-476.77"/>
<text text-anchor="middle" x="702" y="-500.08" font-family="Arial" font-size="8.00" fill="#374151">RIB</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M773.2,-533.94C759.91,-519.59 740.34,-498.47 723.01,-479.77"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="724.75,-478.03 718.19,-474.57 721.15,-481.37 724.75,-478.03"/>
<text text-anchor="middle" x="753" y="-500.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="M680,-574.48C680,-574.48 604,-574.48 604,-574.48 598,-574.48 592,-568.48 592,-562.48 592,-562.48 592,-541.48 592,-541.48 592,-535.48 598,-529.48 604,-529.48 604,-529.48 680,-529.48 680,-529.48 686,-529.48 692,-535.48 692,-541.48 692,-541.48 692,-562.48 692,-562.48 692,-568.48 686,-574.48 680,-574.48"/>
<text text-anchor="middle" x="642" y="-560.48" font-family="Arial" font-size="10.00" fill="#111827">Selected user core</text>
<text text-anchor="middle" x="642" y="-549.48" font-family="Arial" font-size="10.00" fill="#111827">C0&#45;C5, one active</text>
<text text-anchor="middle" x="642" y="-538.48" font-family="Arial" font-size="10.00" fill="#111827">PicoRV32 at C5</text>
<path fill="#ddf2e3" stroke="#4b5563" stroke-width="1.1" d="M717,-574.48C717,-574.48 641,-574.48 641,-574.48 635,-574.48 629,-568.48 629,-562.48 629,-562.48 629,-541.48 629,-541.48 629,-535.48 635,-529.48 641,-529.48 641,-529.48 717,-529.48 717,-529.48 723,-529.48 729,-535.48 729,-541.48 729,-541.48 729,-562.48 729,-562.48 729,-568.48 723,-574.48 717,-574.48"/>
<text text-anchor="middle" x="679" y="-560.48" font-family="Arial" font-size="10.00" fill="#111827">Selected user core</text>
<text text-anchor="middle" x="679" y="-549.48" font-family="Arial" font-size="10.00" fill="#111827">C0&#45;C5, one active</text>
<text text-anchor="middle" x="679" y="-538.48" font-family="Arial" font-size="10.00" fill="#111827">PicoRV32 at C5</text>
</g>
<!-- user_core&#45;&gt;fabric -->
<g id="edge9" class="edge">
<title>user_core&#45;&gt;fabric</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M635.06,-529.34C630.73,-515.85 625.01,-498.03 619.8,-481.75"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="622.07,-480.81 617.59,-474.89 617.4,-482.31 622.07,-480.81"/>
<text text-anchor="middle" x="634" y="-500.08" font-family="Arial" font-size="8.00" fill="#374151">RIB</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M680.16,-529.34C680.87,-515.97 681.81,-498.34 682.68,-482.19"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="685.14,-482.01 683.07,-474.89 680.25,-481.75 685.14,-482.01"/>
<text text-anchor="middle" x="688" y="-500.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="M553,-569.98C553,-569.98 435,-569.98 435,-569.98 429,-569.98 423,-563.98 423,-557.98 423,-557.98 423,-545.98 423,-545.98 423,-539.98 429,-533.98 435,-533.98 435,-533.98 553,-533.98 553,-533.98 559,-533.98 565,-539.98 565,-545.98 565,-545.98 565,-557.98 565,-557.98 565,-563.98 559,-569.98 553,-569.98"/>
<text text-anchor="middle" x="494" y="-554.98" font-family="Arial" font-size="10.00" fill="#111827">DMA</text>
<text text-anchor="middle" x="494" y="-543.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="M570,-569.98C570,-569.98 452,-569.98 452,-569.98 446,-569.98 440,-563.98 440,-557.98 440,-557.98 440,-545.98 440,-545.98 440,-539.98 446,-533.98 452,-533.98 452,-533.98 570,-533.98 570,-533.98 576,-533.98 582,-539.98 582,-545.98 582,-545.98 582,-557.98 582,-557.98 582,-563.98 576,-569.98 570,-569.98"/>
<text text-anchor="middle" x="511" y="-554.98" font-family="Arial" font-size="10.00" fill="#111827">DMA</text>
<text text-anchor="middle" x="511" y="-543.98" font-family="Arial" font-size="10.00" fill="#111827">RIB master and RIBP target</text>
</g>
<!-- dma&#45;&gt;fabric -->
<g id="edge10" class="edge">
<title>dma&#45;&gt;fabric</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M511.01,-533.94C525.33,-519.59 546.4,-498.47 565.06,-479.77"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="567.04,-481.25 570.25,-474.57 563.58,-477.79 567.04,-481.25"/>
<text text-anchor="middle" x="553" y="-500.08" font-family="Arial" font-size="8.00" fill="#374151">RIB</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M537.43,-533.94C560.15,-519.28 593.81,-497.56 623.25,-478.57"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="624.91,-480.42 629.47,-474.57 622.26,-476.3 624.91,-480.42"/>
<text text-anchor="middle" x="599" y="-500.08" font-family="Arial" font-size="8.00" fill="#374151">RIB</text>
</g>
<!-- ws2812 -->
<g id="node18" class="node">
<title>ws2812</title>
<path fill="#f9e1e7" stroke="#4b5563" stroke-width="1.1" d="M540,-255C540,-255 406,-255 406,-255 400,-255 394,-249 394,-243 394,-243 394,-222 394,-222 394,-216 400,-210 406,-210 406,-210 540,-210 540,-210 546,-210 552,-216 552,-222 552,-222 552,-243 552,-243 552,-249 546,-255 540,-255"/>
<text text-anchor="middle" x="473" y="-241" font-family="Arial" font-size="10.00" fill="#111827">WS2812 TX</text>
<text text-anchor="middle" x="473" y="-230" font-family="Arial" font-size="10.00" fill="#111827">24&#45;bit MSB&#45;first serializer</text>
<text text-anchor="middle" x="473" 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="edge21" class="edge">
<title>dma&#45;&gt;ws2812</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" stroke-dasharray="5,2" d="M506.69,-533.63C501.08,-511.05 492,-474.48 492,-474.48 492,-474.48 480.31,-326.28 475.27,-262.32"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="477.71,-262.1 474.72,-255.31 472.83,-262.48 477.71,-262.1"/>
<text text-anchor="middle" x="511.5" y="-371.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="M1109.5,-455.74C1109.5,-455.74 1000.5,-455.74 1000.5,-455.74 994.5,-455.74 988.5,-449.74 988.5,-443.74 988.5,-443.74 988.5,-431.74 988.5,-431.74 988.5,-425.74 994.5,-419.74 1000.5,-419.74 1000.5,-419.74 1109.5,-419.74 1109.5,-419.74 1115.5,-419.74 1121.5,-425.74 1121.5,-431.74 1121.5,-431.74 1121.5,-443.74 1121.5,-443.74 1121.5,-449.74 1115.5,-455.74 1109.5,-455.74"/>
<text text-anchor="middle" x="1055" y="-440.74" font-family="Arial" font-size="10.00" fill="#111827">Error slave</text>
<text text-anchor="middle" x="1055" y="-429.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="M1065.5,-455.74C1065.5,-455.74 956.5,-455.74 956.5,-455.74 950.5,-455.74 944.5,-449.74 944.5,-443.74 944.5,-443.74 944.5,-431.74 944.5,-431.74 944.5,-425.74 950.5,-419.74 956.5,-419.74 956.5,-419.74 1065.5,-419.74 1065.5,-419.74 1071.5,-419.74 1077.5,-425.74 1077.5,-431.74 1077.5,-431.74 1077.5,-443.74 1077.5,-443.74 1077.5,-449.74 1071.5,-455.74 1065.5,-455.74"/>
<text text-anchor="middle" x="1011" y="-440.74" font-family="Arial" font-size="10.00" fill="#111827">Error slave</text>
<text text-anchor="middle" x="1011" y="-429.74" font-family="Arial" font-size="10.00" fill="#111827">access and protocol faults</text>
</g>
<!-- fabric&#45;&gt;fault -->
<g id="edge11" class="edge">
<title>fabric&#45;&gt;fault</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M728.45,-466.01C793.63,-481.05 860.5,-496.48 860.5,-496.48 860.5,-496.48 913.5,-496.48 913.5,-496.48 913.5,-496.48 975,-474.48 975,-474.48 975,-474.48 991.11,-467.08 1008.88,-458.93"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="728.71,-463.55 721.34,-464.36 727.61,-468.33 728.71,-463.55"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1010.13,-461.05 1015.47,-455.9 1008.08,-456.59 1010.13,-461.05"/>
<text text-anchor="middle" x="887" y="-500.08" font-family="Arial" font-size="8.00" fill="#374151">invalid / denied</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M871.98,-437.74C894.93,-437.74 917.24,-437.74 937.02,-437.74"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="871.69,-435.29 864.69,-437.74 871.69,-440.19 871.69,-435.29"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="937.19,-440.19 944.19,-437.74 937.19,-435.29 937.19,-440.19"/>
<text text-anchor="middle" x="904.56" y="-443.34" 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="M949,-455.74C949,-455.74 851,-455.74 851,-455.74 845,-455.74 839,-449.74 839,-443.74 839,-443.74 839,-431.74 839,-431.74 839,-425.74 845,-419.74 851,-419.74 851,-419.74 949,-419.74 949,-419.74 955,-419.74 961,-425.74 961,-431.74 961,-431.74 961,-443.74 961,-443.74 961,-449.74 955,-455.74 949,-455.74"/>
<text text-anchor="middle" x="900" y="-440.74" font-family="Arial" font-size="10.00" fill="#111827">On&#45;chip SRAM</text>
<text text-anchor="middle" x="900" y="-429.74" font-family="Arial" font-size="10.00" fill="#111827">optional RIB2RAM path</text>
<path fill="#fff3c9" stroke="#4b5563" stroke-width="1.1" d="M1215,-455.74C1215,-455.74 1117,-455.74 1117,-455.74 1111,-455.74 1105,-449.74 1105,-443.74 1105,-443.74 1105,-431.74 1105,-431.74 1105,-425.74 1111,-419.74 1117,-419.74 1117,-419.74 1215,-419.74 1215,-419.74 1221,-419.74 1227,-425.74 1227,-431.74 1227,-431.74 1227,-443.74 1227,-443.74 1227,-449.74 1221,-455.74 1215,-455.74"/>
<text text-anchor="middle" x="1166" y="-440.74" font-family="Arial" font-size="10.00" fill="#111827">On&#45;chip SRAM</text>
<text text-anchor="middle" x="1166" y="-429.74" font-family="Arial" font-size="10.00" fill="#111827">optional RIB2RAM path</text>
</g>
<!-- fabric&#45;&gt;sram -->
<g id="edge12" class="edge">
<title>fabric&#45;&gt;sram</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M785.91,-437.74C801.95,-437.74 817.44,-437.74 831.56,-437.74"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="831.81,-440.19 838.81,-437.74 831.81,-435.29 831.81,-440.19"/>
<text text-anchor="middle" x="812.31" y="-443.34" font-family="Arial" font-size="8.00" fill="#374151">RIB</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M805.88,-462.06C886.21,-478.22 977,-496.48 977,-496.48 977,-496.48 991,-496.48 991,-496.48 991,-496.48 1091,-474.48 1091,-474.48 1091,-474.48 1105.84,-467.21 1122.32,-459.14"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1123.73,-461.18 1128.94,-455.9 1121.58,-456.78 1123.73,-461.18"/>
<text text-anchor="middle" x="984" y="-500.08" 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="M764.5,-346C764.5,-346 723.5,-346 723.5,-346 717.5,-346 711.5,-340 711.5,-334 711.5,-334 711.5,-322 711.5,-322 711.5,-316 717.5,-310 723.5,-310 723.5,-310 764.5,-310 764.5,-310 770.5,-310 776.5,-316 776.5,-322 776.5,-322 776.5,-334 776.5,-334 776.5,-340 770.5,-346 764.5,-346"/>
<text text-anchor="middle" x="744" y="-325.5" font-family="Arial" font-size="10.00" fill="#111827">RIB2RIBP</text>
<path fill="#e5e7eb" stroke="#4b5563" stroke-width="1.1" d="M708.5,-346C708.5,-346 667.5,-346 667.5,-346 661.5,-346 655.5,-340 655.5,-334 655.5,-334 655.5,-322 655.5,-322 655.5,-316 661.5,-310 667.5,-310 667.5,-310 708.5,-310 708.5,-310 714.5,-310 720.5,-316 720.5,-322 720.5,-322 720.5,-334 720.5,-334 720.5,-340 714.5,-346 708.5,-346"/>
<text text-anchor="middle" x="688" y="-325.5" font-family="Arial" font-size="10.00" fill="#111827">RIB2RIBP</text>
</g>
<!-- fabric&#45;&gt;ribp_adapter -->
<g id="edge13" class="edge">
<title>fabric&#45;&gt;ribp_adapter</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M651.95,-400.87C673.23,-384.26 697.8,-365.07 716.21,-350.7"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="718.19,-352.26 722.2,-346.02 715.17,-348.4 718.19,-352.26"/>
<text text-anchor="middle" x="698" y="-371.6" font-family="Arial" font-size="8.00" fill="#374151">RIB</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M686,-400.87C686.43,-385.27 686.93,-367.39 687.32,-353.38"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="689.78,-353.09 687.53,-346.02 684.88,-352.95 689.78,-353.09"/>
<text text-anchor="middle" x="693" y="-371.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="M599,-346C599,-346 529,-346 529,-346 523,-346 517,-340 517,-334 517,-334 517,-322 517,-322 517,-316 523,-310 529,-310 529,-310 599,-310 599,-310 605,-310 611,-316 611,-322 611,-322 611,-334 611,-334 611,-340 605,-346 599,-346"/>
<text text-anchor="middle" x="564" y="-331" font-family="Arial" font-size="10.00" fill="#111827">RIB2APB</text>
<text text-anchor="middle" x="564" y="-320" font-family="Arial" font-size="10.00" fill="#111827">direct APB bridge</text>
<path fill="#e5e7eb" stroke="#4b5563" stroke-width="1.1" d="M875,-346C875,-346 805,-346 805,-346 799,-346 793,-340 793,-334 793,-334 793,-322 793,-322 793,-316 799,-310 805,-310 805,-310 875,-310 875,-310 881,-310 887,-316 887,-322 887,-322 887,-334 887,-334 887,-340 881,-346 875,-346"/>
<text text-anchor="middle" x="840" y="-331" font-family="Arial" font-size="10.00" fill="#111827">RIB2APB</text>
<text text-anchor="middle" x="840" y="-320" font-family="Arial" font-size="10.00" fill="#111827">direct APB bridge</text>
</g>
<!-- fabric&#45;&gt;apb_adapter -->
<g id="edge14" class="edge">
<title>fabric&#45;&gt;apb_adapter</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M592.01,-400.87C585.82,-384.98 578.71,-366.73 573.2,-352.6"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="575.46,-351.65 570.64,-346.02 570.9,-353.43 575.46,-351.65"/>
<text text-anchor="middle" x="589" y="-371.6" font-family="Arial" font-size="8.00" fill="#374151">RIB</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M736.61,-400.87C760.82,-384.04 788.83,-364.57 809.59,-350.14"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="811.16,-352.03 815.51,-346.02 808.37,-348.01 811.16,-352.03"/>
<text text-anchor="middle" x="787" y="-371.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="M825.5,-255C825.5,-255 662.5,-255 662.5,-255 656.5,-255 650.5,-249 650.5,-243 650.5,-243 650.5,-222 650.5,-222 650.5,-216 656.5,-210 662.5,-210 662.5,-210 825.5,-210 825.5,-210 831.5,-210 837.5,-216 837.5,-222 837.5,-222 837.5,-243 837.5,-243 837.5,-249 831.5,-255 825.5,-255"/>
<text text-anchor="middle" x="744" y="-241" font-family="Arial" font-size="10.00" fill="#111827">SYSCTRL and CLINT</text>
<text text-anchor="middle" x="744" 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="744" 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="M1010.5,-255C1010.5,-255 847.5,-255 847.5,-255 841.5,-255 835.5,-249 835.5,-243 835.5,-243 835.5,-222 835.5,-222 835.5,-216 841.5,-210 847.5,-210 847.5,-210 1010.5,-210 1010.5,-210 1016.5,-210 1022.5,-216 1022.5,-222 1022.5,-222 1022.5,-243 1022.5,-243 1022.5,-249 1016.5,-255 1010.5,-255"/>
<text text-anchor="middle" x="929" y="-241" font-family="Arial" font-size="10.00" fill="#111827">SYSCTRL and CLINT</text>
<text text-anchor="middle" x="929" 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="929" y="-219" font-family="Arial" font-size="10.00" fill="#111827">fault and performance control</text>
</g>
<!-- ribp_adapter&#45;&gt;sysctrl -->
<g id="edge15" class="edge">
<title>ribp_adapter&#45;&gt;sysctrl</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M744,-309.85C744,-296.62 744,-278.02 744,-262.49"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="746.45,-262.15 744,-255.15 741.55,-262.15 746.45,-262.15"/>
<text text-anchor="middle" x="753.5" y="-280.6" font-family="Arial" font-size="8.00" fill="#374151">RIBP</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M720.5,-314.39C757.85,-299.9 820,-275.79 866.75,-257.65"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="867.89,-259.84 873.53,-255.02 866.12,-255.27 867.89,-259.84"/>
<text text-anchor="middle" x="817.5" y="-280.6" font-family="Arial" font-size="8.00" fill="#374151">RIBP</text>
</g>
<!-- ribp_targets -->
<g id="node17" class="node">
<title>ribp_targets</title>
<path fill="#f9e1e7" stroke="#4b5563" stroke-width="1.1" d="M1066,-255C1066,-255 876,-255 876,-255 870,-255 864,-249 864,-243 864,-243 864,-222 864,-222 864,-216 870,-210 876,-210 876,-210 1066,-210 1066,-210 1072,-210 1078,-216 1078,-222 1078,-222 1078,-243 1078,-243 1078,-249 1072,-255 1066,-255"/>
<text text-anchor="middle" x="971" y="-241" font-family="Arial" font-size="10.00" fill="#111827">RIBP target wrapper</text>
<text text-anchor="middle" x="971" y="-230" font-family="Arial" font-size="10.00" fill="#111827">XPI/Flash, SDRAM, PSRAM, SPI&#45;SD, DVP</text>
<text text-anchor="middle" x="971" y="-219" font-family="Arial" font-size="10.00" fill="#111827">GPIO, UART0, timers, I2C, I2S and 1&#45;Wire</text>
<path fill="#f9e1e7" stroke="#4b5563" stroke-width="1.1" d="M796.5,-255C796.5,-255 591.5,-255 591.5,-255 585.5,-255 579.5,-249 579.5,-243 579.5,-243 579.5,-222 579.5,-222 579.5,-216 585.5,-210 591.5,-210 591.5,-210 796.5,-210 796.5,-210 802.5,-210 808.5,-216 808.5,-222 808.5,-222 808.5,-243 808.5,-243 808.5,-249 802.5,-255 796.5,-255"/>
<text text-anchor="middle" x="694" y="-241" font-family="Arial" font-size="10.00" fill="#111827">RIBP target wrapper</text>
<text text-anchor="middle" x="694" 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="694" y="-219" font-family="Arial" font-size="10.00" fill="#111827">GPIO, UART0, timers, I2C and I2S</text>
</g>
<!-- ribp_adapter&#45;&gt;ribp_targets -->
<g id="edge16" class="edge">
<title>ribp_adapter&#45;&gt;ribp_targets</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M776.52,-313.6C811.74,-299.1 868.65,-275.66 911.84,-257.87"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="912.83,-260.11 918.37,-255.18 910.97,-255.58 912.83,-260.11"/>
<text text-anchor="middle" x="866.5" y="-280.6" font-family="Arial" font-size="8.00" fill="#374151">RIBP</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M689.1,-309.85C689.95,-296.62 691.14,-278.02 692.14,-262.49"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="694.61,-262.29 692.61,-255.15 689.72,-261.98 694.61,-262.29"/>
<text text-anchor="middle" x="700.5" y="-280.6" font-family="Arial" font-size="8.00" fill="#374151">RIBP</text>
</g>
<!-- ribp_adapter&#45;&gt;ws2812 -->
<g id="edge17" class="edge">
<title>ribp_adapter&#45;&gt;ws2812</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M655.35,-312.8C622.03,-298.31 569.68,-275.54 529.6,-258.11"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="530.17,-255.69 522.77,-255.15 528.21,-260.18 530.17,-255.69"/>
<text text-anchor="middle" x="602.5" y="-280.6" font-family="Arial" font-size="8.00" fill="#374151">RIBP</text>
</g>
<!-- apb_targets -->
<g id="node18" class="node">
<g id="node19" class="node">
<title>apb_targets</title>
<path fill="#e6f4ea" stroke="#4b5563" stroke-width="1.1" d="M611.5,-255C611.5,-255 420.5,-255 420.5,-255 414.5,-255 408.5,-249 408.5,-243 408.5,-243 408.5,-222 408.5,-222 408.5,-216 414.5,-210 420.5,-210 420.5,-210 611.5,-210 611.5,-210 617.5,-210 623.5,-216 623.5,-222 623.5,-222 623.5,-243 623.5,-243 623.5,-249 617.5,-255 611.5,-255"/>
<text text-anchor="middle" x="516" y="-241" font-family="Arial" font-size="10.00" fill="#111827">APB target wrapper</text>
<text text-anchor="middle" x="516" y="-230" font-family="Arial" font-size="10.00" fill="#111827">Archinfo, RNG, UART1, PWM, PS/2, RTC</text>
<text text-anchor="middle" x="516" y="-219" font-family="Arial" font-size="10.00" fill="#111827">watchdog, CRC, advanced timer and user IP</text>
<path fill="#e6f4ea" stroke="#4b5563" stroke-width="1.1" d="M1252.5,-255C1252.5,-255 1061.5,-255 1061.5,-255 1055.5,-255 1049.5,-249 1049.5,-243 1049.5,-243 1049.5,-222 1049.5,-222 1049.5,-216 1055.5,-210 1061.5,-210 1061.5,-210 1252.5,-210 1252.5,-210 1258.5,-210 1264.5,-216 1264.5,-222 1264.5,-222 1264.5,-243 1264.5,-243 1264.5,-249 1258.5,-255 1252.5,-255"/>
<text text-anchor="middle" x="1157" y="-241" font-family="Arial" font-size="10.00" fill="#111827">APB target wrapper</text>
<text text-anchor="middle" x="1157" y="-230" font-family="Arial" font-size="10.00" fill="#111827">Archinfo, RNG, UART1, PWM, PS/2, RTC</text>
<text text-anchor="middle" x="1157" 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="edge17" class="edge">
<g id="edge18" class="edge">
<title>apb_adapter&#45;&gt;apb_targets</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M555.2,-309.85C548.28,-296.38 538.51,-277.34 530.45,-261.65"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="532.49,-260.26 527.11,-255.15 528.13,-262.5 532.49,-260.26"/>
<text text-anchor="middle" x="550" y="-280.6" font-family="Arial" font-size="8.00" fill="#374151">APB</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M887.12,-313.1C937.19,-298.33 1017.25,-274.72 1077.19,-257.04"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1078,-259.35 1084.02,-255.02 1076.62,-254.65 1078,-259.35"/>
<text text-anchor="middle" x="1006" y="-280.6" font-family="Arial" font-size="8.00" fill="#374151">APB</text>
</g>
<!-- ribp_targets&#45;&gt;ext_io -->
<g id="edge20" class="edge">
<g id="edge24" class="edge">
<title>ribp_targets&#45;&gt;ext_io</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M963.24,-203.09C951.61,-160.49 931,-85 931,-85 931,-85 922.54,-67.66 914.6,-51.39"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="960.89,-203.78 965.1,-209.88 965.62,-202.49 960.89,-203.78"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="916.76,-50.23 911.49,-45.01 912.36,-52.38 916.76,-50.23"/>
<text text-anchor="middle" x="979" 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="M780.98,-208.04C820.68,-197.33 859,-187 859,-187 859,-187 859,-178 859,-178 859,-178 837,-85 837,-85 837,-85 828.54,-67.66 820.6,-51.39"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="780.2,-205.71 774.08,-209.9 781.48,-210.44 780.2,-205.71"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="822.76,-50.23 817.49,-45.01 818.36,-52.38 822.76,-50.23"/>
<text text-anchor="middle" x="883" y="-135.1" font-family="Arial" font-size="8.00" fill="#374151">device interfaces</text>
</g>
<!-- gpio_bridge -->
<g id="node19" class="node">
<g id="node20" class="node">
<title>gpio_bridge</title>
<path fill="#e5e7eb" stroke="#4b5563" stroke-width="1.1" d="M905,-155C905,-155 757,-155 757,-155 751,-155 745,-149 745,-143 745,-143 745,-131 745,-131 745,-125 751,-119 757,-119 757,-119 905,-119 905,-119 911,-119 917,-125 917,-131 917,-131 917,-143 917,-143 917,-149 911,-155 905,-155"/>
<text text-anchor="middle" x="831" y="-140" font-family="Arial" font-size="10.00" fill="#111827">GPIO pad bridge</text>
<text text-anchor="middle" x="831" y="-129" font-family="Arial" font-size="10.00" fill="#111827">software, alternate and user modes</text>
<path fill="#e5e7eb" stroke="#4b5563" stroke-width="1.1" d="M811,-155C811,-155 663,-155 663,-155 657,-155 651,-149 651,-143 651,-143 651,-131 651,-131 651,-125 657,-119 663,-119 663,-119 811,-119 811,-119 817,-119 823,-125 823,-131 823,-131 823,-143 823,-143 823,-149 817,-155 811,-155"/>
<text text-anchor="middle" x="737" y="-140" font-family="Arial" font-size="10.00" fill="#111827">GPIO pad bridge</text>
<text text-anchor="middle" x="737" y="-129" font-family="Arial" font-size="10.00" fill="#111827">software, alternate and user modes</text>
</g>
<!-- ribp_targets&#45;&gt;gpio_bridge -->
<g id="edge18" class="edge">
<title>ribp_targets&#45;&gt;gpio_bridge</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M918.58,-209.9C891.06,-198.56 863,-187 863,-187 863,-187 854.45,-173.9 846.19,-161.25"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="848.07,-159.66 842.19,-155.14 843.97,-162.34 848.07,-159.66"/>
<text text-anchor="middle" x="907" y="-180.6" font-family="Arial" font-size="8.00" fill="#374151">GPIO alternate functions</text>
</g>
<!-- apb_targets&#45;&gt;gpio_bridge -->
<g id="edge19" class="edge">
<title>apb_targets&#45;&gt;gpio_bridge</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M588.66,-209.93C642.48,-193.96 715.14,-172.39 766.82,-157.05"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="767.64,-159.36 773.65,-155.02 766.24,-154.67 767.64,-159.36"/>
<text text-anchor="middle" x="710" y="-180.6" font-family="Arial" font-size="8.00" fill="#374151">user_gpio</text>
<title>ribp_targets&#45;&gt;gpio_bridge</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M695.22,-209.74C696.05,-194.93 697,-178 697,-178 697,-178 705.52,-169.48 714.69,-160.31"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="716.59,-161.88 719.8,-155.2 713.12,-158.41 716.59,-161.88"/>
<text text-anchor="middle" x="741" y="-180.6" font-family="Arial" font-size="8.00" fill="#374151">GPIO alternate functions</text>
</g>
<!-- gpio_bridge&#45;&gt;ext_io -->
<g id="edge21" class="edge">
<title>gpio_bridge&#45;&gt;ext_io</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M845.43,-112.81C856.49,-95.04 871.79,-70.44 883.54,-51.56"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="843.25,-111.68 841.63,-118.91 847.41,-114.26 843.25,-111.68"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="885.76,-52.63 887.38,-45.39 881.6,-50.04 885.76,-52.63"/>
<text text-anchor="middle" x="892" y="-70.6" font-family="Arial" font-size="8.00" fill="#374151">pad control</text>
<!-- ws2812&#45;&gt;gpio_bridge -->
<g id="edge20" class="edge">
<title>ws2812&#45;&gt;gpio_bridge</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M533.9,-209.93C578.56,-194.11 638.71,-172.81 681.94,-157.5"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="682.94,-159.74 688.73,-155.1 681.31,-155.12 682.94,-159.74"/>
<text text-anchor="middle" x="642.5" y="-180.6" font-family="Arial" font-size="8.00" fill="#374151">GPIO2 ALT1</text>
</g>
<!-- irq -->
<g id="node20" class="node">
<g id="node21" class="node">
<title>irq</title>
<path fill="#fce7f3" stroke="#4b5563" stroke-width="1.1" d="M659,-155C659,-155 453,-155 453,-155 447,-155 441,-149 441,-143 441,-143 441,-131 441,-131 441,-125 447,-119 453,-119 453,-119 659,-119 659,-119 665,-119 671,-125 671,-131 671,-131 671,-143 671,-143 671,-149 665,-155 659,-155"/>
<text text-anchor="middle" x="556" y="-140" font-family="Arial" font-size="10.00" fill="#111827">Interrupt aggregation</text>
<text text-anchor="middle" x="556" 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="M612,-155C612,-155 406,-155 406,-155 400,-155 394,-149 394,-143 394,-143 394,-131 394,-131 394,-125 400,-119 406,-119 406,-119 612,-119 612,-119 618,-119 624,-125 624,-131 624,-131 624,-143 624,-143 624,-149 618,-155 612,-155"/>
<text text-anchor="middle" x="509" y="-140" font-family="Arial" font-size="10.00" fill="#111827">Interrupt aggregation</text>
<text text-anchor="middle" x="509" y="-129" font-family="Arial" font-size="10.00" fill="#111827">RIBP and APB target IRQs to management core</text>
</g>
<!-- ws2812&#45;&gt;irq -->
<g id="edge22" class="edge">
<title>ws2812&#45;&gt;irq</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M481.35,-209.81C486.85,-195.54 494.05,-176.83 499.73,-162.09"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="502.15,-162.62 502.38,-155.2 497.57,-160.86 502.15,-162.62"/>
<text text-anchor="middle" x="505" y="-180.6" font-family="Arial" font-size="8.00" fill="#374151">IRQ17</text>
</g>
<!-- apb_targets&#45;&gt;gpio_bridge -->
<g id="edge23" class="edge">
<title>apb_targets&#45;&gt;gpio_bridge</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1060.38,-209.99C987.9,-193.86 889.69,-171.99 820.58,-156.61"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="820.93,-154.17 813.56,-155.04 819.86,-158.96 820.93,-154.17"/>
<text text-anchor="middle" x="965" y="-180.6" font-family="Arial" font-size="8.00" fill="#374151">user_gpio</text>
</g>
<!-- gpio_bridge&#45;&gt;ext_io -->
<g id="edge25" class="edge">
<title>gpio_bridge&#45;&gt;ext_io</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M751.43,-112.81C762.49,-95.04 777.79,-70.44 789.54,-51.56"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="749.25,-111.68 747.63,-118.91 753.41,-114.26 749.25,-111.68"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="791.76,-52.63 793.38,-45.39 787.6,-50.04 791.76,-52.63"/>
<text text-anchor="middle" x="798" y="-70.6" font-family="Arial" font-size="8.00" fill="#374151">pad control</text>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 29 KiB

+13
View File
@@ -26,6 +26,19 @@ 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.
## WS2812 output
The single-channel WS2812 transmitter occupies RIBP address `0x10008000` and
drives GPIO2 alternate function 1. Software must configure GPIO2 for ALT1
before starting a frame. The transmitter interrupt is RIBP group bit 10 and
management-core interrupt 17. The data pin is forced low while idle, during
reset/latch time, after abort, and after underflow.
The RIBP TX FIFO is also a fixed-address target for the generic DMA engine.
DMA remains a normal RIB master and receives RIBP backpressure when the FIFO
is full; the transmitter does not own a private DMA request channel. See
[ws2812.md](ws2812.md) for the register and transfer contract.
## Management JTAG
The fixed Hazard3 management core always exposes five ASIC pads:
+113
View File
@@ -0,0 +1,113 @@
# WS2812 Transmitter
The Mini SoC WS2812 block is a single-channel, transmit-only peripheral for
24-bit GRB LED words. It replaces the former generic one-wire block; it is not
a Dallas/Maxim 1-Wire controller. The block has a strict RIBP register target,
a 16-word FIFO, programmable symbol and reset timing, sticky interrupt and
error state, and a GPIO alternate-function output.
## Integration
| Property | Value |
| --- | --- |
| RIBP base address | `0x10008000` |
| FIFO depth | 16 24-bit words |
| Pixel order | GRB, bit 23 first |
| GPIO route | GPIO2 alternate function 1 |
| RIBP interrupt group | Bit 10 |
| Management-core interrupt | 17 |
| DMA target | Fixed address `TXDATA` |
`IP_INFO` reports the implemented data width, FIFO depth, and ABI version so
software does not need to duplicate the configured FIFO depth. The current
value is `0x18100100`: 24-bit data, 16 entries, ABI 1.0.
## Register ABI
All registers are 32 bits and naturally aligned. Unmapped, unaligned, and
direction-invalid accesses complete with `resp_err`. `TXDATA` requires all
four byte strobes; partial writes complete with `resp_err` and set the command
error. Timing and frame registers support byte strobes unless noted below.
| Offset | Name | Access | Description |
| --- | --- | --- | --- |
| `0x00` | `BIT_CYCLES` | RW | Total system-clock cycles per serialized bit; implemented width is 16 bits. |
| `0x04` | `T0H_CYCLES` | RW | High cycles for a zero bit; implemented width is 16 bits. |
| `0x08` | `T1H_CYCLES` | RW | High cycles for a one bit; implemented width is 16 bits. |
| `0x0C` | `RESET_CYCLES` | RW | Low cycles after a frame, abort, or underflow. |
| `0x10` | `TXDATA` | WO | Push one 24-bit GRB word into the FIFO. Bits 31:24 must be zero in software. |
| `0x14` | `CTRL` | WO | One-hot commands: bit 0 `START`, bit 1 `ABORT`, bit 2 `FIFO_FLUSH`. |
| `0x18` | `STATUS` | RO | Bits 0:4 are `BUSY`, `FIFO_EMPTY`, `FIFO_FULL`, `CONFIG_VALID`, and `RESET_ACTIVE`. |
| `0x1C` | `FRAME_WORDS` | RW | Exact number of FIFO words consumed by the next frame. |
| `0x20` | `FIFO_LEVEL` | RO | Current FIFO occupancy. |
| `0x24` | `FIFO_WATERMARK` | RW | Low-watermark threshold; valid values are 0 through 15. |
| `0x28` | `REMAINING_WORDS` | RO | Frame words not yet completely serialized. |
| `0x2C` | `ERROR_STATUS` | RW1C | Bits 0:2 are configuration, underflow, and command errors. |
| `0x30` | `INTR_STATE` | RW1C | Bits 0:3 are done, FIFO-low, error, and aborted state. |
| `0x34` | `INTR_ENABLE` | RW | Interrupt enable mask. |
| `0x38` | `INTR_TEST` | WO | Write-one software interrupt test bits. |
| `0x3C` | `IP_INFO` | RO | `{data_width, fifo_depth, abi_major, abi_minor}`. |
The external interrupt is the reduction OR of `INTR_STATE & INTR_ENABLE`.
`ERROR_STATUS` and `INTR_STATE` are sticky until software writes one to clear
the corresponding bit. `INTR_STATE.ERROR` reflects any newly observed error.
`FIFO_LOW` is asserted while a frame still needs input and FIFO occupancy is
at or below the programmed watermark.
## Timing contract
The active timing is captured on an accepted `START` command and cannot change
mid-frame. A valid configuration satisfies:
```text
0 < T0H_CYCLES < T1H_CYCLES < BIT_CYCLES
RESET_CYCLES > 0
```
For a source frequency `f` and duration in nanoseconds, the HAL rounds the bit
and high durations to the nearest integer cycle. It rounds reset duration up,
so the specified minimum reset/latch time is never shortened. The default HAL
profile is a 1,250 ns bit, 350 ns zero-high, 700 ns one-high, and 300 us reset.
Software must recompute and program the timing after changing the source clock.
## Transfer sequence
1. Configure GPIO2 for alternate function 1 and call `rs_ws2812_init()`.
2. Clear old errors and interrupts, flush the idle FIFO, and enqueue initial
24-bit GRB words.
3. Program the exact nonzero `FRAME_WORDS` count and issue `START`.
4. Refill `TXDATA` with the CPU or generic DMA before the FIFO empties.
5. Wait for `DONE`; the output remains low for `RESET_CYCLES` before the frame
is complete and the done state is raised.
When `TXDATA` is full during an active frame, RIBP applies backpressure until a
FIFO entry becomes available. A full-FIFO write while idle is rejected instead
of waiting indefinitely. Underflow aborts the frame, flushes queued data, sets
the underflow and error state, and holds the output low for the configured
reset interval. `ABORT` has the same output-safety behavior and additionally
sets the aborted interrupt state. `FIFO_FLUSH` is accepted only while idle.
`rs_ws2812_write()` implements bounded CPU refill. `rs_ws2812_write_dma()`
preloads the FIFO, programs the generic DMA for incrementing source and fixed
`TXDATA` destination writes, then starts the frame and DMA. Both paths validate
the complete pixel array before modifying peripheral state and use caller
timeouts for all waits.
## Verification and current scope
The self-checking RTL test verifies exact zero/one pulse widths, MSB-first word
order, frame length, reset low time, FIFO backpressure, watermark and done
interrupts, invalid accesses, underflow, abort, and software interrupt test.
The DMA integration test connects the production `dma_core` to `TXDATA` and
checks fixed-destination writes, full-FIFO backpressure, completion, and the
resulting serialized frame.
The formal target proves bounded FIFO, RIBP response, idle-low output, and
sticky event invariants and covers normal, underflow-precondition, and abort
paths. Host C
tests cover GRB packing and integer timing conversion. SoC topology tests lock
the address, interrupt, and GPIO route.
The current delivery is deliberately one channel and 24-bit GRB. RGBW framing,
parallel lanes, per-pixel brightness processing, and a dedicated DMA request
line are outside this ABI and require a versioned extension rather than an
incompatible register reinterpretation.
+1 -1
View File
@@ -5,7 +5,7 @@ module retrosoc_top (
inout rst_n_i,
inout ps2_clk_i,
inout ps2_dat_i,
output onewire_dat_o,
output ws2812_dat_o,
inout i2c_scl_io,
inout i2c_sda_io,
output pwm_2_o,
+2 -2
View File
@@ -12,8 +12,8 @@ set_property -dict {PACKAGE_PIN AB11 IOSTANDARD LVCMOS18} [get_ports rst_n_i]
# ps2
set_property -dict {PACKAGE_PIN AB4 IOSTANDARD LVCMOS18} [get_ports ps2_clk_i]
set_property -dict {PACKAGE_PIN AB5 IOSTANDARD LVCMOS18} [get_ports ps2_dat_i]
#onewire
set_property -dict {PACKAGE_PIN AB10 IOSTANDARD LVCMOS18} [get_ports onewire_dat_o]
# ws2812
set_property -dict {PACKAGE_PIN AB10 IOSTANDARD LVCMOS18} [get_ports ws2812_dat_o]
# i2c1
set_property -dict {PACKAGE_PIN W15 IOSTANDARD LVCMOS18} [get_ports i2c_scl_io]
set_property -dict {PACKAGE_PIN Y15 IOSTANDARD LVCMOS18} [get_ports i2c_sda_io]
-216
View File
@@ -1,216 +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 ONEWIRE_DEF_SV
`define ONEWIRE_DEF_SV
// verilog_format: off
`define RIBP_ONEWIRE_CLKDIV 8'h00
`define RIBP_ONEWIRE_ZEROCNT 8'h04
`define RIBP_ONEWIRE_ONECNT 8'h08
`define RIBP_ONEWIRE_RSTCNT 8'h0C
`define RIBP_ONEWIRE_TXDATA 8'h10
`define RIBP_ONEWIRE_CTRL 8'h14
`define RIBP_ONEWIRE_STATUS 8'h18
// verilog_format: on
`endif
interface onewire_if ();
logic dat_o;
modport dut(output dat_o);
endinterface
// generate 1250ns for ws2812x only
module ribp_onewire (
// verilog_format: off
input logic clk_i,
input logic rst_n_i,
ribp_if.slave ribp,
onewire_if.dut onewire
// 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;
logic s_onewire_clkdiv_en;
logic [7:0] s_onewire_clkdiv_d, s_onewire_clkdiv_q;
logic s_onewire_zerocnt_en;
logic [7:0] s_onewire_zerocnt_d, s_onewire_zerocnt_q;
logic s_onewire_onecnt_en;
logic [7:0] s_onewire_onecnt_d, s_onewire_onecnt_q;
logic s_onewire_rstcnt_en;
logic [7:0] s_onewire_rstcnt_d, s_onewire_rstcnt_q;
logic [1:0] s_onewire_ctrl_d, s_onewire_ctrl_q;
logic [2:0] s_onewire_status_d, s_onewire_status_q;
// fifo
logic s_tx_push_valid, s_tx_empty, s_tx_full;
logic s_tx_pop_valid, s_tx_pop_ready;
logic [23:0] s_tx_push_data, s_tx_pop_data;
logic s_done;
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_onewire_clkdiv_en = s_ribp_wr_hdshk && ribp.addr[7:0] == `RIBP_ONEWIRE_CLKDIV;
assign s_onewire_clkdiv_d = ribp.wdata[7:0];
dffer #(8) u_onewire_clkdiv_dffer (
clk_i,
rst_n_i,
s_onewire_clkdiv_en,
s_onewire_clkdiv_d,
s_onewire_clkdiv_q
);
assign s_onewire_zerocnt_en = s_ribp_wr_hdshk && ribp.addr[7:0] == `RIBP_ONEWIRE_ZEROCNT;
assign s_onewire_zerocnt_d = ribp.wdata[7:0];
dffer #(8) u_onewire_zerocnt_dffer (
clk_i,
rst_n_i,
s_onewire_zerocnt_en,
s_onewire_zerocnt_d,
s_onewire_zerocnt_q
);
assign s_onewire_onecnt_en = s_ribp_wr_hdshk && ribp.addr[7:0] == `RIBP_ONEWIRE_ONECNT;
assign s_onewire_onecnt_d = ribp.wdata[7:0];
dffer #(8) u_onewire_onecnt_dffer (
clk_i,
rst_n_i,
s_onewire_onecnt_en,
s_onewire_onecnt_d,
s_onewire_onecnt_q
);
assign s_onewire_rstcnt_en = s_ribp_wr_hdshk && ribp.addr[7:0] == `RIBP_ONEWIRE_RSTCNT;
assign s_onewire_rstcnt_d = ribp.wdata[7:0];
dffer #(8) u_onewire_rstcnt_dffer (
clk_i,
rst_n_i,
s_onewire_rstcnt_en,
s_onewire_rstcnt_d,
s_onewire_rstcnt_q
);
always_comb begin
s_tx_push_valid = 1'b0;
s_tx_push_data = '0;
if (s_ribp_wr_hdshk && ribp.addr[7:0] == `RIBP_ONEWIRE_TXDATA) begin
s_tx_push_valid = 1'b1;
if (ribp.wstrb[0]) s_tx_push_data[7:0] = ribp.wdata[7:0];
if (ribp.wstrb[1]) s_tx_push_data[15:8] = ribp.wdata[15:8];
if (ribp.wstrb[2]) s_tx_push_data[23:16] = ribp.wdata[23:16];
end
end
assign s_tx_pop_ready = ~s_tx_empty;
fifo #(
.DATA_WIDTH (24),
.BUFFER_DEPTH(8)
) u_tx_fifo (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.flush_i(s_onewire_ctrl_q[0]),
.push_i (s_tx_push_valid),
.full_o (s_tx_full),
.dat_i (s_tx_push_data),
.pop_i (s_tx_pop_valid),
.empty_o(s_tx_empty),
.dat_o (s_tx_pop_data),
.cnt_o ()
);
// [0] clear fifo [1] start
always_comb begin
if (s_ribp_wr_hdshk && ribp.addr[7:0] == `RIBP_ONEWIRE_CTRL) begin
s_onewire_ctrl_d = ribp.wdata[1:0];
end else begin
s_onewire_ctrl_d = '0;
end
end
dffr #(2) u_onewire_ctrl_dffr (
clk_i,
rst_n_i,
s_onewire_ctrl_d,
s_onewire_ctrl_q
);
// [0] xfer done [1] fifo full, [2] fifo empty
always_comb begin
s_onewire_status_d = s_onewire_status_q;
s_onewire_status_d[1] = s_tx_full;
s_onewire_status_d[2] = s_tx_empty;
if (s_done) begin
s_onewire_status_d[0] = 1'b1;
end else if (s_ribp_rd_hdshk && ribp.addr[7:0] == `RIBP_ONEWIRE_STATUS) begin
s_onewire_status_d[0] = 1'b0;
end
end
dffr #(3) u_onewire_status_dffr (
clk_i,
rst_n_i,
s_onewire_status_d,
s_onewire_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_ONEWIRE_CLKDIV: s_ribp_rdata_d = {24'd0, s_onewire_clkdiv_q};
`RIBP_ONEWIRE_ZEROCNT: s_ribp_rdata_d = {24'd0, s_onewire_zerocnt_q};
`RIBP_ONEWIRE_ONECNT: s_ribp_rdata_d = {24'd0, s_onewire_onecnt_q};
`RIBP_ONEWIRE_RSTCNT: s_ribp_rdata_d = {24'd0, s_onewire_rstcnt_q};
`RIBP_ONEWIRE_STATUS: s_ribp_rdata_d = {29'd0, s_onewire_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
);
onewire_core u_onewire_core (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.clkdiv_i (s_onewire_clkdiv_q),
.zerocnt_i (s_onewire_zerocnt_q),
.onecnt_i (s_onewire_onecnt_q),
.rstcnt_i (s_onewire_rstcnt_q),
.start_i (s_onewire_ctrl_q[1]),
.data_req_o(s_tx_pop_valid),
.data_rdy_i(s_tx_pop_ready),
.data_i (s_tx_pop_data),
.done_o (s_done),
.onewire (onewire)
);
endmodule
-156
View File
@@ -1,156 +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.
module onewire_core (
// verilog_format: off
input logic clk_i,
input logic rst_n_i,
input logic [7:0] clkdiv_i,
input logic [7:0] zerocnt_i,
input logic [7:0] onecnt_i,
input logic [7:0] rstcnt_i,
input logic start_i,
output logic data_req_o,
input logic data_rdy_i,
input logic [23:0] data_i,
output logic done_o,
onewire_if.dut onewire
// verilog_format: on
);
localparam FSM_IDLE = 2'd0;
localparam FSM_XFER = 2'd1;
localparam FSM_RST = 2'd2;
logic [1:0] s_fsm_d, s_fsm_q;
logic s_clkdiv_cnt_en;
logic [7:0] s_clkdiv_cnt_d, s_clkdiv_cnt_q;
logic [7:0] s_rst_cnt_d, s_rst_cnt_q;
logic [4:0] s_bit_cnt_d, s_bit_cnt_q;
logic [23:0] s_xfer_data_d, s_xfer_data_q;
logic s_done_d, s_done_q;
assign done_o = s_done_q;
always_comb begin
s_fsm_d = s_fsm_q;
s_clkdiv_cnt_d = s_clkdiv_cnt_q;
s_bit_cnt_d = s_bit_cnt_q;
s_rst_cnt_d = s_rst_cnt_q;
s_xfer_data_d = s_xfer_data_q;
s_done_d = s_done_q;
data_req_o = '0;
onewire.dat_o = '0;
unique case (s_fsm_q)
FSM_IDLE: begin
s_done_d = 1'b0;
if (start_i && data_rdy_i) begin
data_req_o = 1'b1;
s_xfer_data_d = data_i;
s_fsm_d = FSM_XFER;
end
end
FSM_XFER: begin
// handle the data
// xfer '1'
if (s_xfer_data_q[23]) begin
if (s_clkdiv_cnt_q <= onecnt_i) onewire.dat_o = 1'b1;
else onewire.dat_o = 1'b0;
end else begin
if (s_clkdiv_cnt_q <= zerocnt_i) onewire.dat_o = 1'b1;
else onewire.dat_o = 1'b0;
end
s_clkdiv_cnt_d = s_clkdiv_cnt_q + 1'b1;
if (s_clkdiv_cnt_q == clkdiv_i) begin
s_clkdiv_cnt_d = '0;
s_xfer_data_d = {s_xfer_data_q[22:0], 1'b0};
s_bit_cnt_d = s_bit_cnt_q + 1'b1;
end
if (s_bit_cnt_q == 5'd23 && s_clkdiv_cnt_q == clkdiv_i) begin
s_bit_cnt_d = '0;
if (~data_rdy_i) begin
s_fsm_d = FSM_RST;
end else begin
data_req_o = 1'b1;
s_xfer_data_d = data_i;
end
end
end
FSM_RST: begin
if (s_rst_cnt_q == rstcnt_i) begin
s_rst_cnt_d = '0;
s_clkdiv_cnt_d = '0;
s_fsm_d = FSM_IDLE;
s_done_d = 1'b1;
end else begin
s_clkdiv_cnt_d = s_clkdiv_cnt_q + 1'b1;
if (s_clkdiv_cnt_q == clkdiv_i) begin
s_clkdiv_cnt_d = '0;
s_rst_cnt_d = s_rst_cnt_q + 1'b1;
end
end
end
default: begin
s_fsm_d = s_fsm_q;
s_clkdiv_cnt_d = s_clkdiv_cnt_q;
s_bit_cnt_d = s_bit_cnt_q;
s_rst_cnt_d = s_rst_cnt_q;
s_xfer_data_d = s_xfer_data_q;
s_done_d = s_done_q;
onewire.dat_o = '0;
end
endcase
end
dffr #(2) u_fsm_dffr (
clk_i,
rst_n_i,
s_fsm_d,
s_fsm_q
);
assign s_clkdiv_cnt_en = s_fsm_q != FSM_IDLE;
dffer #(8) u_clkdiv_cnt_dffer (
clk_i,
rst_n_i,
s_clkdiv_cnt_en,
s_clkdiv_cnt_d,
s_clkdiv_cnt_q
);
dffr #(1) u_done_dffr (
clk_i,
rst_n_i,
s_done_d,
s_done_q
);
dffr #(5) u_bit_cnt_dffr (
clk_i,
rst_n_i,
s_bit_cnt_d,
s_bit_cnt_q
);
dffr #(8) u_rst_cnt_dffr (
clk_i,
rst_n_i,
s_rst_cnt_d,
s_rst_cnt_q
);
dffr #(24) u_xfer_data_dffr (
clk_i,
rst_n_i,
s_xfer_data_d,
s_xfer_data_q
);
endmodule
+88
View File
@@ -0,0 +1,88 @@
// 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.
module ribp_ws2812 #(
parameter int TX_FIFO_DEPTH = 16
) (
// verilog_format: off
input logic clk_i,
input logic rst_n_i,
ribp_if.slave ribp,
ws2812_if.dut ws2812
// verilog_format: on
);
logic [15:0] s_bit_cycles;
logic [15:0] s_t0h_cycles;
logic [15:0] s_t1h_cycles;
logic [31:0] s_reset_cycles;
logic [31:0] s_frame_words;
logic s_start, s_abort;
logic s_data_valid, s_data_pop;
logic [23:0] s_data;
logic s_fifo_flush;
logic s_busy, s_reset_active;
logic [31:0] s_remaining_words;
logic s_done, s_underflow, s_aborted;
logic s_irq, s_dat;
assign ws2812.dat_o = s_dat;
assign ws2812.irq_o = s_irq;
ws2812_reg #(
.TX_FIFO_DEPTH(TX_FIFO_DEPTH)
) u_ws2812_reg (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.ribp (ribp),
.bit_cycles_o (s_bit_cycles),
.t0h_cycles_o (s_t0h_cycles),
.t1h_cycles_o (s_t1h_cycles),
.reset_cycles_o (s_reset_cycles),
.frame_words_o (s_frame_words),
.start_o (s_start),
.abort_o (s_abort),
.data_valid_o (s_data_valid),
.data_o (s_data),
.data_pop_i (s_data_pop),
.core_fifo_flush_i (s_fifo_flush),
.core_busy_i (s_busy),
.core_reset_active_i (s_reset_active),
.core_remaining_words_i(s_remaining_words),
.core_done_i (s_done),
.core_underflow_i (s_underflow),
.core_aborted_i (s_aborted),
.irq_o (s_irq)
);
ws2812_core u_ws2812_core (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.bit_cycles_i (s_bit_cycles),
.t0h_cycles_i (s_t0h_cycles),
.t1h_cycles_i (s_t1h_cycles),
.reset_cycles_i (s_reset_cycles),
.frame_words_i (s_frame_words),
.start_i (s_start),
.abort_i (s_abort),
.data_valid_i (s_data_valid),
.data_i (s_data),
.data_pop_o (s_data_pop),
.fifo_flush_o (s_fifo_flush),
.busy_o (s_busy),
.reset_active_o (s_reset_active),
.remaining_words_o(s_remaining_words),
.done_o (s_done),
.underflow_o (s_underflow),
.aborted_o (s_aborted),
.dat_o (s_dat)
);
endmodule
+279
View File
@@ -0,0 +1,279 @@
// 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.
module ws2812_core (
// verilog_format: off
input logic clk_i,
input logic rst_n_i,
input logic [15:0] bit_cycles_i,
input logic [15:0] t0h_cycles_i,
input logic [15:0] t1h_cycles_i,
input logic [31:0] reset_cycles_i,
input logic [31:0] frame_words_i,
input logic start_i,
input logic abort_i,
input logic data_valid_i,
input logic [23:0] data_i,
output logic data_pop_o,
output logic fifo_flush_o,
output logic busy_o,
output logic reset_active_o,
output logic [31:0] remaining_words_o,
output logic done_o,
output logic underflow_o,
output logic aborted_o,
output logic dat_o
// verilog_format: on
);
typedef enum logic [1:0] {
WS_IDLE,
WS_XFER,
WS_RESET
} ws_state_t;
ws_state_t s_state_d, s_state_q;
logic [15:0] s_bit_cycles_d, s_bit_cycles_q;
logic [15:0] s_t0h_cycles_d, s_t0h_cycles_q;
logic [15:0] s_t1h_cycles_d, s_t1h_cycles_q;
logic [31:0] s_reset_cycles_d, s_reset_cycles_q;
logic [15:0] s_bit_cycle_d, s_bit_cycle_q;
logic [4:0] s_bit_index_d, s_bit_index_q;
logic [31:0] s_reset_cycle_d, s_reset_cycle_q;
logic [31:0] s_remaining_words_d, s_remaining_words_q;
logic [23:0] s_xfer_data_d, s_xfer_data_q;
logic s_success_pending_d, s_success_pending_q;
logic s_abort_pending_d, s_abort_pending_q;
logic s_done_d, s_done_q;
logic s_underflow_d, s_underflow_q;
logic s_aborted_d, s_aborted_q;
logic [15:0] s_high_cycles;
assign busy_o = s_state_q != WS_IDLE;
assign reset_active_o = s_state_q == WS_RESET;
assign remaining_words_o = s_remaining_words_q;
assign done_o = s_done_q;
assign underflow_o = s_underflow_q;
assign aborted_o = s_aborted_q;
assign s_high_cycles = s_xfer_data_q[23] ? s_t1h_cycles_q : s_t0h_cycles_q;
always_comb begin
s_state_d = s_state_q;
s_bit_cycles_d = s_bit_cycles_q;
s_t0h_cycles_d = s_t0h_cycles_q;
s_t1h_cycles_d = s_t1h_cycles_q;
s_reset_cycles_d = s_reset_cycles_q;
s_bit_cycle_d = s_bit_cycle_q;
s_bit_index_d = s_bit_index_q;
s_reset_cycle_d = s_reset_cycle_q;
s_remaining_words_d = s_remaining_words_q;
s_xfer_data_d = s_xfer_data_q;
s_success_pending_d = s_success_pending_q;
s_abort_pending_d = s_abort_pending_q;
s_done_d = 1'b0;
s_underflow_d = 1'b0;
s_aborted_d = 1'b0;
data_pop_o = 1'b0;
fifo_flush_o = 1'b0;
dat_o = 1'b0;
unique case (s_state_q)
WS_IDLE: begin
s_bit_cycle_d = '0;
s_bit_index_d = '0;
s_reset_cycle_d = '0;
s_remaining_words_d = '0;
s_success_pending_d = 1'b0;
s_abort_pending_d = 1'b0;
if (start_i) begin
s_bit_cycles_d = bit_cycles_i;
s_t0h_cycles_d = t0h_cycles_i;
s_t1h_cycles_d = t1h_cycles_i;
s_reset_cycles_d = reset_cycles_i;
s_remaining_words_d = frame_words_i;
s_xfer_data_d = data_i;
data_pop_o = 1'b1;
s_state_d = WS_XFER;
end
end
WS_XFER: begin
dat_o = s_bit_cycle_q < s_high_cycles;
if (abort_i) begin
s_bit_cycle_d = '0;
s_bit_index_d = '0;
s_reset_cycle_d = '0;
s_remaining_words_d = '0;
s_success_pending_d = 1'b0;
s_abort_pending_d = 1'b1;
fifo_flush_o = 1'b1;
s_state_d = WS_RESET;
end else if (s_bit_cycle_q == (s_bit_cycles_q - 1'b1)) begin
s_bit_cycle_d = '0;
if (s_bit_index_q == 5'd23) begin
s_bit_index_d = '0;
if (s_remaining_words_q == 32'd1) begin
s_remaining_words_d = '0;
s_reset_cycle_d = '0;
s_success_pending_d = 1'b1;
s_state_d = WS_RESET;
end else if (data_valid_i) begin
s_remaining_words_d = s_remaining_words_q - 1'b1;
s_xfer_data_d = data_i;
data_pop_o = 1'b1;
end else begin
s_remaining_words_d = '0;
s_reset_cycle_d = '0;
s_success_pending_d = 1'b0;
s_abort_pending_d = 1'b0;
s_underflow_d = 1'b1;
fifo_flush_o = 1'b1;
s_state_d = WS_RESET;
end
end else begin
s_bit_index_d = s_bit_index_q + 1'b1;
s_xfer_data_d = {s_xfer_data_q[22:0], 1'b0};
end
end else begin
s_bit_cycle_d = s_bit_cycle_q + 1'b1;
end
end
WS_RESET: begin
if (abort_i) begin
s_reset_cycle_d = '0;
s_success_pending_d = 1'b0;
s_abort_pending_d = 1'b1;
fifo_flush_o = 1'b1;
end else if (s_reset_cycle_q == (s_reset_cycles_q - 1'b1)) begin
s_reset_cycle_d = '0;
s_state_d = WS_IDLE;
if (s_abort_pending_q) begin
s_aborted_d = 1'b1;
end else if (s_success_pending_q) begin
s_done_d = 1'b1;
end
s_success_pending_d = 1'b0;
s_abort_pending_d = 1'b0;
end else begin
s_reset_cycle_d = s_reset_cycle_q + 1'b1;
end
end
default: begin
s_state_d = WS_IDLE;
s_bit_cycle_d = '0;
s_bit_index_d = '0;
s_reset_cycle_d = '0;
s_remaining_words_d = '0;
s_success_pending_d = 1'b0;
s_abort_pending_d = 1'b0;
fifo_flush_o = 1'b1;
end
endcase
end
dffercn #(
.REG_TYPE (ws_state_t),
.RESET_VAL(WS_IDLE)
) u_state_dffercn (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (1'b1),
.dat_i (s_state_d),
.dat_o (s_state_q)
);
dffr #(16) u_bit_cycles_dffr (
clk_i,
rst_n_i,
s_bit_cycles_d,
s_bit_cycles_q
);
dffr #(16) u_t0h_cycles_dffr (
clk_i,
rst_n_i,
s_t0h_cycles_d,
s_t0h_cycles_q
);
dffr #(16) u_t1h_cycles_dffr (
clk_i,
rst_n_i,
s_t1h_cycles_d,
s_t1h_cycles_q
);
dffr #(32) u_reset_cycles_dffr (
clk_i,
rst_n_i,
s_reset_cycles_d,
s_reset_cycles_q
);
dffr #(16) u_bit_cycle_dffr (
clk_i,
rst_n_i,
s_bit_cycle_d,
s_bit_cycle_q
);
dffr #(5) u_bit_index_dffr (
clk_i,
rst_n_i,
s_bit_index_d,
s_bit_index_q
);
dffr #(32) u_reset_cycle_dffr (
clk_i,
rst_n_i,
s_reset_cycle_d,
s_reset_cycle_q
);
dffr #(32) u_remaining_words_dffr (
clk_i,
rst_n_i,
s_remaining_words_d,
s_remaining_words_q
);
dffr #(24) u_xfer_data_dffr (
clk_i,
rst_n_i,
s_xfer_data_d,
s_xfer_data_q
);
dffr #(1) u_success_pending_dffr (
clk_i,
rst_n_i,
s_success_pending_d,
s_success_pending_q
);
dffr #(1) u_abort_pending_dffr (
clk_i,
rst_n_i,
s_abort_pending_d,
s_abort_pending_q
);
dffr #(1) u_done_dffr (
clk_i,
rst_n_i,
s_done_d,
s_done_q
);
dffr #(1) u_underflow_dffr (
clk_i,
rst_n_i,
s_underflow_d,
s_underflow_q
);
dffr #(1) u_aborted_dffr (
clk_i,
rst_n_i,
s_aborted_d,
s_aborted_q
);
endmodule
+46
View File
@@ -0,0 +1,46 @@
// Copyright (c) 2023-2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
// See the Mulan PSL v2 for more details.
`ifndef WS2812_DEFINE_SVH
`define WS2812_DEFINE_SVH
// verilog_format: off
`define RIBP_WS2812_BIT_CYCLES 8'h00
`define RIBP_WS2812_T0H_CYCLES 8'h04
`define RIBP_WS2812_T1H_CYCLES 8'h08
`define RIBP_WS2812_RESET_CYCLES 8'h0C
`define RIBP_WS2812_TXDATA 8'h10
`define RIBP_WS2812_CTRL 8'h14
`define RIBP_WS2812_STATUS 8'h18
`define RIBP_WS2812_FRAME_WORDS 8'h1C
`define RIBP_WS2812_FIFO_LEVEL 8'h20
`define RIBP_WS2812_FIFO_WATERMARK 8'h24
`define RIBP_WS2812_REMAINING_WORDS 8'h28
`define RIBP_WS2812_ERROR_STATUS 8'h2C
`define RIBP_WS2812_INTR_STATE 8'h30
`define RIBP_WS2812_INTR_ENABLE 8'h34
`define RIBP_WS2812_INTR_TEST 8'h38
`define RIBP_WS2812_IP_INFO 8'h3C
`define WS2812_CTRL_START 0
`define WS2812_CTRL_ABORT 1
`define WS2812_CTRL_FIFO_FLUSH 2
`define WS2812_STATUS_BUSY 0
`define WS2812_STATUS_FIFO_EMPTY 1
`define WS2812_STATUS_FIFO_FULL 2
`define WS2812_STATUS_CONFIG_VALID 3
`define WS2812_STATUS_RESET_ACTIVE 4
`define WS2812_ERROR_CONFIG 0
`define WS2812_ERROR_UNDERFLOW 1
`define WS2812_ERROR_COMMAND 2
`define WS2812_INTR_DONE 0
`define WS2812_INTR_FIFO_LOW 1
`define WS2812_INTR_ERROR 2
`define WS2812_INTR_ABORTED 3
// verilog_format: on
`endif
+16
View File
@@ -0,0 +1,16 @@
// 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.
interface ws2812_if ();
logic dat_o;
logic irq_o;
modport dut(output dat_o, output irq_o);
endinterface
+458
View File
@@ -0,0 +1,458 @@
// 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.
`include "ws2812_define.svh"
module ws2812_reg #(
parameter int TX_FIFO_DEPTH = 16,
parameter int TX_FIFO_LOG_DEPTH = $clog2(TX_FIFO_DEPTH)
) (
// verilog_format: off
input logic clk_i,
input logic rst_n_i,
ribp_if.slave ribp,
output logic [15:0] bit_cycles_o,
output logic [15:0] t0h_cycles_o,
output logic [15:0] t1h_cycles_o,
output logic [31:0] reset_cycles_o,
output logic [31:0] frame_words_o,
output logic start_o,
output logic abort_o,
output logic data_valid_o,
output logic [23:0] data_o,
input logic data_pop_i,
input logic core_fifo_flush_i,
input logic core_busy_i,
input logic core_reset_active_i,
input logic [31:0] core_remaining_words_i,
input logic core_done_i,
input logic core_underflow_i,
input logic core_aborted_i,
output logic irq_o
// verilog_format: on
);
localparam logic [31:0] FIFO_WATERMARK_RESET = (TX_FIFO_DEPTH > 4) ? 32'd4 : TX_FIFO_DEPTH - 1;
localparam logic [7:0] FIFO_DEPTH_INFO = TX_FIFO_DEPTH[7:0];
localparam logic [31:0] IP_INFO = {8'd24, FIFO_DEPTH_INFO, 8'd1, 8'd0};
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_bit_cycles_en;
logic [15:0] s_bit_cycles_d, s_bit_cycles_q;
logic s_t0h_cycles_en;
logic [15:0] s_t0h_cycles_d, s_t0h_cycles_q;
logic s_t1h_cycles_en;
logic [15:0] s_t1h_cycles_d, s_t1h_cycles_q;
logic s_reset_cycles_en;
logic [31:0] s_reset_cycles_d, s_reset_cycles_q;
logic s_frame_words_en;
logic [31:0] s_frame_words_d, s_frame_words_q;
logic s_fifo_watermark_en;
logic [31:0] s_fifo_watermark_d, s_fifo_watermark_q;
logic s_intr_enable_en;
logic [3:0] s_intr_enable_d, s_intr_enable_q;
logic s_fifo_flush_cmd;
logic s_tx_push;
logic [23:0] s_tx_push_data;
logic s_tx_empty, s_tx_full;
logic [ 23:0] s_tx_pop_data;
logic [TX_FIFO_LOG_DEPTH:0] s_tx_count;
logic [31:0] s_load_remaining_d, s_load_remaining_q;
logic [2:0] s_error_status_d, s_error_status_q;
logic [3:0] s_intr_state_d, s_intr_state_q;
logic [ 2:0] s_error_clear;
logic [ 3:0] s_intr_clear;
logic [ 3:0] s_intr_test;
logic s_config_error_event;
logic s_command_error_event;
logic s_fifo_low;
logic s_config_valid;
logic [31:0] s_status;
logic [31:0] s_fifo_level;
logic [31:0] s_watermark_write_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
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 bit_cycles_o = s_bit_cycles_q;
assign t0h_cycles_o = s_t0h_cycles_q;
assign t1h_cycles_o = s_t1h_cycles_q;
assign reset_cycles_o = s_reset_cycles_q;
assign frame_words_o = s_frame_words_q;
assign data_valid_o = !s_tx_empty;
assign data_o = s_tx_pop_data;
assign irq_o = |(s_intr_state_q & s_intr_enable_q);
assign s_fifo_level = {{(31 - TX_FIFO_LOG_DEPTH) {1'b0}}, s_tx_count};
assign s_watermark_write_value = merge_wstrb(s_fifo_watermark_q, ribp.wdata, ribp.wstrb);
assign s_config_valid = (s_bit_cycles_q != 16'd0) &&
(s_t0h_cycles_q != 16'd0) &&
(s_t1h_cycles_q != 16'd0) &&
(s_t0h_cycles_q < s_t1h_cycles_q) &&
(s_t1h_cycles_q < s_bit_cycles_q) &&
(s_reset_cycles_q != 32'd0);
assign s_fifo_low = core_busy_i && (s_load_remaining_q != 32'd0) &&
(s_fifo_level <= s_fifo_watermark_q);
always_comb begin
s_status = '0;
s_status[`WS2812_STATUS_BUSY] = core_busy_i;
s_status[`WS2812_STATUS_FIFO_EMPTY] = s_tx_empty;
s_status[`WS2812_STATUS_FIFO_FULL] = s_tx_full;
s_status[`WS2812_STATUS_CONFIG_VALID] = s_config_valid;
s_status[`WS2812_STATUS_RESET_ACTIVE] = core_reset_active_i;
end
always_comb begin
s_req_accept = s_req;
s_access_error = 1'b0;
s_bit_cycles_en = 1'b0;
s_t0h_cycles_en = 1'b0;
s_t1h_cycles_en = 1'b0;
s_reset_cycles_en = 1'b0;
s_frame_words_en = 1'b0;
s_fifo_watermark_en = 1'b0;
s_intr_enable_en = 1'b0;
s_bit_cycles_d = s_bit_cycles_q;
s_t0h_cycles_d = s_t0h_cycles_q;
s_t1h_cycles_d = s_t1h_cycles_q;
s_reset_cycles_d = s_reset_cycles_q;
s_frame_words_d = s_frame_words_q;
s_fifo_watermark_d = s_fifo_watermark_q;
s_intr_enable_d = s_intr_enable_q;
s_fifo_flush_cmd = 1'b0;
s_tx_push = 1'b0;
s_tx_push_data = ribp.wdata[23:0];
start_o = 1'b0;
abort_o = 1'b0;
s_error_clear = '0;
s_intr_clear = '0;
s_intr_test = '0;
s_config_error_event = 1'b0;
s_command_error_event = 1'b0;
s_ribp_rdata_d = '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_WS2812_BIT_CYCLES: begin
s_bit_cycles_en = 1'b1;
s_bit_cycles_d = 16'(merge_wstrb({16'd0, s_bit_cycles_q}, ribp.wdata, ribp.wstrb));
end
`RIBP_WS2812_T0H_CYCLES: begin
s_t0h_cycles_en = 1'b1;
s_t0h_cycles_d = 16'(merge_wstrb({16'd0, s_t0h_cycles_q}, ribp.wdata, ribp.wstrb));
end
`RIBP_WS2812_T1H_CYCLES: begin
s_t1h_cycles_en = 1'b1;
s_t1h_cycles_d = 16'(merge_wstrb({16'd0, s_t1h_cycles_q}, ribp.wdata, ribp.wstrb));
end
`RIBP_WS2812_RESET_CYCLES: begin
s_reset_cycles_en = 1'b1;
s_reset_cycles_d = merge_wstrb(s_reset_cycles_q, ribp.wdata, ribp.wstrb);
end
`RIBP_WS2812_TXDATA: begin
if (ribp.wstrb != 4'hF || (core_busy_i && (s_load_remaining_q == 32'd0))) begin
s_access_error = 1'b1;
s_command_error_event = 1'b1;
end else if (s_tx_full && core_busy_i) begin
s_req_accept = 1'b0;
end else if (s_tx_full && !core_busy_i) begin
s_access_error = 1'b1;
s_command_error_event = 1'b1;
end else begin
s_tx_push = 1'b1;
end
end
`RIBP_WS2812_CTRL: begin
if (!ribp.wstrb[0]) begin
s_access_error = 1'b1;
s_command_error_event = 1'b1;
end else begin
unique case (ribp.wdata[2:0])
3'b001: begin
if (core_busy_i || (s_frame_words_q == 32'd0) || s_tx_empty ||
(s_fifo_level > s_frame_words_q)) begin
s_access_error = 1'b1;
s_command_error_event = 1'b1;
end else if (!s_config_valid) begin
s_access_error = 1'b1;
s_config_error_event = 1'b1;
end else begin
start_o = 1'b1;
end
end
3'b010: begin
if (!core_busy_i) begin
s_access_error = 1'b1;
s_command_error_event = 1'b1;
end else begin
abort_o = 1'b1;
end
end
3'b100: begin
if (core_busy_i) begin
s_access_error = 1'b1;
s_command_error_event = 1'b1;
end else begin
s_fifo_flush_cmd = 1'b1;
end
end
default: begin
s_access_error = 1'b1;
s_command_error_event = 1'b1;
end
endcase
end
end
`RIBP_WS2812_FRAME_WORDS: begin
s_frame_words_en = 1'b1;
s_frame_words_d = merge_wstrb(s_frame_words_q, ribp.wdata, ribp.wstrb);
end
`RIBP_WS2812_FIFO_WATERMARK: begin
if (s_watermark_write_value >= TX_FIFO_DEPTH) begin
s_access_error = 1'b1;
s_command_error_event = 1'b1;
end else begin
s_fifo_watermark_en = 1'b1;
s_fifo_watermark_d = s_watermark_write_value;
end
end
`RIBP_WS2812_ERROR_STATUS: begin
if (!ribp.wstrb[0]) begin
s_access_error = 1'b1;
end else begin
s_error_clear = ribp.wdata[2:0];
end
end
`RIBP_WS2812_INTR_STATE: begin
if (!ribp.wstrb[0]) begin
s_access_error = 1'b1;
end else begin
s_intr_clear = ribp.wdata[3:0];
end
end
`RIBP_WS2812_INTR_ENABLE: begin
s_intr_enable_en = 1'b1;
s_intr_enable_d = 4'(merge_wstrb({28'd0, s_intr_enable_q}, ribp.wdata, ribp.wstrb));
end
`RIBP_WS2812_INTR_TEST: begin
if (!ribp.wstrb[0]) begin
s_access_error = 1'b1;
end else begin
s_intr_test = ribp.wdata[3:0];
end
end
default: s_access_error = 1'b1;
endcase
end else begin
unique case (ribp.addr[7:0])
`RIBP_WS2812_BIT_CYCLES: s_ribp_rdata_d = {16'd0, s_bit_cycles_q};
`RIBP_WS2812_T0H_CYCLES: s_ribp_rdata_d = {16'd0, s_t0h_cycles_q};
`RIBP_WS2812_T1H_CYCLES: s_ribp_rdata_d = {16'd0, s_t1h_cycles_q};
`RIBP_WS2812_RESET_CYCLES: s_ribp_rdata_d = s_reset_cycles_q;
`RIBP_WS2812_STATUS: s_ribp_rdata_d = s_status;
`RIBP_WS2812_FRAME_WORDS: s_ribp_rdata_d = s_frame_words_q;
`RIBP_WS2812_FIFO_LEVEL: s_ribp_rdata_d = s_fifo_level;
`RIBP_WS2812_FIFO_WATERMARK: s_ribp_rdata_d = s_fifo_watermark_q;
`RIBP_WS2812_REMAINING_WORDS: s_ribp_rdata_d = core_remaining_words_i;
`RIBP_WS2812_ERROR_STATUS: s_ribp_rdata_d = {29'd0, s_error_status_q};
`RIBP_WS2812_INTR_STATE: s_ribp_rdata_d = {28'd0, s_intr_state_q};
`RIBP_WS2812_INTR_ENABLE: s_ribp_rdata_d = {28'd0, s_intr_enable_q};
`RIBP_WS2812_IP_INFO: s_ribp_rdata_d = IP_INFO;
default: begin
s_ribp_rdata_d = '0;
s_access_error = 1'b1;
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,
rst_n_i,
s_ribp_ready_d,
s_ribp_ready_q
);
dffer #(1) u_ribp_resp_err_dffer (
clk_i,
rst_n_i,
s_req_accept,
s_ribp_resp_err_d,
s_ribp_resp_err_q
);
dffer #(32) u_ribp_rdata_dffer (
clk_i,
rst_n_i,
s_req_accept,
s_ribp_rdata_d,
s_ribp_rdata_q
);
dffer #(16) u_bit_cycles_dffer (
clk_i,
rst_n_i,
s_bit_cycles_en,
s_bit_cycles_d,
s_bit_cycles_q
);
dffer #(16) u_t0h_cycles_dffer (
clk_i,
rst_n_i,
s_t0h_cycles_en,
s_t0h_cycles_d,
s_t0h_cycles_q
);
dffer #(16) u_t1h_cycles_dffer (
clk_i,
rst_n_i,
s_t1h_cycles_en,
s_t1h_cycles_d,
s_t1h_cycles_q
);
dffer #(32) u_reset_cycles_dffer (
clk_i,
rst_n_i,
s_reset_cycles_en,
s_reset_cycles_d,
s_reset_cycles_q
);
dffer #(32) u_frame_words_dffer (
clk_i,
rst_n_i,
s_frame_words_en,
s_frame_words_d,
s_frame_words_q
);
dfferc #(
.DATA_WIDTH(32),
.RESET_VAL (FIFO_WATERMARK_RESET)
) u_fifo_watermark_dfferc (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_fifo_watermark_en),
.dat_i (s_fifo_watermark_d),
.dat_o (s_fifo_watermark_q)
);
dffer #(4) u_intr_enable_dffer (
clk_i,
rst_n_i,
s_intr_enable_en,
s_intr_enable_d,
s_intr_enable_q
);
fifo #(
.DATA_WIDTH (24),
.BUFFER_DEPTH (TX_FIFO_DEPTH),
.LOG_BUFFER_DEPTH(TX_FIFO_LOG_DEPTH)
) u_tx_fifo (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.flush_i(s_fifo_flush_cmd || core_fifo_flush_i),
.push_i (s_tx_push),
.full_o (s_tx_full),
.dat_i (s_tx_push_data),
.pop_i (data_pop_i),
.empty_o(s_tx_empty),
.dat_o (s_tx_pop_data),
.cnt_o (s_tx_count)
);
always_comb begin
s_load_remaining_d = s_load_remaining_q;
if (start_o) begin
s_load_remaining_d = s_frame_words_q - s_fifo_level;
end else if (core_done_i || core_underflow_i || core_aborted_i) begin
s_load_remaining_d = '0;
end else if (s_tx_push && core_busy_i) begin
s_load_remaining_d = s_load_remaining_q - 1'b1;
end
end
dffr #(32) u_load_remaining_dffr (
clk_i,
rst_n_i,
s_load_remaining_d,
s_load_remaining_q
);
always_comb begin
s_error_status_d = s_error_status_q & ~s_error_clear;
if (s_config_error_event) begin
s_error_status_d[`WS2812_ERROR_CONFIG] = 1'b1;
end
if (core_underflow_i) begin
s_error_status_d[`WS2812_ERROR_UNDERFLOW] = 1'b1;
end
if (s_command_error_event) begin
s_error_status_d[`WS2812_ERROR_COMMAND] = 1'b1;
end
end
dffr #(3) u_error_status_dffr (
clk_i,
rst_n_i,
s_error_status_d,
s_error_status_q
);
always_comb begin
s_intr_state_d = (s_intr_state_q & ~s_intr_clear) | s_intr_test;
if (core_done_i) begin
s_intr_state_d[`WS2812_INTR_DONE] = 1'b1;
end
if (s_fifo_low) begin
s_intr_state_d[`WS2812_INTR_FIFO_LOW] = 1'b1;
end
if (s_config_error_event || s_command_error_event || core_underflow_i) begin
s_intr_state_d[`WS2812_INTR_ERROR] = 1'b1;
end
if (core_aborted_i) begin
s_intr_state_d[`WS2812_INTR_ABORTED] = 1'b1;
end
end
dffr #(4) u_intr_state_dffr (
clk_i,
rst_n_i,
s_intr_state_d,
s_intr_state_q
);
endmodule
+1 -1
View File
@@ -43,7 +43,7 @@
{"symbol": "RIBP_SPISD", "base": "0x10005000", "size": "0x00001000", "route": "ribp", "kind": "active", "public": true, "user_access": "rw"},
{"symbol": "RIBP_I2C0", "base": "0x10006000", "size": "0x00001000", "route": "ribp", "kind": "active", "public": true, "user_access": "rw"},
{"symbol": "RIBP_I2S", "base": "0x10007000", "size": "0x00001000", "route": "ribp", "kind": "active", "public": true, "user_access": "rw"},
{"symbol": "RIBP_ONEWIRE", "base": "0x10008000", "size": "0x00001000", "route": "ribp", "kind": "active", "public": true, "user_access": "rw"},
{"symbol": "RIBP_WS2812", "base": "0x10008000", "size": "0x00001000", "route": "ribp", "kind": "active", "public": true, "user_access": "rw"},
{"symbol": "RIBP_XPI", "base": "0x10009000", "size": "0x00001000", "route": "ribp", "kind": "active", "public": true, "user_access": "none"},
{"symbol": "RIBP_DMA", "base": "0x1000A000", "size": "0x00001000", "route": "ribp", "kind": "active", "public": true, "user_access": "none"},
{"symbol": "RIBP_SYSCTRL", "base": "0x1000B000", "size": "0x00001000", "route": "ribp", "kind": "active", "public": true, "user_access": "none"},
+4 -2
View File
@@ -19,8 +19,10 @@
/ip/ribp/serial/i2s_send.sv
/ip/ribp/serial/i2s_core.sv
/ip/ribp/serial/i2s.sv
/ip/ribp/serial/onewire_core.sv
/ip/ribp/serial/onewire.sv
/ip/ribp/serial/ws2812_if.sv
/ip/ribp/serial/ws2812_reg.sv
/ip/ribp/serial/ws2812_core.sv
/ip/ribp/serial/ribp_ws2812.sv
/ip/ribp/storage/xpi_reg.sv
/ip/ribp/storage/xpi_mm.sv
/ip/ribp/storage/xpi_clkgen.sv
@@ -19,6 +19,7 @@ from filelist import FileList, write_filelist # noqa: E402
COMMON_RTL = ROOT / "rtl/managed/clusterip/common/rtl"
INTERCONNECT = ROOT / "rtl/ip/ribp/interconnect"
PERIPHERAL = ROOT / "rtl/ip/ribp/peripheral"
SERIAL = ROOT / "rtl/ip/ribp/serial"
TOP = ROOT / "rtl/mini/top"
@@ -87,6 +88,17 @@ def source_files(target: str) -> list[Path]:
PERIPHERAL / "gpio.sv",
SCRIPT_DIR / "gpio_user_formal.sv",
]
if target == "ws2812":
return [
COMMON_RTL / "interface/ribp_if.sv",
COMMON_RTL / "utils/register.sv",
COMMON_RTL / "utils/fifo.sv",
SERIAL / "ws2812_if.sv",
SERIAL / "ws2812_reg.sv",
SERIAL / "ws2812_core.sv",
SERIAL / "ribp_ws2812.sv",
SCRIPT_DIR / "ws2812_formal.sv",
]
raise ValueError(f"unsupported formal target: {target}")
@@ -109,6 +121,7 @@ def generate(
COMMON_RTL / "utils",
INTERCONNECT,
PERIPHERAL,
SERIAL,
],
files=source_files(target),
).deduplicate()
@@ -126,6 +139,7 @@ def parse_args() -> argparse.Namespace:
"sysctrl",
"pll_rcu",
"gpio_user",
"ws2812",
),
required=True,
)
+85
View File
@@ -0,0 +1,85 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
module ws2812_formal_design (
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 dat,
output logic irq,
output logic busy,
output logic reset_active,
output logic start,
output logic abort_cmd,
output logic data_valid,
output logic data_pop,
output logic done,
output logic underflow,
output logic aborted,
output logic [ 4:0] fifo_level,
output logic [31:0] load_remaining,
output logic [ 2:0] error_status,
output logic [ 3:0] intr_state,
output logic [ 3:0] intr_enable
);
ribp_if rib ();
ws2812_if ws2812 ();
(* 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;
assign rib.valid = f_rib_valid;
assign rib.addr = f_rib_addr;
assign rib.wdata = f_rib_wdata;
assign rib.wstrb = f_rib_wstrb;
assign rib_valid = rib.valid;
assign rib_addr = rib.addr;
assign rib_wdata = rib.wdata;
assign rib_wstrb = rib.wstrb;
assign rib_ready = rib.ready;
assign rib_resp_err = rib.resp_err;
assign dat = ws2812.dat_o;
assign irq = ws2812.irq_o;
assign busy = u_dut.s_busy;
assign reset_active = u_dut.s_reset_active;
assign start = u_dut.s_start;
assign abort_cmd = u_dut.s_abort;
assign data_valid = u_dut.s_data_valid;
assign data_pop = u_dut.s_data_pop;
assign done = u_dut.s_done;
assign underflow = u_dut.s_underflow;
assign aborted = u_dut.s_aborted;
assign fifo_level = u_dut.u_ws2812_reg.s_tx_count;
assign load_remaining = u_dut.u_ws2812_reg.s_load_remaining_q;
assign error_status = u_dut.u_ws2812_reg.s_error_status_q;
assign intr_state = u_dut.u_ws2812_reg.s_intr_state_q;
assign intr_enable = u_dut.u_ws2812_reg.s_intr_enable_q;
ribp_ws2812 u_dut (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.ribp (rib),
.ws2812 (ws2812)
);
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
+100
View File
@@ -0,0 +1,100 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
module ws2812_formal;
(* anyseq *) (* gclk *)reg 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 dat;
wire irq;
wire busy;
wire reset_active;
wire start;
wire abort_cmd;
wire data_valid;
wire data_pop;
wire done;
wire underflow;
wire aborted;
wire [ 4:0] fifo_level;
wire [31:0] load_remaining;
wire [ 2:0] error_status;
wire [ 3:0] intr_state;
wire [ 3:0] intr_enable;
ws2812_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),
.dat (dat),
.irq (irq),
.busy (busy),
.reset_active (reset_active),
.start (start),
.abort_cmd (abort_cmd),
.data_valid (data_valid),
.data_pop (data_pop),
.done (done),
.underflow (underflow),
.aborted (aborted),
.fifo_level (fifo_level),
.load_remaining(load_remaining),
.error_status (error_status),
.intr_state (intr_state),
.intr_enable (intr_enable)
);
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 (fifo_level <= 5'd16);
assert (irq == |(intr_state & intr_enable));
if (!busy || reset_active) begin
assert (!dat);
end
if (data_pop) begin
assert (data_valid);
end
if (f_past_valid && $past(rst_n_i && start)) begin
assert (busy);
end
if (f_past_valid && $past(rst_n_i && done)) begin
assert (intr_state[0]);
end
if (f_past_valid && $past(rst_n_i && underflow)) begin
assert (error_status[1]);
assert (intr_state[2]);
end
if (f_past_valid && $past(rst_n_i && aborted)) begin
assert (intr_state[3]);
end
cover (busy && dat);
cover (reset_active);
cover (busy && (load_remaining != 32'd0) && (fifo_level == 5'd0));
cover (aborted);
cover (irq);
cover (rib_ready && rib_resp_err);
end
end
endmodule
@@ -44,6 +44,7 @@ COMPATIBILITY_IRQ_BINDINGS = (
("watchdog_reset", "apb", 4, 14, "u_wdg_if.rst_o"),
("advanced_timer", "apb", 5, 15, "u_tmr_if.irq_o"),
("reserved", "apb", 6, 16, "1'b0"),
("ws2812", "ribp", 10, 17, "ws2812.irq_o"),
)
+5 -4
View File
@@ -3,7 +3,7 @@
"gpio_pins": 32,
"irq_vector_width": 32,
"irq_groups": [
{"name": "ribp", "width": 10, "top_signal": "s_ribp_irq"},
{"name": "ribp", "width": 11, "top_signal": "s_ribp_irq"},
{"name": "apb", "width": 7, "top_signal": "s_apb_irq"}
],
"interrupts": [
@@ -23,7 +23,8 @@
{"name": "rtc", "description": "APB RTC interrupt", "group": "apb", "group_bit": 3, "core_bit": 13, "signal": "u_rtc_if.irq_o"},
{"name": "watchdog_reset", "description": "APB watchdog reset request", "group": "apb", "group_bit": 4, "core_bit": 14, "signal": "u_wdg_if.rst_o"},
{"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": "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"}
],
"ribp_targets": [
{"slot": 0, "name": "uart0", "interface": "u_uart_ribp_if", "regions": ["RIBP_UART0"]},
@@ -34,7 +35,7 @@
{"slot": 5, "name": "spisd", "interface": "u_spisd_ribp_if", "regions": ["RIBP_SPISD", "SPISD"]},
{"slot": 6, "name": "i2c0", "interface": "u_i2c0_ribp_if", "regions": ["RIBP_I2C0"]},
{"slot": 7, "name": "i2s", "interface": "u_i2s_ribp_if", "regions": ["RIBP_I2S"]},
{"slot": 8, "name": "onewire", "interface": "u_onewire_ribp_if", "regions": ["RIBP_ONEWIRE"]},
{"slot": 8, "name": "ws2812", "interface": "u_ws2812_ribp_if", "regions": ["RIBP_WS2812"]},
{"slot": 9, "name": "xpi", "interface": "u_xpi_ribp_if", "regions": ["RIBP_XPI", "FLASH", "XPI"]},
{"slot": 10, "name": "dma", "interface": "u_dma_ribp_if", "regions": ["RIBP_DMA"]},
{"slot": 11, "name": "sysctrl", "interface": "u_sysctrl_ribp_if", "regions": ["RIBP_SYSCTRL"]},
@@ -67,7 +68,7 @@
"gpio_alt_functions": [
{"pin": 0, "alt0": {"inputs": ["u_uart1_if.rx_i"], "do": "1'b0", "oe": "1'b0"}, "alt1": {"inputs": ["u_ps2_if.ps2_clk_i"], "do": "1'b0", "oe": "1'b0"}},
{"pin": 1, "alt0": {"inputs": [], "do": "u_uart1_if.tx_o", "oe": "1'b1"}, "alt1": {"inputs": ["u_ps2_if.ps2_dat_i"], "do": "1'b0", "oe": "1'b0"}},
{"pin": 2, "alt0": {"inputs": ["s_tmr_capch"], "do": "1'b0", "oe": "1'b0"}, "alt1": {"inputs": [], "do": "u_onewire_if.dat_o", "oe": "1'b1"}},
{"pin": 2, "alt0": {"inputs": ["s_tmr_capch"], "do": "1'b0", "oe": "1'b0"}, "alt1": {"inputs": [], "do": "u_ws2812_if.dat_o", "oe": "1'b1"}},
{"pin": 3, "alt0": {"inputs": [], "do": "u_pwm_if.do_o[0]", "oe": "1'b1"}, "alt1": {"inputs": ["u_i2c1_if.scl_i"], "do": "u_i2c1_if.scl_o", "oe": "u_i2c1_if.scl_oe_o"}},
{"pin": 4, "alt0": {"inputs": [], "do": "u_pwm_if.do_o[1]", "oe": "1'b1"}, "alt1": {"inputs": ["u_i2c1_if.sda_i"], "do": "u_i2c1_if.sda_o", "oe": "u_i2c1_if.sda_oe_o"}},
{"pin": 5, "alt0": {"inputs": [], "do": "u_pwm_if.do_o[2]", "oe": "1'b1"}, "alt1": {"inputs": [], "do": "clkdiv4_i", "oe": "1'b1"}},
+16 -6
View File
@@ -5,8 +5,10 @@ FORMAL_SOLVER := bitwuzla
FORMAL_SOLVER_DIR := $(FORMAL_DIR)/bin
FORMAL_SOLVER_WRAPPER := $(FORMAL_SOLVER_DIR)/bitwuzla
FORMAL_DEPTH ?= 20
FORMAL_WS2812_DEPTH ?= 120
FORMAL_TIMEOUT ?= 60
FORMAL_TARGETS := bus rib_adapter rib2apb sysctrl pll_rcu gpio_user
FORMAL_WS2812_TIMEOUT ?= 120
FORMAL_TARGETS := bus rib_adapter rib2apb sysctrl pll_rcu gpio_user ws2812
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
@@ -22,6 +24,8 @@ FORMAL_SOURCE_FILES := $(RTL_PATH)/formal/bus_formal.sv \
$(RTL_PATH)/formal/pll_rcu_formal_props.sv \
$(RTL_PATH)/formal/gpio_user_formal.sv \
$(RTL_PATH)/formal/gpio_user_formal_props.sv \
$(RTL_PATH)/formal/ws2812_formal.sv \
$(RTL_PATH)/formal/ws2812_formal_props.sv \
$(RTL_PATH)/top/bus.sv \
$(RTL_PATH)/top/rib_error_slave.sv \
$(RTL_PATH)/top/rib_if.sv \
@@ -33,6 +37,10 @@ FORMAL_SOURCE_FILES := $(RTL_PATH)/formal/bus_formal.sv \
$(ROOT_PATH)/rtl/ip/ribp/peripheral/gpio.sv \
$(ROOT_PATH)/rtl/ip/ribp/peripheral/pll_ctrl_if.sv \
$(ROOT_PATH)/rtl/ip/ribp/peripheral/sysctrl.sv \
$(ROOT_PATH)/rtl/ip/ribp/serial/ws2812_if.sv \
$(ROOT_PATH)/rtl/ip/ribp/serial/ws2812_reg.sv \
$(ROOT_PATH)/rtl/ip/ribp/serial/ws2812_core.sv \
$(ROOT_PATH)/rtl/ip/ribp/serial/ribp_ws2812.sv \
$(RTL_PATH)/top/rcu.sv \
$(ROOT_PATH)/rtl/managed/clusterip/common/rtl/interface/ribp_if.sv \
$(ROOT_PATH)/rtl/managed/clusterip/common/rtl/interface/apb4_pure_if.sv \
@@ -71,13 +79,13 @@ $(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 $(FORMAL_DEPTH) --output $@
--mode prove --depth $(if $(filter ws2812,$*),$(FORMAL_WS2812_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 $(FORMAL_DEPTH) --output $@
--mode cover --depth $(if $(filter ws2812,$*),$(FORMAL_WS2812_DEPTH),$(FORMAL_DEPTH)) --output $@
$(FORMAL_SOLVER_WRAPPER): $(ROOT_PATH)/scripts/bitwuzla_smt2.py
@mkdir -p $(@D)
@@ -89,7 +97,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 $(FORMAL_TIMEOUT)s $(FORMAL_SBY) \
-- timeout --foreground --kill-after=5s $(if $(filter ws2812,$*),$(FORMAL_WS2812_TIMEOUT),$(FORMAL_TIMEOUT))s $(FORMAL_SBY) \
-f -d $(@D)/prove $<
@test "$$(awk '{print $$1}' $(@D)/prove/status)" = PASS
@touch $@
@@ -99,7 +107,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 $(FORMAL_TIMEOUT)s $(FORMAL_SBY) \
-- timeout --foreground --kill-after=5s $(if $(filter ws2812,$*),$(FORMAL_WS2812_TIMEOUT),$(FORMAL_TIMEOUT))s $(FORMAL_SBY) \
-f -d $(@D)/cover $<
@test "$$(awk '{print $$1}' $(@D)/cover/status)" = PASS
@touch $@
@@ -125,10 +133,12 @@ formal-pll-rcu: $(FORMAL_DIR)/pll_rcu/.stamp | manifest
formal-gpio-user: $(FORMAL_DIR)/gpio_user/.stamp | manifest
formal-ws2812: $(FORMAL_DIR)/ws2812/.stamp | manifest
formal-doctor:
$(MAKE) FORMAL=YES SIMU=IVERILOG SYNTH=YOSYS STA=NONE 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-user formal-doctor formal-clean
.PHONY: formal formal-bus formal-rib-adapter formal-rib2apb formal-sysctrl formal-pll-rcu formal-gpio-user formal-ws2812 formal-doctor formal-clean
+2 -1
View File
@@ -77,7 +77,8 @@ CRT_SRCS := $(ROOT_PATH)/crt/arch/riscv/startup.S \
$(ROOT_PATH)/crt/src/hal/hpuart.c \
$(ROOT_PATH)/crt/src/hal/ps2.c \
$(ROOT_PATH)/crt/src/hal/i2c.c \
$(ROOT_PATH)/crt/src/hal/onewire.c \
$(ROOT_PATH)/crt/src/hal/ws2812_math.c \
$(ROOT_PATH)/crt/src/hal/ws2812.c \
$(ROOT_PATH)/crt/src/hal/dma.c \
$(ROOT_PATH)/crt/src/hal/perf.c \
$(ROOT_PATH)/crt/src/hal/lcd.c \
+1 -1
View File
@@ -149,7 +149,7 @@
"jtag_tdo_o_pad": null,
"gpio_0_io_pad": "ps2_clk_i",
"gpio_1_io_pad": "ps2_dat_i",
"gpio_2_io_pad": "onewire_dat_o",
"gpio_2_io_pad": "ws2812_dat_o",
"gpio_3_io_pad": "i2c_scl_io",
"gpio_4_io_pad": "i2c_sda_io",
"gpio_5_io_pad": "pwm_2_o",
+4 -4
View File
@@ -26,7 +26,7 @@ module ip_ribp_wrapper (
spi_if.dut spisd,
i2c_if.dut i2c0,
i2s_if.dut i2s,
onewire_if.dut onewire,
ws2812_if.dut ws2812,
xpi_if.dut xpi,
rib_if.master dma_rib,
sysctrl_if.dut sysctrl,
@@ -143,11 +143,11 @@ module ip_ribp_wrapper (
.i2s (i2s)
);
ribp_onewire u_rib_onewire (
ribp_ws2812 u_rib_ws2812 (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.ribp (u_onewire_ribp_if),
.onewire(onewire)
.ribp (u_ws2812_ribp_if),
.ws2812 (ws2812)
);
ribp_xpi u_rib_xpi (
+2 -2
View File
@@ -48,7 +48,7 @@ module retrosoc (
spi_if u_spisd_if ();
i2c_if u_i2c0_if ();
i2s_if u_i2s_if ();
onewire_if u_onewire_if ();
ws2812_if u_ws2812_if ();
sysctrl_if u_sysctrl_if ();
dvp_if u_dvp_if ();
sdio_if u_sdio_if ();
@@ -173,7 +173,7 @@ core_wrapper u_core_wrapper (
.spisd (u_spisd_if),
.i2c0 (u_i2c0_if),
.i2s (u_i2s_if),
.onewire (u_onewire_if),
.ws2812 (u_ws2812_if),
.xpi (xpi),
.sysctrl (u_sysctrl_if),
.pll_ctrl (pll_ctrl),
+1
View File
@@ -20,6 +20,7 @@ TEST_SOURCES = (
"crt/src/lib/printf.c",
"crt/src/lib/stdlib.c",
"crt/src/lib/string.c",
"crt/src/hal/ws2812_math.c",
"app/media/src/video_player.c",
"app/media/src/wav_audio.c",
"tests/c/test_runtime.c",
+35
View File
@@ -5,6 +5,7 @@
#include <retrosoc/core/wait.h>
#include <retrosoc/hal/lcd.h>
#include <retrosoc/hal/spisd.h>
#include <retrosoc/hal/ws2812.h>
#include <retrosoc/lib/printf.h>
#include <retrosoc/lib/stdlib.h>
#include <retrosoc/lib/string.h>
@@ -152,6 +153,39 @@ static int test_wait_helper(void)
return 0;
}
static int test_ws2812_helpers(void)
{
const rs_ws2812_config_t config = {
72000000U,
RS_WS2812_DEFAULT_BIT_PERIOD_NS,
RS_WS2812_DEFAULT_T0H_NS,
RS_WS2812_DEFAULT_T1H_NS,
RS_WS2812_DEFAULT_RESET_NS,
4U,
};
rs_ws2812_config_t invalid = config;
rs_ws2812_timing_t timing;
if ((rs_ws2812_pack_grb(0x12U, 0x34U, 0x56U) != UINT32_C(0x341256)) ||
(rs_ws2812_timing_from_ns(&config, &timing) != RS_OK) ||
(timing.bit_cycles != 90U) || (timing.t0h_cycles != 25U) ||
(timing.t1h_cycles != 50U) || (timing.reset_cycles != 21600U)) {
return 1;
}
invalid.t0h_ns = invalid.t1h_ns;
if ((rs_ws2812_timing_from_ns(&invalid, &timing) != RS_EINVAL) ||
(rs_ws2812_timing_from_ns(NULL, &timing) != RS_EINVAL) ||
(rs_ws2812_timing_from_ns(&config, NULL) != RS_EINVAL)) {
return 2;
}
invalid = config;
invalid.source_clock_hz = 1U;
if (rs_ws2812_timing_from_ns(&invalid, &timing) != RS_EINVAL) {
return 3;
}
return 0;
}
static int test_wav_parser(void)
{
static const uint8_t wav[] = {
@@ -200,6 +234,7 @@ int main(void)
test_formatter(),
test_compiler_helpers(),
test_wait_helper(),
test_ws2812_helpers(),
test_wav_parser(),
test_video_parser(),
};
+3 -3
View File
@@ -87,7 +87,7 @@ module ribp_topology_tb;
u_spisd_ribp_if.ready = 1'b1;
u_i2c0_ribp_if.ready = 1'b1;
u_i2s_ribp_if.ready = 1'b1;
u_onewire_ribp_if.ready = 1'b1;
u_ws2812_ribp_if.ready = 1'b1;
u_xpi_ribp_if.ready = 1'b1;
u_dma_ribp_if.ready = 1'b1;
u_sysctrl_ribp_if.ready = 1'b1;
@@ -106,7 +106,7 @@ module ribp_topology_tb;
u_spisd_ribp_if.rdata = 32'h5555_5555;
u_i2c0_ribp_if.rdata = 32'h6666_6666;
u_i2s_ribp_if.rdata = 32'h7777_7777;
u_onewire_ribp_if.rdata = 32'h8888_8888;
u_ws2812_ribp_if.rdata = 32'h8888_8888;
u_xpi_ribp_if.rdata = 32'h9999_9999;
u_dma_ribp_if.rdata = 32'hAAAA_AAAA;
u_sysctrl_ribp_if.rdata = 32'hBBBB_BBBB;
@@ -125,7 +125,7 @@ module ribp_topology_tb;
u_spisd_ribp_if.resp_err = 1'b0;
u_i2c0_ribp_if.resp_err = 1'b0;
u_i2s_ribp_if.resp_err = 1'b0;
u_onewire_ribp_if.resp_err = 1'b0;
u_ws2812_ribp_if.resp_err = 1'b0;
u_xpi_ribp_if.resp_err = 1'b0;
u_dma_ribp_if.resp_err = 1'b0;
u_sysctrl_ribp_if.resp_err = 1'b0;
+7 -3
View File
@@ -23,13 +23,17 @@ module soc_irq_topology_tb;
s_apb_irq = '0;
expect_irq('0);
for (int bit_index = 0; bit_index < `SOC_IRQ_RIBP_WIDTH; bit_index++) begin
for (int bit_index = 0; bit_index < 10; bit_index++) begin
s_ribp_irq = '0;
s_ribp_irq[bit_index] = 1'b1;
s_apb_irq = '0;
expect_irq(32'd1 << bit_index);
end
s_ribp_irq = '0;
s_ribp_irq[10] = 1'b1;
expect_irq(32'd1 << 17);
for (int bit_index = 0; bit_index < `SOC_IRQ_APB_WIDTH; bit_index++) begin
s_ribp_irq = '0;
s_apb_irq = '0;
@@ -37,8 +41,8 @@ module soc_irq_topology_tb;
expect_irq(32'd1 << (10 + bit_index));
end
if (s_irq[31:17] !== '0) begin
$fatal(1, "unallocated core IRQ bits are not low: %h", s_irq[31:17]);
if (s_irq[31:18] !== '0) begin
$fatal(1, "unallocated core IRQ bits are not low: %h", s_irq[31:18]);
end
$display("SoC topology IRQ routing test passed");
+257
View File
@@ -0,0 +1,257 @@
`timescale 1ns / 1ps
`include "rib_defs.svh"
`include "ws2812_define.svh"
module ws2812_dma_tb;
localparam logic [31:0] SOURCE_BASE = 32'h4000_0000;
localparam logic [31:0] WS2812_TXDATA = 32'h1000_8010;
localparam int BIT_CYCLES = 4;
localparam int T0H_CYCLES = 1;
localparam int T1H_CYCLES = 3;
localparam int RESET_CYCLES = 5;
typedef enum logic [1:0] {
BUS_IDLE,
BUS_READ_RESP,
BUS_WRITE_DATA,
BUS_WRITE_RESP
} bus_state_t;
logic clk_i = 1'b0;
logic rst_n_i = 1'b0;
logic dma_start = 1'b0;
logic dma_done;
logic dma_error;
logic [ 2:0] dma_error_code;
logic [31:0] dma_error_addr;
logic [ 1:0] dma_fsm;
logic host_active = 1'b1;
logic host_valid = 1'b0;
logic [31:0] host_addr = '0;
logic [31:0] host_wdata = '0;
logic [ 3:0] host_wstrb = '0;
logic dma_ws_valid;
bus_state_t bus_state_q = BUS_IDLE;
logic [31:0] bus_addr_q = '0;
integer dma_write_count = 0;
integer dma_backpressure_cycles = 0;
logic [23:0] pixels [0:5];
integer observed_pixel = 0;
integer observed_bit = 0;
integer observed_cycle = 0;
integer reset_low_cycles = 0;
dma_hw_trg_if hw_trg ();
rib_if dma_rib ();
ribp_if ws_ribp ();
ws2812_if ws2812 ();
always #5 clk_i = ~clk_i;
initial begin
repeat (5000) @(posedge clk_i);
$fatal(1, "WS2812 DMA integration test timeout");
end
assign ws_ribp.valid = host_active ? host_valid : dma_ws_valid;
assign ws_ribp.addr = host_active ? host_addr : bus_addr_q;
assign ws_ribp.wdata = host_active ? host_wdata : dma_rib.wdata;
assign ws_ribp.wstrb = host_active ? host_wstrb : dma_rib.wstrb;
assign dma_rib.cmd_ready = bus_state_q == BUS_IDLE;
assign dma_rib.w_ready = (bus_state_q == BUS_WRITE_DATA) && ws_ribp.ready;
assign dma_rib.rsp_valid = (bus_state_q == BUS_READ_RESP) || (bus_state_q == BUS_WRITE_RESP);
assign dma_rib.rdata = pixels[bus_addr_q[4:2]];
assign dma_rib.resp_err = 1'b0;
assign dma_rib.resp_code = `RIB_RESP_OK;
assign dma_rib.rsp_beat = 2'd0;
assign dma_rib.rsp_last = 1'b1;
assign dma_ws_valid = (bus_state_q == BUS_WRITE_DATA) && dma_rib.w_valid;
always_ff @(posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
bus_state_q <= BUS_IDLE;
bus_addr_q <= '0;
dma_write_count <= 0;
dma_backpressure_cycles <= 0;
end else begin
if ((bus_state_q == BUS_WRITE_DATA) && dma_rib.w_valid && !dma_rib.w_ready) begin
dma_backpressure_cycles <= dma_backpressure_cycles + 1;
end
unique case (bus_state_q)
BUS_IDLE: begin
if (dma_rib.cmd_valid && dma_rib.cmd_ready) begin
bus_addr_q <= dma_rib.cmd_addr;
if (dma_rib.cmd_len != `RIB_LEN_INCR1) begin
$fatal(1, "fixed-destination WS2812 DMA transfer used a burst");
end
if (dma_rib.cmd_write) begin
if (dma_rib.cmd_addr != WS2812_TXDATA) begin
$fatal(1, "DMA wrote unexpected destination %h", dma_rib.cmd_addr);
end
bus_state_q <= BUS_WRITE_DATA;
end else begin
if ((dma_rib.cmd_addr < (SOURCE_BASE + 32'd4)) ||
(dma_rib.cmd_addr > (SOURCE_BASE + 32'd20))) begin
$fatal(1, "DMA read unexpected source %h", dma_rib.cmd_addr);
end
bus_state_q <= BUS_READ_RESP;
end
end
end
BUS_READ_RESP: begin
if (dma_rib.rsp_valid && dma_rib.rsp_ready) begin
bus_state_q <= BUS_IDLE;
end
end
BUS_WRITE_DATA: begin
if (dma_rib.w_valid && dma_rib.w_ready) begin
if (!dma_rib.wlast) begin
$fatal(1, "INCR1 WS2812 write did not assert wlast");
end
dma_write_count <= dma_write_count + 1;
bus_state_q <= BUS_WRITE_RESP;
end
end
BUS_WRITE_RESP: begin
if (dma_rib.rsp_valid && dma_rib.rsp_ready) begin
bus_state_q <= BUS_IDLE;
end
end
default: bus_state_q <= BUS_IDLE;
endcase
end
end
dma_core u_dma_core (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.mode_i (3'd0),
.srcaddr_i (SOURCE_BASE + 32'd4),
.srcincr_i (1'b1),
.dstaddr_i (WS2812_TXDATA),
.dstincr_i (1'b0),
.xferlen_i (32'd5),
.start_i (dma_start),
.stop_i (1'b0),
.reset_i (1'b0),
.done_o (dma_done),
.error_o (dma_error),
.error_code_o(dma_error_code),
.error_addr_o(dma_error_addr),
.fsm_o (dma_fsm),
.hw_trg (hw_trg),
.rib (dma_rib)
);
ribp_ws2812 #(
.TX_FIFO_DEPTH(4)
) u_ws2812 (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.ribp (ws_ribp),
.ws2812 (ws2812)
);
task automatic host_write(input logic [31:0] address, input logic [31:0] data);
begin
@(negedge clk_i);
host_addr = address;
host_wdata = data;
host_wstrb = 4'hF;
host_valid = 1'b1;
while (!ws_ribp.ready) begin
@(posedge clk_i);
#1;
end
if (ws_ribp.resp_err) begin
$fatal(1, "host write %h failed", address);
end
@(negedge clk_i);
host_valid = 1'b0;
host_wstrb = '0;
end
endtask
always @(posedge clk_i) begin
#1;
if (rst_n_i && u_ws2812.s_busy && !u_ws2812.s_reset_active) begin
integer high_cycles;
logic expected_data;
high_cycles = pixels[observed_pixel][23-observed_bit] ? T1H_CYCLES : T0H_CYCLES;
expected_data = observed_cycle < high_cycles;
if (ws2812.dat_o !== expected_data) begin
$fatal(1, "DMA waveform mismatch pixel=%0d bit=%0d cycle=%0d", observed_pixel,
observed_bit, observed_cycle);
end
observed_cycle = observed_cycle + 1;
if (observed_cycle == BIT_CYCLES) begin
observed_cycle = 0;
observed_bit = observed_bit + 1;
if (observed_bit == 24) begin
observed_bit = 0;
observed_pixel = observed_pixel + 1;
end
end
end
if (rst_n_i && u_ws2812.s_reset_active) begin
if (ws2812.dat_o !== 1'b0) begin
$fatal(1, "DMA frame reset interval drove the output high");
end
reset_low_cycles = reset_low_cycles + 1;
end
if (dma_error) begin
$fatal(1, "DMA failed code=%0d address=%h", dma_error_code, dma_error_addr);
end
end
initial begin
pixels[0] = 24'h800001;
pixels[1] = 24'h010203;
pixels[2] = 24'hA5A5A5;
pixels[3] = 24'h5A5A5A;
pixels[4] = 24'hFFFFFF;
pixels[5] = 24'h000000;
hw_trg.i2s_tx_proc = 1'b0;
hw_trg.i2s_rx_proc = 1'b0;
hw_trg.qspi_tx_proc = 1'b0;
hw_trg.qspi_rx_proc = 1'b0;
repeat (3) @(posedge clk_i);
rst_n_i = 1'b1;
host_write(`RIBP_WS2812_BIT_CYCLES, BIT_CYCLES);
host_write(`RIBP_WS2812_T0H_CYCLES, T0H_CYCLES);
host_write(`RIBP_WS2812_T1H_CYCLES, T1H_CYCLES);
host_write(`RIBP_WS2812_RESET_CYCLES, RESET_CYCLES);
host_write(`RIBP_WS2812_FIFO_WATERMARK, 3);
host_write(`RIBP_WS2812_INTR_ENABLE, 1);
host_write(`RIBP_WS2812_TXDATA, pixels[0]);
host_write(`RIBP_WS2812_FRAME_WORDS, 6);
host_write(`RIBP_WS2812_CTRL, 1);
host_active = 1'b0;
@(negedge clk_i);
dma_start = 1'b1;
@(negedge clk_i);
dma_start = 1'b0;
wait (dma_done);
wait (!u_ws2812.s_busy);
@(posedge clk_i);
#1;
if (dma_write_count != 5) begin
$fatal(1, "DMA completed %0d WS2812 writes, expected 5", dma_write_count);
end
if (dma_backpressure_cycles < 40) begin
$fatal(1, "DMA did not observe full-FIFO backpressure: %0d cycles", dma_backpressure_cycles);
end
if ((observed_pixel != 6) || (reset_low_cycles < RESET_CYCLES) || !ws2812.irq_o) begin
$fatal(1, "DMA frame did not complete pixels=%0d reset=%0d irq=%b", observed_pixel,
reset_low_cycles, ws2812.irq_o);
end
$display("WS2812 DMA backpressure integration test passed");
$finish;
end
endmodule
+298
View File
@@ -0,0 +1,298 @@
`timescale 1ns / 1ps
`include "ws2812_define.svh"
module ws2812_tb;
localparam int FIFO_DEPTH = 4;
localparam int BIT_CYCLES = 4;
localparam int T0H_CYCLES = 1;
localparam int T1H_CYCLES = 3;
localparam int RESET_CYCLES = 5;
logic clk_i = 1'b0;
logic rst_n_i = 1'b0;
ribp_if ribp ();
ws2812_if ws2812 ();
logic monitor_enable = 1'b0;
logic [23:0] expected_pixels [0:7];
integer expected_pixel_count = 0;
integer observed_pixel = 0;
integer observed_bit = 0;
integer observed_cycle = 0;
integer expected_bit_cycles = BIT_CYCLES;
integer expected_t0h_cycles = T0H_CYCLES;
integer expected_t1h_cycles = T1H_CYCLES;
integer reset_low_cycles = 0;
integer test_stage = 0;
always #5 clk_i = ~clk_i;
initial begin
repeat (10000) @(posedge clk_i);
$fatal(1, "WS2812 test timeout at stage %0d state=%0d valid=%b ready=%b", test_stage,
u_dut.u_ws2812_core.s_state_q, ribp.valid, ribp.ready);
end
ribp_ws2812 #(
.TX_FIFO_DEPTH(FIFO_DEPTH)
) u_dut (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.ribp (ribp),
.ws2812 (ws2812)
);
task automatic ribp_write(input logic [31:0] address, input logic [31:0] data,
input logic [3:0] strobe, input logic expected_error,
output integer wait_cycles);
begin
@(negedge clk_i);
ribp.addr = address;
ribp.wdata = data;
ribp.wstrb = strobe;
ribp.valid = 1'b1;
wait_cycles = 0;
while (!ribp.ready) begin
@(posedge clk_i);
#1;
wait_cycles = wait_cycles + 1;
end
if (ribp.resp_err !== expected_error) begin
$fatal(1, "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, "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 write_ok(input logic [31:0] address, input logic [31:0] data);
integer ignored_wait;
begin
ribp_write(address, data, 4'hF, 1'b0, ignored_wait);
end
endtask
task automatic wait_until_idle;
logic [31:0] status;
begin
status = 32'h1;
while (status[`WS2812_STATUS_BUSY]) begin
ribp_read(`RIBP_WS2812_STATUS, 1'b0, status);
end
end
endtask
task automatic configure_timing;
begin
write_ok(`RIBP_WS2812_BIT_CYCLES, BIT_CYCLES);
write_ok(`RIBP_WS2812_T0H_CYCLES, T0H_CYCLES);
write_ok(`RIBP_WS2812_T1H_CYCLES, T1H_CYCLES);
write_ok(`RIBP_WS2812_RESET_CYCLES, RESET_CYCLES);
write_ok(`RIBP_WS2812_FIFO_WATERMARK, 3);
write_ok(`RIBP_WS2812_INTR_ENABLE, 4'hF);
end
endtask
always @(posedge clk_i) begin
#1;
if (!rst_n_i && ws2812.dat_o !== 1'b0) begin
$fatal(1, "WS2812 output is not low during reset");
end
if (monitor_enable && u_dut.s_busy && !u_dut.s_reset_active) begin
integer high_cycles;
logic expected_data;
high_cycles = expected_pixels[observed_pixel][23-observed_bit] ? expected_t1h_cycles :
expected_t0h_cycles;
expected_data = observed_cycle < high_cycles;
if (ws2812.dat_o !== expected_data) begin
$fatal(1, "waveform mismatch pixel=%0d bit=%0d cycle=%0d", observed_pixel, observed_bit,
observed_cycle);
end
observed_cycle = observed_cycle + 1;
if (observed_cycle == expected_bit_cycles) begin
observed_cycle = 0;
observed_bit = observed_bit + 1;
if (observed_bit == 24) begin
observed_bit = 0;
observed_pixel = observed_pixel + 1;
end
end
end
if (monitor_enable && u_dut.s_reset_active) begin
if (ws2812.dat_o !== 1'b0) begin
$fatal(1, "WS2812 output is high during latch/reset");
end
reset_low_cycles = reset_low_cycles + 1;
end
end
task automatic begin_monitor(input integer pixel_count);
begin
observed_pixel = 0;
observed_bit = 0;
observed_cycle = 0;
reset_low_cycles = 0;
expected_pixel_count = pixel_count;
monitor_enable = 1'b1;
end
endtask
task automatic end_monitor(input logic expect_complete_frame);
begin
monitor_enable = 1'b0;
if (expect_complete_frame && observed_pixel != expected_pixel_count) begin
$fatal(1, "observed %0d pixels, expected %0d", observed_pixel, expected_pixel_count);
end
if (reset_low_cycles < RESET_CYCLES) begin
$fatal(1, "latch/reset low interval was only %0d cycles", reset_low_cycles);
end
end
endtask
initial begin
logic [31:0] value;
integer wait_cycles;
ribp.valid = 1'b0;
ribp.addr = '0;
ribp.wdata = '0;
ribp.wstrb = '0;
repeat (3) @(posedge clk_i);
rst_n_i = 1'b1;
test_stage = 1;
ribp_read(`RIBP_WS2812_IP_INFO, 1'b0, value);
if (value !== 32'h1804_0100) begin
$fatal(1, "unexpected IP_INFO %h", value);
end
ribp_read(`RIBP_WS2812_STATUS, 1'b0, value);
if (value[`WS2812_STATUS_CONFIG_VALID] || !value[`WS2812_STATUS_FIFO_EMPTY]) begin
$fatal(1, "unexpected reset status %h", value);
end
ribp_read(`RIBP_WS2812_CTRL, 1'b1, value);
ribp_write(8'h02, 32'd1, 4'hF, 1'b1, wait_cycles);
ribp_read(32'h00000100, 1'b1, value);
ribp_write(`RIBP_WS2812_TXDATA, 24'h112233, 4'h7, 1'b1, wait_cycles);
write_ok(`RIBP_WS2812_TXDATA, 24'h112233);
write_ok(`RIBP_WS2812_FRAME_WORDS, 1);
ribp_write(`RIBP_WS2812_CTRL, 32'h1, 4'hF, 1'b1, wait_cycles);
ribp_read(`RIBP_WS2812_ERROR_STATUS, 1'b0, value);
if (!value[`WS2812_ERROR_CONFIG]) begin
$fatal(1, "invalid timing did not set CONFIG error");
end
write_ok(`RIBP_WS2812_ERROR_STATUS, 32'h7);
write_ok(`RIBP_WS2812_CTRL, 32'h4);
configure_timing();
ribp_read(`RIBP_WS2812_STATUS, 1'b0, value);
if (!value[`WS2812_STATUS_CONFIG_VALID]) begin
$fatal(1, "legal timing was not accepted");
end
test_stage = 2;
for (integer index = 0; index < FIFO_DEPTH; index = index + 1) begin
write_ok(`RIBP_WS2812_TXDATA, index);
end
ribp_write(`RIBP_WS2812_TXDATA, 32'h55, 4'hF, 1'b1, wait_cycles);
write_ok(`RIBP_WS2812_CTRL, 32'h4);
write_ok(`RIBP_WS2812_ERROR_STATUS, 32'h7);
write_ok(`RIBP_WS2812_INTR_STATE, 32'hF);
test_stage = 3;
expected_pixels[0] = 24'h800001;
expected_pixels[1] = 24'h010203;
expected_pixels[2] = 24'hA5A5A5;
expected_pixels[3] = 24'h5A5A5A;
expected_pixels[4] = 24'hFFFFFF;
expected_pixels[5] = 24'h000000;
for (integer index = 0; index < FIFO_DEPTH; index = index + 1) begin
write_ok(`RIBP_WS2812_TXDATA, expected_pixels[index]);
end
write_ok(`RIBP_WS2812_FRAME_WORDS, 6);
begin_monitor(6);
write_ok(`RIBP_WS2812_CTRL, 32'h1);
write_ok(`RIBP_WS2812_TXDATA, expected_pixels[4]);
ribp_write(`RIBP_WS2812_TXDATA, expected_pixels[5], 4'hF, 1'b0, wait_cycles);
if (wait_cycles < (BIT_CYCLES * 20)) begin
$fatal(1, "full FIFO write was not backpressured: %0d cycles", wait_cycles);
end
wait_until_idle();
end_monitor(1'b1);
ribp_read(`RIBP_WS2812_INTR_STATE, 1'b0, value);
if ((value & 32'h3) != 32'h3 || !ws2812.irq_o) begin
$fatal(1, "done/low-watermark interrupt missing: %h", value);
end
write_ok(`RIBP_WS2812_INTR_STATE, 32'hF);
if (ws2812.irq_o) begin
$fatal(1, "IRQ did not clear");
end
test_stage = 4;
expected_pixels[0] = 24'h123456;
write_ok(`RIBP_WS2812_TXDATA, expected_pixels[0]);
write_ok(`RIBP_WS2812_FRAME_WORDS, 2);
begin_monitor(1);
write_ok(`RIBP_WS2812_CTRL, 32'h1);
wait_until_idle();
end_monitor(1'b1);
ribp_read(`RIBP_WS2812_ERROR_STATUS, 1'b0, value);
if (!value[`WS2812_ERROR_UNDERFLOW]) begin
$fatal(1, "underflow was not reported");
end
ribp_read(`RIBP_WS2812_FIFO_LEVEL, 1'b0, value);
if (value != 0) begin
$fatal(1, "underflow did not flush FIFO");
end
write_ok(`RIBP_WS2812_ERROR_STATUS, 32'h7);
write_ok(`RIBP_WS2812_INTR_STATE, 32'hF);
test_stage = 5;
expected_pixels[0] = 24'hABCDEF;
expected_pixels[1] = 24'h765432;
write_ok(`RIBP_WS2812_TXDATA, expected_pixels[0]);
write_ok(`RIBP_WS2812_TXDATA, expected_pixels[1]);
write_ok(`RIBP_WS2812_FRAME_WORDS, 2);
begin_monitor(2);
write_ok(`RIBP_WS2812_CTRL, 32'h1);
repeat (8) @(posedge clk_i);
write_ok(`RIBP_WS2812_CTRL, 32'h2);
wait_until_idle();
end_monitor(1'b0);
ribp_read(`RIBP_WS2812_INTR_STATE, 1'b0, value);
if (!value[`WS2812_INTR_ABORTED]) begin
$fatal(1, "abort completion was not reported");
end
write_ok(`RIBP_WS2812_INTR_TEST, 32'h1);
if (!ws2812.irq_o) begin
$fatal(1, "interrupt test did not assert IRQ");
end
write_ok(`RIBP_WS2812_INTR_STATE, 32'hF);
$display("WS2812 register, waveform, streaming, and error test passed");
$finish;
end
endmodule
+11 -1
View File
@@ -204,6 +204,7 @@ def test_formal_filelists_are_scoped_to_the_protocol_duts(tmp_path: Path) -> Non
sysctrl_filelist = tmp_path / "sysctrl.fl"
pll_rcu_filelist = tmp_path / "pll_rcu.fl"
gpio_user_filelist = tmp_path / "gpio_user.fl"
ws2812_filelist = tmp_path / "ws2812.fl"
assert generate_formal_filelist("bus", bus_filelist, memory_map, topology, user_extensions)
assert generate_formal_filelist(
"rib_adapter", rib_adapter_filelist, memory_map, topology, user_extensions
@@ -220,6 +221,9 @@ def test_formal_filelists_are_scoped_to_the_protocol_duts(tmp_path: Path) -> Non
assert generate_formal_filelist(
"gpio_user", gpio_user_filelist, memory_map, topology, user_extensions
)
assert generate_formal_filelist(
"ws2812", ws2812_filelist, memory_map, topology, user_extensions
)
bus = parse_filelists([bus_filelist], require_files=False)
rib_adapter = parse_filelists([rib_adapter_filelist], require_files=False)
@@ -227,6 +231,7 @@ def test_formal_filelists_are_scoped_to_the_protocol_duts(tmp_path: Path) -> Non
sysctrl = parse_filelists([sysctrl_filelist], require_files=False)
pll_rcu = parse_filelists([pll_rcu_filelist], require_files=False)
gpio_user = parse_filelists([gpio_user_filelist], require_files=False)
ws2812 = parse_filelists([ws2812_filelist], require_files=False)
assert "+define+SV_ASSRT_DISABLE" in bus.defines
assert ROOT / "rtl/mini/top/bus.sv" in bus.files
assert ROOT / "rtl/mini/top/rib_if.sv" in bus.files
@@ -246,6 +251,9 @@ def test_formal_filelists_are_scoped_to_the_protocol_duts(tmp_path: Path) -> Non
assert ROOT / "rtl/ip/ribp/peripheral/gpio.sv" in gpio_user.files
assert ROOT / "rtl/managed/clusterip/common/rtl/cdc/cdc_sync.sv" in gpio_user.files
assert ROOT / "rtl/mini/formal/gpio_user_formal.sv" in gpio_user.files
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
def test_sysctrl_formal_properties_use_exported_user_core_shape() -> None:
@@ -291,7 +299,7 @@ def test_fatfs_release_script_uses_the_locked_archive_contract() -> None:
def test_formal_result_summary_requires_every_passing_step(tmp_path: Path) -> None:
proofs = ("bus", "rib2apb", "sysctrl", "pll_rcu", "gpio_user")
proofs = ("bus", "rib2apb", "sysctrl", "pll_rcu", "gpio_user", "ws2812")
for proof in proofs:
directory = tmp_path / proof
directory.mkdir()
@@ -320,6 +328,8 @@ def test_formal_result_summary_requires_every_passing_step(tmp_path: Path) -> No
f"pll_rcu={tmp_path / 'pll_rcu'}",
"--proof",
f"gpio_user={tmp_path / 'gpio_user'}",
"--proof",
f"ws2812={tmp_path / 'ws2812'}",
)
result = json.loads(output.read_text(encoding="utf-8"))
assert result["status"] == "passed"
+7 -4
View File
@@ -83,6 +83,7 @@ def test_topology_generates_complete_rib_apb_and_gpio_bindings(tmp_path: Path) -
assert "assign u_sdio_ribp_if.valid = 1'b0;" in routes
assert gpio.count("// GPIO") == 64
assert "assign u_uart1_if.rx_i = u_gpio_if.di_i[0];" in gpio
assert "assign u_gpio_if.alt1_do_i[2] = u_ws2812_if.dat_o;" in gpio
assert "assign u_gpio_if.alt1_do_i[22] = u_psram_if.nss_o[0];" in gpio
assert apb_interfaces.count("apb4_if u_") == 10
assert "apb4_pure_if u_archinfo_apb_pure_if ();" in apb_interfaces
@@ -96,9 +97,10 @@ 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_IRQ_RIBP_WIDTH 10" in irq_config
assert "`define SOC_IRQ_RIBP_WIDTH 11" in irq_config
assert "`define SOC_IRQ_APB_WIDTH 7" in irq_config
assert "assign irq_o[0] = u_clint_if.sfr_irq_o;" in rib_irq
assert "assign irq_o[10] = ws2812.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
@@ -119,7 +121,7 @@ def test_topology_preserves_default_irq_compatibility_mapping() -> None:
)
for interrupt in document["interrupts"]
]
assert mappings[:17] == [
assert mappings[:18] == [
("clint_software", "ribp", 0, 0, "u_clint_if.sfr_irq_o"),
("clint_timer", "ribp", 1, 1, "u_clint_if.tmr_irq_o"),
("uart0", "ribp", 2, 2, "uart.irq_o"),
@@ -137,8 +139,9 @@ def test_topology_preserves_default_irq_compatibility_mapping() -> None:
("watchdog_reset", "apb", 4, 14, "u_wdg_if.rst_o"),
("advanced_timer", "apb", 5, 15, "u_tmr_if.irq_o"),
("reserved", "apb", 6, 16, "1'b0"),
("ws2812", "ribp", 10, 17, "ws2812.irq_o"),
]
assert all(mapping[3] >= 17 for mapping in mappings[17:])
assert all(mapping[3] >= 18 for mapping in mappings[18:])
def test_topology_always_adds_the_user_apb_target(tmp_path: Path) -> None:
@@ -249,7 +252,7 @@ def test_topology_rejects_invalid_irq_groups_and_bindings(tmp_path: Path) -> Non
assert "core interrupt bit 0 is duplicated" in result.stderr
document = json.loads(TOPOLOGY.read_text(encoding="utf-8"))
document["interrupts"].pop()
document["interrupts"].pop(16)
result = validate(write_invalid_topology(tmp_path, document))
assert result.returncode != 0
assert "interrupt group apb does not cover every group bit" in result.stderr
+132
View File
@@ -0,0 +1,132 @@
"""WS2812 register, waveform, and streaming regression tests."""
from __future__ import annotations
import shutil
import subprocess
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
def test_ws2812_register_waveform_streaming_and_errors(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"
source_list = tmp_path / "ws2812.fl"
source_list.write_text(
"\n".join(
[
"+define+SV_ASSRT_DISABLE",
f"+incdir+{common}",
f"+incdir+{common / 'interface'}",
f"+incdir+{common / 'utils'}",
f"+incdir+{serial}",
str(common / "interface/ribp_if.sv"),
str(common / "utils/register.sv"),
str(common / "utils/fifo.sv"),
str(serial / "ws2812_if.sv"),
str(serial / "ws2812_reg.sv"),
str(serial / "ws2812_core.sv"),
str(serial / "ribp_ws2812.sv"),
str(ROOT / "tests/rtl/ws2812_tb.sv"),
"",
]
),
encoding="utf-8",
)
converted = tmp_path / "ws2812_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 / "ws2812_tb"
subprocess.run(
[iverilog, "-g2012", "-s", "ws2812_tb", "-o", str(simulation), str(converted)],
check=True,
)
result = subprocess.run([vvp, str(simulation)], text=True, capture_output=True, check=True)
assert "WS2812 register, waveform, streaming, and error test passed" in result.stdout
def test_ws2812_accepts_dma_fixed_destination_backpressure(tmp_path: Path) -> None:
iverilog = shutil.which("iverilog")
vvp = shutil.which("vvp")
if iverilog is None or vvp is None:
return
memory_map = tmp_path / "memory_map"
subprocess.run(
[
sys.executable,
str(ROOT / "rtl/mini/address_map/generate_memory_map.py"),
"--map",
str(ROOT / "rtl/mini/address_map/memory_map.json"),
"--output-dir",
str(memory_map),
"--have-sram-if",
"NO",
],
check=True,
)
common = ROOT / "rtl/managed/clusterip/common/rtl"
serial = ROOT / "rtl/ip/ribp/serial"
source_list = tmp_path / "ws2812_dma.fl"
source_list.write_text(
"\n".join(
[
"+define+SV_ASSRT_DISABLE",
f"+incdir+{memory_map / 'rtl'}",
f"+incdir+{common}",
f"+incdir+{common / 'interface'}",
f"+incdir+{common / 'utils'}",
f"+incdir+{ROOT / 'rtl/mini/top'}",
f"+incdir+{serial}",
str(common / "interface/ribp_if.sv"),
str(ROOT / "rtl/mini/top/rib_if.sv"),
str(common / "utils/register.sv"),
str(common / "utils/fifo.sv"),
str(ROOT / "rtl/ip/ribp/peripheral/dma.sv"),
str(ROOT / "rtl/ip/ribp/peripheral/dma_core.sv"),
str(serial / "ws2812_if.sv"),
str(serial / "ws2812_reg.sv"),
str(serial / "ws2812_core.sv"),
str(serial / "ribp_ws2812.sv"),
str(ROOT / "tests/rtl/ws2812_dma_tb.sv"),
"",
]
),
encoding="utf-8",
)
converted = tmp_path / "ws2812_dma_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 / "ws2812_dma_tb"
subprocess.run(
[iverilog, "-g2012", "-s", "ws2812_dma_tb", "-o", str(simulation), str(converted)],
check=True,
)
result = subprocess.run([vvp, str(simulation)], text=True, capture_output=True, check=True)
assert "WS2812 DMA backpressure integration test passed" in result.stdout