feat: add GPIO V2 support and related tests

- Updated  to include  for testing.
- Modified  to include GPIO header and added tests for GPIO functionality.
- Created a new testbench  for GPIO V2, covering register and pad-control contracts.
- Removed obsolete  and its associated test.
- Updated  to include GPIO admin base routing.
- Added a new Python test  for GPIO V2 register and pad-control contract.
- Removed  as its functionality is now covered in .
- Updated memory map tests to ensure GPIO admin base is correctly referenced.
- Adjusted formal filelist generation to reflect changes in GPIO handling.
- Enhanced SOC topology tests to include GPIO interrupt handling.
This commit is contained in:
maksyuki committed 2026-08-08 17:59:02 +08:00
1 parent 4b40dbf262
commit d517ce0fea
53 files changed
+2512 -1200

No files matched your search

+2 -2
View File
@@ -252,7 +252,7 @@ endif
benchmark-report coremark-report \
pin-map check-pin-map soc-topology check-soc-topology user-extensions check-user-extensions \
check-clock-reset-domains tech-cell-test rtl-lint check-rtl-lint \
formal formal-bus formal-rib-adapter formal-rib2apb formal-ws2812 formal-timer formal-clean formal-doctor
formal formal-bus formal-rib-adapter formal-rib2apb formal-gpio formal-ws2812 formal-timer formal-clean formal-doctor
.NOTPARALLEL: setup
help:
@@ -280,7 +280,7 @@ help:
' check-clock-reset-domains validate the root clock/reset and CDC inventory' \
' rtl-lint | check-rtl-lint run/check strict Verilator RTL lint warnings' \
' formal | formal-bus | formal-rib-adapter | formal-rib2apb run SBY protocol proofs' \
' formal-sysctrl | formal-pll-rcu | formal-gpio-user | formal-ws2812 | formal-timer | formal-clint run peripheral proofs' \
' formal-sysctrl | formal-pll-rcu | formal-gpio | formal-ws2812 | formal-timer | formal-clint run peripheral proofs' \
' formal-doctor check the SBY, Yosys, sv2v, and Bitwuzla formal toolchain' \
' benchmark-report run the memory/DMA profile and write meta/performance.json' \
' coremark-report run the quick CoreMark profile and write meta/coremark.json' \
+33 -1
View File
@@ -1,5 +1,6 @@
#include <retrosoc/core/soc.h>
#include <retrosoc/hal/clint.h>
#include <retrosoc/hal/gpio.h>
#include <retrosoc/hal/timer.h>
#include <retrosoc/hal/uart.h>
#include <retrosoc/lib/printf.h>
@@ -111,23 +112,54 @@ static bool rs_ci_smoke_timer_periodic(void) {
return passed;
}
static bool rs_ci_smoke_gpio_v2(void) {
const rs_gpio_config_t config = {
.mode = RS_GPIO_MODE_OUTPUT,
.pull = RS_GPIO_PULL_NONE,
.trigger = RS_GPIO_TRIGGER_NONE,
.output_high = false,
.open_drain = false,
.input_cmos = false,
.filter_enable = false,
.interrupt_enable = false,
};
rs_gpio_capabilities_t capabilities;
if ((rs_gpio_get_capabilities(&capabilities) != RS_OK) ||
(capabilities.version != UINT32_C(0x00020000)) ||
((capabilities.features & UINT32_C(0xFF)) != RS_GPIO_PIN_COUNT) ||
((capabilities.features & RS_GPIO_CAP_ATOMIC_OUT) == 0U) ||
(rs_gpio_configure(31U, &config) != RS_OK) || (rs_gpio_write(31U, true) != RS_OK) ||
(rs_gpio_toggle(31U) != RS_OK)) {
return false;
}
return rs_gpio_write(31U, false) == RS_OK;
}
int main(void) {
uart0_init(CPU_FREQ, UART_BPS);
if (!rs_ci_smoke_archinfo_matches_reset_values()) {
rs_test_finish(RS_TEST_FAILED, 1U);
}
printf("ci_smoke: archinfo passed\n");
if (!rs_ci_smoke_clint_standard_map()) {
rs_test_finish(RS_TEST_FAILED, 2U);
}
printf("ci_smoke: CLINT passed\n");
if (!rs_ci_smoke_timer_one_shot()) {
rs_test_finish(RS_TEST_FAILED, 3U);
}
printf("ci_smoke: timer0 passed\n");
if (!rs_ci_smoke_timer_periodic()) {
rs_test_finish(RS_TEST_FAILED, 4U);
}
printf("ci_smoke: timer1 passed\n");
if (!rs_ci_smoke_gpio_v2()) {
rs_test_finish(RS_TEST_FAILED, 5U);
}
printf("ci_smoke: archinfo, CLINT, and timer tests passed\n");
printf("ci_smoke: archinfo, CLINT, timer, and GPIO tests passed\n");
rs_test_finish(RS_TEST_PASSED, 0U);
return 0;
}
+1 -1
View File
@@ -56,7 +56,7 @@ int main(void) {
(void)rs_shell_register("arch", "archinfo test", true, ip_archinfo_test);
(void)rs_shell_register("ws2812", "ws2812 test", true, ip_ws2812_test);
(void)rs_shell_register("tim", "timer test", true, rs_timer_shell_test);
(void)rs_shell_register("gpio", "gpio test", true, ip_gpio_test);
(void)rs_shell_register("gpio", "gpio test", true, rs_gpio_shell_test);
(void)rs_shell_register("pwm", "pwm test", true, ip_pwm_test);
(void)rs_shell_register("rtc", "rtc test", true, ip_rtc_test);
(void)rs_shell_register("wdg", "wdg test", true, ip_wdg_test);
+11 -10
View File
@@ -2,19 +2,20 @@
GCC = riscv32-unknown-elf-
AS = $(GCC)as
LD = $(GCC)ld
OBJDUMP = $(GCC)objdump
OBJCOPY = $(GCC)objcopy
AS_FLAGS = -fno-pic -march=rv32im -mabi=ilp32
LDFLAGS = -T flash.ld -melf32lriscv
APP ?= hello
OUT_DIR ?= .
APP_ALL = $(OUT_DIR)/$(APP)-asm
AS = $(GCC)gcc
LD = $(GCC)ld
OBJDUMP = $(GCC)objdump
OBJCOPY = $(GCC)objcopy
AS_FLAGS = -x assembler-with-cpp -fno-pic -march=rv32im -mabi=ilp32
GENERATED_INCLUDE ?= .
LDFLAGS = -T flash.ld -melf32lriscv
APP ?= hello
OUT_DIR ?= .
APP_ALL = $(OUT_DIR)/$(APP)-asm
all: $(APP).s
@mkdir -p $(OUT_DIR)
$(AS) $(AS_FLAGS) $(APP).s -o $(APP_ALL).o
$(AS) $(AS_FLAGS) -I$(GENERATED_INCLUDE) -c $(APP).s -o $(APP_ALL).o
$(LD) $(LDFLAGS) $(APP_ALL).o -o $(APP_ALL).elf
$(OBJDUMP) -d $(APP_ALL).elf > $(APP_ALL).txt
$(OBJCOPY) -S -O binary $(APP_ALL).elf $(APP_ALL).bin
+8 -6
View File
@@ -1,3 +1,5 @@
#include "retrosoc/generated/memory_map.h"
.global _start
_start:
UART_INIT:
@@ -30,17 +32,17 @@ CHECK:
ble t1, t2, APP_LOOP
.equ GPIO_IOFCFG, 0x10000028
.equ GPIO_PINMUX, 0x1000002C
.equ BIT_MASK, 0x3FE00000 # Bits 21 to 29 set to 1
.equ PSRAM_INIT, 0x10004008
.equ GPIO_ALT_ENABLE, RS_SOC_RIBP_GPIO_ADMIN_BASE + 0x34
.equ GPIO_ALT_SELECT, RS_SOC_RIBP_GPIO_ADMIN_BASE + 0x38
.equ BIT_MASK, 0x3FE00000 # Bits 21 to 29 set to 1
.equ PSRAM_INIT, RS_SOC_RIBP_PSRAM_BASE + 0x08
PINMUX_PSRAM:
li t0, GPIO_IOFCFG
li t0, GPIO_ALT_ENABLE
lw t1, 0(t0)
li t2, BIT_MASK
or t1, t1, t2
sw t1, 0(t0)
li t0, GPIO_PINMUX
li t0, GPIO_ALT_SELECT
lw t1, 0(t0)
or t1, t1, t2
sw t1, 0(t0)
+7 -6
View File
@@ -3,6 +3,7 @@
.type _start,@function
#include "retrosoc/arch/riscv/system_csr.h"
#include "retrosoc/generated/memory_map.h"
_start:
#ifdef CSR_ENABLE
@@ -68,18 +69,18 @@ wait_psram_reset:
# test: using psram before loading
# Define the addresses
.equ GPIO_IOFCFG, 0x10000028
.equ GPIO_PINMUX, 0x1000002C
.equ BIT_MASK, 0x3FE00000 # Bits 21 to 29 set to 1
.equ PSRAM_INIT, 0x10004008
.equ GPIO_ALT_ENABLE, RS_SOC_RIBP_GPIO_ADMIN_BASE + 0x34
.equ GPIO_ALT_SELECT, RS_SOC_RIBP_GPIO_ADMIN_BASE + 0x38
.equ BIT_MASK, 0x3FE00000 # Bits 21 to 29 set to 1
.equ PSRAM_INIT, RS_SOC_RIBP_PSRAM_BASE + 0x08
PINMUX_PSRAM:
li t0, GPIO_IOFCFG
li t0, GPIO_ALT_ENABLE
lw t1, 0(t0)
li t2, BIT_MASK
or t1, t1, t2
sw t1, 0(t0)
li t0, GPIO_PINMUX
li t0, GPIO_ALT_SELECT
lw t1, 0(t0)
or t1, t1, t2
sw t1, 0(t0)
-16
View File
@@ -31,22 +31,6 @@
#define RS_SOC_REG32(base, offset) (*(volatile uint32_t *)(uintptr_t)((base) + (offset)))
// gpio
#define reg_gpio_oe RS_SOC_REG32(RS_SOC_RIBP_GPIO_BASE, UINT32_C(0x00))
#define reg_gpio_cs RS_SOC_REG32(RS_SOC_RIBP_GPIO_BASE, UINT32_C(0x04))
#define reg_gpio_pu RS_SOC_REG32(RS_SOC_RIBP_GPIO_BASE, UINT32_C(0x08))
#define reg_gpio_pd RS_SOC_REG32(RS_SOC_RIBP_GPIO_BASE, UINT32_C(0x0C))
#define reg_gpio_do RS_SOC_REG32(RS_SOC_RIBP_GPIO_BASE, UINT32_C(0x10))
#define reg_gpio_di RS_SOC_REG32(RS_SOC_RIBP_GPIO_BASE, UINT32_C(0x14))
#define reg_gpio_ien RS_SOC_REG32(RS_SOC_RIBP_GPIO_BASE, UINT32_C(0x18))
#define reg_gpio_itype0 RS_SOC_REG32(RS_SOC_RIBP_GPIO_BASE, UINT32_C(0x1C))
#define reg_gpio_itype1 RS_SOC_REG32(RS_SOC_RIBP_GPIO_BASE, UINT32_C(0x20))
#define reg_gpio_istat RS_SOC_REG32(RS_SOC_RIBP_GPIO_BASE, UINT32_C(0x24))
#define reg_gpio_iofcfg RS_SOC_REG32(RS_SOC_RIBP_GPIO_BASE, UINT32_C(0x28))
#define reg_gpio_pinmux RS_SOC_REG32(RS_SOC_RIBP_GPIO_BASE, UINT32_C(0x2C))
#define reg_gpio_user_sel RS_SOC_REG32(RS_SOC_RIBP_GPIO_BASE, UINT32_C(0x30))
#define reg_gpio_user_lock RS_SOC_REG32(RS_SOC_RIBP_GPIO_BASE, UINT32_C(0x34))
#define reg_gpio_user_status RS_SOC_REG32(RS_SOC_RIBP_GPIO_BASE, UINT32_C(0x38))
// uart0
#define reg_uart0_clkdiv RS_SOC_REG32(RS_SOC_RIBP_UART0_BASE, UINT32_C(0x00))
#define reg_uart0_data RS_SOC_REG32(RS_SOC_RIBP_UART0_BASE, UINT32_C(0x04))
+88 -3
View File
@@ -1,6 +1,91 @@
#ifndef RETROSOC_GPIO_H
#define RETROSOC_GPIO_H
#ifndef RETROSOC_HAL_GPIO_H
#define RETROSOC_HAL_GPIO_H
void ip_gpio_test(int argc, char **argv);
#include <stdbool.h>
#include <stdint.h>
#include <retrosoc/core/status.h>
#define RS_GPIO_PIN_COUNT 32U
#define RS_GPIO_IRQ 18U
#define RS_GPIO_CAP_OPEN_DRAIN (UINT32_C(1) << 16U)
#define RS_GPIO_CAP_FILTER (UINT32_C(1) << 17U)
#define RS_GPIO_CAP_USER_HANDOFF (UINT32_C(1) << 18U)
#define RS_GPIO_CAP_CONFIG_LOCK (UINT32_C(1) << 19U)
#define RS_GPIO_CAP_ATOMIC_OUT (UINT32_C(1) << 20U)
#define RS_GPIO_CAP_ATOMIC_OE (UINT32_C(1) << 21U)
#define RS_GPIO_CAP_BOTH_EDGE_IRQ (UINT32_C(1) << 22U)
#define RS_GPIO_PAD_CAP_INPUT_CMOS (UINT32_C(1) << 0U)
#define RS_GPIO_PAD_CAP_PULL_UP (UINT32_C(1) << 1U)
#define RS_GPIO_PAD_CAP_PULL_DOWN (UINT32_C(1) << 2U)
typedef enum {
RS_GPIO_MODE_INPUT = 0,
RS_GPIO_MODE_OUTPUT = 1,
RS_GPIO_MODE_ALT0 = 2,
RS_GPIO_MODE_ALT1 = 3,
RS_GPIO_MODE_USER_IP = 4,
} rs_gpio_mode_t;
typedef enum {
RS_GPIO_PULL_NONE = 0,
RS_GPIO_PULL_UP = 1,
RS_GPIO_PULL_DOWN = 2,
} rs_gpio_pull_t;
typedef enum {
RS_GPIO_TRIGGER_NONE = 0,
RS_GPIO_TRIGGER_RISING = 1,
RS_GPIO_TRIGGER_FALLING = 2,
RS_GPIO_TRIGGER_BOTH = 3,
RS_GPIO_TRIGGER_HIGH = 4,
RS_GPIO_TRIGGER_LOW = 5,
} rs_gpio_trigger_t;
typedef struct {
rs_gpio_mode_t mode;
rs_gpio_pull_t pull;
rs_gpio_trigger_t trigger;
bool output_high;
bool open_drain;
bool input_cmos;
bool filter_enable;
bool interrupt_enable;
} rs_gpio_config_t;
typedef struct {
uint16_t divider;
uint8_t stable_samples;
} rs_gpio_filter_timing_t;
typedef struct {
uint32_t version;
uint32_t features;
uint32_t pad_features;
} rs_gpio_capabilities_t;
rs_status_t rs_gpio_filter_timing_from_us(uint32_t source_clock_hz, uint32_t sample_period_us,
uint8_t stable_samples, rs_gpio_filter_timing_t *timing);
rs_status_t rs_gpio_get_capabilities(rs_gpio_capabilities_t *capabilities);
rs_status_t rs_gpio_configure(uint32_t pin, const rs_gpio_config_t *config);
rs_status_t rs_gpio_read(uint32_t pin, bool *high);
rs_status_t rs_gpio_write(uint32_t pin, bool high);
rs_status_t rs_gpio_toggle(uint32_t pin);
rs_status_t rs_gpio_port_read(uint32_t *value);
rs_status_t rs_gpio_port_set(uint32_t mask);
rs_status_t rs_gpio_port_clear(uint32_t mask);
rs_status_t rs_gpio_port_toggle(uint32_t mask);
rs_status_t rs_gpio_interrupt_state(uint32_t *state);
rs_status_t rs_gpio_interrupt_enable(uint32_t mask, bool enable);
rs_status_t rs_gpio_interrupt_clear(uint32_t mask);
rs_status_t rs_gpio_interrupt_test(uint32_t mask);
rs_status_t rs_gpio_filter_configure(uint32_t enable_mask, const rs_gpio_filter_timing_t *timing);
rs_status_t rs_gpio_user_access_set(uint32_t mask);
rs_status_t rs_gpio_user_ip_select(uint32_t mask, bool enable);
rs_status_t rs_gpio_user_ip_lock(uint32_t mask);
rs_status_t rs_gpio_configuration_lock(uint32_t mask);
void rs_gpio_shell_test(int argc, char **argv);
#endif
-3
View File
@@ -3,9 +3,6 @@
#include <stdint.h>
#define lcd_dc_clr (reg_gpio_do = (uint32_t)0b000)
#define lcd_dc_set (reg_gpio_do = (uint32_t)0b100)
#define USE_HORIZONTAL 2
#if USE_HORIZONTAL == 0 || USE_HORIZONTAL == 1
+261 -50
View File
@@ -1,60 +1,271 @@
#include <stddef.h>
#include <stdint.h>
#include <retrosoc/core/soc.h>
#include <retrosoc/lib/printf.h>
#include <retrosoc/hal/timer.h>
#include <retrosoc/hal/gpio.h>
#include <retrosoc/hal/timer.h>
#include <retrosoc/lib/printf.h>
void ip_gpio_test(int argc, char **argv) {
(void)argc;
(void)argv;
#define RS_GPIO_DATA_IN_OFFSET UINT32_C(0x000)
#define RS_GPIO_DATA_OUT_OFFSET UINT32_C(0x004)
#define RS_GPIO_OUT_SET_OFFSET UINT32_C(0x008)
#define RS_GPIO_OUT_CLEAR_OFFSET UINT32_C(0x00C)
#define RS_GPIO_OUT_TOGGLE_OFFSET UINT32_C(0x010)
#define RS_GPIO_OUTPUT_ENABLE_OFFSET UINT32_C(0x014)
#define RS_GPIO_OE_SET_OFFSET UINT32_C(0x018)
#define RS_GPIO_OE_CLEAR_OFFSET UINT32_C(0x01C)
#define RS_GPIO_OPEN_DRAIN_OFFSET UINT32_C(0x024)
#define RS_GPIO_INPUT_CMOS_OFFSET UINT32_C(0x028)
#define RS_GPIO_PULL_UP_OFFSET UINT32_C(0x02C)
#define RS_GPIO_PULL_DOWN_OFFSET UINT32_C(0x030)
#define RS_GPIO_ALT_ENABLE_OFFSET UINT32_C(0x034)
#define RS_GPIO_ALT_SELECT_OFFSET UINT32_C(0x038)
#define RS_GPIO_USER_SELECT_OFFSET UINT32_C(0x03C)
#define RS_GPIO_USER_LOCK_OFFSET UINT32_C(0x040)
#define RS_GPIO_USER_ACCESS_MASK_OFFSET UINT32_C(0x048)
#define RS_GPIO_INTR_RISE_ENABLE_OFFSET UINT32_C(0x04C)
#define RS_GPIO_INTR_FALL_ENABLE_OFFSET UINT32_C(0x050)
#define RS_GPIO_INTR_HIGH_ENABLE_OFFSET UINT32_C(0x054)
#define RS_GPIO_INTR_LOW_ENABLE_OFFSET UINT32_C(0x058)
#define RS_GPIO_INTR_ENABLE_OFFSET UINT32_C(0x05C)
#define RS_GPIO_INTR_STATE_OFFSET UINT32_C(0x060)
#define RS_GPIO_INTR_TEST_OFFSET UINT32_C(0x068)
#define RS_GPIO_FILTER_ENABLE_OFFSET UINT32_C(0x06C)
#define RS_GPIO_FILTER_DIV_OFFSET UINT32_C(0x070)
#define RS_GPIO_FILTER_COUNT_OFFSET UINT32_C(0x074)
#define RS_GPIO_CONFIG_LOCK_OFFSET UINT32_C(0x078)
#define RS_GPIO_PAD_CAPABILITY_OFFSET UINT32_C(0x0F4)
#define RS_GPIO_IP_VERSION_OFFSET UINT32_C(0x0F8)
#define RS_GPIO_CAPABILITY_OFFSET UINT32_C(0x0FC)
printf("[RIB IP] gpio test\n");
#define RS_GPIO_REG(offset) RS_SOC_REG32(RS_SOC_RIBP_GPIO_ADMIN_BASE, (offset))
printf("[GPIO OE] %x\n", reg_gpio_oe);
reg_gpio_oe = (uint32_t)0b1111;
printf("[GPIO OE] %x\n", reg_gpio_oe);
static void rs_gpio_update_bits(uint32_t offset, uint32_t mask, bool set) {
uint32_t value = RS_GPIO_REG(offset);
printf("[GPIO DATA] %x\n", reg_gpio_do);
reg_gpio_do = (uint32_t)0xffff;
printf("[GPIO DATA] %x\n", reg_gpio_do);
reg_gpio_do = (uint32_t)0x0000;
printf("[GPIO DATA] %x\n", reg_gpio_do);
printf("led output test\n");
for (int i = 0; i < 50; ++i) {
if (rs_timer_delay_ms(RS_TIMER_0, 300U, RS_TIMER_DELAY_TIMEOUT) != RS_OK) {
printf("timer delay failed\n");
return;
}
if (reg_gpio_do == 0b00)
reg_gpio_do = (uint32_t)0b01;
else
reg_gpio_do = (uint32_t)0b00;
if (set) {
value |= mask;
} else {
value &= ~mask;
}
RS_GPIO_REG(offset) = value;
}
reg_gpio_do = (uint32_t)0b00;
printf("key input test\n"); // need extn board
reg_gpio_oe = (uint32_t)0b1101;
printf("[GPIO OE] %x\n", reg_gpio_oe);
printf("[GPIO DATA] %x\n", reg_gpio_do);
return;
for (int i = 0; i < 60; ++i) {
uint32_t led_val = 0b00;
if (((reg_gpio_do & 0b10) >> 1) == 0b0) {
if (rs_timer_delay_ms(RS_TIMER_0, 100U, RS_TIMER_DELAY_TIMEOUT) != RS_OK) {
printf("timer delay failed\n");
return;
}
if (((reg_gpio_do & 0b10) >> 1) == 0b0) {
printf("key detect\n");
if (led_val == 0b00) {
led_val = 0b01;
reg_gpio_do = led_val;
} else {
led_val = 0b00;
reg_gpio_do = led_val;
}
}
}
static bool rs_gpio_config_valid(const rs_gpio_config_t *config) {
return (config != NULL) && (config->mode <= RS_GPIO_MODE_USER_IP) &&
(config->pull <= RS_GPIO_PULL_DOWN) && (config->trigger <= RS_GPIO_TRIGGER_LOW);
}
static rs_status_t rs_gpio_check_pad_capabilities(const rs_gpio_config_t *config) {
const uint32_t capability = RS_GPIO_REG(RS_GPIO_PAD_CAPABILITY_OFFSET);
if ((config->input_cmos && ((capability & RS_GPIO_PAD_CAP_INPUT_CMOS) == 0U)) ||
((config->pull == RS_GPIO_PULL_UP) && ((capability & RS_GPIO_PAD_CAP_PULL_UP) == 0U)) ||
((config->pull == RS_GPIO_PULL_DOWN) && ((capability & RS_GPIO_PAD_CAP_PULL_DOWN) == 0U))) {
return RS_ENOTSUP;
}
return RS_OK;
}
static void rs_gpio_configure_trigger(uint32_t mask, rs_gpio_trigger_t trigger) {
rs_gpio_update_bits(RS_GPIO_INTR_RISE_ENABLE_OFFSET, mask, false);
rs_gpio_update_bits(RS_GPIO_INTR_FALL_ENABLE_OFFSET, mask, false);
rs_gpio_update_bits(RS_GPIO_INTR_HIGH_ENABLE_OFFSET, mask, false);
rs_gpio_update_bits(RS_GPIO_INTR_LOW_ENABLE_OFFSET, mask, false);
if ((trigger == RS_GPIO_TRIGGER_RISING) || (trigger == RS_GPIO_TRIGGER_BOTH)) {
rs_gpio_update_bits(RS_GPIO_INTR_RISE_ENABLE_OFFSET, mask, true);
}
if ((trigger == RS_GPIO_TRIGGER_FALLING) || (trigger == RS_GPIO_TRIGGER_BOTH)) {
rs_gpio_update_bits(RS_GPIO_INTR_FALL_ENABLE_OFFSET, mask, true);
}
if (trigger == RS_GPIO_TRIGGER_HIGH) {
rs_gpio_update_bits(RS_GPIO_INTR_HIGH_ENABLE_OFFSET, mask, true);
}
if (trigger == RS_GPIO_TRIGGER_LOW) {
rs_gpio_update_bits(RS_GPIO_INTR_LOW_ENABLE_OFFSET, mask, true);
}
}
rs_status_t rs_gpio_get_capabilities(rs_gpio_capabilities_t *capabilities) {
if (capabilities == NULL) {
return RS_EINVAL;
}
capabilities->version = RS_GPIO_REG(RS_GPIO_IP_VERSION_OFFSET);
capabilities->features = RS_GPIO_REG(RS_GPIO_CAPABILITY_OFFSET);
capabilities->pad_features = RS_GPIO_REG(RS_GPIO_PAD_CAPABILITY_OFFSET);
return RS_OK;
}
rs_status_t rs_gpio_configure(uint32_t pin, const rs_gpio_config_t *config) {
uint32_t mask;
rs_status_t status;
if ((pin >= RS_GPIO_PIN_COUNT) || !rs_gpio_config_valid(config)) {
return RS_EINVAL;
}
status = rs_gpio_check_pad_capabilities(config);
if (status != RS_OK) {
return status;
}
mask = UINT32_C(1) << pin;
RS_GPIO_REG(config->output_high ? RS_GPIO_OUT_SET_OFFSET : RS_GPIO_OUT_CLEAR_OFFSET) = mask;
rs_gpio_update_bits(RS_GPIO_OPEN_DRAIN_OFFSET, mask, config->open_drain);
rs_gpio_update_bits(RS_GPIO_INPUT_CMOS_OFFSET, mask, config->input_cmos);
rs_gpio_update_bits(RS_GPIO_PULL_UP_OFFSET, mask, false);
rs_gpio_update_bits(RS_GPIO_PULL_DOWN_OFFSET, mask, false);
if (config->pull == RS_GPIO_PULL_UP) {
rs_gpio_update_bits(RS_GPIO_PULL_UP_OFFSET, mask, true);
} else if (config->pull == RS_GPIO_PULL_DOWN) {
rs_gpio_update_bits(RS_GPIO_PULL_DOWN_OFFSET, mask, true);
} else {
/* No pull requested. */
}
rs_gpio_configure_trigger(mask, config->trigger);
rs_gpio_update_bits(RS_GPIO_FILTER_ENABLE_OFFSET, mask, config->filter_enable);
rs_gpio_update_bits(RS_GPIO_INTR_ENABLE_OFFSET, mask, config->interrupt_enable);
rs_gpio_update_bits(RS_GPIO_USER_SELECT_OFFSET, mask, config->mode == RS_GPIO_MODE_USER_IP);
rs_gpio_update_bits(RS_GPIO_ALT_SELECT_OFFSET, mask, config->mode == RS_GPIO_MODE_ALT1);
rs_gpio_update_bits(RS_GPIO_ALT_ENABLE_OFFSET, mask,
(config->mode == RS_GPIO_MODE_ALT0) || (config->mode == RS_GPIO_MODE_ALT1));
if (config->mode == RS_GPIO_MODE_OUTPUT) {
RS_GPIO_REG(RS_GPIO_OE_SET_OFFSET) = mask;
} else {
RS_GPIO_REG(RS_GPIO_OE_CLEAR_OFFSET) = mask;
}
return RS_OK;
}
rs_status_t rs_gpio_read(uint32_t pin, bool *high) {
if ((pin >= RS_GPIO_PIN_COUNT) || (high == NULL)) {
return RS_EINVAL;
}
*high = (RS_GPIO_REG(RS_GPIO_DATA_IN_OFFSET) & (UINT32_C(1) << pin)) != 0U;
return RS_OK;
}
rs_status_t rs_gpio_write(uint32_t pin, bool high) {
if (pin >= RS_GPIO_PIN_COUNT) {
return RS_EINVAL;
}
RS_GPIO_REG(high ? RS_GPIO_OUT_SET_OFFSET : RS_GPIO_OUT_CLEAR_OFFSET) = UINT32_C(1) << pin;
return RS_OK;
}
rs_status_t rs_gpio_toggle(uint32_t pin) {
if (pin >= RS_GPIO_PIN_COUNT) {
return RS_EINVAL;
}
RS_GPIO_REG(RS_GPIO_OUT_TOGGLE_OFFSET) = UINT32_C(1) << pin;
return RS_OK;
}
rs_status_t rs_gpio_port_read(uint32_t *value) {
if (value == NULL) {
return RS_EINVAL;
}
*value = RS_GPIO_REG(RS_GPIO_DATA_IN_OFFSET);
return RS_OK;
}
rs_status_t rs_gpio_port_set(uint32_t mask) {
RS_GPIO_REG(RS_GPIO_OUT_SET_OFFSET) = mask;
return RS_OK;
}
rs_status_t rs_gpio_port_clear(uint32_t mask) {
RS_GPIO_REG(RS_GPIO_OUT_CLEAR_OFFSET) = mask;
return RS_OK;
}
rs_status_t rs_gpio_port_toggle(uint32_t mask) {
RS_GPIO_REG(RS_GPIO_OUT_TOGGLE_OFFSET) = mask;
return RS_OK;
}
rs_status_t rs_gpio_interrupt_state(uint32_t *state) {
if (state == NULL) {
return RS_EINVAL;
}
*state = RS_GPIO_REG(RS_GPIO_INTR_STATE_OFFSET);
return RS_OK;
}
rs_status_t rs_gpio_interrupt_enable(uint32_t mask, bool enable) {
rs_gpio_update_bits(RS_GPIO_INTR_ENABLE_OFFSET, mask, enable);
return RS_OK;
}
rs_status_t rs_gpio_interrupt_clear(uint32_t mask) {
RS_GPIO_REG(RS_GPIO_INTR_STATE_OFFSET) = mask;
return RS_OK;
}
rs_status_t rs_gpio_interrupt_test(uint32_t mask) {
RS_GPIO_REG(RS_GPIO_INTR_TEST_OFFSET) = mask;
return RS_OK;
}
rs_status_t rs_gpio_filter_configure(uint32_t enable_mask, const rs_gpio_filter_timing_t *timing) {
if ((timing == NULL) || (timing->stable_samples == 0U) || (timing->stable_samples > 15U)) {
return RS_EINVAL;
}
RS_GPIO_REG(RS_GPIO_FILTER_ENABLE_OFFSET) = 0U;
RS_GPIO_REG(RS_GPIO_FILTER_DIV_OFFSET) = timing->divider;
RS_GPIO_REG(RS_GPIO_FILTER_COUNT_OFFSET) = timing->stable_samples;
RS_GPIO_REG(RS_GPIO_FILTER_ENABLE_OFFSET) = enable_mask;
return RS_OK;
}
rs_status_t rs_gpio_user_access_set(uint32_t mask) {
RS_GPIO_REG(RS_GPIO_USER_ACCESS_MASK_OFFSET) = mask;
return RS_OK;
}
rs_status_t rs_gpio_user_ip_select(uint32_t mask, bool enable) {
rs_gpio_update_bits(RS_GPIO_USER_SELECT_OFFSET, mask, enable);
return RS_OK;
}
rs_status_t rs_gpio_user_ip_lock(uint32_t mask) {
RS_GPIO_REG(RS_GPIO_USER_LOCK_OFFSET) = mask;
return RS_OK;
}
rs_status_t rs_gpio_configuration_lock(uint32_t mask) {
RS_GPIO_REG(RS_GPIO_CONFIG_LOCK_OFFSET) = mask;
return RS_OK;
}
void rs_gpio_shell_test(int argc, char **argv) {
const rs_gpio_config_t config = {
RS_GPIO_MODE_OUTPUT,
RS_GPIO_PULL_NONE,
RS_GPIO_TRIGGER_NONE,
false,
false,
false,
false,
false,
};
(void)argc;
(void)argv;
printf("gpio test\n");
if (rs_gpio_configure(0U, &config) != RS_OK) {
printf("gpio configuration failed\n");
return;
}
for (uint32_t iteration = 0U; iteration < 10U; ++iteration) {
(void)rs_gpio_toggle(0U);
if (rs_timer_delay_ms(RS_TIMER_0, 300U, RS_TIMER_DELAY_TIMEOUT) != RS_OK) {
printf("gpio timer delay failed\n");
return;
}
}
(void)rs_gpio_write(0U, false);
printf("gpio test passed\n");
}
+26
View File
@@ -0,0 +1,26 @@
#include <stddef.h>
#include <stdint.h>
#include <retrosoc/hal/gpio.h>
#define RS_GPIO_MICROSECONDS_PER_SECOND UINT64_C(1000000)
#define RS_GPIO_MAX_SAMPLE_CYCLES UINT64_C(65536)
rs_status_t rs_gpio_filter_timing_from_us(uint32_t source_clock_hz, uint32_t sample_period_us,
uint8_t stable_samples, rs_gpio_filter_timing_t *timing) {
uint64_t product;
uint64_t cycles;
if ((source_clock_hz == 0U) || (sample_period_us == 0U) || (stable_samples == 0U) ||
(stable_samples > 15U) || (timing == NULL)) {
return RS_EINVAL;
}
product = (uint64_t)source_clock_hz * (uint64_t)sample_period_us;
cycles = (product + RS_GPIO_MICROSECONDS_PER_SECOND - 1U) / RS_GPIO_MICROSECONDS_PER_SECOND;
if ((cycles == 0U) || (cycles > RS_GPIO_MAX_SAMPLE_CYCLES)) {
return RS_EINVAL;
}
timing->divider = (uint16_t)(cycles - 1U);
timing->stable_samples = stable_samples;
return RS_OK;
}
+5 -5
View File
@@ -55,22 +55,22 @@ static bool rs_lcd_region_valid(uint16_t x_start, uint16_t y_start, uint16_t x_e
}
static void lcd_wr_dc_cmd(uint8_t cmd) {
lcd_dc_clr;
(void)rs_gpio_port_clear(UINT32_C(1) << 2U);
qspi0_wr_dat8(cmd);
}
static void lcd_wr_dc_data8(uint8_t dat) {
lcd_dc_set;
(void)rs_gpio_port_set(UINT32_C(1) << 2U);
qspi0_wr_dat8(dat);
}
static void lcd_wr_dc_data16(uint16_t dat) {
lcd_dc_set;
(void)rs_gpio_port_set(UINT32_C(1) << 2U);
qspi0_wr_dat16(dat);
}
static void lcd_wr_data32(uint32_t *dat, uint32_t len) {
lcd_dc_set;
(void)rs_gpio_port_set(UINT32_C(1) << 2U);
qspi0_wr_data32(dat, len);
}
@@ -228,7 +228,7 @@ void lcd_fill_image(uint16_t xsta, uint16_t ysta, uint16_t xend, uint16_t yend,
uint32_t total_pixels = (uint32_t)(xend - xsta) * (uint32_t)(yend - ysta);
#ifdef USE_QSPI0_DMA
lcd_dc_set;
(void)rs_gpio_port_set(UINT32_C(1) << 2U);
uintptr_t addr = (uintptr_t)data;
// printf("addr: %x\n\n", addr);
qspi0_dma_xfer(addr, total_pixels / 2U); // every xfer contains two RGB565 pixels
+13 -1
View File
@@ -1,5 +1,6 @@
#include <retrosoc/core/soc.h>
#include <retrosoc/core/wait.h>
#include <retrosoc/hal/gpio.h>
#include <retrosoc/hal/timer.h>
#include <retrosoc/hal/qspi.h>
#include <retrosoc/hal/dma.h>
@@ -16,7 +17,18 @@ static bool rs_qspi_wait(uint32_t mask, uint32_t expected) {
}
void qspi0_init(QSPI0_InitStruct_t val) {
reg_gpio_oe = (uint32_t)0b100;
const rs_gpio_config_t gpio_config = {
.mode = RS_GPIO_MODE_OUTPUT,
.pull = RS_GPIO_PULL_NONE,
.trigger = RS_GPIO_TRIGGER_NONE,
.output_high = false,
.open_drain = false,
.input_cmos = false,
.filter_enable = false,
.interrupt_enable = false,
};
(void)rs_gpio_configure(2U, &gpio_config);
reg_xpi_mode = val.mode;
reg_xpi_nss = val.nss;
reg_xpi_clkdiv = val.clkdiv;
+12 -6
View File
@@ -3,6 +3,7 @@
#include <retrosoc/core/soc.h>
#include <retrosoc/hal/dma.h>
#include <retrosoc/hal/gpio.h>
#include <retrosoc/hal/ws2812.h>
#include <retrosoc/lib/printf.h>
@@ -18,8 +19,6 @@
#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;
}
@@ -253,14 +252,21 @@ void ip_ws2812_test(int argc, char **argv) {
rs_ws2812_pack_grb(0x00U, 0x00U, 0x20U),
rs_ws2812_pack_grb(0x10U, 0x10U, 0x10U),
};
const rs_gpio_config_t gpio_config = {
.mode = RS_GPIO_MODE_ALT1,
.pull = RS_GPIO_PULL_NONE,
.trigger = RS_GPIO_TRIGGER_NONE,
.output_high = false,
.open_drain = false,
.input_cmos = false,
.filter_enable = false,
.interrupt_enable = false,
};
(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) ||
if ((rs_gpio_configure(2U, &gpio_config) != RS_OK) || (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");
+2 -1
View File
@@ -95,7 +95,8 @@ void rs_app_info(void) {
TF_CARD_START + TF_CARD_OFFST - 1, TF_CARD_OFFST / 1024 / 1024 / 1024);
printf("Memory Map IO Device:\n");
printf(" 1 x GPIO(32PIN) @%p\n", (void *)&reg_gpio_oe);
printf(" 1 x GPIO(32PIN) @%p\n",
(void *)(uintptr_t)RS_SOC_RIBP_GPIO_ADMIN_BASE);
printf(" 1 x UART0 @%p\n", (void *)&reg_uart0_clkdiv);
printf(" 2 x TIMER(0,1) @%p,%p\n",
(void *)(uintptr_t)RS_SOC_RIBP_TIM0_BASE, (void *)(uintptr_t)RS_SOC_RIBP_TIM1_BASE);
+2
View File
@@ -26,6 +26,8 @@ the root README and subsystem guides.
counting modes, interrupt, debug-freeze, HAL, and verification contracts.
- [ip/clint.md](ip/clint.md) defines the standard CLINT register map, fixed
timebase, RV32 access rules, interrupt behavior, and verification contract.
- [ip/gpio.md](ip/gpio.md) defines the GPIO V2 dual-window ABI, pad ownership,
filtering, interrupts, PDK capabilities, HAL, and verification contract.
- [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
+5 -4
View File
@@ -130,14 +130,15 @@ Verilator regressions.
`make CONFIG=configs/ci/ihp130.mk formal` proves selected
protocol invariants with SymbiYosys, Yosys, `sv2v`, and Bitwuzla. The current
targets are `bus`, `rib_adapter`, `rib2apb`, `sysctrl`, `pll_rcu`,
`gpio_user`, and `ws2812`; each uses the SBY `prove` task for bounded model
targets are `bus`, `rib_adapter`, `rib2apb`, `sysctrl`, `pll_rcu`, `gpio`,
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
state machine. `gpio` checks dual-window access isolation, fixed user-GPIO
ownership, locks, handoff, open-drain safety, and mux behavior. `rib_adapter`
checks both single-word compatibility adapters
under backpressure. `ws2812` checks RIBP response stability, FIFO bounds,
waveform idle safety, sticky error and interrupt propagation, abort, and
underflow preconditions. Direct Common utility proofs, including `spill_register`,
+1
View File
@@ -6,6 +6,7 @@ verification documentation for self-owned retroSoC peripheral IP.
- [timer.md](timer.md) defines the dual RIBP general timer.
- [ws2812.md](ws2812.md) defines the WS2812 transmitter.
- [clint.md](clint.md) defines the management-hart software and timer interrupt block.
- [gpio.md](gpio.md) defines the dual-window GPIO controller.
The corresponding RTL and HAL implementations remain the executable sources
of truth. Update the affected document whenever an IP interface or register ABI
+152
View File
@@ -0,0 +1,152 @@
# GPIO Controller
The Mini SoC GPIO V2 block controls 32 bidirectional pads. It provides
software, alternate-function, and user-IP ownership modes, atomic output and
output-enable commands, open-drain operation, synchronized and optionally
filtered inputs, per-pin interrupts, irreversible configuration locks, and
capability discovery. The register ABI is directly encoded in RTL and C; a
register generator is intentionally not used in this version.
## Integration
| Property | Value |
| --- | --- |
| User-core RIBP window | `0x10000000`, user access `rw` |
| Management RIBP window | `0x10014000`, user access `none` |
| Pins | 32 |
| RIBP interrupt group | Bit 11 |
| Core interrupt | 18 |
| Input synchronizer | Two stages in the SoC clock domain |
| ABI version | `0x00020000` |
The two windows reach one register block. The user window exposes only data
and interrupt operations selected by `USER_ACCESS_MASK`; this mask resets to
zero. The management window owns pin mode, pad controls, filters, interrupt
trigger configuration, user-IP handoff, access policy, and locks. The existing
SoC bus firewall rejects user-core transactions to the management window, so
the peripheral does not infer privilege from request timing or address alone.
## User window
All registers are 32 bits and naturally aligned. Unmapped, unaligned, and
direction-invalid accesses complete with `resp_err`. User data returned from
the block is ANDed with `USER_ACCESS_MASK`; writes can affect only mask bits
that are one.
| Offset | Name | Access | Description |
| --- | --- | --- | --- |
| `0x000` | `DATA_IN` | RO | Synchronized and filtered pin inputs. |
| `0x004` | `DATA_OUT` | RW | Software output latch. |
| `0x008` | `OUT_SET` | WO | Atomically set output-latch bits. |
| `0x00C` | `OUT_CLEAR` | WO | Atomically clear output-latch bits. |
| `0x010` | `OUT_TOGGLE` | WO | Atomically toggle output-latch bits. |
| `0x014` | `INTR_STATE` | RW1C | Sticky raw interrupt state. |
| `0x018` | `INTR_STATUS` | RO | `INTR_STATE & INTR_ENABLE`. |
| `0x01C` | `INTR_ENABLE` | RW | Per-pin interrupt enable. |
| `0x020` | `INTR_ENABLE_SET` | WO | Atomically set interrupt-enable bits. |
| `0x024` | `INTR_ENABLE_CLEAR` | WO | Atomically clear interrupt-enable bits. |
| `0x0F8` | `IP_VERSION` | RO | ABI version. |
| `0x0FC` | `CAPABILITY` | RO | Implemented digital feature summary. |
## Management window
| Offset | Name | Access | Reset | Description |
| --- | --- | --- | --- | --- |
| `0x000` | `DATA_IN` | RO | `0` | Synchronized and filtered inputs. |
| `0x004` | `DATA_OUT` | RW | `0` | Software output latch. |
| `0x008` | `OUT_SET` | WO | - | Atomic output set. |
| `0x00C` | `OUT_CLEAR` | WO | - | Atomic output clear. |
| `0x010` | `OUT_TOGGLE` | WO | - | Atomic output toggle. |
| `0x014` | `OUTPUT_ENABLE` | RW | `0` | Software output-enable latch. |
| `0x018` | `OE_SET` | WO | - | Atomic output-enable set. |
| `0x01C` | `OE_CLEAR` | WO | - | Atomic output-enable clear. |
| `0x020` | `OE_TOGGLE` | WO | - | Atomic output-enable toggle. |
| `0x024` | `OPEN_DRAIN` | RW | `0` | Drive zero for output low and release for output high. |
| `0x028` | `INPUT_CMOS` | RW | `0` | Select the PDK CMOS input characteristic when supported. |
| `0x02C` | `PULL_UP` | RW | `0` | Enable pad pull-up when supported. |
| `0x030` | `PULL_DOWN` | RW | `0` | Enable pad pull-down when supported. |
| `0x034` | `ALT_ENABLE` | RW | `0` | Select an alternate function instead of software output. |
| `0x038` | `ALT_SELECT` | RW | `0` | Select ALT1 when set and ALT0 when clear. |
| `0x03C` | `USER_SELECT` | RW | `0` | Select user-IP pad ownership. |
| `0x040` | `USER_LOCK` | W1S/RO | `0` | Permanently lock `USER_SELECT` bits until reset. |
| `0x044` | `USER_STATUS` | RO | `0` | Active user ownership after the handoff guard cycle. |
| `0x048` | `USER_ACCESS_MASK` | RW | `0` | Permit user-window data and interrupt bits. |
| `0x04C` | `INTR_RISE_ENABLE` | RW | `0` | Rising-edge event selection. |
| `0x050` | `INTR_FALL_ENABLE` | RW | `0` | Falling-edge event selection. |
| `0x054` | `INTR_HIGH_ENABLE` | RW | `0` | High-level event selection. |
| `0x058` | `INTR_LOW_ENABLE` | RW | `0` | Low-level event selection. |
| `0x05C` | `INTR_ENABLE` | RW | `0` | Per-pin interrupt output enable. |
| `0x060` | `INTR_STATE` | RW1C | `0` | Sticky raw interrupt state. Events win over clear. |
| `0x064` | `INTR_STATUS` | RO | `0` | Enabled interrupt state. |
| `0x068` | `INTR_TEST` | WO | - | Write-one software interrupt test. |
| `0x06C` | `FILTER_ENABLE` | RW | `0` | Enable the digital stability filter per pin. |
| `0x070` | `FILTER_DIV` | RW | `0` | Sample every `FILTER_DIV + 1` clocks. |
| `0x074` | `FILTER_COUNT` | RW | `0` | Required stable samples, valid range 1 through 15. |
| `0x078` | `CONFIG_LOCK` | W1S/RO | `0` | Permanently lock per-pin configuration until reset. |
| `0x0F4` | `PAD_CAPABILITY` | RO | PDK-specific | CMOS, pull-up, and pull-down support. |
| `0x0F8` | `IP_VERSION` | RO | `0x00020000` | ABI version 2.0. |
| `0x0FC` | `CAPABILITY` | RO | `0x007F4220` | Digital feature, filter width, ABI, and pin count. |
`CONFIG_LOCK` protects output enable, open-drain, input mode, pulls, alternate
selection, user access, interrupt trigger selection, and filter enable.
`USER_LOCK` independently protects user-IP ownership. Both are write-one-set
and clear only on peripheral reset. Writes that request an unsupported pad
feature, simultaneous pull-up and pull-down, conflicting edge and level
triggers, a zero filter count, a timing change while any filter is active, or
a change to a locked bit complete with `resp_err` and do not update state.
## Pin and interrupt behavior
Software output and output-enable aliases avoid read-modify-write races. ALT0
and ALT1 outputs are selected per pin. Setting `USER_SELECT` changes the source
to `user_gpio_if`; every ownership transition forces the physical output
enable low for one complete SoC clock before the new owner can drive. Open
drain is applied after this mux: logical zero drives low, while logical one
releases the pad.
The raw pad input passes through a two-stage synchronizer. When filtering is
disabled, the synchronized value feeds `DATA_IN`, the interrupt detector, and
the user IP. When enabled, the input must disagree with the current filtered
value for `FILTER_COUNT` samples before changing. Alternate peripheral inputs
continue to receive the raw pad signal because each peripheral owns its CDC
requirements, for example the DVP pixel clock.
Each pin supports rising, falling, both-edge, high-level, or low-level
interrupt operation. Edge and level modes cannot be combined for one pin, and
high plus low is invalid. Level events reassert sticky state while the level
remains active. The output interrupt is the reduction OR of
`INTR_STATE & INTR_ENABLE`.
## Pad capabilities
Digital functionality is identical for every PDK, but pad electrical controls
are reported rather than emulated when a technology lacks a matching cell.
| PDK | CMOS input select | Pull-up | Pull-down |
| --- | --- | --- | --- |
| GF180 | Yes | Yes | Yes |
| ICS55 | Yes | Yes | Yes |
| SKY130 | Yes | No | No |
| IHP130 | No | No | No |
| S110 / generic behavioral | No | No | No |
Drive strength, slew rate, sleep retention, and wakeup are deliberately not
implemented as generic GPIO bits. They require qualified PDK pad cells,
power-domain state, and UPF/physical-design ownership before a portable ABI is
safe.
## Software and verification
`<retrosoc/hal/gpio.h>` exposes structured pin configuration, atomic data
operations, interrupt control, user ownership and access policy, lock
commands, capability discovery, and integer filter timing conversion. The HAL
returns `RS_ENOTSUP` before requesting unsupported electrical features.
The self-checking RTL test covers reset values, access errors, atomic output
and OE, user-window masking, guarded user-IP handoff, open drain, pull
conflicts, edge interrupt state, W1C, interrupt test, trigger conflicts,
filtering, and configuration lock. The SBY target checks RIBP request
stability, lock monotonicity, user-window isolation, handoff high impedance,
and open-drain safety, with covers for ownership, error, and interrupt paths.
Host C tests cover filter timing limits, and `ci_smoke` checks capability
discovery and public HAL output operations in full-SoC simulation.
+12 -5
View File
@@ -128,7 +128,11 @@ digraph mini_soc_block_diagram {
];
ribp_targets [
fillcolor="#F9E1E7",
label="RIBP target wrapper\nXPI/Flash, SDRAM, PSRAM, SPI-SD and DVP\nGPIO, UART0, I2C and I2S"
label="RIBP target wrapper\nXPI/Flash, SDRAM, PSRAM, SPI-SD and DVP\nUART0, I2C and I2S"
];
gpio_controller [
fillcolor="#F9E1E7",
label="GPIO V2\nuser + management RIBP windows\natomic I/O, filter, locks, IRQ18"
];
timers [
fillcolor="#F9E1E7",
@@ -144,7 +148,7 @@ digraph mini_soc_block_diagram {
];
gpio_bridge [
fillcolor="#E5E7EB",
label="GPIO pad bridge\nsoftware, alternate and user modes"
label="GPIO pad bridge\nPDK capability-adapted controls"
];
irq [
fillcolor="#FCE7F3",
@@ -157,7 +161,7 @@ digraph mini_soc_block_diagram {
{ rank=same; user_core; dma; }
{ rank=same; fault; fabric; sram; }
{ rank=same; ribp_adapter; apb_adapter; }
{ rank=same; sysctrl; clint; ribp_targets; timers; ws2812; apb_targets; }
{ rank=same; sysctrl; clint; ribp_targets; gpio_controller; timers; ws2812; apb_targets; }
{ rank=same; gpio_bridge; irq; }
ext_clock -> rcu [label="clock / reset"];
@@ -182,17 +186,20 @@ digraph mini_soc_block_diagram {
ribp_adapter -> sysctrl [label="RIBP"];
ribp_adapter -> clint [label="RIBP"];
ribp_adapter -> ribp_targets [label="RIBP"];
ribp_adapter -> gpio_controller [label="RIBP\nuser/admin"];
ribp_adapter -> timers [label="RIBP"];
ribp_adapter -> ws2812 [label="RIBP"];
apb_adapter -> apb_targets [label="APB"];
ribp_targets -> gpio_bridge [label="GPIO alternate functions"];
ribp_targets -> gpio_controller [label="alternate functions"];
gpio_controller -> gpio_bridge [label="pad control"];
gpio_controller -> irq [label="IRQ18"];
timers -> irq [label="IRQ3 / IRQ4"];
clint -> irq [label="machine IRQ0 / IRQ1"];
ws2812 -> gpio_bridge [label="GPIO2 ALT1"];
dma -> ws2812 [label="TXDATA writes", style=dashed];
ws2812 -> irq [label="IRQ17"];
apb_targets -> gpio_bridge [label="user_gpio"];
apb_targets -> gpio_controller [label="user_gpio"];
ribp_targets -> ext_io [dir=both, label="device interfaces"];
gpio_bridge -> ext_io [dir=both, label="pad control"];
}
+230 -200
View File
@@ -4,86 +4,86 @@
<!-- Generated by graphviz version 2.43.0 (0)
-->
<!-- Title: mini_soc_block_diagram Pages: 1 -->
<svg width="1719pt" height="887pt"
viewBox="0.00 0.00 1719.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)">
<svg width="1986pt" height="896pt"
viewBox="0.00 0.00 1986.00 896.48" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 892.48)">
<title>mini_soc_block_diagram</title>
<text text-anchor="middle" x="132" y="-861.08" font-family="Arial" font-size="18.00">retroSoC Mini SoC Architecture</text>
<text text-anchor="middle" x="132" y="-870.08" font-family="Arial" font-size="18.00">retroSoC Mini SoC Architecture</text>
<g id="clust1" class="cluster">
<title>cluster_asic</title>
<path fill="transparent" stroke="#111827" d="M160,-85C160,-85 1691,-85 1691,-85 1697,-85 1703,-91 1703,-97 1703,-97 1703,-763.48 1703,-763.48 1703,-769.48 1697,-775.48 1691,-775.48 1691,-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>
<path fill="transparent" stroke="#111827" d="M160,-85C160,-85 1958,-85 1958,-85 1964,-85 1970,-91 1970,-97 1970,-97 1970,-772.48 1970,-772.48 1970,-778.48 1964,-784.48 1958,-784.48 1958,-784.48 160,-784.48 160,-784.48 154,-784.48 148,-778.48 148,-772.48 148,-772.48 148,-97 148,-97 148,-91 154,-85 160,-85"/>
<text text-anchor="middle" x="196.5" y="-769.28" font-family="Arial" font-size="14.00" fill="#111827">retrosoc_asic</text>
</g>
<g id="clust2" class="cluster">
<title>cluster_soc</title>
<path fill="transparent" stroke="#94a3b8" stroke-dasharray="5,2" d="M390,-103C390,-103 1673,-103 1673,-103 1679,-103 1685,-109 1685,-115 1685,-115 1685,-720.48 1685,-720.48 1685,-726.48 1679,-732.48 1673,-732.48 1673,-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>
<path fill="transparent" stroke="#94a3b8" stroke-dasharray="5,2" d="M390,-103C390,-103 1940,-103 1940,-103 1946,-103 1952,-109 1952,-115 1952,-115 1952,-729.48 1952,-729.48 1952,-735.48 1946,-741.48 1940,-741.48 1940,-741.48 390,-741.48 390,-741.48 384,-741.48 378,-735.48 378,-729.48 378,-729.48 378,-115 378,-115 378,-109 384,-103 390,-103"/>
<text text-anchor="middle" x="407.5" y="-727.88" font-family="Arial" font-size="12.00" fill="#334155">retrosoc</text>
</g>
<!-- ext_clock -->
<g id="node1" class="node">
<title>ext_clock</title>
<path fill="#f3f4f6" stroke="#4b5563" stroke-width="1.1" d="M118,-669.98C118,-669.98 12,-669.98 12,-669.98 6,-669.98 0,-663.98 0,-657.98 0,-657.98 0,-645.98 0,-645.98 0,-639.98 6,-633.98 12,-633.98 12,-633.98 118,-633.98 118,-633.98 124,-633.98 130,-639.98 130,-645.98 130,-645.98 130,-657.98 130,-657.98 130,-663.98 124,-669.98 118,-669.98"/>
<text text-anchor="middle" x="65" y="-654.98" font-family="Arial" font-size="10.00" fill="#111827">External clocks and reset</text>
<text text-anchor="middle" x="65" y="-643.98" font-family="Arial" font-size="10.00" fill="#111827">ext_clk, aud_clk, reset</text>
<path fill="#f3f4f6" stroke="#4b5563" stroke-width="1.1" d="M118,-678.98C118,-678.98 12,-678.98 12,-678.98 6,-678.98 0,-672.98 0,-666.98 0,-666.98 0,-654.98 0,-654.98 0,-648.98 6,-642.98 12,-642.98 12,-642.98 118,-642.98 118,-642.98 124,-642.98 130,-648.98 130,-654.98 130,-654.98 130,-666.98 130,-666.98 130,-672.98 124,-678.98 118,-678.98"/>
<text text-anchor="middle" x="65" y="-663.98" font-family="Arial" font-size="10.00" fill="#111827">External clocks and reset</text>
<text text-anchor="middle" x="65" y="-652.98" font-family="Arial" font-size="10.00" fill="#111827">ext_clk, aud_clk, reset</text>
</g>
<!-- rcu -->
<g id="node4" class="node">
<title>rcu</title>
<path fill="#ede9fe" stroke="#4b5563" stroke-width="1.1" d="M350,-574.48C350,-574.48 178,-574.48 178,-574.48 172,-574.48 166,-568.48 166,-562.48 166,-562.48 166,-541.48 166,-541.48 166,-535.48 172,-529.48 178,-529.48 178,-529.48 350,-529.48 350,-529.48 356,-529.48 362,-535.48 362,-541.48 362,-541.48 362,-562.48 362,-562.48 362,-568.48 356,-574.48 350,-574.48"/>
<text text-anchor="middle" x="264" y="-560.48" font-family="Arial" font-size="10.00" fill="#111827">RCU</text>
<text text-anchor="middle" x="264" y="-549.48" font-family="Arial" font-size="10.00" fill="#111827">clock buffering and reset synchronization</text>
<text text-anchor="middle" x="264" y="-538.48" font-family="Arial" font-size="10.00" fill="#111827">PLL CDC and fixed 1 MHz timebase</text>
<path fill="#ede9fe" stroke="#4b5563" stroke-width="1.1" d="M350,-583.48C350,-583.48 178,-583.48 178,-583.48 172,-583.48 166,-577.48 166,-571.48 166,-571.48 166,-550.48 166,-550.48 166,-544.48 172,-538.48 178,-538.48 178,-538.48 350,-538.48 350,-538.48 356,-538.48 362,-544.48 362,-550.48 362,-550.48 362,-571.48 362,-571.48 362,-577.48 356,-583.48 350,-583.48"/>
<text text-anchor="middle" x="264" y="-569.48" font-family="Arial" font-size="10.00" fill="#111827">RCU</text>
<text text-anchor="middle" x="264" y="-558.48" font-family="Arial" font-size="10.00" fill="#111827">clock buffering and reset synchronization</text>
<text text-anchor="middle" x="264" y="-547.48" font-family="Arial" font-size="10.00" fill="#111827">PLL CDC and fixed 1 MHz timebase</text>
</g>
<!-- ext_clock&#45;&gt;rcu -->
<g id="edge1" class="edge">
<title>ext_clock&#45;&gt;rcu</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M99.66,-633.91C131.11,-618.43 177.87,-595.4 213.48,-577.87"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="214.79,-579.95 219.99,-574.66 212.63,-575.55 214.79,-579.95"/>
<text text-anchor="middle" x="191.5" y="-600.08" font-family="Arial" font-size="8.00" fill="#374151">clock / reset</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M99.66,-642.91C131.11,-627.43 177.87,-604.4 213.48,-586.87"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="214.79,-588.95 219.99,-583.66 212.63,-584.55 214.79,-588.95"/>
<text text-anchor="middle" x="191.5" y="-609.08" font-family="Arial" font-size="8.00" fill="#374151">clock / reset</text>
</g>
<!-- ext_io -->
<g id="node2" class="node">
<title>ext_io</title>
<path fill="#f3f4f6" stroke="#4b5563" stroke-width="1.1" d="M1557,-45C1557,-45 1365,-45 1365,-45 1359,-45 1353,-39 1353,-33 1353,-33 1353,-12 1353,-12 1353,-6 1359,0 1365,0 1365,0 1557,0 1557,0 1563,0 1569,-6 1569,-12 1569,-12 1569,-33 1569,-33 1569,-39 1563,-45 1557,-45"/>
<text text-anchor="middle" x="1461" y="-31" font-family="Arial" font-size="10.00" fill="#111827">External I/O and memory</text>
<text text-anchor="middle" x="1461" y="-20" font-family="Arial" font-size="10.00" fill="#111827">GPIO, UART0, XPI Flash, SDRAM, PSRAM</text>
<text text-anchor="middle" x="1461" y="-9" font-family="Arial" font-size="10.00" fill="#111827">SD card, I2C, I2S, DVP and WS2812</text>
<path fill="#f3f4f6" stroke="#4b5563" stroke-width="1.1" d="M1063,-45C1063,-45 871,-45 871,-45 865,-45 859,-39 859,-33 859,-33 859,-12 859,-12 859,-6 865,0 871,0 871,0 1063,0 1063,0 1069,0 1075,-6 1075,-12 1075,-12 1075,-33 1075,-33 1075,-39 1069,-45 1063,-45"/>
<text text-anchor="middle" x="967" y="-31" font-family="Arial" font-size="10.00" fill="#111827">External I/O and memory</text>
<text text-anchor="middle" x="967" y="-20" font-family="Arial" font-size="10.00" fill="#111827">GPIO, UART0, XPI Flash, SDRAM, PSRAM</text>
<text text-anchor="middle" x="967" 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="M678.5,-851.48C678.5,-851.48 547.5,-851.48 547.5,-851.48 541.5,-851.48 535.5,-845.48 535.5,-839.48 535.5,-839.48 535.5,-827.48 535.5,-827.48 535.5,-821.48 541.5,-815.48 547.5,-815.48 547.5,-815.48 678.5,-815.48 678.5,-815.48 684.5,-815.48 690.5,-821.48 690.5,-827.48 690.5,-827.48 690.5,-839.48 690.5,-839.48 690.5,-845.48 684.5,-851.48 678.5,-851.48"/>
<text text-anchor="middle" x="613" y="-836.48" font-family="Arial" font-size="10.00" fill="#111827">Management JTAG</text>
<text text-anchor="middle" x="613" 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="M1078.5,-860.48C1078.5,-860.48 947.5,-860.48 947.5,-860.48 941.5,-860.48 935.5,-854.48 935.5,-848.48 935.5,-848.48 935.5,-836.48 935.5,-836.48 935.5,-830.48 941.5,-824.48 947.5,-824.48 947.5,-824.48 1078.5,-824.48 1078.5,-824.48 1084.5,-824.48 1090.5,-830.48 1090.5,-836.48 1090.5,-836.48 1090.5,-848.48 1090.5,-848.48 1090.5,-854.48 1084.5,-860.48 1078.5,-860.48"/>
<text text-anchor="middle" x="1013" y="-845.48" font-family="Arial" font-size="10.00" fill="#111827">Management JTAG</text>
<text text-anchor="middle" x="1013" y="-834.48" font-family="Arial" font-size="10.00" fill="#111827">TCK, TMS, TDI, TRST_n, TDO</text>
</g>
<!-- jtag_dtm -->
<g id="node6" class="node">
<title>jtag_dtm</title>
<path fill="#e5e7eb" stroke="#4b5563" stroke-width="1.1" d="M688,-669.98C688,-669.98 538,-669.98 538,-669.98 532,-669.98 526,-663.98 526,-657.98 526,-657.98 526,-645.98 526,-645.98 526,-639.98 532,-633.98 538,-633.98 538,-633.98 688,-633.98 688,-633.98 694,-633.98 700,-639.98 700,-645.98 700,-645.98 700,-657.98 700,-657.98 700,-663.98 694,-669.98 688,-669.98"/>
<text text-anchor="middle" x="613" y="-654.98" font-family="Arial" font-size="10.00" fill="#111827">Hazard3 JTAG DTM</text>
<text text-anchor="middle" x="613" 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="M1088,-678.98C1088,-678.98 938,-678.98 938,-678.98 932,-678.98 926,-672.98 926,-666.98 926,-666.98 926,-654.98 926,-654.98 926,-648.98 932,-642.98 938,-642.98 938,-642.98 1088,-642.98 1088,-642.98 1094,-642.98 1100,-648.98 1100,-654.98 1100,-654.98 1100,-666.98 1100,-666.98 1100,-672.98 1094,-678.98 1088,-678.98"/>
<text text-anchor="middle" x="1013" y="-663.98" font-family="Arial" font-size="10.00" fill="#111827">Hazard3 JTAG DTM</text>
<text text-anchor="middle" x="1013" y="-652.98" font-family="Arial" font-size="10.00" fill="#111827">10 MHz asynchronous TCK domain</text>
</g>
<!-- ext_jtag&#45;&gt;jtag_dtm -->
<g id="edge2" class="edge">
<title>ext_jtag&#45;&gt;jtag_dtm</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M613,-815.27C613,-783.24 613,-714.39 613,-677.51"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="615.45,-677.14 613,-670.14 610.55,-677.14 615.45,-677.14"/>
<text text-anchor="middle" x="633" 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="M1013,-824.27C1013,-792.24 1013,-723.39 1013,-686.51"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1015.45,-686.14 1013,-679.14 1010.55,-686.14 1015.45,-686.14"/>
<text text-anchor="middle" x="1033" y="-795.08" font-family="Arial" font-size="8.00" fill="#374151">JTAG pads</text>
</g>
<!-- fabric -->
<g id="node11" class="node">
<title>fabric</title>
<path fill="#dcecf8" stroke="#4b5563" stroke-width="1.1" d="M1066.66,-442.31C1066.66,-442.31 998.98,-470.16 998.98,-470.16 993.43,-472.45 981.88,-474.73 975.88,-474.73 975.88,-474.73 820.12,-474.73 820.12,-474.73 814.12,-474.73 802.57,-472.45 797.02,-470.16 797.02,-470.16 729.34,-442.31 729.34,-442.31 723.79,-440.03 723.79,-435.46 729.34,-433.18 729.34,-433.18 797.02,-405.32 797.02,-405.32 802.57,-403.04 814.12,-400.76 820.12,-400.76 820.12,-400.76 975.88,-400.76 975.88,-400.76 981.88,-400.76 993.43,-403.04 998.98,-405.32 998.98,-405.32 1066.66,-433.18 1066.66,-433.18 1072.21,-435.46 1072.21,-440.03 1066.66,-442.31"/>
<text text-anchor="middle" x="898" y="-446.24" font-family="Arial" font-size="10.00" fill="#111827">RIB fabric</text>
<text text-anchor="middle" x="898" 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="898" 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="M1086.66,-451.31C1086.66,-451.31 1018.98,-479.16 1018.98,-479.16 1013.43,-481.45 1001.88,-483.73 995.88,-483.73 995.88,-483.73 840.12,-483.73 840.12,-483.73 834.12,-483.73 822.57,-481.45 817.02,-479.16 817.02,-479.16 749.34,-451.31 749.34,-451.31 743.79,-449.03 743.79,-444.46 749.34,-442.18 749.34,-442.18 817.02,-414.32 817.02,-414.32 822.57,-412.04 834.12,-409.76 840.12,-409.76 840.12,-409.76 995.88,-409.76 995.88,-409.76 1001.88,-409.76 1013.43,-412.04 1018.98,-414.32 1018.98,-414.32 1086.66,-442.18 1086.66,-442.18 1092.21,-444.46 1092.21,-449.03 1086.66,-451.31"/>
<text text-anchor="middle" x="918" y="-455.24" font-family="Arial" font-size="10.00" fill="#111827">RIB fabric</text>
<text text-anchor="middle" x="918" y="-444.24" font-family="Arial" font-size="10.00" fill="#111827">3&#45;master round&#45;robin arbitration</text>
<text text-anchor="middle" x="918" y="-433.24" font-family="Arial" font-size="10.00" fill="#111827">INCR1 / INCR4, access policy, fault reporting</text>
</g>
<!-- rcu&#45;&gt;fabric -->
<g id="edge3" class="edge">
<title>rcu&#45;&gt;fabric</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" stroke-dasharray="5,2" d="M362.03,-531.47C368.2,-530.24 372,-529.48 372,-529.48 372,-529.48 697,-474.48 697,-474.48 697,-474.48 728.7,-468.85 766.93,-462.05"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="767.67,-464.41 774.14,-460.77 766.82,-459.58 767.67,-464.41"/>
<text text-anchor="middle" x="588" 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="M362.03,-540.47C368.2,-539.24 372,-538.48 372,-538.48 372,-538.48 734,-483.48 734,-483.48 734,-483.48 758.75,-478.68 790.1,-472.59"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="790.92,-474.92 797.32,-471.18 789.98,-470.11 790.92,-474.92"/>
<text text-anchor="middle" x="609" y="-509.08" font-family="Arial" font-size="8.00" fill="#374151">system clock / reset</text>
</g>
<!-- clint -->
<g id="node17" class="node">
@@ -96,319 +96,349 @@
<!-- rcu&#45;&gt;clint -->
<g id="edge8" class="edge">
<title>rcu&#45;&gt;clint</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" stroke-dasharray="5,2" d="M277.95,-529.16C313.89,-472.95 409.6,-323.28 449.33,-261.14"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="451.51,-262.27 453.22,-255.05 447.38,-259.63 451.51,-262.27"/>
<text text-anchor="middle" x="397.5" y="-371.6" font-family="Arial" font-size="8.00" fill="#374151">1 MHz tick</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" stroke-dasharray="5,2" d="M277.48,-538.31C313.09,-481.03 409.7,-325.66 449.5,-261.65"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="451.78,-262.62 453.4,-255.38 447.62,-260.03 451.78,-262.62"/>
<text text-anchor="middle" x="392.5" y="-380.6" font-family="Arial" font-size="8.00" fill="#374151">1 MHz tick</text>
</g>
<!-- mgmt_core -->
<g id="node5" class="node">
<title>mgmt_core</title>
<path fill="#ddf2e3" stroke="#4b5563" stroke-width="1.1" d="M1273,-674.48C1273,-674.48 1119,-674.48 1119,-674.48 1113,-674.48 1107,-668.48 1107,-662.48 1107,-662.48 1107,-641.48 1107,-641.48 1107,-635.48 1113,-629.48 1119,-629.48 1119,-629.48 1273,-629.48 1273,-629.48 1279,-629.48 1285,-635.48 1285,-641.48 1285,-641.48 1285,-662.48 1285,-662.48 1285,-668.48 1279,-674.48 1273,-674.48"/>
<text text-anchor="middle" x="1196" y="-660.48" font-family="Arial" font-size="10.00" fill="#111827">Management core</text>
<text text-anchor="middle" x="1196" y="-649.48" font-family="Arial" font-size="10.00" fill="#111827">Fixed Hazard3, branch predictor</text>
<text text-anchor="middle" x="1196" 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="M1293,-683.48C1293,-683.48 1139,-683.48 1139,-683.48 1133,-683.48 1127,-677.48 1127,-671.48 1127,-671.48 1127,-650.48 1127,-650.48 1127,-644.48 1133,-638.48 1139,-638.48 1139,-638.48 1293,-638.48 1293,-638.48 1299,-638.48 1305,-644.48 1305,-650.48 1305,-650.48 1305,-671.48 1305,-671.48 1305,-677.48 1299,-683.48 1293,-683.48"/>
<text text-anchor="middle" x="1216" y="-669.48" font-family="Arial" font-size="10.00" fill="#111827">Management core</text>
<text text-anchor="middle" x="1216" y="-658.48" font-family="Arial" font-size="10.00" fill="#111827">Fixed Hazard3, branch predictor</text>
<text text-anchor="middle" x="1216" y="-647.48" font-family="Arial" font-size="10.00" fill="#111827">JTAG Debug Module always present</text>
</g>
<!-- mgmt_adapter -->
<g id="node8" class="node">
<title>mgmt_adapter</title>
<path fill="#e5e7eb" stroke="#4b5563" stroke-width="1.1" d="M1216.5,-569.98C1216.5,-569.98 1175.5,-569.98 1175.5,-569.98 1169.5,-569.98 1163.5,-563.98 1163.5,-557.98 1163.5,-557.98 1163.5,-545.98 1163.5,-545.98 1163.5,-539.98 1169.5,-533.98 1175.5,-533.98 1175.5,-533.98 1216.5,-533.98 1216.5,-533.98 1222.5,-533.98 1228.5,-539.98 1228.5,-545.98 1228.5,-545.98 1228.5,-557.98 1228.5,-557.98 1228.5,-563.98 1222.5,-569.98 1216.5,-569.98"/>
<text text-anchor="middle" x="1196" y="-549.48" font-family="Arial" font-size="10.00" fill="#111827">RIBP2RIB</text>
<path fill="#e5e7eb" stroke="#4b5563" stroke-width="1.1" d="M1236.5,-578.98C1236.5,-578.98 1195.5,-578.98 1195.5,-578.98 1189.5,-578.98 1183.5,-572.98 1183.5,-566.98 1183.5,-566.98 1183.5,-554.98 1183.5,-554.98 1183.5,-548.98 1189.5,-542.98 1195.5,-542.98 1195.5,-542.98 1236.5,-542.98 1236.5,-542.98 1242.5,-542.98 1248.5,-548.98 1248.5,-554.98 1248.5,-554.98 1248.5,-566.98 1248.5,-566.98 1248.5,-572.98 1242.5,-578.98 1236.5,-578.98"/>
<text text-anchor="middle" x="1216" y="-558.48" font-family="Arial" font-size="10.00" fill="#111827">RIBP2RIB</text>
</g>
<!-- mgmt_core&#45;&gt;mgmt_adapter -->
<g id="edge9" class="edge">
<title>mgmt_core&#45;&gt;mgmt_adapter</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1196,-629.24C1196,-613.92 1196,-593.4 1196,-577.51"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1198.45,-577.12 1196,-570.12 1193.55,-577.12 1198.45,-577.12"/>
<text text-anchor="middle" x="1205.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="M1216,-638.24C1216,-622.92 1216,-602.4 1216,-586.51"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1218.45,-586.12 1216,-579.12 1213.55,-586.12 1218.45,-586.12"/>
<text text-anchor="middle" x="1225.5" y="-609.08" font-family="Arial" font-size="8.00" fill="#374151">RIBP</text>
</g>
<!-- debug_module -->
<g id="node7" class="node">
<title>debug_module</title>
<path fill="#fce7f3" stroke="#4b5563" stroke-width="1.1" d="M949.5,-674.48C949.5,-674.48 828.5,-674.48 828.5,-674.48 822.5,-674.48 816.5,-668.48 816.5,-662.48 816.5,-662.48 816.5,-641.48 816.5,-641.48 816.5,-635.48 822.5,-629.48 828.5,-629.48 828.5,-629.48 949.5,-629.48 949.5,-629.48 955.5,-629.48 961.5,-635.48 961.5,-641.48 961.5,-641.48 961.5,-662.48 961.5,-662.48 961.5,-668.48 955.5,-674.48 949.5,-674.48"/>
<text text-anchor="middle" x="889" y="-660.48" font-family="Arial" font-size="10.00" fill="#111827">Hazard3 Debug Module</text>
<text text-anchor="middle" x="889" y="-649.48" font-family="Arial" font-size="10.00" fill="#111827">abstract commands, no SBA</text>
<text text-anchor="middle" x="889" 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="M1583.5,-683.48C1583.5,-683.48 1462.5,-683.48 1462.5,-683.48 1456.5,-683.48 1450.5,-677.48 1450.5,-671.48 1450.5,-671.48 1450.5,-650.48 1450.5,-650.48 1450.5,-644.48 1456.5,-638.48 1462.5,-638.48 1462.5,-638.48 1583.5,-638.48 1583.5,-638.48 1589.5,-638.48 1595.5,-644.48 1595.5,-650.48 1595.5,-650.48 1595.5,-671.48 1595.5,-671.48 1595.5,-677.48 1589.5,-683.48 1583.5,-683.48"/>
<text text-anchor="middle" x="1523" y="-669.48" font-family="Arial" font-size="10.00" fill="#111827">Hazard3 Debug Module</text>
<text text-anchor="middle" x="1523" y="-658.48" font-family="Arial" font-size="10.00" fill="#111827">abstract commands, no SBA</text>
<text text-anchor="middle" x="1523" y="-647.48" font-family="Arial" font-size="10.00" fill="#111827">management&#45;hart&#45;only reset</text>
</g>
<!-- jtag_dtm&#45;&gt;debug_module -->
<g id="edge4" class="edge">
<title>jtag_dtm&#45;&gt;debug_module</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M700.33,-651.98C735.12,-651.98 774.91,-651.98 808.95,-651.98"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="809.12,-654.43 816.12,-651.98 809.12,-649.53 809.12,-654.43"/>
<text text-anchor="middle" x="758.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="M1031.06,-679.13C1060.11,-708.33 1114,-762.48 1114,-762.48 1114,-762.48 1319,-762.48 1319,-762.48 1319,-762.48 1410.81,-716.8 1471.25,-686.73"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1472.55,-688.82 1477.73,-683.51 1470.37,-684.44 1472.55,-688.82"/>
<text text-anchor="middle" x="1275" y="-795.08" 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="M961.75,-651.98C1002.99,-651.98 1055.09,-651.98 1099.69,-651.98"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1099.77,-654.43 1106.77,-651.98 1099.77,-649.53 1099.77,-654.43"/>
<text text-anchor="middle" x="1034.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="M1450.11,-660.98C1408.89,-660.98 1356.84,-660.98 1312.29,-660.98"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1312.21,-658.53 1305.21,-660.98 1312.21,-663.43 1312.21,-658.53"/>
<text text-anchor="middle" x="1377.75" y="-666.58" font-family="Arial" font-size="8.00" fill="#374151">halt, resume, abstract commands</text>
</g>
<!-- debug_module&#45;&gt;mgmt_adapter -->
<g id="edge6" class="edge">
<title>debug_module&#45;&gt;mgmt_adapter</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" stroke-dasharray="5,2" d="M961.7,-629.68C1038.93,-607.04 1150,-574.48 1150,-574.48 1150,-574.48 1152.57,-573.29 1156.49,-571.45"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1157.75,-573.56 1163.06,-568.38 1155.68,-569.13 1157.75,-573.56"/>
<text text-anchor="middle" x="1103" 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="M1450.23,-638.57C1373.31,-615.94 1263,-583.48 1263,-583.48 1263,-583.48 1259.87,-582.05 1255.22,-579.93"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1256.03,-577.6 1248.64,-576.92 1253.99,-582.06 1256.03,-577.6"/>
<text text-anchor="middle" x="1407" y="-609.08" font-family="Arial" font-size="8.00" fill="#374151">reset after bridge idle</text>
</g>
<!-- timers -->
<g id="node19" class="node">
<g id="node20" class="node">
<title>timers</title>
<path fill="#f9e1e7" stroke="#4b5563" stroke-width="1.1" d="M759.5,-255C759.5,-255 578.5,-255 578.5,-255 572.5,-255 566.5,-249 566.5,-243 566.5,-243 566.5,-222 566.5,-222 566.5,-216 572.5,-210 578.5,-210 578.5,-210 759.5,-210 759.5,-210 765.5,-210 771.5,-216 771.5,-222 771.5,-222 771.5,-243 771.5,-243 771.5,-249 765.5,-255 759.5,-255"/>
<text text-anchor="middle" x="669" y="-241" font-family="Arial" font-size="10.00" fill="#111827">General timers 0/1</text>
<text text-anchor="middle" x="669" y="-230" font-family="Arial" font-size="10.00" fill="#111827">32&#45;bit counter, 16&#45;bit prescaler</text>
<text text-anchor="middle" x="669" y="-219" font-family="Arial" font-size="10.00" fill="#111827">periodic / one&#45;shot, compare, debug freeze</text>
<path fill="#f9e1e7" stroke="#4b5563" stroke-width="1.1" d="M1681.5,-255C1681.5,-255 1500.5,-255 1500.5,-255 1494.5,-255 1488.5,-249 1488.5,-243 1488.5,-243 1488.5,-222 1488.5,-222 1488.5,-216 1494.5,-210 1500.5,-210 1500.5,-210 1681.5,-210 1681.5,-210 1687.5,-210 1693.5,-216 1693.5,-222 1693.5,-222 1693.5,-243 1693.5,-243 1693.5,-249 1687.5,-255 1681.5,-255"/>
<text text-anchor="middle" x="1591" y="-241" font-family="Arial" font-size="10.00" fill="#111827">General timers 0/1</text>
<text text-anchor="middle" x="1591" y="-230" font-family="Arial" font-size="10.00" fill="#111827">32&#45;bit counter, 16&#45;bit prescaler</text>
<text text-anchor="middle" x="1591" y="-219" font-family="Arial" font-size="10.00" fill="#111827">periodic / one&#45;shot, compare, debug freeze</text>
</g>
<!-- debug_module&#45;&gt;timers -->
<g id="edge7" class="edge">
<title>debug_module&#45;&gt;timers</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" stroke-dasharray="5,2" d="M840.72,-629.48C775.96,-600.63 669,-552.98 669,-552.98 669,-552.98 669,-552.98 669,-327 669,-327 669,-289.76 669,-262.28"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="671.45,-262.19 669,-255.19 666.55,-262.19 671.45,-262.19"/>
<text text-anchor="middle" x="680" y="-435.84" font-family="Arial" font-size="8.00" fill="#374151">halted</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" stroke-dasharray="5,2" d="M1530.32,-638.24C1540.05,-609.36 1556,-561.98 1556,-561.98 1556,-561.98 1556,-561.98 1556,-336 1556,-336 1570.96,-292.2 1581.31,-261.86"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1583.63,-262.65 1583.58,-255.24 1579,-261.07 1583.63,-262.65"/>
<text text-anchor="middle" x="1567" y="-444.84" font-family="Arial" font-size="8.00" fill="#374151">halted</text>
</g>
<!-- mgmt_adapter&#45;&gt;fabric -->
<g id="edge10" class="edge">
<title>mgmt_adapter&#45;&gt;fabric</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1177.65,-533.76C1160.99,-518.12 1139,-497.48 1139,-497.48 1139,-497.48 1077.55,-482.51 1016.71,-467.68"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1017.06,-465.24 1009.68,-465.96 1015.9,-470 1017.06,-465.24"/>
<text text-anchor="middle" x="1155" y="-500.08" font-family="Arial" font-size="8.00" fill="#374151">RIB</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1197.65,-542.76C1180.99,-527.12 1159,-506.48 1159,-506.48 1159,-506.48 1097.55,-491.51 1036.71,-476.68"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1037.06,-474.24 1029.68,-474.96 1035.9,-479 1037.06,-474.24"/>
<text text-anchor="middle" x="1175" y="-509.08" font-family="Arial" font-size="8.00" fill="#374151">RIB</text>
</g>
<!-- user_core -->
<g id="node9" class="node">
<title>user_core</title>
<path fill="#ddf2e3" stroke="#4b5563" stroke-width="1.1" d="M1124,-574.48C1124,-574.48 1048,-574.48 1048,-574.48 1042,-574.48 1036,-568.48 1036,-562.48 1036,-562.48 1036,-541.48 1036,-541.48 1036,-535.48 1042,-529.48 1048,-529.48 1048,-529.48 1124,-529.48 1124,-529.48 1130,-529.48 1136,-535.48 1136,-541.48 1136,-541.48 1136,-562.48 1136,-562.48 1136,-568.48 1130,-574.48 1124,-574.48"/>
<text text-anchor="middle" x="1086" y="-560.48" font-family="Arial" font-size="10.00" fill="#111827">Selected user core</text>
<text text-anchor="middle" x="1086" y="-549.48" font-family="Arial" font-size="10.00" fill="#111827">C0&#45;C5, one active</text>
<text text-anchor="middle" x="1086" 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="M1364,-583.48C1364,-583.48 1288,-583.48 1288,-583.48 1282,-583.48 1276,-577.48 1276,-571.48 1276,-571.48 1276,-550.48 1276,-550.48 1276,-544.48 1282,-538.48 1288,-538.48 1288,-538.48 1364,-538.48 1364,-538.48 1370,-538.48 1376,-544.48 1376,-550.48 1376,-550.48 1376,-571.48 1376,-571.48 1376,-577.48 1370,-583.48 1364,-583.48"/>
<text text-anchor="middle" x="1326" y="-569.48" font-family="Arial" font-size="10.00" fill="#111827">Selected user core</text>
<text text-anchor="middle" x="1326" y="-558.48" font-family="Arial" font-size="10.00" fill="#111827">C0&#45;C5, one active</text>
<text text-anchor="middle" x="1326" y="-547.48" font-family="Arial" font-size="10.00" fill="#111827">PicoRV32 at C5</text>
</g>
<!-- user_core&#45;&gt;fabric -->
<g id="edge11" class="edge">
<title>user_core&#45;&gt;fabric</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1102.11,-529.26C1110.49,-517.96 1119,-506.48 1119,-506.48 1119,-506.48 1119,-497.48 1119,-497.48 1119,-497.48 1066.59,-483.55 1012.81,-469.26"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1013.3,-466.86 1005.91,-467.43 1012.05,-471.59 1013.3,-466.86"/>
<text text-anchor="middle" x="1126" y="-500.08" font-family="Arial" font-size="8.00" fill="#374151">RIB</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1275.79,-540.52C1236.33,-525.22 1188,-506.48 1188,-506.48 1188,-506.48 1112.22,-490 1040.95,-474.49"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1041.43,-472.09 1034.07,-473 1040.39,-476.88 1041.43,-472.09"/>
<text text-anchor="middle" x="1218" y="-509.08" font-family="Arial" font-size="8.00" fill="#374151">RIB</text>
</g>
<!-- dma -->
<g id="node10" class="node">
<title>dma</title>
<path fill="#ddf2e3" stroke="#4b5563" stroke-width="1.1" d="M1487,-569.98C1487,-569.98 1369,-569.98 1369,-569.98 1363,-569.98 1357,-563.98 1357,-557.98 1357,-557.98 1357,-545.98 1357,-545.98 1357,-539.98 1363,-533.98 1369,-533.98 1369,-533.98 1487,-533.98 1487,-533.98 1493,-533.98 1499,-539.98 1499,-545.98 1499,-545.98 1499,-557.98 1499,-557.98 1499,-563.98 1493,-569.98 1487,-569.98"/>
<text text-anchor="middle" x="1428" y="-554.98" font-family="Arial" font-size="10.00" fill="#111827">DMA</text>
<text text-anchor="middle" x="1428" 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="M957,-578.98C957,-578.98 839,-578.98 839,-578.98 833,-578.98 827,-572.98 827,-566.98 827,-566.98 827,-554.98 827,-554.98 827,-548.98 833,-542.98 839,-542.98 839,-542.98 957,-542.98 957,-542.98 963,-542.98 969,-548.98 969,-554.98 969,-554.98 969,-566.98 969,-566.98 969,-572.98 963,-578.98 957,-578.98"/>
<text text-anchor="middle" x="898" y="-563.98" font-family="Arial" font-size="10.00" fill="#111827">DMA</text>
<text text-anchor="middle" x="898" y="-552.98" font-family="Arial" font-size="10.00" fill="#111827">RIB master and RIBP target</text>
</g>
<!-- dma&#45;&gt;fabric -->
<g id="edge12" class="edge">
<title>dma&#45;&gt;fabric</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1356.8,-535.91C1271.31,-517.8 1126.48,-487.13 1022.39,-465.08"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1022.63,-462.63 1015.28,-463.58 1021.62,-467.43 1022.63,-462.63"/>
<text text-anchor="middle" x="1217" y="-500.08" font-family="Arial" font-size="8.00" fill="#374151">RIB</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M969,-546.87C1040.09,-533.75 1139,-515.48 1139,-515.48 1139,-515.48 1139,-506.48 1139,-506.48 1139,-506.48 1086.59,-492.55 1032.81,-478.26"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1033.3,-475.86 1025.91,-476.43 1032.05,-480.59 1033.3,-475.86"/>
<text text-anchor="middle" x="1146" y="-509.08" font-family="Arial" font-size="8.00" fill="#374151">RIB</text>
</g>
<!-- ws2812 -->
<g id="node20" class="node">
<g id="node21" class="node">
<title>ws2812</title>
<path fill="#f9e1e7" stroke="#4b5563" stroke-width="1.1" d="M1400,-255C1400,-255 1266,-255 1266,-255 1260,-255 1254,-249 1254,-243 1254,-243 1254,-222 1254,-222 1254,-216 1260,-210 1266,-210 1266,-210 1400,-210 1400,-210 1406,-210 1412,-216 1412,-222 1412,-222 1412,-243 1412,-243 1412,-249 1406,-255 1400,-255"/>
<text text-anchor="middle" x="1333" y="-241" font-family="Arial" font-size="10.00" fill="#111827">WS2812 TX</text>
<text text-anchor="middle" x="1333" y="-230" font-family="Arial" font-size="10.00" fill="#111827">24&#45;bit MSB&#45;first serializer</text>
<text text-anchor="middle" x="1333" y="-219" font-family="Arial" font-size="10.00" fill="#111827">16&#45;word FIFO, IRQ17, DMA&#45;fed</text>
<path fill="#f9e1e7" stroke="#4b5563" stroke-width="1.1" d="M713,-255C713,-255 579,-255 579,-255 573,-255 567,-249 567,-243 567,-243 567,-222 567,-222 567,-216 573,-210 579,-210 579,-210 713,-210 713,-210 719,-210 725,-216 725,-222 725,-222 725,-243 725,-243 725,-249 719,-255 713,-255"/>
<text text-anchor="middle" x="646" y="-241" font-family="Arial" font-size="10.00" fill="#111827">WS2812 TX</text>
<text text-anchor="middle" x="646" y="-230" font-family="Arial" font-size="10.00" fill="#111827">24&#45;bit MSB&#45;first serializer</text>
<text text-anchor="middle" x="646" 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="edge27" class="edge">
<g id="edge30" class="edge">
<title>dma&#45;&gt;ws2812</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" stroke-dasharray="5,2" d="M1433.9,-533.63C1441.57,-511.05 1454,-474.48 1454,-474.48 1454,-474.48 1454,-401 1454,-401 1454,-401 1387.1,-308.39 1352.79,-260.89"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1354.66,-259.29 1348.57,-255.05 1350.68,-262.16 1354.66,-259.29"/>
<text text-anchor="middle" x="1463.5" y="-371.6" font-family="Arial" font-size="8.00" fill="#374151">TXDATA writes</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" stroke-dasharray="5,2" d="M859.18,-542.82C808.17,-520.26 725,-483.48 725,-483.48 725,-483.48 689,-278 689,-278 689,-278 681.11,-269.83 672.08,-260.49"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="673.62,-258.56 667,-255.23 670.1,-261.96 673.62,-258.56"/>
<text text-anchor="middle" x="734.5" y="-380.6" font-family="Arial" font-size="8.00" fill="#374151">TXDATA writes</text>
</g>
<!-- fault -->
<g id="node12" class="node">
<title>fault</title>
<path fill="#fce7f3" stroke="#4b5563" stroke-width="1.1" d="M1278.5,-455.74C1278.5,-455.74 1169.5,-455.74 1169.5,-455.74 1163.5,-455.74 1157.5,-449.74 1157.5,-443.74 1157.5,-443.74 1157.5,-431.74 1157.5,-431.74 1157.5,-425.74 1163.5,-419.74 1169.5,-419.74 1169.5,-419.74 1278.5,-419.74 1278.5,-419.74 1284.5,-419.74 1290.5,-425.74 1290.5,-431.74 1290.5,-431.74 1290.5,-443.74 1290.5,-443.74 1290.5,-449.74 1284.5,-455.74 1278.5,-455.74"/>
<text text-anchor="middle" x="1224" y="-440.74" font-family="Arial" font-size="10.00" fill="#111827">Error slave</text>
<text text-anchor="middle" x="1224" 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="M1298.5,-464.74C1298.5,-464.74 1189.5,-464.74 1189.5,-464.74 1183.5,-464.74 1177.5,-458.74 1177.5,-452.74 1177.5,-452.74 1177.5,-440.74 1177.5,-440.74 1177.5,-434.74 1183.5,-428.74 1189.5,-428.74 1189.5,-428.74 1298.5,-428.74 1298.5,-428.74 1304.5,-428.74 1310.5,-434.74 1310.5,-440.74 1310.5,-440.74 1310.5,-452.74 1310.5,-452.74 1310.5,-458.74 1304.5,-464.74 1298.5,-464.74"/>
<text text-anchor="middle" x="1244" y="-449.74" font-family="Arial" font-size="10.00" fill="#111827">Error slave</text>
<text text-anchor="middle" x="1244" y="-438.74" font-family="Arial" font-size="10.00" fill="#111827">access and protocol faults</text>
</g>
<!-- fabric&#45;&gt;fault -->
<g id="edge13" class="edge">
<title>fabric&#45;&gt;fault</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1084.98,-437.74C1107.93,-437.74 1130.24,-437.74 1150.02,-437.74"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1084.69,-435.29 1077.69,-437.74 1084.69,-440.19 1084.69,-435.29"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1150.19,-440.19 1157.19,-437.74 1150.19,-435.29 1150.19,-440.19"/>
<text text-anchor="middle" x="1117.56" y="-443.34" font-family="Arial" font-size="8.00" fill="#374151">invalid / denied</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1104.98,-446.74C1127.93,-446.74 1150.24,-446.74 1170.02,-446.74"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1104.69,-444.29 1097.69,-446.74 1104.69,-449.19 1104.69,-444.29"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1170.19,-449.19 1177.19,-446.74 1170.19,-444.29 1170.19,-449.19"/>
<text text-anchor="middle" x="1137.56" y="-452.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="M1428,-455.74C1428,-455.74 1330,-455.74 1330,-455.74 1324,-455.74 1318,-449.74 1318,-443.74 1318,-443.74 1318,-431.74 1318,-431.74 1318,-425.74 1324,-419.74 1330,-419.74 1330,-419.74 1428,-419.74 1428,-419.74 1434,-419.74 1440,-425.74 1440,-431.74 1440,-431.74 1440,-443.74 1440,-443.74 1440,-449.74 1434,-455.74 1428,-455.74"/>
<text text-anchor="middle" x="1379" y="-440.74" font-family="Arial" font-size="10.00" fill="#111827">On&#45;chip SRAM</text>
<text text-anchor="middle" x="1379" 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="M1448,-464.74C1448,-464.74 1350,-464.74 1350,-464.74 1344,-464.74 1338,-458.74 1338,-452.74 1338,-452.74 1338,-440.74 1338,-440.74 1338,-434.74 1344,-428.74 1350,-428.74 1350,-428.74 1448,-428.74 1448,-428.74 1454,-428.74 1460,-434.74 1460,-440.74 1460,-440.74 1460,-452.74 1460,-452.74 1460,-458.74 1454,-464.74 1448,-464.74"/>
<text text-anchor="middle" x="1399" y="-449.74" font-family="Arial" font-size="10.00" fill="#111827">On&#45;chip SRAM</text>
<text text-anchor="middle" x="1399" y="-438.74" font-family="Arial" font-size="10.00" fill="#111827">optional RIB2RAM path</text>
</g>
<!-- fabric&#45;&gt;sram -->
<g id="edge14" class="edge">
<title>fabric&#45;&gt;sram</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1004.32,-468.06C1054.69,-482.42 1104,-496.48 1104,-496.48 1104,-496.48 1118,-496.48 1118,-496.48 1118,-496.48 1304,-474.48 1304,-474.48 1304,-474.48 1318.84,-467.21 1335.32,-459.14"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1336.73,-461.18 1341.94,-455.9 1334.58,-456.78 1336.73,-461.18"/>
<text text-anchor="middle" x="1111" y="-500.08" font-family="Arial" font-size="8.00" fill="#374151">RIB</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1024.32,-477.06C1074.69,-491.42 1124,-505.48 1124,-505.48 1124,-505.48 1138,-505.48 1138,-505.48 1138,-505.48 1324,-483.48 1324,-483.48 1324,-483.48 1338.84,-476.21 1355.32,-468.14"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1356.73,-470.18 1361.94,-464.9 1354.58,-465.78 1356.73,-470.18"/>
<text text-anchor="middle" x="1131" y="-509.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="M1060.5,-346C1060.5,-346 1019.5,-346 1019.5,-346 1013.5,-346 1007.5,-340 1007.5,-334 1007.5,-334 1007.5,-322 1007.5,-322 1007.5,-316 1013.5,-310 1019.5,-310 1019.5,-310 1060.5,-310 1060.5,-310 1066.5,-310 1072.5,-316 1072.5,-322 1072.5,-322 1072.5,-334 1072.5,-334 1072.5,-340 1066.5,-346 1060.5,-346"/>
<text text-anchor="middle" x="1040" y="-325.5" font-family="Arial" font-size="10.00" fill="#111827">RIB2RIBP</text>
<path fill="#e5e7eb" stroke="#4b5563" stroke-width="1.1" d="M938.5,-355C938.5,-355 897.5,-355 897.5,-355 891.5,-355 885.5,-349 885.5,-343 885.5,-343 885.5,-331 885.5,-331 885.5,-325 891.5,-319 897.5,-319 897.5,-319 938.5,-319 938.5,-319 944.5,-319 950.5,-325 950.5,-331 950.5,-331 950.5,-343 950.5,-343 950.5,-349 944.5,-355 938.5,-355"/>
<text text-anchor="middle" x="918" y="-334.5" font-family="Arial" font-size="10.00" fill="#111827">RIB2RIBP</text>
</g>
<!-- fabric&#45;&gt;ribp_adapter -->
<g id="edge15" class="edge">
<title>fabric&#45;&gt;ribp_adapter</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M945.28,-400.87C967.36,-384.11 992.9,-364.73 1011.89,-350.33"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1013.47,-352.2 1017.57,-346.02 1010.51,-348.3 1013.47,-352.2"/>
<text text-anchor="middle" x="992" y="-371.6" font-family="Arial" font-size="8.00" fill="#374151">RIB</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M918,-409.87C918,-394.27 918,-376.39 918,-362.38"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="920.45,-362.02 918,-355.02 915.55,-362.02 920.45,-362.02"/>
<text text-anchor="middle" x="925" y="-380.6" font-family="Arial" font-size="8.00" fill="#374151">RIB</text>
</g>
<!-- apb_adapter -->
<g id="node15" class="node">
<title>apb_adapter</title>
<path fill="#e5e7eb" stroke="#4b5563" stroke-width="1.1" d="M938,-346C938,-346 868,-346 868,-346 862,-346 856,-340 856,-334 856,-334 856,-322 856,-322 856,-316 862,-310 868,-310 868,-310 938,-310 938,-310 944,-310 950,-316 950,-322 950,-322 950,-334 950,-334 950,-340 944,-346 938,-346"/>
<text text-anchor="middle" x="903" y="-331" font-family="Arial" font-size="10.00" fill="#111827">RIB2APB</text>
<text text-anchor="middle" x="903" 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="M1395,-355C1395,-355 1325,-355 1325,-355 1319,-355 1313,-349 1313,-343 1313,-343 1313,-331 1313,-331 1313,-325 1319,-319 1325,-319 1325,-319 1395,-319 1395,-319 1401,-319 1407,-325 1407,-331 1407,-331 1407,-343 1407,-343 1407,-349 1401,-355 1395,-355"/>
<text text-anchor="middle" x="1360" y="-340" font-family="Arial" font-size="10.00" fill="#111827">RIB2APB</text>
<text text-anchor="middle" x="1360" y="-329" font-family="Arial" font-size="10.00" fill="#111827">direct APB bridge</text>
</g>
<!-- fabric&#45;&gt;apb_adapter -->
<g id="edge16" class="edge">
<title>fabric&#45;&gt;apb_adapter</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M899.66,-400.87C900.39,-385.27 901.22,-367.39 901.87,-353.38"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="904.33,-353.13 902.21,-346.02 899.44,-352.9 904.33,-353.13"/>
<text text-anchor="middle" x="908" y="-371.6" font-family="Arial" font-size="8.00" fill="#374151">RIB</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1029.54,-418.55C1117.93,-397.01 1237.57,-367.84 1306,-351.16"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1306.64,-353.53 1312.86,-349.49 1305.48,-348.77 1306.64,-353.53"/>
<text text-anchor="middle" x="1198" y="-380.6" font-family="Arial" font-size="8.00" fill="#374151">RIB</text>
</g>
<!-- sysctrl -->
<g id="node16" class="node">
<title>sysctrl</title>
<path fill="#f9e1e7" stroke="#4b5563" stroke-width="1.1" d="M1215.5,-255C1215.5,-255 1052.5,-255 1052.5,-255 1046.5,-255 1040.5,-249 1040.5,-243 1040.5,-243 1040.5,-222 1040.5,-222 1040.5,-216 1046.5,-210 1052.5,-210 1052.5,-210 1215.5,-210 1215.5,-210 1221.5,-210 1227.5,-216 1227.5,-222 1227.5,-222 1227.5,-243 1227.5,-243 1227.5,-249 1221.5,-255 1215.5,-255"/>
<text text-anchor="middle" x="1134" y="-241" font-family="Arial" font-size="10.00" fill="#111827">SYSCTRL</text>
<text text-anchor="middle" x="1134" 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="1134" 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="M1449.5,-255C1449.5,-255 1286.5,-255 1286.5,-255 1280.5,-255 1274.5,-249 1274.5,-243 1274.5,-243 1274.5,-222 1274.5,-222 1274.5,-216 1280.5,-210 1286.5,-210 1286.5,-210 1449.5,-210 1449.5,-210 1455.5,-210 1461.5,-216 1461.5,-222 1461.5,-222 1461.5,-243 1461.5,-243 1461.5,-249 1455.5,-255 1449.5,-255"/>
<text text-anchor="middle" x="1368" y="-241" font-family="Arial" font-size="10.00" fill="#111827">SYSCTRL</text>
<text text-anchor="middle" x="1368" 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="1368" y="-219" font-family="Arial" font-size="10.00" fill="#111827">fault and performance control</text>
</g>
<!-- ribp_adapter&#45;&gt;sysctrl -->
<g id="edge17" class="edge">
<title>ribp_adapter&#45;&gt;sysctrl</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1057.24,-309.85C1071.15,-296.02 1090.95,-276.32 1106.96,-260.39"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1109,-261.82 1112.24,-255.15 1105.54,-258.35 1109,-261.82"/>
<text text-anchor="middle" x="1096.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="M950.67,-331.33C1024.53,-320.78 1198,-296 1198,-296 1198,-296 1254.85,-275.1 1302.8,-257.47"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1303.77,-259.72 1309.5,-255.01 1302.08,-255.12 1303.77,-259.72"/>
<text text-anchor="middle" x="1252.5" y="-285.1" font-family="Arial" font-size="8.00" fill="#374151">RIBP</text>
</g>
<!-- ribp_adapter&#45;&gt;clint -->
<g id="edge18" class="edge">
<title>ribp_adapter&#45;&gt;clint</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1007.33,-319.69C986.73,-315.08 964,-310 964,-310 964,-310 553,-255 553,-255 553,-255 550.57,-254.39 546.54,-253.38"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="546.95,-250.96 539.56,-251.64 545.76,-255.72 546.95,-250.96"/>
<text text-anchor="middle" x="792.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="M885.32,-320.57C883.27,-319.6 882,-319 882,-319 882,-319 740,-296 740,-296 740,-296 708,-278 708,-278 708,-278 554,-255 554,-255 554,-255 551.14,-254.29 546.46,-253.14"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="547.02,-250.75 539.63,-251.45 545.84,-255.51 547.02,-250.75"/>
<text text-anchor="middle" x="749.5" y="-285.1" font-family="Arial" font-size="8.00" fill="#374151">RIBP</text>
</g>
<!-- ribp_targets -->
<g id="node18" class="node">
<title>ribp_targets</title>
<path fill="#f9e1e7" stroke="#4b5563" stroke-width="1.1" d="M1656.5,-255C1656.5,-255 1451.5,-255 1451.5,-255 1445.5,-255 1439.5,-249 1439.5,-243 1439.5,-243 1439.5,-222 1439.5,-222 1439.5,-216 1445.5,-210 1451.5,-210 1451.5,-210 1656.5,-210 1656.5,-210 1662.5,-210 1668.5,-216 1668.5,-222 1668.5,-222 1668.5,-243 1668.5,-243 1668.5,-249 1662.5,-255 1656.5,-255"/>
<text text-anchor="middle" x="1554" y="-241" font-family="Arial" font-size="10.00" fill="#111827">RIBP target wrapper</text>
<text text-anchor="middle" x="1554" 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="1554" y="-219" font-family="Arial" font-size="10.00" fill="#111827">GPIO, UART0, I2C and I2S</text>
<path fill="#f9e1e7" stroke="#4b5563" stroke-width="1.1" d="M1235.5,-255C1235.5,-255 1030.5,-255 1030.5,-255 1024.5,-255 1018.5,-249 1018.5,-243 1018.5,-243 1018.5,-222 1018.5,-222 1018.5,-216 1024.5,-210 1030.5,-210 1030.5,-210 1235.5,-210 1235.5,-210 1241.5,-210 1247.5,-216 1247.5,-222 1247.5,-222 1247.5,-243 1247.5,-243 1247.5,-249 1241.5,-255 1235.5,-255"/>
<text text-anchor="middle" x="1133" y="-241" font-family="Arial" font-size="10.00" fill="#111827">RIBP target wrapper</text>
<text text-anchor="middle" x="1133" 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="1133" y="-219" font-family="Arial" font-size="10.00" fill="#111827">UART0, I2C and I2S</text>
</g>
<!-- ribp_adapter&#45;&gt;ribp_targets -->
<g id="edge19" class="edge">
<title>ribp_adapter&#45;&gt;ribp_targets</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1072.54,-321.08C1143.35,-308.2 1314.49,-277.07 1432.4,-255.62"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1432.94,-258.01 1439.39,-254.35 1432.06,-253.19 1432.94,-258.01"/>
<text text-anchor="middle" x="1306.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="M950.65,-320.43C985.33,-303.9 1040.64,-277.53 1081.26,-258.17"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1082.38,-260.35 1087.65,-255.12 1080.28,-255.92 1082.38,-260.35"/>
<text text-anchor="middle" x="1047.5" y="-285.1" font-family="Arial" font-size="8.00" fill="#374151">RIBP</text>
</g>
<!-- gpio_controller -->
<g id="node19" class="node">
<title>gpio_controller</title>
<path fill="#f9e1e7" stroke="#4b5563" stroke-width="1.1" d="M914,-255C914,-255 764,-255 764,-255 758,-255 752,-249 752,-243 752,-243 752,-222 752,-222 752,-216 758,-210 764,-210 764,-210 914,-210 914,-210 920,-210 926,-216 926,-222 926,-222 926,-243 926,-243 926,-249 920,-255 914,-255"/>
<text text-anchor="middle" x="839" y="-241" font-family="Arial" font-size="10.00" fill="#111827">GPIO V2</text>
<text text-anchor="middle" x="839" y="-230" font-family="Arial" font-size="10.00" fill="#111827">user + management RIBP windows</text>
<text text-anchor="middle" x="839" y="-219" font-family="Arial" font-size="10.00" fill="#111827">atomic I/O, filter, locks, IRQ18</text>
</g>
<!-- ribp_adapter&#45;&gt;gpio_controller -->
<g id="edge20" class="edge">
<title>ribp_adapter&#45;&gt;gpio_controller</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M904.6,-318.61C892.5,-302.91 874.48,-279.53 860.38,-261.25"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="861.98,-259.31 855.77,-255.26 858.1,-262.3 861.98,-259.31"/>
<text text-anchor="middle" x="905" y="-289.6" font-family="Arial" font-size="8.00" fill="#374151">RIBP</text>
<text text-anchor="middle" x="905" y="-280.6" font-family="Arial" font-size="8.00" fill="#374151">user/admin</text>
</g>
<!-- ribp_adapter&#45;&gt;timers -->
<g id="edge20" class="edge">
<g id="edge21" class="edge">
<title>ribp_adapter&#45;&gt;timers</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1007.42,-318.91C960.37,-307.22 879,-287 879,-287 879,-287 817,-271.21 760.38,-256.78"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="760.9,-254.38 753.51,-255.03 759.69,-259.13 760.9,-254.38"/>
<text text-anchor="middle" x="888.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="M950.61,-332.27C1037.5,-322.34 1268,-296 1268,-296 1268,-296 1387.71,-272.84 1481.38,-254.71"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1481.95,-257.1 1488.36,-253.36 1481.02,-252.29 1481.95,-257.1"/>
<text text-anchor="middle" x="1363.5" y="-285.1" font-family="Arial" font-size="8.00" fill="#374151">RIBP</text>
</g>
<!-- ribp_adapter&#45;&gt;ws2812 -->
<g id="edge21" class="edge">
<g id="edge22" class="edge">
<title>ribp_adapter&#45;&gt;ws2812</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1072.76,-316.55C1117.4,-302.3 1198.65,-276.37 1258.52,-257.27"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1259.42,-259.55 1265.35,-255.09 1257.93,-254.88 1259.42,-259.55"/>
<text text-anchor="middle" x="1195.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="M885.46,-323.74C842.24,-307.45 764.93,-278.32 709.85,-257.56"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="710.66,-255.25 703.25,-255.07 708.94,-259.84 710.66,-255.25"/>
<text text-anchor="middle" x="815.5" y="-285.1" font-family="Arial" font-size="8.00" fill="#374151">RIBP</text>
</g>
<!-- apb_targets -->
<g id="node21" class="node">
<g id="node22" class="node">
<title>apb_targets</title>
<path fill="#e6f4ea" stroke="#4b5563" stroke-width="1.1" d="M1001.5,-255C1001.5,-255 810.5,-255 810.5,-255 804.5,-255 798.5,-249 798.5,-243 798.5,-243 798.5,-222 798.5,-222 798.5,-216 804.5,-210 810.5,-210 810.5,-210 1001.5,-210 1001.5,-210 1007.5,-210 1013.5,-216 1013.5,-222 1013.5,-222 1013.5,-243 1013.5,-243 1013.5,-249 1007.5,-255 1001.5,-255"/>
<text text-anchor="middle" x="906" y="-241" font-family="Arial" font-size="10.00" fill="#111827">APB target wrapper</text>
<text text-anchor="middle" x="906" y="-230" font-family="Arial" font-size="10.00" fill="#111827">Archinfo, RNG, UART1, PWM, PS/2, RTC</text>
<text text-anchor="middle" x="906" 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="M1923.5,-255C1923.5,-255 1732.5,-255 1732.5,-255 1726.5,-255 1720.5,-249 1720.5,-243 1720.5,-243 1720.5,-222 1720.5,-222 1720.5,-216 1726.5,-210 1732.5,-210 1732.5,-210 1923.5,-210 1923.5,-210 1929.5,-210 1935.5,-216 1935.5,-222 1935.5,-222 1935.5,-243 1935.5,-243 1935.5,-249 1929.5,-255 1923.5,-255"/>
<text text-anchor="middle" x="1828" y="-241" font-family="Arial" font-size="10.00" fill="#111827">APB target wrapper</text>
<text text-anchor="middle" x="1828" y="-230" font-family="Arial" font-size="10.00" fill="#111827">Archinfo, RNG, UART1, PWM, PS/2, RTC</text>
<text text-anchor="middle" x="1828" 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="edge22" class="edge">
<g id="edge23" class="edge">
<title>apb_adapter&#45;&gt;apb_targets</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M903.55,-309.85C903.97,-296.62 904.57,-278.02 905.07,-262.49"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="907.53,-262.22 905.31,-255.15 902.63,-262.07 907.53,-262.22"/>
<text text-anchor="middle" x="912" y="-280.6" font-family="Arial" font-size="8.00" fill="#374151">APB</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1407.02,-325.7C1480.36,-309.64 1623.83,-278.22 1722.74,-256.55"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1723.47,-258.9 1729.78,-255.01 1722.42,-254.12 1723.47,-258.9"/>
<text text-anchor="middle" x="1630" y="-285.1" font-family="Arial" font-size="8.00" fill="#374151">APB</text>
</g>
<!-- irq -->
<g id="node23" class="node">
<g id="node24" class="node">
<title>irq</title>
<path fill="#fce7f3" stroke="#4b5563" stroke-width="1.1" d="M772,-155C772,-155 566,-155 566,-155 560,-155 554,-149 554,-143 554,-143 554,-131 554,-131 554,-125 560,-119 566,-119 566,-119 772,-119 772,-119 778,-119 784,-125 784,-131 784,-131 784,-143 784,-143 784,-149 778,-155 772,-155"/>
<text text-anchor="middle" x="669" y="-140" font-family="Arial" font-size="10.00" fill="#111827">Interrupt aggregation</text>
<text text-anchor="middle" x="669" 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="M749,-155C749,-155 543,-155 543,-155 537,-155 531,-149 531,-143 531,-143 531,-131 531,-131 531,-125 537,-119 543,-119 543,-119 749,-119 749,-119 755,-119 761,-125 761,-131 761,-131 761,-143 761,-143 761,-149 755,-155 749,-155"/>
<text text-anchor="middle" x="646" y="-140" font-family="Arial" font-size="10.00" fill="#111827">Interrupt aggregation</text>
<text text-anchor="middle" x="646" y="-129" font-family="Arial" font-size="10.00" fill="#111827">RIBP and APB target IRQs to management core</text>
</g>
<!-- clint&#45;&gt;irq -->
<g id="edge25" class="edge">
<g id="edge28" class="edge">
<title>clint&#45;&gt;irq</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M513.6,-209.93C547.35,-194.31 592.66,-173.34 625.64,-158.07"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="626.74,-160.26 632.06,-155.1 624.68,-155.81 626.74,-160.26"/>
<text text-anchor="middle" x="618.5" y="-180.6" font-family="Arial" font-size="8.00" fill="#374151">machine IRQ0 / IRQ1</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M505.77,-209.89C532.41,-195.05 563,-178 563,-178 563,-178 583.39,-168.18 603.95,-158.26"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="605.08,-160.44 610.32,-155.2 602.95,-156.03 605.08,-160.44"/>
<text text-anchor="middle" x="601.5" y="-180.6" font-family="Arial" font-size="8.00" fill="#374151">machine IRQ0 / IRQ1</text>
</g>
<!-- ribp_targets&#45;&gt;ext_io -->
<g id="edge30" class="edge">
<g id="edge33" class="edge">
<title>ribp_targets&#45;&gt;ext_io</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1541.78,-203.07C1536.28,-190.28 1531,-178 1531,-178 1531,-178 1494.07,-96.48 1473.85,-51.87"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1539.63,-204.28 1544.65,-209.74 1544.13,-202.34 1539.63,-204.28"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1476,-50.67 1470.88,-45.31 1471.54,-52.7 1476,-50.67"/>
<text text-anchor="middle" x="1550" 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="M1111.03,-203.97C1079.15,-164.03 1020.42,-90.44 988.7,-50.69"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1109.27,-205.69 1115.55,-209.63 1113.1,-202.63 1109.27,-205.69"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="990.5,-49.02 984.22,-45.07 986.67,-52.07 990.5,-49.02"/>
<text text-anchor="middle" x="1101" y="-135.1" font-family="Arial" font-size="8.00" fill="#374151">device interfaces</text>
</g>
<!-- ribp_targets&#45;&gt;gpio_controller -->
<g id="edge24" class="edge">
<title>ribp_targets&#45;&gt;gpio_controller</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1018.39,-232.5C990.19,-232.5 960.32,-232.5 933.21,-232.5"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="933.02,-230.05 926.02,-232.5 933.02,-234.95 933.02,-230.05"/>
<text text-anchor="middle" x="972.25" y="-238.1" font-family="Arial" font-size="8.00" fill="#374151">alternate functions</text>
</g>
<!-- gpio_bridge -->
<g id="node22" class="node">
<g id="node23" class="node">
<title>gpio_bridge</title>
<path fill="#e5e7eb" stroke="#4b5563" stroke-width="1.1" d="M1448,-155C1448,-155 1300,-155 1300,-155 1294,-155 1288,-149 1288,-143 1288,-143 1288,-131 1288,-131 1288,-125 1294,-119 1300,-119 1300,-119 1448,-119 1448,-119 1454,-119 1460,-125 1460,-131 1460,-131 1460,-143 1460,-143 1460,-149 1454,-155 1448,-155"/>
<text text-anchor="middle" x="1374" y="-140" font-family="Arial" font-size="10.00" fill="#111827">GPIO pad bridge</text>
<text text-anchor="middle" x="1374" 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="M935.5,-155C935.5,-155 800.5,-155 800.5,-155 794.5,-155 788.5,-149 788.5,-143 788.5,-143 788.5,-131 788.5,-131 788.5,-125 794.5,-119 800.5,-119 800.5,-119 935.5,-119 935.5,-119 941.5,-119 947.5,-125 947.5,-131 947.5,-131 947.5,-143 947.5,-143 947.5,-149 941.5,-155 935.5,-155"/>
<text text-anchor="middle" x="868" y="-140" font-family="Arial" font-size="10.00" fill="#111827">GPIO pad bridge</text>
<text text-anchor="middle" x="868" y="-129" font-family="Arial" font-size="10.00" fill="#111827">PDK capability&#45;adapted controls</text>
</g>
<!-- ribp_targets&#45;&gt;gpio_bridge -->
<g id="edge23" class="edge">
<title>ribp_targets&#45;&gt;gpio_bridge</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1499.16,-209.9C1470.36,-198.56 1441,-187 1441,-187 1441,-187 1421.43,-172.69 1403.44,-159.53"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1404.53,-157.29 1397.43,-155.14 1401.64,-161.25 1404.53,-157.29"/>
<text text-anchor="middle" x="1485" y="-180.6" font-family="Arial" font-size="8.00" fill="#374151">GPIO alternate functions</text>
<!-- gpio_controller&#45;&gt;gpio_bridge -->
<g id="edge25" class="edge">
<title>gpio_controller&#45;&gt;gpio_bridge</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M845.73,-209.81C850.15,-195.54 855.96,-176.83 860.53,-162.09"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="862.93,-162.62 862.66,-155.2 858.25,-161.16 862.93,-162.62"/>
<text text-anchor="middle" x="875" y="-180.6" font-family="Arial" font-size="8.00" fill="#374151">pad control</text>
</g>
<!-- gpio_controller&#45;&gt;irq -->
<g id="edge26" class="edge">
<title>gpio_controller&#45;&gt;irq</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M780.27,-209.9C749.44,-198.56 718,-187 718,-187 718,-187 696.6,-172.44 677.14,-159.19"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="678.35,-157.05 671.18,-155.14 675.59,-161.1 678.35,-157.05"/>
<text text-anchor="middle" x="730" y="-180.6" font-family="Arial" font-size="8.00" fill="#374151">IRQ18</text>
</g>
<!-- timers&#45;&gt;irq -->
<g id="edge24" class="edge">
<g id="edge27" class="edge">
<title>timers&#45;&gt;irq</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M669,-209.81C669,-195.66 669,-177.17 669,-162.49"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="671.45,-162.2 669,-155.2 666.55,-162.2 671.45,-162.2"/>
<text text-anchor="middle" x="691.5" y="-180.6" font-family="Arial" font-size="8.00" fill="#374151">IRQ3 / IRQ4</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1488.31,-212.47C1480.16,-210.96 1475,-210 1475,-210 1475,-210 775,-155 775,-155 775,-155 772.48,-154.67 768.16,-154.1"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="768.45,-151.67 761.19,-153.18 767.81,-156.52 768.45,-151.67"/>
<text text-anchor="middle" x="1188.5" y="-180.6" font-family="Arial" font-size="8.00" fill="#374151">IRQ3 / IRQ4</text>
</g>
<!-- ws2812&#45;&gt;gpio_bridge -->
<g id="edge26" class="edge">
<g id="edge29" class="edge">
<title>ws2812&#45;&gt;gpio_bridge</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1341.95,-209.74C1348.04,-194.93 1355,-178 1355,-178 1355,-178 1358.68,-170.25 1362.8,-161.58"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1365.04,-162.57 1365.83,-155.2 1360.62,-160.47 1365.04,-162.57"/>
<text text-anchor="middle" x="1377.5" y="-180.6" font-family="Arial" font-size="8.00" fill="#374151">GPIO2 ALT1</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M697.21,-209.93C734.46,-194.24 784.51,-173.16 820.8,-157.88"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="821.91,-160.07 827.41,-155.1 820,-155.56 821.91,-160.07"/>
<text text-anchor="middle" x="792.5" y="-180.6" font-family="Arial" font-size="8.00" fill="#374151">GPIO2 ALT1</text>
</g>
<!-- ws2812&#45;&gt;irq -->
<g id="edge28" class="edge">
<g id="edge31" class="edge">
<title>ws2812&#45;&gt;irq</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1253.79,-212.99C1246.05,-211.18 1241,-210 1241,-210 1241,-210 959.53,-174.57 791.48,-153.42"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="791.32,-150.93 784.06,-152.48 790.7,-155.79 791.32,-150.93"/>
<text text-anchor="middle" x="1067" y="-180.6" font-family="Arial" font-size="8.00" fill="#374151">IRQ17</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M646,-209.81C646,-195.66 646,-177.17 646,-162.49"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="648.45,-162.2 646,-155.2 643.55,-162.2 648.45,-162.2"/>
<text text-anchor="middle" x="658" y="-180.6" font-family="Arial" font-size="8.00" fill="#374151">IRQ17</text>
</g>
<!-- apb_targets&#45;&gt;gpio_bridge -->
<g id="edge29" class="edge">
<title>apb_targets&#45;&gt;gpio_bridge</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1013.66,-209.99C1094.75,-193.79 1204.75,-171.81 1281.8,-156.42"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1282.3,-158.82 1288.69,-155.04 1281.34,-154.01 1282.3,-158.82"/>
<text text-anchor="middle" x="1186" y="-180.6" font-family="Arial" font-size="8.00" fill="#374151">user_gpio</text>
<!-- apb_targets&#45;&gt;gpio_controller -->
<g id="edge32" class="edge">
<title>apb_targets&#45;&gt;gpio_controller</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1720.41,-252.51C1712.19,-254.04 1707,-255 1707,-255 1707,-255 1192,-282.5 1192,-282.5 1192,-282.5 1156,-282.5 1156,-282.5 1156,-282.5 1026.45,-262.07 932.99,-247.32"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="933.34,-244.9 926.04,-246.23 932.57,-249.74 933.34,-244.9"/>
<text text-anchor="middle" x="1174" y="-285.1" font-family="Arial" font-size="8.00" fill="#374151">user_gpio</text>
</g>
<!-- gpio_bridge&#45;&gt;ext_io -->
<g id="edge31" class="edge">
<g id="edge34" class="edge">
<title>gpio_bridge&#45;&gt;ext_io</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1391.54,-113.32C1405.42,-95.37 1424.86,-70.24 1439.64,-51.12"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1389.56,-111.88 1387.21,-118.91 1393.43,-114.88 1389.56,-111.88"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1441.73,-52.43 1444.07,-45.39 1437.85,-49.43 1441.73,-52.43"/>
<text text-anchor="middle" x="1445" y="-70.6" font-family="Arial" font-size="8.00" fill="#374151">pad control</text>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M887.73,-113.58C903.67,-95.47 926.16,-69.91 943.11,-50.65"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="885.82,-112.04 883.04,-118.91 889.5,-115.28 885.82,-112.04"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="944.95,-52.26 947.74,-45.39 941.27,-49.03 944.95,-52.26"/>
<text text-anchor="middle" x="946" y="-70.6" font-family="Arial" font-size="8.00" fill="#374151">pad control</text>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 37 KiB

+16 -9
View File
@@ -56,17 +56,24 @@ them to the local remote-bitbang server used by the debug acceptance flow.
The fixed user-IP integration does not add dedicated user GPIO pads. A user IP
can instead drive any of the 32 rib GPIO pads through `user_gpio_if`. Software
selects an owner per pin with the rib GPIO `USER_SEL` register at offset
`0x30`; clear selects the existing software/alternate GPIO path and set selects
the user IP. `USER_LOCK` at `0x34` is write-one-set and prevents a selected
owner bit from changing until reset. `USER_STATUS` at `0x38` reports the user
IP pins that are actively connected.
selects an owner per pin with `USER_SELECT` at management-window offset
`0x03C`; clear selects the existing software/alternate GPIO path and set
selects the user IP. `USER_LOCK` at `0x040` is write-one-set and prevents a
selected owner bit from changing until reset. `USER_STATUS` at `0x044` reports
the user-IP pins that are actively connected.
On every accepted ownership change, the pad output enable is forced low for one
full system clock. The rib GPIO block retains `CS`, `PU`, and `PD` control
in all modes. User IPs provide only output data, output enable, and sampled pad
input. Configure the target output data and enable before writing `USER_SEL`,
then program `USER_LOCK` after the handoff when the assignment is permanent.
full system clock. The GPIO block retains input-mode, pull, open-drain, filter,
and interrupt control in all modes. User IPs provide only output data, output
enable, and synchronized/filtered pad input. Configure the target output data
and enable before writing `USER_SELECT`, then program `USER_LOCK` after the
handoff when the assignment is permanent.
The user core accesses only the data and interrupt window at `0x10000000`, and
only bits permitted by management `USER_ACCESS_MASK`. Configuration remains in
the management-only window at `0x10014000`; the SoC access firewall denies user
transactions to that region. See [gpio.md](ip/gpio.md) for the complete ABI,
PDK capabilities, and lock semantics.
User-IP RTL must declare its GPIO port as `user_gpio_if.user_ip gpio`; the only
signals available through that port are `do_o`, `oe_o`, and `di_i`. The MPW
-386
View File
@@ -1,386 +0,0 @@
/*
* Ravenna - A full example SoC using PicoRV32 in ASIC
*
* Copyright (C) 2017 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2018,2019 Tim Edwards <tim@efabless.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
// -- Adaptable modifications are redistributed under compatible License --
//
// 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 "gpio_define.svh"
interface gpio_if #(
parameter int DATA_WIDTH = `RIBP_GPIO_NUM
) ();
logic [DATA_WIDTH-1:0] oe_o;
logic [DATA_WIDTH-1:0] cs_o;
logic [DATA_WIDTH-1:0] pu_o;
logic [DATA_WIDTH-1:0] pd_o;
logic [DATA_WIDTH-1:0] do_o;
logic [DATA_WIDTH-1:0] di_i;
logic [DATA_WIDTH-1:0] alt0_do_i;
logic [DATA_WIDTH-1:0] alt0_oe_i;
logic [DATA_WIDTH-1:0] alt1_do_i;
logic [DATA_WIDTH-1:0] alt1_oe_i;
logic irq_o;
modport dut(
output oe_o,
output cs_o,
output pu_o,
output pd_o,
output do_o,
input di_i,
input alt0_do_i,
input alt0_oe_i,
input alt1_do_i,
input alt1_oe_i,
output irq_o
);
modport pad(input oe_o, input cs_o, input pu_o, input pd_o, input do_o, output di_i);
modport soc_pad(output oe_o, output cs_o, output pu_o, output pd_o, output do_o, input di_i);
endinterface
interface user_gpio_if #(
parameter int DATA_WIDTH = `RIBP_GPIO_NUM
) ();
logic [DATA_WIDTH-1:0] do_o;
logic [DATA_WIDTH-1:0] oe_o;
logic [DATA_WIDTH-1:0] di_i;
modport user_ip(output do_o, output oe_o, input di_i);
modport padctrl(input do_o, input oe_o, output di_i);
endinterface
module ribp_gpio (
// verilog_format: off
input logic clk_i,
input logic rst_n_i,
ribp_if.slave ribp,
gpio_if.dut gpio,
user_gpio_if.padctrl user_gpio
// verilog_format: on
);
// ribp
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;
// reg
logic s_gpio_oe_en;
logic [`RIBP_GPIO_NUM-1:0] s_gpio_oe_d, s_gpio_oe_q;
logic s_gpio_cs_en;
logic [`RIBP_GPIO_NUM-1:0] s_gpio_cs_d, s_gpio_cs_q;
logic s_gpio_pu_en;
logic [`RIBP_GPIO_NUM-1:0] s_gpio_pu_d, s_gpio_pu_q;
logic s_gpio_pd_en;
logic [`RIBP_GPIO_NUM-1:0] s_gpio_pd_d, s_gpio_pd_q;
logic s_gpio_do_en;
logic [`RIBP_GPIO_NUM-1:0] s_gpio_do_d, s_gpio_do_q;
logic [`RIBP_GPIO_NUM-1:0] s_gpio_di;
logic s_gpio_ien_en;
logic [`RIBP_GPIO_NUM-1:0] s_gpio_ien_d, s_gpio_ien_q;
logic s_gpio_itype0_en;
logic [`RIBP_GPIO_NUM-1:0] s_gpio_itype0_d, s_gpio_itype0_q;
logic s_gpio_itype1_en;
logic [`RIBP_GPIO_NUM-1:0] s_gpio_itype1_d, s_gpio_itype1_q;
logic s_gpio_istat_en;
logic [`RIBP_GPIO_NUM-1:0] s_gpio_istat_d, s_gpio_istat_q;
logic s_gpio_iofcfg_en;
logic [`RIBP_GPIO_NUM-1:0] s_gpio_iofcfg_d, s_gpio_iofcfg_q;
logic s_gpio_pinmux_en;
logic [`RIBP_GPIO_NUM-1:0] s_gpio_pinmux_d, s_gpio_pinmux_q;
logic s_gpio_user_sel_en;
logic [`RIBP_GPIO_NUM-1:0] s_gpio_user_sel_d, s_gpio_user_sel_q;
logic s_gpio_user_lock_en;
logic [`RIBP_GPIO_NUM-1:0] s_gpio_user_lock_d, s_gpio_user_lock_q;
logic [`RIBP_GPIO_NUM-1:0] s_gpio_user_handoff_d, s_gpio_user_handoff_q;
logic [`RIBP_GPIO_NUM-1:0] s_gpio_user_status;
// irq
logic [`RIBP_GPIO_NUM-1:0] s_gpio_di_re, s_gpio_di_fe, s_gpio_irq;
logic [`RIBP_GPIO_NUM-1:0] s_irq_rise, s_irq_fall;
logic [`RIBP_GPIO_NUM-1:0] s_irq_lev0, s_irq_lev1;
logic [`RIBP_GPIO_NUM-1:0] s_irq_masked;
logic s_irq_trg, s_irq_stat;
// alt
logic [`RIBP_GPIO_NUM-1:0] s_gpio_alt_out, s_gpio_alt_oe;
logic [`RIBP_GPIO_NUM-1:0] s_gpio_native_out, s_gpio_native_oe;
// shake
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;
// gpio
assign gpio.cs_o = s_gpio_cs_q; // 1: CMOS 0: SCHMI
assign gpio.pu_o = s_gpio_pu_q;
assign gpio.pd_o = s_gpio_pd_q;
for (genvar i = 0; i < `RIBP_GPIO_NUM; i++) begin : ALT_PINMUX_BLOCK
assign s_gpio_alt_oe[i] = s_gpio_pinmux_q[i] ? gpio.alt1_oe_i[i] : gpio.alt0_oe_i[i];
assign s_gpio_alt_out[i] = s_gpio_pinmux_q[i] ? gpio.alt1_do_i[i] : gpio.alt0_do_i[i];
end
for (genvar i = 0; i < `RIBP_GPIO_NUM; i++) begin : IOF_PINMUX_BLOCK
assign s_gpio_native_oe[i] = s_gpio_iofcfg_q[i] ? s_gpio_alt_oe[i] : s_gpio_oe_q[i];
assign s_gpio_native_out[i] = s_gpio_iofcfg_q[i] ? s_gpio_alt_out[i] : s_gpio_do_q[i];
end
assign user_gpio.di_i = gpio.di_i;
for (genvar i = 0; i < `RIBP_GPIO_NUM; i++) begin : USER_GPIO_MUX_BLOCK
assign gpio.oe_o[i] = s_gpio_user_handoff_q[i]
? 1'b0
: (s_gpio_user_sel_q[i] ? user_gpio.oe_o[i] : s_gpio_native_oe[i]);
assign gpio.do_o[i] = s_gpio_user_sel_q[i] ? user_gpio.do_o[i] : s_gpio_native_out[i];
end
assign s_irq_stat = |s_gpio_istat_q;
assign gpio.irq_o = s_irq_stat;
// verilog_format: off
// irq
assign s_irq_rise = (~s_gpio_itype1_q & ~s_gpio_itype0_q) & s_gpio_di_re;
assign s_irq_fall = (~s_gpio_itype1_q & s_gpio_itype0_q) & s_gpio_di_fe;
assign s_irq_lev0 = (s_gpio_itype1_q & ~s_gpio_itype0_q) & ~s_gpio_di;
assign s_irq_lev1 = (s_gpio_itype1_q & s_gpio_itype0_q) & s_gpio_di;
assign s_gpio_irq = s_irq_rise | s_irq_fall | s_irq_lev0 | s_irq_lev1;
assign s_irq_masked = s_gpio_ien_q & s_gpio_irq;
assign s_irq_trg = |s_irq_masked;
// verilog_format: on
edge_det #(
.STAGE (2),
.DATA_WIDTH(`RIBP_GPIO_NUM)
) u_gpio_di_edge_det (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (gpio.di_i),
.dat_o (s_gpio_di),
.re_o (s_gpio_di_re),
.fe_o (s_gpio_di_fe)
);
// register
assign s_gpio_oe_en = s_ribp_wr_hdshk && ribp.addr[7:0] == `RIBP_GPIO_OE;
assign s_gpio_oe_d = ribp.wdata[`RIBP_GPIO_NUM-1:0];
dffer #(`RIBP_GPIO_NUM) u_gpio_oe_dffer (
clk_i,
rst_n_i,
s_gpio_oe_en,
s_gpio_oe_d,
s_gpio_oe_q
);
assign s_gpio_cs_en = s_ribp_wr_hdshk && ribp.addr[7:0] == `RIBP_GPIO_CS;
assign s_gpio_cs_d = ribp.wdata[`RIBP_GPIO_NUM-1:0];
dffer #(`RIBP_GPIO_NUM) u_gpio_cs_dffer (
clk_i,
rst_n_i,
s_gpio_cs_en,
s_gpio_cs_d,
s_gpio_cs_q
);
assign s_gpio_pu_en = s_ribp_wr_hdshk && ribp.addr[7:0] == `RIBP_GPIO_PU;
assign s_gpio_pu_d = ribp.wdata[`RIBP_GPIO_NUM-1:0];
dffer #(`RIBP_GPIO_NUM) u_gpio_pu_dffer (
clk_i,
rst_n_i,
s_gpio_pu_en,
s_gpio_pu_d,
s_gpio_pu_q
);
assign s_gpio_pd_en = s_ribp_wr_hdshk && ribp.addr[7:0] == `RIBP_GPIO_PD;
assign s_gpio_pd_d = ribp.wdata[`RIBP_GPIO_NUM-1:0];
dffer #(`RIBP_GPIO_NUM) u_gpio_pd_dffer (
clk_i,
rst_n_i,
s_gpio_pd_en,
s_gpio_pd_d,
s_gpio_pd_q
);
assign s_gpio_do_en = s_ribp_wr_hdshk && ribp.addr[7:0] == `RIBP_GPIO_DO;
assign s_gpio_do_d = ribp.wdata[`RIBP_GPIO_NUM-1:0];
dffer #(`RIBP_GPIO_NUM) u_gpio_do_dffer (
clk_i,
rst_n_i,
s_gpio_do_en,
s_gpio_do_d,
s_gpio_do_q
);
assign s_gpio_ien_en = s_ribp_wr_hdshk && ribp.addr[7:0] == `RIBP_GPIO_IEN;
assign s_gpio_ien_d = ribp.wdata[`RIBP_GPIO_NUM-1:0];
dffer #(`RIBP_GPIO_NUM) u_gpio_ien_dffer (
clk_i,
rst_n_i,
s_gpio_ien_en,
s_gpio_ien_d,
s_gpio_ien_q
);
assign s_gpio_itype0_en = s_ribp_wr_hdshk && ribp.addr[7:0] == `RIBP_GPIO_ITYPE0;
assign s_gpio_itype0_d = ribp.wdata[`RIBP_GPIO_NUM-1:0];
dffer #(`RIBP_GPIO_NUM) u_gpio_itype0_dffer (
clk_i,
rst_n_i,
s_gpio_itype0_en,
s_gpio_itype0_d,
s_gpio_itype0_q
);
assign s_gpio_itype1_en = s_ribp_wr_hdshk && ribp.addr[7:0] == `RIBP_GPIO_ITYPE1;
assign s_gpio_itype1_d = ribp.wdata[`RIBP_GPIO_NUM-1:0];
dffer #(`RIBP_GPIO_NUM) u_gpio_itype1_dffer (
clk_i,
rst_n_i,
s_gpio_itype1_en,
s_gpio_itype1_d,
s_gpio_itype1_q
);
assign s_gpio_iofcfg_en = s_ribp_wr_hdshk && ribp.addr[7:0] == `RIBP_GPIO_IOFCFG;
assign s_gpio_iofcfg_d = ribp.wdata[`RIBP_GPIO_NUM-1:0];
dffer #(`RIBP_GPIO_NUM) u_gpio_iocfg_dffer (
clk_i,
rst_n_i,
s_gpio_iofcfg_en,
s_gpio_iofcfg_d,
s_gpio_iofcfg_q
);
always_comb begin
s_gpio_istat_en = 1'b0;
s_gpio_istat_d = s_gpio_istat_q;
if (s_irq_stat && s_ribp_rd_hdshk && ribp.addr[7:0] == `RIBP_GPIO_ISTAT) begin
s_gpio_istat_en = 1'b1;
// HACK: clear all irq
s_gpio_istat_d = '0;
end else if (~s_irq_stat && s_irq_trg) begin
s_gpio_istat_en = 1'b1;
s_gpio_istat_d = s_irq_masked;
end
end
dffer #(`RIBP_GPIO_NUM) u_gpio_istat_dffer (
clk_i,
rst_n_i,
s_gpio_istat_en,
s_gpio_istat_d,
s_gpio_istat_q
);
assign s_gpio_pinmux_en = s_ribp_wr_hdshk && ribp.addr[7:0] == `RIBP_GPIO_PINMUX;
assign s_gpio_pinmux_d = ribp.wdata[`RIBP_GPIO_NUM-1:0];
dffer #(`RIBP_GPIO_NUM) u_gpio_pinmux_dffer (
clk_i,
rst_n_i,
s_gpio_pinmux_en,
s_gpio_pinmux_d,
s_gpio_pinmux_q
);
assign s_gpio_user_sel_en = s_ribp_wr_hdshk && ribp.addr[7:0] == `RIBP_GPIO_USER_SEL;
assign s_gpio_user_sel_d = (s_gpio_user_sel_q & s_gpio_user_lock_q) |
(ribp.wdata[`RIBP_GPIO_NUM-1:0] & ~s_gpio_user_lock_q);
dffer #(`RIBP_GPIO_NUM) u_gpio_user_sel_dffer (
clk_i,
rst_n_i,
s_gpio_user_sel_en,
s_gpio_user_sel_d,
s_gpio_user_sel_q
);
assign s_gpio_user_lock_en = s_ribp_wr_hdshk && ribp.addr[7:0] == `RIBP_GPIO_USER_LOCK;
assign s_gpio_user_lock_d = s_gpio_user_lock_q | ribp.wdata[`RIBP_GPIO_NUM-1:0];
dffer #(`RIBP_GPIO_NUM) u_gpio_user_lock_dffer (
clk_i,
rst_n_i,
s_gpio_user_lock_en,
s_gpio_user_lock_d,
s_gpio_user_lock_q
);
assign s_gpio_user_handoff_d = s_gpio_user_sel_en ? (s_gpio_user_sel_d ^ s_gpio_user_sel_q) : '0;
dffr #(`RIBP_GPIO_NUM) u_gpio_user_handoff_dffr (
clk_i,
rst_n_i,
s_gpio_user_handoff_d,
s_gpio_user_handoff_q
);
assign s_gpio_user_status = s_gpio_user_sel_q & ~s_gpio_user_handoff_q;
// ribp resp
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_GPIO_OE: s_ribp_rdata_d = {{(32 - `RIBP_GPIO_NUM) {1'b0}}, s_gpio_oe_q};
`RIBP_GPIO_CS: s_ribp_rdata_d = {{(32 - `RIBP_GPIO_NUM) {1'b0}}, s_gpio_cs_q};
`RIBP_GPIO_PU: s_ribp_rdata_d = {{(32 - `RIBP_GPIO_NUM) {1'b0}}, s_gpio_pu_q};
`RIBP_GPIO_PD: s_ribp_rdata_d = {{(32 - `RIBP_GPIO_NUM) {1'b0}}, s_gpio_pd_q};
`RIBP_GPIO_DO: s_ribp_rdata_d = {{(32 - `RIBP_GPIO_NUM) {1'b0}}, s_gpio_do_q};
`RIBP_GPIO_DI: s_ribp_rdata_d = {{(32 - `RIBP_GPIO_NUM) {1'b0}}, s_gpio_di};
`RIBP_GPIO_IEN: s_ribp_rdata_d = {{(32 - `RIBP_GPIO_NUM) {1'b0}}, s_gpio_ien_q};
`RIBP_GPIO_ITYPE0: s_ribp_rdata_d = {{(32 - `RIBP_GPIO_NUM) {1'b0}}, s_gpio_itype0_q};
`RIBP_GPIO_ITYPE1: s_ribp_rdata_d = {{(32 - `RIBP_GPIO_NUM) {1'b0}}, s_gpio_itype1_q};
`RIBP_GPIO_ISTAT: s_ribp_rdata_d = {{(32 - `RIBP_GPIO_NUM) {1'b0}}, s_gpio_istat_q};
`RIBP_GPIO_IOFCFG: s_ribp_rdata_d = {{(32 - `RIBP_GPIO_NUM) {1'b0}}, s_gpio_iofcfg_q};
`RIBP_GPIO_PINMUX: s_ribp_rdata_d = {{(32 - `RIBP_GPIO_NUM) {1'b0}}, s_gpio_pinmux_q};
`RIBP_GPIO_USER_SEL: s_ribp_rdata_d = {{(32 - `RIBP_GPIO_NUM) {1'b0}}, s_gpio_user_sel_q};
`RIBP_GPIO_USER_LOCK: s_ribp_rdata_d = {{(32 - `RIBP_GPIO_NUM) {1'b0}}, s_gpio_user_lock_q};
`RIBP_GPIO_USER_STATUS: s_ribp_rdata_d = {{(32 - `RIBP_GPIO_NUM) {1'b0}}, s_gpio_user_status};
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
);
endmodule
+147
View File
@@ -0,0 +1,147 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
module gpio_core #(
parameter int PIN_NUM = 32
) (
// verilog_format: off
input logic clk_i,
input logic rst_n_i,
input logic [PIN_NUM-1:0] data_out_i,
input logic [PIN_NUM-1:0] output_enable_i,
input logic [PIN_NUM-1:0] open_drain_i,
input logic [PIN_NUM-1:0] input_cmos_i,
input logic [PIN_NUM-1:0] pull_up_i,
input logic [PIN_NUM-1:0] pull_down_i,
input logic [PIN_NUM-1:0] alt_enable_i,
input logic [PIN_NUM-1:0] alt_select_i,
input logic [PIN_NUM-1:0] user_select_i,
input logic [PIN_NUM-1:0] user_handoff_i,
input logic [PIN_NUM-1:0] filter_enable_i,
input logic [15:0] filter_div_i,
input logic [3:0] filter_count_i,
input logic [PIN_NUM-1:0] intr_rise_enable_i,
input logic [PIN_NUM-1:0] intr_fall_enable_i,
input logic [PIN_NUM-1:0] intr_high_enable_i,
input logic [PIN_NUM-1:0] intr_low_enable_i,
input logic irq_i,
output logic [PIN_NUM-1:0] data_in_o,
output logic [PIN_NUM-1:0] intr_event_o,
gpio_if.dut gpio,
user_gpio_if.padctrl user_gpio
// verilog_format: on
);
logic [ 15:0] s_sample_count;
logic s_sample_tick;
logic s_sample_overflow;
logic [PIN_NUM-1:0] s_input_sync;
logic [PIN_NUM-1:0] s_input_filtered_d, s_input_filtered_q;
logic [3:0] s_stable_count_d[0:PIN_NUM-1];
logic [3:0] s_stable_count_q[0:PIN_NUM-1];
logic [PIN_NUM-1:0] s_input_rise, s_input_fall;
logic [PIN_NUM-1:0] s_alt_data, s_alt_oe;
logic [PIN_NUM-1:0] s_native_data, s_native_oe;
logic [PIN_NUM-1:0] s_selected_data, s_selected_oe;
initial begin
if ((PIN_NUM < 1) || (PIN_NUM > 32)) begin
$fatal(1, "gpio_core: PIN_NUM must be between 1 and 32");
end
end
cdc_sync #(
.STAGE (2),
.DATA_WIDTH(PIN_NUM)
) u_input_cdc_sync (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (gpio.di_i),
.dat_o (s_input_sync)
);
assign s_sample_tick = (s_sample_count == 16'd0) && !s_sample_overflow;
rs_counter #(
.DATA_WIDTH(16)
) u_filter_sample_counter (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.clr_i (1'b0),
.en_i (!s_sample_tick),
.load_i (s_sample_tick),
.down_i (1'b1),
.dat_i (filter_div_i),
.dat_o (s_sample_count),
.ovf_o (s_sample_overflow)
);
always_comb begin
s_input_filtered_d = s_input_filtered_q;
for (int pin = 0; pin < PIN_NUM; pin++) begin
s_stable_count_d[pin] = s_stable_count_q[pin];
if (!filter_enable_i[pin]) begin
s_input_filtered_d[pin] = s_input_sync[pin];
s_stable_count_d[pin] = 4'd0;
end else if (s_sample_tick) begin
if (s_input_sync[pin] == s_input_filtered_q[pin]) begin
s_stable_count_d[pin] = 4'd0;
end else if ((s_stable_count_q[pin] + 1'b1) >= filter_count_i) begin
s_input_filtered_d[pin] = s_input_sync[pin];
s_stable_count_d[pin] = 4'd0;
end else begin
s_stable_count_d[pin] = s_stable_count_q[pin] + 1'b1;
end
end
end
end
dffr #(PIN_NUM) u_input_filtered_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_input_filtered_d),
.dat_o (s_input_filtered_q)
);
for (genvar pin = 0; pin < PIN_NUM; pin++) begin : gen_stable_count
dffr #(4) u_stable_count_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_stable_count_d[pin]),
.dat_o (s_stable_count_q[pin])
);
end
edge_det_sync #(
.DATA_WIDTH(PIN_NUM)
) u_filtered_edge_det (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_input_filtered_q),
.re_o (s_input_rise),
.fe_o (s_input_fall)
);
assign data_in_o = s_input_filtered_q;
assign user_gpio.di_i = s_input_filtered_q;
assign intr_event_o = (s_input_rise & intr_rise_enable_i) |
(s_input_fall & intr_fall_enable_i) |
(s_input_filtered_q & intr_high_enable_i) |
(~s_input_filtered_q & intr_low_enable_i);
for (genvar pin = 0; pin < PIN_NUM; pin++) begin : gen_output_mux
assign s_alt_data[pin] = alt_select_i[pin] ? gpio.alt1_do_i[pin] : gpio.alt0_do_i[pin];
assign s_alt_oe[pin] = alt_select_i[pin] ? gpio.alt1_oe_i[pin] : gpio.alt0_oe_i[pin];
assign s_native_data[pin] = alt_enable_i[pin] ? s_alt_data[pin] : data_out_i[pin];
assign s_native_oe[pin] = alt_enable_i[pin] ? s_alt_oe[pin] : output_enable_i[pin];
assign s_selected_data[pin] = user_select_i[pin] ? user_gpio.do_o[pin] : s_native_data[pin];
assign s_selected_oe[pin] = user_select_i[pin] ? user_gpio.oe_o[pin] : s_native_oe[pin];
assign gpio.do_o[pin] = open_drain_i[pin] ? 1'b0 : s_selected_data[pin];
assign gpio.oe_o[pin] = !user_handoff_i[pin] && s_selected_oe[pin] &&
!(open_drain_i[pin] && s_selected_data[pin]);
end
assign gpio.cs_o = input_cmos_i;
assign gpio.pu_o = pull_up_i;
assign gpio.pd_o = pull_down_i;
assign gpio.irq_o = irq_i;
endmodule
+62 -25
View File
@@ -1,32 +1,69 @@
// Copyright (c) 2023-2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
`ifndef RIBP_GPIO_DEF_SV
`define RIBP_GPIO_DEF_SV
`ifndef RIBP_GPIO_DEFINE_SVH
`define RIBP_GPIO_DEFINE_SVH
// verilog_format: off
`define RIBP_GPIO_NUM 32
`define RIBP_GPIO_OE 8'h00 // rw
`define RIBP_GPIO_CS 8'h04 // rw
`define RIBP_GPIO_PU 8'h08 // rw
`define RIBP_GPIO_PD 8'h0C // rw
`define RIBP_GPIO_DO 8'h10 // rw
`define RIBP_GPIO_DI 8'h14 // ro
`define RIBP_GPIO_IEN 8'h18 // rw
`define RIBP_GPIO_ITYPE0 8'h1c // rw
`define RIBP_GPIO_ITYPE1 8'h20 // rw
`define RIBP_GPIO_ISTAT 8'h24 // ro
`define RIBP_GPIO_IOFCFG 8'h28 // rw
`define RIBP_GPIO_PINMUX 8'h2c // rw
`define RIBP_GPIO_USER_SEL 8'h30 // rw
`define RIBP_GPIO_USER_LOCK 8'h34 // rw1s
`define RIBP_GPIO_USER_STATUS 8'h38 // ro
`define RIBP_GPIO_NUM 32
`define RIBP_GPIO_USER_DATA_IN 12'h000
`define RIBP_GPIO_USER_DATA_OUT 12'h004
`define RIBP_GPIO_USER_OUT_SET 12'h008
`define RIBP_GPIO_USER_OUT_CLEAR 12'h00C
`define RIBP_GPIO_USER_OUT_TOGGLE 12'h010
`define RIBP_GPIO_USER_INTR_STATE 12'h014
`define RIBP_GPIO_USER_INTR_STATUS 12'h018
`define RIBP_GPIO_USER_INTR_ENABLE 12'h01C
`define RIBP_GPIO_USER_INTR_ENABLE_SET 12'h020
`define RIBP_GPIO_USER_INTR_ENABLE_CLEAR 12'h024
`define RIBP_GPIO_ADMIN_DATA_IN 12'h000
`define RIBP_GPIO_ADMIN_DATA_OUT 12'h004
`define RIBP_GPIO_ADMIN_OUT_SET 12'h008
`define RIBP_GPIO_ADMIN_OUT_CLEAR 12'h00C
`define RIBP_GPIO_ADMIN_OUT_TOGGLE 12'h010
`define RIBP_GPIO_ADMIN_OUTPUT_ENABLE 12'h014
`define RIBP_GPIO_ADMIN_OE_SET 12'h018
`define RIBP_GPIO_ADMIN_OE_CLEAR 12'h01C
`define RIBP_GPIO_ADMIN_OE_TOGGLE 12'h020
`define RIBP_GPIO_ADMIN_OPEN_DRAIN 12'h024
`define RIBP_GPIO_ADMIN_INPUT_CMOS 12'h028
`define RIBP_GPIO_ADMIN_PULL_UP 12'h02C
`define RIBP_GPIO_ADMIN_PULL_DOWN 12'h030
`define RIBP_GPIO_ADMIN_ALT_ENABLE 12'h034
`define RIBP_GPIO_ADMIN_ALT_SELECT 12'h038
`define RIBP_GPIO_ADMIN_USER_SELECT 12'h03C
`define RIBP_GPIO_ADMIN_USER_LOCK 12'h040
`define RIBP_GPIO_ADMIN_USER_STATUS 12'h044
`define RIBP_GPIO_ADMIN_USER_ACCESS_MASK 12'h048
`define RIBP_GPIO_ADMIN_INTR_RISE_ENABLE 12'h04C
`define RIBP_GPIO_ADMIN_INTR_FALL_ENABLE 12'h050
`define RIBP_GPIO_ADMIN_INTR_HIGH_ENABLE 12'h054
`define RIBP_GPIO_ADMIN_INTR_LOW_ENABLE 12'h058
`define RIBP_GPIO_ADMIN_INTR_ENABLE 12'h05C
`define RIBP_GPIO_ADMIN_INTR_STATE 12'h060
`define RIBP_GPIO_ADMIN_INTR_STATUS 12'h064
`define RIBP_GPIO_ADMIN_INTR_TEST 12'h068
`define RIBP_GPIO_ADMIN_FILTER_ENABLE 12'h06C
`define RIBP_GPIO_ADMIN_FILTER_DIV 12'h070
`define RIBP_GPIO_ADMIN_FILTER_COUNT 12'h074
`define RIBP_GPIO_ADMIN_CONFIG_LOCK 12'h078
`define RIBP_GPIO_PAD_CAPABILITY 12'h0F4
`define RIBP_GPIO_IP_VERSION 12'h0F8
`define RIBP_GPIO_CAPABILITY 12'h0FC
`define GPIO_PAD_CAP_INPUT_CMOS 0
`define GPIO_PAD_CAP_PULL_UP 1
`define GPIO_PAD_CAP_PULL_DOWN 2
`define GPIO_CAP_OPEN_DRAIN 16
`define GPIO_CAP_FILTER 17
`define GPIO_CAP_USER_HANDOFF 18
`define GPIO_CAP_CONFIG_LOCK 19
`define GPIO_CAP_ATOMIC_OUT 20
`define GPIO_CAP_ATOMIC_OE 21
`define GPIO_CAP_BOTH_EDGE_IRQ 22
// verilog_format: on
`endif
+37
View File
@@ -0,0 +1,37 @@
// Copyright (c) 2023-2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
`include "gpio_define.svh"
interface gpio_if #(
parameter int DATA_WIDTH = `RIBP_GPIO_NUM
) ();
logic [DATA_WIDTH-1:0] oe_o;
logic [DATA_WIDTH-1:0] cs_o;
logic [DATA_WIDTH-1:0] pu_o;
logic [DATA_WIDTH-1:0] pd_o;
logic [DATA_WIDTH-1:0] do_o;
logic [DATA_WIDTH-1:0] di_i;
logic [DATA_WIDTH-1:0] alt0_do_i;
logic [DATA_WIDTH-1:0] alt0_oe_i;
logic [DATA_WIDTH-1:0] alt1_do_i;
logic [DATA_WIDTH-1:0] alt1_oe_i;
logic irq_o;
modport dut(
output oe_o,
output cs_o,
output pu_o,
output pd_o,
output do_o,
input di_i,
input alt0_do_i,
input alt0_oe_i,
input alt1_do_i,
input alt1_oe_i,
output irq_o
);
modport pad(input oe_o, input cs_o, input pu_o, input pd_o, input do_o, output di_i);
modport soc_pad(output oe_o, output cs_o, output pu_o, output pd_o, output do_o, input di_i);
endinterface
+649
View File
@@ -0,0 +1,649 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
`include "gpio_define.svh"
module gpio_reg #(
parameter int PIN_NUM = 32,
parameter logic [31:0] USER_BASE_ADDR = 32'h1000_0000,
parameter logic [31:0] ADMIN_BASE_ADDR = 32'h1001_4000,
parameter bit HAS_INPUT_CMOS = 1'b0,
parameter bit HAS_PULL_UP = 1'b0,
parameter bit HAS_PULL_DOWN = 1'b0
) (
// verilog_format: off
input logic clk_i,
input logic rst_n_i,
ribp_if.slave ribp,
input logic [PIN_NUM-1:0] data_in_i,
input logic [PIN_NUM-1:0] intr_event_i,
output logic [PIN_NUM-1:0] data_out_o,
output logic [PIN_NUM-1:0] output_enable_o,
output logic [PIN_NUM-1:0] open_drain_o,
output logic [PIN_NUM-1:0] input_cmos_o,
output logic [PIN_NUM-1:0] pull_up_o,
output logic [PIN_NUM-1:0] pull_down_o,
output logic [PIN_NUM-1:0] alt_enable_o,
output logic [PIN_NUM-1:0] alt_select_o,
output logic [PIN_NUM-1:0] user_select_o,
output logic [PIN_NUM-1:0] user_handoff_o,
output logic [PIN_NUM-1:0] filter_enable_o,
output logic [15:0] filter_div_o,
output logic [3:0] filter_count_o,
output logic [PIN_NUM-1:0] intr_rise_enable_o,
output logic [PIN_NUM-1:0] intr_fall_enable_o,
output logic [PIN_NUM-1:0] intr_high_enable_o,
output logic [PIN_NUM-1:0] intr_low_enable_o,
output logic irq_o
// verilog_format: on
);
localparam logic [31:0] IP_VERSION = 32'h0002_0000;
localparam logic [31:0] CAPABILITY = 32'h007F_0000 | (32'(4) << 12) |
(32'(2) << 8) | 32'(PIN_NUM);
localparam logic [31:0] PAD_CAPABILITY = {29'd0, HAS_PULL_DOWN, HAS_PULL_UP, HAS_INPUT_CMOS};
logic s_req, s_write, s_req_accept;
logic s_public_window, s_admin_window, s_aligned;
logic [ 11:0] s_offset;
logic [ 31:0] s_byte_mask;
logic [ 31:0] s_write_word;
logic [PIN_NUM-1:0] s_write_bits;
logic s_access_error;
logic [31:0] s_ribp_rdata_d, s_ribp_rdata_q;
logic s_ribp_ready_q;
logic s_ribp_resp_err_d, s_ribp_resp_err_q;
logic [PIN_NUM-1:0] s_data_out_d, s_data_out_q;
logic [PIN_NUM-1:0] s_output_enable_d, s_output_enable_q;
logic [PIN_NUM-1:0] s_open_drain_d, s_open_drain_q;
logic [PIN_NUM-1:0] s_input_cmos_d, s_input_cmos_q;
logic [PIN_NUM-1:0] s_pull_up_d, s_pull_up_q;
logic [PIN_NUM-1:0] s_pull_down_d, s_pull_down_q;
logic [PIN_NUM-1:0] s_alt_enable_d, s_alt_enable_q;
logic [PIN_NUM-1:0] s_alt_select_d, s_alt_select_q;
logic [PIN_NUM-1:0] s_user_select_d, s_user_select_q;
logic [PIN_NUM-1:0] s_user_lock_d, s_user_lock_q;
logic [PIN_NUM-1:0] s_user_handoff_d, s_user_handoff_q;
logic [PIN_NUM-1:0] s_user_access_d, s_user_access_q;
logic [PIN_NUM-1:0] s_intr_rise_d, s_intr_rise_q;
logic [PIN_NUM-1:0] s_intr_fall_d, s_intr_fall_q;
logic [PIN_NUM-1:0] s_intr_high_d, s_intr_high_q;
logic [PIN_NUM-1:0] s_intr_low_d, s_intr_low_q;
logic [PIN_NUM-1:0] s_intr_enable_d, s_intr_enable_q;
logic [PIN_NUM-1:0] s_intr_state_d, s_intr_state_q;
logic [PIN_NUM-1:0] s_filter_enable_d, s_filter_enable_q;
logic [PIN_NUM-1:0] s_config_lock_d, s_config_lock_q;
logic [15:0] s_filter_div_d, s_filter_div_q;
logic [3:0] s_filter_count_d, s_filter_count_q;
logic s_data_out_en, s_output_enable_en, s_open_drain_en, s_input_cmos_en;
logic s_pull_up_en, s_pull_down_en, s_alt_enable_en, s_alt_select_en;
logic s_user_select_en, s_user_lock_en, s_user_access_en;
logic s_intr_rise_en, s_intr_fall_en, s_intr_high_en, s_intr_low_en;
logic s_intr_enable_en, s_intr_state_en, s_filter_enable_en, s_config_lock_en;
logic s_filter_div_en, s_filter_count_en;
logic [PIN_NUM-1:0] s_intr_clear, s_intr_test;
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] mask;
begin
mask = {{8{strobe[3]}}, {8{strobe[2]}}, {8{strobe[1]}}, {8{strobe[0]}}};
return (current & ~mask) | (value & mask);
end
endfunction
function automatic logic [PIN_NUM-1:0] merge_pins(
input logic [PIN_NUM-1:0] current, input logic [31:0] value, input logic [3:0] strobe);
logic [31:0] current_word;
logic [31:0] merged_word;
begin
current_word = '0;
current_word[PIN_NUM-1:0] = current;
merged_word = merge_wstrb(current_word, value, strobe);
return merged_word[PIN_NUM-1:0];
end
endfunction
function automatic logic [31:0] extend_pins(input logic [PIN_NUM-1:0] value);
logic [31:0] result;
begin
result = '0;
result[PIN_NUM-1:0] = value;
return result;
end
endfunction
function automatic logic irq_config_valid(
input logic [PIN_NUM-1:0] rise, input logic [PIN_NUM-1:0] fall,
input logic [PIN_NUM-1:0] high, input logic [PIN_NUM-1:0] low);
logic [PIN_NUM-1:0] edge_mode;
logic [PIN_NUM-1:0] level_mode;
begin
edge_mode = rise | fall;
level_mode = high | low;
return !((|(high & low)) || (|(edge_mode & level_mode)));
end
endfunction
initial begin
if ((PIN_NUM < 1) || (PIN_NUM > 32)) begin
$fatal(1, "gpio_reg: PIN_NUM must be between 1 and 32");
end
if ((USER_BASE_ADDR[11:0] != 12'd0) || (ADMIN_BASE_ADDR[11:0] != 12'd0) ||
(USER_BASE_ADDR == ADMIN_BASE_ADDR)) begin
$fatal(1, "gpio_reg: register window bases must be distinct and 4 KiB aligned");
end
end
assign s_req = ribp.valid && !s_ribp_ready_q;
assign s_write = |ribp.wstrb;
assign s_req_accept = s_req;
assign s_public_window = ribp.addr[31:12] == USER_BASE_ADDR[31:12];
assign s_admin_window = ribp.addr[31:12] == ADMIN_BASE_ADDR[31:12];
assign s_aligned = ribp.addr[1:0] == 2'b00;
assign s_offset = ribp.addr[11:0];
assign s_byte_mask = {
{8{ribp.wstrb[3]}}, {8{ribp.wstrb[2]}}, {8{ribp.wstrb[1]}}, {8{ribp.wstrb[0]}}
};
assign s_write_word = ribp.wdata & s_byte_mask;
assign s_write_bits = s_write_word[PIN_NUM-1:0];
assign ribp.ready = s_ribp_ready_q;
assign ribp.rdata = s_ribp_rdata_q;
assign ribp.resp_err = s_ribp_resp_err_q;
assign irq_o = |(s_intr_state_q & s_intr_enable_q);
always_comb begin
s_access_error = !s_aligned || !(s_public_window || s_admin_window);
s_ribp_rdata_d = '0;
s_data_out_en = 1'b0;
s_output_enable_en = 1'b0;
s_open_drain_en = 1'b0;
s_input_cmos_en = 1'b0;
s_pull_up_en = 1'b0;
s_pull_down_en = 1'b0;
s_alt_enable_en = 1'b0;
s_alt_select_en = 1'b0;
s_user_select_en = 1'b0;
s_user_lock_en = 1'b0;
s_user_access_en = 1'b0;
s_intr_rise_en = 1'b0;
s_intr_fall_en = 1'b0;
s_intr_high_en = 1'b0;
s_intr_low_en = 1'b0;
s_intr_enable_en = 1'b0;
s_filter_enable_en = 1'b0;
s_config_lock_en = 1'b0;
s_filter_div_en = 1'b0;
s_filter_count_en = 1'b0;
s_intr_clear = '0;
s_intr_test = '0;
s_data_out_d = s_data_out_q;
s_output_enable_d = s_output_enable_q;
s_open_drain_d = s_open_drain_q;
s_input_cmos_d = s_input_cmos_q;
s_pull_up_d = s_pull_up_q;
s_pull_down_d = s_pull_down_q;
s_alt_enable_d = s_alt_enable_q;
s_alt_select_d = s_alt_select_q;
s_user_select_d = s_user_select_q;
s_user_lock_d = s_user_lock_q;
s_user_access_d = s_user_access_q;
s_intr_rise_d = s_intr_rise_q;
s_intr_fall_d = s_intr_fall_q;
s_intr_high_d = s_intr_high_q;
s_intr_low_d = s_intr_low_q;
s_intr_enable_d = s_intr_enable_q;
s_filter_enable_d = s_filter_enable_q;
s_config_lock_d = s_config_lock_q;
s_filter_div_d = s_filter_div_q;
s_filter_count_d = s_filter_count_q;
if (s_req && !s_access_error) begin
if (s_public_window) begin
if (s_write) begin
unique case (s_offset)
`RIBP_GPIO_USER_DATA_OUT: begin
s_data_out_en = 1'b1;
s_data_out_d = (s_data_out_q & ~s_user_access_q) |
(merge_pins(s_data_out_q, ribp.wdata, ribp.wstrb) & s_user_access_q);
end
`RIBP_GPIO_USER_OUT_SET: begin
s_data_out_en = 1'b1;
s_data_out_d = s_data_out_q | (s_write_bits & s_user_access_q);
end
`RIBP_GPIO_USER_OUT_CLEAR: begin
s_data_out_en = 1'b1;
s_data_out_d = s_data_out_q & ~(s_write_bits & s_user_access_q);
end
`RIBP_GPIO_USER_OUT_TOGGLE: begin
s_data_out_en = 1'b1;
s_data_out_d = s_data_out_q ^ (s_write_bits & s_user_access_q);
end
`RIBP_GPIO_USER_INTR_STATE: begin
s_intr_clear = s_write_bits & s_user_access_q;
end
`RIBP_GPIO_USER_INTR_ENABLE: begin
s_intr_enable_en = 1'b1;
s_intr_enable_d = (s_intr_enable_q & ~s_user_access_q) |
(merge_pins(s_intr_enable_q, ribp.wdata, ribp.wstrb) & s_user_access_q);
end
`RIBP_GPIO_USER_INTR_ENABLE_SET: begin
s_intr_enable_en = 1'b1;
s_intr_enable_d = s_intr_enable_q | (s_write_bits & s_user_access_q);
end
`RIBP_GPIO_USER_INTR_ENABLE_CLEAR: begin
s_intr_enable_en = 1'b1;
s_intr_enable_d = s_intr_enable_q & ~(s_write_bits & s_user_access_q);
end
default: s_access_error = 1'b1;
endcase
end else begin
unique case (s_offset)
`RIBP_GPIO_USER_DATA_IN: s_ribp_rdata_d = extend_pins(data_in_i & s_user_access_q);
`RIBP_GPIO_USER_DATA_OUT: s_ribp_rdata_d = extend_pins(s_data_out_q & s_user_access_q);
`RIBP_GPIO_USER_INTR_STATE:
s_ribp_rdata_d = extend_pins(s_intr_state_q & s_user_access_q);
`RIBP_GPIO_USER_INTR_STATUS:
s_ribp_rdata_d = extend_pins(s_intr_state_q & s_intr_enable_q & s_user_access_q);
`RIBP_GPIO_USER_INTR_ENABLE:
s_ribp_rdata_d = extend_pins(s_intr_enable_q & s_user_access_q);
`RIBP_GPIO_IP_VERSION: s_ribp_rdata_d = IP_VERSION;
`RIBP_GPIO_CAPABILITY: s_ribp_rdata_d = CAPABILITY;
default: s_access_error = 1'b1;
endcase
end
end else if (s_write) begin
unique case (s_offset)
`RIBP_GPIO_ADMIN_DATA_OUT: begin
s_data_out_en = 1'b1;
s_data_out_d = merge_pins(s_data_out_q, ribp.wdata, ribp.wstrb);
end
`RIBP_GPIO_ADMIN_OUT_SET: begin
s_data_out_en = 1'b1;
s_data_out_d = s_data_out_q | s_write_bits;
end
`RIBP_GPIO_ADMIN_OUT_CLEAR: begin
s_data_out_en = 1'b1;
s_data_out_d = s_data_out_q & ~s_write_bits;
end
`RIBP_GPIO_ADMIN_OUT_TOGGLE: begin
s_data_out_en = 1'b1;
s_data_out_d = s_data_out_q ^ s_write_bits;
end
`RIBP_GPIO_ADMIN_OUTPUT_ENABLE,
`RIBP_GPIO_ADMIN_OE_SET,
`RIBP_GPIO_ADMIN_OE_CLEAR,
`RIBP_GPIO_ADMIN_OE_TOGGLE: begin
if (s_offset == `RIBP_GPIO_ADMIN_OUTPUT_ENABLE) begin
s_output_enable_d = merge_pins(s_output_enable_q, ribp.wdata, ribp.wstrb);
end else if (s_offset == `RIBP_GPIO_ADMIN_OE_SET) begin
s_output_enable_d = s_output_enable_q | s_write_bits;
end else if (s_offset == `RIBP_GPIO_ADMIN_OE_CLEAR) begin
s_output_enable_d = s_output_enable_q & ~s_write_bits;
end else begin
s_output_enable_d = s_output_enable_q ^ s_write_bits;
end
if (|((s_output_enable_d ^ s_output_enable_q) & s_config_lock_q)) begin
s_access_error = 1'b1;
end else s_output_enable_en = 1'b1;
end
`RIBP_GPIO_ADMIN_OPEN_DRAIN: begin
s_open_drain_d = merge_pins(s_open_drain_q, ribp.wdata, ribp.wstrb);
if (|((s_open_drain_d ^ s_open_drain_q) & s_config_lock_q)) s_access_error = 1'b1;
else s_open_drain_en = 1'b1;
end
`RIBP_GPIO_ADMIN_INPUT_CMOS: begin
s_input_cmos_d = merge_pins(s_input_cmos_q, ribp.wdata, ribp.wstrb);
if ((!HAS_INPUT_CMOS && (|s_input_cmos_d)) ||
(|((s_input_cmos_d ^ s_input_cmos_q) & s_config_lock_q))) begin
s_access_error = 1'b1;
end else s_input_cmos_en = 1'b1;
end
`RIBP_GPIO_ADMIN_PULL_UP: begin
s_pull_up_d = merge_pins(s_pull_up_q, ribp.wdata, ribp.wstrb);
if ((!HAS_PULL_UP && (|s_pull_up_d)) || (|(s_pull_up_d & s_pull_down_q)) ||
(|((s_pull_up_d ^ s_pull_up_q) & s_config_lock_q))) begin
s_access_error = 1'b1;
end else s_pull_up_en = 1'b1;
end
`RIBP_GPIO_ADMIN_PULL_DOWN: begin
s_pull_down_d = merge_pins(s_pull_down_q, ribp.wdata, ribp.wstrb);
if ((!HAS_PULL_DOWN && (|s_pull_down_d)) || (|(s_pull_down_d & s_pull_up_q)) ||
(|((s_pull_down_d ^ s_pull_down_q) & s_config_lock_q))) begin
s_access_error = 1'b1;
end else s_pull_down_en = 1'b1;
end
`RIBP_GPIO_ADMIN_ALT_ENABLE: begin
s_alt_enable_d = merge_pins(s_alt_enable_q, ribp.wdata, ribp.wstrb);
if (|((s_alt_enable_d ^ s_alt_enable_q) & s_config_lock_q)) s_access_error = 1'b1;
else s_alt_enable_en = 1'b1;
end
`RIBP_GPIO_ADMIN_ALT_SELECT: begin
s_alt_select_d = merge_pins(s_alt_select_q, ribp.wdata, ribp.wstrb);
if (|((s_alt_select_d ^ s_alt_select_q) & s_config_lock_q)) s_access_error = 1'b1;
else s_alt_select_en = 1'b1;
end
`RIBP_GPIO_ADMIN_USER_SELECT: begin
s_user_select_d = merge_pins(s_user_select_q, ribp.wdata, ribp.wstrb);
if (|((s_user_select_d ^ s_user_select_q) & s_user_lock_q)) s_access_error = 1'b1;
else s_user_select_en = 1'b1;
end
`RIBP_GPIO_ADMIN_USER_LOCK: begin
s_user_lock_en = 1'b1;
s_user_lock_d = s_user_lock_q | s_write_bits;
end
`RIBP_GPIO_ADMIN_USER_ACCESS_MASK: begin
s_user_access_d = merge_pins(s_user_access_q, ribp.wdata, ribp.wstrb);
if (|((s_user_access_d ^ s_user_access_q) & s_config_lock_q)) s_access_error = 1'b1;
else s_user_access_en = 1'b1;
end
`RIBP_GPIO_ADMIN_INTR_RISE_ENABLE,
`RIBP_GPIO_ADMIN_INTR_FALL_ENABLE,
`RIBP_GPIO_ADMIN_INTR_HIGH_ENABLE,
`RIBP_GPIO_ADMIN_INTR_LOW_ENABLE: begin
if (s_offset == `RIBP_GPIO_ADMIN_INTR_RISE_ENABLE) begin
s_intr_rise_d = merge_pins(s_intr_rise_q, ribp.wdata, ribp.wstrb);
end else if (s_offset == `RIBP_GPIO_ADMIN_INTR_FALL_ENABLE) begin
s_intr_fall_d = merge_pins(s_intr_fall_q, ribp.wdata, ribp.wstrb);
end else if (s_offset == `RIBP_GPIO_ADMIN_INTR_HIGH_ENABLE) begin
s_intr_high_d = merge_pins(s_intr_high_q, ribp.wdata, ribp.wstrb);
end else begin
s_intr_low_d = merge_pins(s_intr_low_q, ribp.wdata, ribp.wstrb);
end
if (!irq_config_valid(
s_intr_rise_d, s_intr_fall_d, s_intr_high_d, s_intr_low_d
) || (|(((s_intr_rise_d ^ s_intr_rise_q) | (s_intr_fall_d ^ s_intr_fall_q) |
(s_intr_high_d ^ s_intr_high_q) | (s_intr_low_d ^ s_intr_low_q)) &
s_config_lock_q))) begin
s_access_error = 1'b1;
end else begin
s_intr_rise_en = s_offset == `RIBP_GPIO_ADMIN_INTR_RISE_ENABLE;
s_intr_fall_en = s_offset == `RIBP_GPIO_ADMIN_INTR_FALL_ENABLE;
s_intr_high_en = s_offset == `RIBP_GPIO_ADMIN_INTR_HIGH_ENABLE;
s_intr_low_en = s_offset == `RIBP_GPIO_ADMIN_INTR_LOW_ENABLE;
end
end
`RIBP_GPIO_ADMIN_INTR_ENABLE: begin
s_intr_enable_en = 1'b1;
s_intr_enable_d = merge_pins(s_intr_enable_q, ribp.wdata, ribp.wstrb);
end
`RIBP_GPIO_ADMIN_INTR_STATE: s_intr_clear = s_write_bits;
`RIBP_GPIO_ADMIN_INTR_TEST: s_intr_test = s_write_bits;
`RIBP_GPIO_ADMIN_FILTER_ENABLE: begin
s_filter_enable_d = merge_pins(s_filter_enable_q, ribp.wdata, ribp.wstrb);
if (((|s_filter_enable_d) && (s_filter_count_q == 4'd0)) ||
(|((s_filter_enable_d ^ s_filter_enable_q) & s_config_lock_q))) begin
s_access_error = 1'b1;
end else s_filter_enable_en = 1'b1;
end
`RIBP_GPIO_ADMIN_FILTER_DIV: begin
if (|s_filter_enable_q) begin
s_access_error = 1'b1;
end else begin
s_ribp_rdata_d = merge_wstrb({16'd0, s_filter_div_q}, ribp.wdata, ribp.wstrb);
if (|s_ribp_rdata_d[31:16]) s_access_error = 1'b1;
else begin
s_filter_div_en = 1'b1;
s_filter_div_d = s_ribp_rdata_d[15:0];
end
end
end
`RIBP_GPIO_ADMIN_FILTER_COUNT: begin
if (|s_filter_enable_q) begin
s_access_error = 1'b1;
end else begin
s_ribp_rdata_d = merge_wstrb({28'd0, s_filter_count_q}, ribp.wdata, ribp.wstrb);
if ((|s_ribp_rdata_d[31:4]) || (s_ribp_rdata_d[3:0] == 4'd0)) begin
s_access_error = 1'b1;
end else begin
s_filter_count_en = 1'b1;
s_filter_count_d = s_ribp_rdata_d[3:0];
end
end
end
`RIBP_GPIO_ADMIN_CONFIG_LOCK: begin
s_config_lock_en = 1'b1;
s_config_lock_d = s_config_lock_q | s_write_bits;
end
default: s_access_error = 1'b1;
endcase
end else begin
unique case (s_offset)
`RIBP_GPIO_ADMIN_DATA_IN: s_ribp_rdata_d = extend_pins(data_in_i);
`RIBP_GPIO_ADMIN_DATA_OUT: s_ribp_rdata_d = extend_pins(s_data_out_q);
`RIBP_GPIO_ADMIN_OUTPUT_ENABLE: s_ribp_rdata_d = extend_pins(s_output_enable_q);
`RIBP_GPIO_ADMIN_OPEN_DRAIN: s_ribp_rdata_d = extend_pins(s_open_drain_q);
`RIBP_GPIO_ADMIN_INPUT_CMOS: s_ribp_rdata_d = extend_pins(s_input_cmos_q);
`RIBP_GPIO_ADMIN_PULL_UP: s_ribp_rdata_d = extend_pins(s_pull_up_q);
`RIBP_GPIO_ADMIN_PULL_DOWN: s_ribp_rdata_d = extend_pins(s_pull_down_q);
`RIBP_GPIO_ADMIN_ALT_ENABLE: s_ribp_rdata_d = extend_pins(s_alt_enable_q);
`RIBP_GPIO_ADMIN_ALT_SELECT: s_ribp_rdata_d = extend_pins(s_alt_select_q);
`RIBP_GPIO_ADMIN_USER_SELECT: s_ribp_rdata_d = extend_pins(s_user_select_q);
`RIBP_GPIO_ADMIN_USER_LOCK: s_ribp_rdata_d = extend_pins(s_user_lock_q);
`RIBP_GPIO_ADMIN_USER_STATUS:
s_ribp_rdata_d = extend_pins(s_user_select_q & ~s_user_handoff_q);
`RIBP_GPIO_ADMIN_USER_ACCESS_MASK: s_ribp_rdata_d = extend_pins(s_user_access_q);
`RIBP_GPIO_ADMIN_INTR_RISE_ENABLE: s_ribp_rdata_d = extend_pins(s_intr_rise_q);
`RIBP_GPIO_ADMIN_INTR_FALL_ENABLE: s_ribp_rdata_d = extend_pins(s_intr_fall_q);
`RIBP_GPIO_ADMIN_INTR_HIGH_ENABLE: s_ribp_rdata_d = extend_pins(s_intr_high_q);
`RIBP_GPIO_ADMIN_INTR_LOW_ENABLE: s_ribp_rdata_d = extend_pins(s_intr_low_q);
`RIBP_GPIO_ADMIN_INTR_ENABLE: s_ribp_rdata_d = extend_pins(s_intr_enable_q);
`RIBP_GPIO_ADMIN_INTR_STATE: s_ribp_rdata_d = extend_pins(s_intr_state_q);
`RIBP_GPIO_ADMIN_INTR_STATUS:
s_ribp_rdata_d = extend_pins(s_intr_state_q & s_intr_enable_q);
`RIBP_GPIO_ADMIN_FILTER_ENABLE: s_ribp_rdata_d = extend_pins(s_filter_enable_q);
`RIBP_GPIO_ADMIN_FILTER_DIV: s_ribp_rdata_d = {16'd0, s_filter_div_q};
`RIBP_GPIO_ADMIN_FILTER_COUNT: s_ribp_rdata_d = {28'd0, s_filter_count_q};
`RIBP_GPIO_ADMIN_CONFIG_LOCK: s_ribp_rdata_d = extend_pins(s_config_lock_q);
`RIBP_GPIO_PAD_CAPABILITY: s_ribp_rdata_d = PAD_CAPABILITY;
`RIBP_GPIO_IP_VERSION: s_ribp_rdata_d = IP_VERSION;
`RIBP_GPIO_CAPABILITY: s_ribp_rdata_d = CAPABILITY;
default: s_access_error = 1'b1;
endcase
end
end
end
assign s_user_handoff_d = s_user_select_en ? (s_user_select_d ^ s_user_select_q) : '0;
assign s_intr_state_d = (s_intr_state_q & ~s_intr_clear) | intr_event_i | s_intr_test;
assign s_intr_state_en = (|s_intr_clear) || (|intr_event_i) || (|s_intr_test);
dffer #(PIN_NUM) u_data_out_dffer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_data_out_en),
.dat_i (s_data_out_d),
.dat_o (s_data_out_q)
);
dffer #(PIN_NUM) u_output_enable_dffer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_output_enable_en),
.dat_i (s_output_enable_d),
.dat_o (s_output_enable_q)
);
dffer #(PIN_NUM) u_open_drain_dffer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_open_drain_en),
.dat_i (s_open_drain_d),
.dat_o (s_open_drain_q)
);
dffer #(PIN_NUM) u_input_cmos_dffer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_input_cmos_en),
.dat_i (s_input_cmos_d),
.dat_o (s_input_cmos_q)
);
dffer #(PIN_NUM) u_pull_up_dffer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_pull_up_en),
.dat_i (s_pull_up_d),
.dat_o (s_pull_up_q)
);
dffer #(PIN_NUM) u_pull_down_dffer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_pull_down_en),
.dat_i (s_pull_down_d),
.dat_o (s_pull_down_q)
);
dffer #(PIN_NUM) u_alt_enable_dffer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_alt_enable_en),
.dat_i (s_alt_enable_d),
.dat_o (s_alt_enable_q)
);
dffer #(PIN_NUM) u_alt_select_dffer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_alt_select_en),
.dat_i (s_alt_select_d),
.dat_o (s_alt_select_q)
);
dffer #(PIN_NUM) u_user_select_dffer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_user_select_en),
.dat_i (s_user_select_d),
.dat_o (s_user_select_q)
);
dffer #(PIN_NUM) u_user_lock_dffer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_user_lock_en),
.dat_i (s_user_lock_d),
.dat_o (s_user_lock_q)
);
dffer #(PIN_NUM) u_user_access_dffer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_user_access_en),
.dat_i (s_user_access_d),
.dat_o (s_user_access_q)
);
dffer #(PIN_NUM) u_intr_rise_dffer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_intr_rise_en),
.dat_i (s_intr_rise_d),
.dat_o (s_intr_rise_q)
);
dffer #(PIN_NUM) u_intr_fall_dffer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_intr_fall_en),
.dat_i (s_intr_fall_d),
.dat_o (s_intr_fall_q)
);
dffer #(PIN_NUM) u_intr_high_dffer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_intr_high_en),
.dat_i (s_intr_high_d),
.dat_o (s_intr_high_q)
);
dffer #(PIN_NUM) u_intr_low_dffer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_intr_low_en),
.dat_i (s_intr_low_d),
.dat_o (s_intr_low_q)
);
dffer #(PIN_NUM) u_intr_enable_dffer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_intr_enable_en),
.dat_i (s_intr_enable_d),
.dat_o (s_intr_enable_q)
);
dffer #(PIN_NUM) u_intr_state_dffer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_intr_state_en),
.dat_i (s_intr_state_d),
.dat_o (s_intr_state_q)
);
dffer #(PIN_NUM) u_filter_enable_dffer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_filter_enable_en),
.dat_i (s_filter_enable_d),
.dat_o (s_filter_enable_q)
);
dffer #(PIN_NUM) u_config_lock_dffer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_config_lock_en),
.dat_i (s_config_lock_d),
.dat_o (s_config_lock_q)
);
dffer #(16) u_filter_div_dffer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_filter_div_en),
.dat_i (s_filter_div_d),
.dat_o (s_filter_div_q)
);
dffer #(4) u_filter_count_dffer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_filter_count_en),
.dat_i (s_filter_count_d),
.dat_o (s_filter_count_q)
);
dffr #(PIN_NUM) u_user_handoff_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_user_handoff_d),
.dat_o (s_user_handoff_q)
);
dffr #(1) u_ribp_ready_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_req_accept),
.dat_o (s_ribp_ready_q)
);
assign s_ribp_resp_err_d = s_access_error;
dffer #(1) u_ribp_resp_err_dffer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_req_accept),
.dat_i (s_ribp_resp_err_d),
.dat_o (s_ribp_resp_err_q)
);
dffer #(32) u_ribp_rdata_dffer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_req_accept),
.dat_i (s_ribp_rdata_d),
.dat_o (s_ribp_rdata_q)
);
assign data_out_o = s_data_out_q;
assign output_enable_o = s_output_enable_q;
assign open_drain_o = s_open_drain_q;
assign input_cmos_o = s_input_cmos_q;
assign pull_up_o = s_pull_up_q;
assign pull_down_o = s_pull_down_q;
assign alt_enable_o = s_alt_enable_q;
assign alt_select_o = s_alt_select_q;
assign user_select_o = s_user_select_q;
assign user_handoff_o = s_user_handoff_q;
assign filter_enable_o = s_filter_enable_q;
assign filter_div_o = s_filter_div_q;
assign filter_count_o = s_filter_count_q;
assign intr_rise_enable_o = s_intr_rise_q;
assign intr_fall_enable_o = s_intr_fall_q;
assign intr_high_enable_o = s_intr_high_q;
assign intr_low_enable_o = s_intr_low_q;
endmodule
+104
View File
@@ -0,0 +1,104 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
module ribp_gpio #(
parameter int PIN_NUM = 32,
parameter logic [31:0] USER_BASE_ADDR = 32'h1000_0000,
parameter logic [31:0] ADMIN_BASE_ADDR = 32'h1001_4000,
parameter bit HAS_INPUT_CMOS = 1'b0,
parameter bit HAS_PULL_UP = 1'b0,
parameter bit HAS_PULL_DOWN = 1'b0
) (
// verilog_format: off
input logic clk_i,
input logic rst_n_i,
ribp_if.slave ribp,
gpio_if.dut gpio,
user_gpio_if.padctrl user_gpio
// verilog_format: on
);
logic [PIN_NUM-1:0] s_data_in;
logic [PIN_NUM-1:0] s_data_out;
logic [PIN_NUM-1:0] s_output_enable;
logic [PIN_NUM-1:0] s_open_drain;
logic [PIN_NUM-1:0] s_input_cmos;
logic [PIN_NUM-1:0] s_pull_up;
logic [PIN_NUM-1:0] s_pull_down;
logic [PIN_NUM-1:0] s_alt_enable;
logic [PIN_NUM-1:0] s_alt_select;
logic [PIN_NUM-1:0] s_user_select;
logic [PIN_NUM-1:0] s_user_handoff;
logic [PIN_NUM-1:0] s_filter_enable;
logic [ 15:0] s_filter_div;
logic [ 3:0] s_filter_count;
logic [PIN_NUM-1:0] s_intr_rise_enable;
logic [PIN_NUM-1:0] s_intr_fall_enable;
logic [PIN_NUM-1:0] s_intr_high_enable;
logic [PIN_NUM-1:0] s_intr_low_enable;
logic [PIN_NUM-1:0] s_intr_event;
logic s_irq;
gpio_reg #(
.PIN_NUM (PIN_NUM),
.USER_BASE_ADDR (USER_BASE_ADDR),
.ADMIN_BASE_ADDR(ADMIN_BASE_ADDR),
.HAS_INPUT_CMOS (HAS_INPUT_CMOS),
.HAS_PULL_UP (HAS_PULL_UP),
.HAS_PULL_DOWN (HAS_PULL_DOWN)
) u_gpio_reg (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.ribp (ribp),
.data_in_i (s_data_in),
.intr_event_i (s_intr_event),
.data_out_o (s_data_out),
.output_enable_o (s_output_enable),
.open_drain_o (s_open_drain),
.input_cmos_o (s_input_cmos),
.pull_up_o (s_pull_up),
.pull_down_o (s_pull_down),
.alt_enable_o (s_alt_enable),
.alt_select_o (s_alt_select),
.user_select_o (s_user_select),
.user_handoff_o (s_user_handoff),
.filter_enable_o (s_filter_enable),
.filter_div_o (s_filter_div),
.filter_count_o (s_filter_count),
.intr_rise_enable_o(s_intr_rise_enable),
.intr_fall_enable_o(s_intr_fall_enable),
.intr_high_enable_o(s_intr_high_enable),
.intr_low_enable_o (s_intr_low_enable),
.irq_o (s_irq)
);
gpio_core #(
.PIN_NUM(PIN_NUM)
) u_gpio_core (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.data_out_i (s_data_out),
.output_enable_i (s_output_enable),
.open_drain_i (s_open_drain),
.input_cmos_i (s_input_cmos),
.pull_up_i (s_pull_up),
.pull_down_i (s_pull_down),
.alt_enable_i (s_alt_enable),
.alt_select_i (s_alt_select),
.user_select_i (s_user_select),
.user_handoff_i (s_user_handoff),
.filter_enable_i (s_filter_enable),
.filter_div_i (s_filter_div),
.filter_count_i (s_filter_count),
.intr_rise_enable_i(s_intr_rise_enable),
.intr_fall_enable_i(s_intr_fall_enable),
.intr_high_enable_i(s_intr_high_enable),
.intr_low_enable_i (s_intr_low_enable),
.irq_i (s_irq),
.data_in_o (s_data_in),
.intr_event_o (s_intr_event),
.gpio (gpio),
.user_gpio (user_gpio)
);
endmodule
+15
View File
@@ -0,0 +1,15 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
`include "gpio_define.svh"
interface user_gpio_if #(
parameter int DATA_WIDTH = `RIBP_GPIO_NUM
) ();
logic [DATA_WIDTH-1:0] do_o;
logic [DATA_WIDTH-1:0] oe_o;
logic [DATA_WIDTH-1:0] di_i;
modport user_ip(output do_o, output oe_o, input di_i);
modport padctrl(input do_o, input oe_o, output di_i);
endinterface
@@ -207,7 +207,11 @@ def render_c(
"#ifndef RETROSOC_GENERATED_MEMORY_MAP_H",
"#define RETROSOC_GENERATED_MEMORY_MAP_H",
"",
"#ifdef __ASSEMBLER__",
"#define UINT32_C(value) value",
"#else",
"#include <stdint.h>",
"#endif",
"",
f"#define RS_SOC_CPU_RESET_ADDR {c_hex32(cpu_reset)}",
f"#define RS_SOC_HAS_SRAM {1 if have_sram_if == 'YES' else 0}U",
+1
View File
@@ -55,6 +55,7 @@
{"symbol": "RIBP_I2C1", "base": "0x10011000", "size": "0x00001000", "route": "ribp", "kind": "active", "public": true, "user_access": "rw"},
{"symbol": "RIBP_GA", "base": "0x10012000", "size": "0x00001000", "route": "reserved", "kind": "reserved", "public": false, "user_access": "none"},
{"symbol": "RIBP_APU", "base": "0x10013000", "size": "0x00001000", "route": "reserved", "kind": "reserved", "public": false, "user_access": "none"},
{"symbol": "RIBP_GPIO_ADMIN", "base": "0x10014000", "size": "0x00001000", "route": "ribp", "kind": "active", "public": true, "user_access": "none"},
{"symbol": "APB_ARCHINFO", "base": "0x20000000", "size": "0x00001000", "route": "apb", "kind": "active", "public": true, "user_access": "ro"},
{"symbol": "APB_RNG", "base": "0x20001000", "size": "0x00001000", "route": "apb", "kind": "active", "public": true, "user_access": "rw"},
{"symbol": "APB_UART1", "base": "0x20002000", "size": "0x00001000", "route": "apb", "kind": "active", "public": true, "user_access": "rw"},
+19 -9
View File
@@ -136,30 +136,40 @@ module soc_gpio_user_handoff_sva #(
input logic [DATA_WIDTH-1:0] user_do_i,
input logic [DATA_WIDTH-1:0] native_oe_i,
input logic [DATA_WIDTH-1:0] native_do_i,
input logic [DATA_WIDTH-1:0] open_drain_i,
input logic [DATA_WIDTH-1:0] selected_oe_i,
input logic [DATA_WIDTH-1:0] selected_do_i,
input logic [DATA_WIDTH-1:0] gpio_oe_i,
input logic [DATA_WIDTH-1:0] gpio_do_i
);
// A changing owner first drives output-enable low for a full handoff cycle.
assert property (@(posedge clk_i) disable iff (!rst_n_i)
gpio_oe_i == ((~user_handoff_i & user_sel_i & user_oe_i) |
(~user_handoff_i & ~user_sel_i & native_oe_i)));
selected_oe_i == ((user_sel_i & user_oe_i) | (~user_sel_i & native_oe_i)));
assert property (@(posedge clk_i) disable iff (!rst_n_i)
gpio_do_i == ((user_sel_i & user_do_i) | (~user_sel_i & native_do_i)));
selected_do_i == ((user_sel_i & user_do_i) | (~user_sel_i & native_do_i)));
assert property (@(posedge clk_i) disable iff (!rst_n_i)
gpio_do_i == (selected_do_i & ~open_drain_i));
assert property (@(posedge clk_i) disable iff (!rst_n_i)
gpio_oe_i == (~user_handoff_i & selected_oe_i &
~(open_drain_i & selected_do_i)));
endmodule
bind ribp_gpio soc_gpio_user_handoff_sva #(
.DATA_WIDTH(`RIBP_GPIO_NUM)
bind gpio_core soc_gpio_user_handoff_sva #(
.DATA_WIDTH(PIN_NUM)
) u_soc_gpio_user_handoff_sva (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.user_sel_i (s_gpio_user_sel_q),
.user_handoff_i(s_gpio_user_handoff_q),
.user_sel_i (user_select_i),
.user_handoff_i(user_handoff_i),
.user_oe_i (user_gpio.oe_o),
.user_do_i (user_gpio.do_o),
.native_oe_i (s_gpio_native_oe),
.native_do_i (s_gpio_native_out),
.native_oe_i (s_native_oe),
.native_do_i (s_native_data),
.open_drain_i (open_drain_i),
.selected_oe_i (s_selected_oe),
.selected_do_i (s_selected_data),
.gpio_oe_i (gpio.oe_o),
.gpio_do_i (gpio.do_o)
);
+5 -1
View File
@@ -47,7 +47,11 @@
/ip/ribp/peripheral/timer_reg.sv
/ip/ribp/peripheral/ribp_timer.sv
/ip/ribp/serial/uart.sv
/ip/ribp/peripheral/gpio.sv
/ip/ribp/peripheral/gpio_if.sv
/ip/ribp/peripheral/user_gpio_if.sv
/ip/ribp/peripheral/gpio_core.sv
/ip/ribp/peripheral/gpio_reg.sv
/ip/ribp/peripheral/ribp_gpio.sv
/top/ahbl2ribp.sv
/ip/ribp/interconnect/axi4l2ribp.sv
/ip/ribp/util/async_fifo.sv
+9 -4
View File
@@ -82,14 +82,19 @@ def source_files(target: str) -> list[Path]:
TOP / "rcu.sv",
SCRIPT_DIR / "pll_rcu_formal.sv",
]
if target == "gpio_user":
if target == "gpio":
return [
*common,
COMMON_RTL / "cdc/cdc_sync.sv",
COMMON_RTL / "cdc/cdc_rst_ctrlr.sv",
COMMON_RTL / "clkrst/counter.sv",
COMMON_RTL / "utils/edge_det.sv",
PERIPHERAL / "gpio.sv",
SCRIPT_DIR / "gpio_user_formal.sv",
PERIPHERAL / "gpio_if.sv",
PERIPHERAL / "user_gpio_if.sv",
PERIPHERAL / "gpio_core.sv",
PERIPHERAL / "gpio_reg.sv",
PERIPHERAL / "ribp_gpio.sv",
SCRIPT_DIR / "gpio_formal.sv",
]
if target == "ws2812":
return [
@@ -162,7 +167,7 @@ def parse_args() -> argparse.Namespace:
"rib2apb",
"sysctrl",
"pll_rcu",
"gpio_user",
"gpio",
"ws2812",
"timer",
"clint",
+108
View File
@@ -0,0 +1,108 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
module gpio_formal_design (
// verilog_format: off
input logic clk_i,
output logic rst_n_i,
output logic f_past_valid,
output logic ribp_valid,
output logic ribp_ready,
output logic ribp_resp_err,
output logic [31:0] ribp_addr,
output logic [31:0] ribp_wdata,
output logic [3:0] ribp_wstrb,
output logic [3:0] gpio_oe,
output logic [3:0] gpio_do,
output logic [3:0] user_oe,
output logic [3:0] user_do,
output logic [3:0] user_select,
output logic [3:0] user_lock,
output logic [3:0] user_handoff,
output logic [3:0] user_access,
output logic [3:0] config_lock,
output logic [3:0] data_out,
output logic [3:0] output_enable,
output logic [3:0] open_drain,
output logic [3:0] selected_data,
output logic [3:0] intr_state,
output logic irq
// verilog_format: on
);
ribp_if ribp ();
gpio_if #(4) gpio ();
user_gpio_if #(4) user_gpio ();
(* anyseq *)logic f_ribp_valid;
(* anyseq *)logic [31:0] f_ribp_addr;
(* anyseq *)logic [31:0] f_ribp_wdata;
(* anyseq *)logic [ 3:0] f_ribp_wstrb;
(* anyseq *)logic [ 3:0] f_gpio_di;
(* anyseq *)logic [ 3:0] f_alt0_do;
(* anyseq *)logic [ 3:0] f_alt0_oe;
(* anyseq *)logic [ 3:0] f_alt1_do;
(* anyseq *)logic [ 3:0] f_alt1_oe;
(* anyseq *)logic [ 3:0] f_user_do;
(* anyseq *)logic [ 3:0] f_user_oe;
assign ribp.valid = f_ribp_valid;
assign ribp.addr = f_ribp_addr;
assign ribp.wdata = f_ribp_wdata;
assign ribp.wstrb = f_ribp_wstrb;
assign gpio.di_i = f_gpio_di;
assign gpio.alt0_do_i = f_alt0_do;
assign gpio.alt0_oe_i = f_alt0_oe;
assign gpio.alt1_do_i = f_alt1_do;
assign gpio.alt1_oe_i = f_alt1_oe;
assign user_gpio.do_o = f_user_do;
assign user_gpio.oe_o = f_user_oe;
assign ribp_valid = ribp.valid;
assign ribp_ready = ribp.ready;
assign ribp_resp_err = ribp.resp_err;
assign ribp_addr = ribp.addr;
assign ribp_wdata = ribp.wdata;
assign ribp_wstrb = ribp.wstrb;
assign gpio_oe = gpio.oe_o;
assign gpio_do = gpio.do_o;
assign user_oe = user_gpio.oe_o;
assign user_do = user_gpio.do_o;
assign user_select = u_dut.u_gpio_reg.s_user_select_q;
assign user_lock = u_dut.u_gpio_reg.s_user_lock_q;
assign user_handoff = u_dut.u_gpio_reg.s_user_handoff_q;
assign user_access = u_dut.u_gpio_reg.s_user_access_q;
assign config_lock = u_dut.u_gpio_reg.s_config_lock_q;
assign data_out = u_dut.u_gpio_reg.s_data_out_q;
assign output_enable = u_dut.u_gpio_reg.s_output_enable_q;
assign open_drain = u_dut.u_gpio_reg.s_open_drain_q;
assign selected_data = u_dut.u_gpio_core.s_selected_data;
assign intr_state = u_dut.u_gpio_reg.s_intr_state_q;
assign irq = gpio.irq_o;
ribp_gpio #(
.PIN_NUM (4),
.USER_BASE_ADDR (32'h1000_0000),
.ADMIN_BASE_ADDR(32'h1001_4000),
.HAS_INPUT_CMOS (1'b1),
.HAS_PULL_UP (1'b1),
.HAS_PULL_DOWN (1'b1)
) u_dut (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.ribp (ribp),
.gpio (gpio),
.user_gpio(user_gpio)
);
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
+88
View File
@@ -0,0 +1,88 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
module gpio_formal;
(* anyseq *) (* gclk *)reg clk_i;
wire rst_n_i;
wire f_past_valid;
wire ribp_valid;
wire ribp_ready;
wire ribp_resp_err;
wire [31:0] ribp_addr;
wire [31:0] ribp_wdata;
wire [ 3:0] ribp_wstrb;
wire [ 3:0] gpio_oe;
wire [ 3:0] gpio_do;
wire [ 3:0] user_oe;
wire [ 3:0] user_do;
wire [ 3:0] user_select;
wire [ 3:0] user_lock;
wire [ 3:0] user_handoff;
wire [ 3:0] user_access;
wire [ 3:0] config_lock;
wire [ 3:0] data_out;
wire [ 3:0] output_enable;
wire [ 3:0] open_drain;
wire [ 3:0] selected_data;
wire [ 3:0] intr_state;
wire irq;
gpio_formal_design u_design (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.f_past_valid (f_past_valid),
.ribp_valid (ribp_valid),
.ribp_ready (ribp_ready),
.ribp_resp_err(ribp_resp_err),
.ribp_addr (ribp_addr),
.ribp_wdata (ribp_wdata),
.ribp_wstrb (ribp_wstrb),
.gpio_oe (gpio_oe),
.gpio_do (gpio_do),
.user_oe (user_oe),
.user_do (user_do),
.user_select (user_select),
.user_lock (user_lock),
.user_handoff (user_handoff),
.user_access (user_access),
.config_lock (config_lock),
.data_out (data_out),
.output_enable(output_enable),
.open_drain (open_drain),
.selected_data(selected_data),
.intr_state (intr_state),
.irq (irq)
);
always @(posedge clk_i) begin
if (f_past_valid && $past(rst_n_i && ribp_valid && !ribp_ready)) begin
assume (ribp_valid);
assume (ribp_addr == $past(ribp_addr));
assume (ribp_wdata == $past(ribp_wdata));
assume (ribp_wstrb == $past(ribp_wstrb));
end
if (rst_n_i) begin
assert ((gpio_oe & user_handoff) == 4'd0);
assert ((gpio_do & open_drain) == 4'd0);
assert ((gpio_oe & open_drain & selected_data) == 4'd0);
if (f_past_valid) begin
assert (user_handoff == (user_select ^ $past(user_select)));
assert ((user_lock & $past(user_lock)) == $past(user_lock));
assert ((config_lock & $past(config_lock)) == $past(config_lock));
assert ((user_select & $past(user_lock)) == ($past(user_select) & $past(user_lock)));
if ($past(ribp_valid && !ribp_ready && (ribp_addr[31:12] == 20'h10000))) begin
assert ((data_out & ~$past(user_access)) == ($past(data_out) & ~$past(user_access)));
assert (user_access == $past(user_access));
assert (config_lock == $past(config_lock));
end
end
cover (|user_handoff);
cover (|user_lock && |user_select);
cover (irq && |intr_state);
cover (ribp_ready && ribp_resp_err);
end
end
endmodule
-91
View File
@@ -1,91 +0,0 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
module gpio_user_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 [31:0] gpio_di,
output logic [31:0] gpio_oe,
output logic [31:0] gpio_do,
output logic [31:0] user_di,
output logic [31:0] user_oe,
output logic [31:0] user_do,
output logic [31:0] user_sel,
output logic [31:0] user_lock,
output logic [31:0] user_handoff,
output logic [31:0] user_status,
output logic [31:0] native_oe,
output logic [31:0] native_do
);
ribp_if rib ();
gpio_if gpio ();
user_gpio_if user_gpio ();
(* anyseq *)logic f_rib_valid;
(* anyseq *)logic [31:0] f_rib_addr;
(* anyseq *)logic [31:0] f_rib_wdata;
(* anyseq *)logic [ 3:0] f_rib_wstrb;
(* anyseq *)logic [31:0] f_gpio_di;
(* anyseq *)logic [31:0] f_alt0_do;
(* anyseq *)logic [31:0] f_alt0_oe;
(* anyseq *)logic [31:0] f_alt1_do;
(* anyseq *)logic [31:0] f_alt1_oe;
(* anyseq *)logic [31:0] f_user_do;
(* anyseq *)logic [31:0] f_user_oe;
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 gpio.di_i = f_gpio_di;
assign gpio.alt0_do_i = f_alt0_do;
assign gpio.alt0_oe_i = f_alt0_oe;
assign gpio.alt1_do_i = f_alt1_do;
assign gpio.alt1_oe_i = f_alt1_oe;
assign user_gpio.do_o = f_user_do;
assign user_gpio.oe_o = f_user_oe;
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 gpio_di = gpio.di_i;
assign gpio_oe = gpio.oe_o;
assign gpio_do = gpio.do_o;
assign user_di = user_gpio.di_i;
assign user_oe = user_gpio.oe_o;
assign user_do = user_gpio.do_o;
assign user_sel = u_dut.s_gpio_user_sel_q;
assign user_lock = u_dut.s_gpio_user_lock_q;
assign user_handoff = u_dut.s_gpio_user_handoff_q;
assign user_status = u_dut.s_gpio_user_status;
assign native_oe = u_dut.s_gpio_native_oe;
assign native_do = u_dut.s_gpio_native_out;
ribp_gpio u_dut (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.ribp (rib),
.gpio (gpio),
.user_gpio(user_gpio)
);
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
-77
View File
@@ -1,77 +0,0 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
module gpio_user_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 [31:0] gpio_di;
wire [31:0] gpio_oe;
wire [31:0] gpio_do;
wire [31:0] user_di;
wire [31:0] user_oe;
wire [31:0] user_do;
wire [31:0] user_sel;
wire [31:0] user_lock;
wire [31:0] user_handoff;
wire [31:0] user_status;
wire [31:0] native_oe;
wire [31:0] native_do;
gpio_user_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),
.gpio_di (gpio_di),
.gpio_oe (gpio_oe),
.gpio_do (gpio_do),
.user_di (user_di),
.user_oe (user_oe),
.user_do (user_do),
.user_sel (user_sel),
.user_lock (user_lock),
.user_handoff(user_handoff),
.user_status (user_status),
.native_oe (native_oe),
.native_do (native_do)
);
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 (user_di == gpio_di);
assert(gpio_oe == ((~user_handoff & user_sel & user_oe) |
(~user_handoff & ~user_sel & native_oe)));
assert (gpio_do == ((user_sel & user_do) | (~user_sel & native_do)));
assert (user_status == (user_sel & ~user_handoff));
if (f_past_valid) begin
assert (user_handoff == (user_sel ^ $past(user_sel)));
if ($past(user_lock)) begin
assert (user_lock);
assert ((user_sel & $past(user_lock)) == ($past(user_sel) & $past(user_lock)));
end
end
cover (|user_handoff);
cover (|user_lock && |user_sel);
cover (|user_status && |gpio_oe);
end
end
endmodule
@@ -45,6 +45,7 @@ COMPATIBILITY_IRQ_BINDINGS = (
("advanced_timer", "apb", 5, 15, "u_tmr_if.irq_o"),
("reserved", "apb", 6, 16, "1'b0"),
("ws2812", "ribp", 10, 17, "ws2812.irq_o"),
("gpio", "ribp", 11, 18, "gpio.irq_o"),
)
+4 -3
View File
@@ -3,7 +3,7 @@
"gpio_pins": 32,
"irq_vector_width": 32,
"irq_groups": [
{"name": "ribp", "width": 11, "top_signal": "s_ribp_irq"},
{"name": "ribp", "width": 12, "top_signal": "s_ribp_irq"},
{"name": "apb", "width": 7, "top_signal": "s_apb_irq"}
],
"interrupts": [
@@ -24,11 +24,12 @@
{"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": "ws2812", "description": "WS2812 transmitter interrupt", "group": "ribp", "group_bit": 10, "core_bit": 17, "signal": "ws2812.irq_o"}
{"name": "ws2812", "description": "WS2812 transmitter interrupt", "group": "ribp", "group_bit": 10, "core_bit": 17, "signal": "ws2812.irq_o"},
{"name": "gpio", "description": "RIBP GPIO combined interrupt", "group": "ribp", "group_bit": 11, "core_bit": 18, "signal": "gpio.irq_o"}
],
"ribp_targets": [
{"slot": 0, "name": "uart0", "interface": "u_uart_ribp_if", "regions": ["RIBP_UART0"]},
{"slot": 1, "name": "gpio", "interface": "u_gpio_ribp_if", "regions": ["RIBP_GPIO"]},
{"slot": 1, "name": "gpio", "interface": "u_gpio_ribp_if", "regions": ["RIBP_GPIO", "RIBP_GPIO_ADMIN"]},
{"slot": 2, "name": "tim0", "interface": "u_tim0_ribp_if", "regions": ["RIBP_TIM0"]},
{"slot": 3, "name": "tim1", "interface": "u_tim1_ribp_if", "regions": ["RIBP_TIM1"]},
{"slot": 4, "name": "psram", "interface": "u_psram_ribp_if", "regions": ["RIBP_PSRAM", "PSRAM"]},
+10 -6
View File
@@ -9,7 +9,7 @@ FORMAL_WS2812_DEPTH ?= 120
FORMAL_CLINT_DEPTH ?= 32
FORMAL_TIMEOUT ?= 60
FORMAL_WS2812_TIMEOUT ?= 120
FORMAL_TARGETS := bus rib_adapter rib2apb sysctrl pll_rcu gpio_user ws2812 timer clint
FORMAL_TARGETS := bus rib_adapter rib2apb sysctrl pll_rcu gpio ws2812 timer clint
FORMAL_FILELIST_GENERATOR := $(RTL_PATH)/formal/generate_formal_filelist.py
FORMAL_SBY_GENERATOR := $(RTL_PATH)/formal/generate_sby_config.py
FORMAL_RESULT_GENERATOR := $(RTL_PATH)/formal/formal_results.py
@@ -23,8 +23,8 @@ FORMAL_SOURCE_FILES := $(RTL_PATH)/formal/bus_formal.sv \
$(RTL_PATH)/formal/sysctrl_formal_props.sv \
$(RTL_PATH)/formal/pll_rcu_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/gpio_formal.sv \
$(RTL_PATH)/formal/gpio_formal_props.sv \
$(RTL_PATH)/formal/ws2812_formal.sv \
$(RTL_PATH)/formal/ws2812_formal_props.sv \
$(RTL_PATH)/formal/timer_formal.sv \
@@ -39,7 +39,11 @@ FORMAL_SOURCE_FILES := $(RTL_PATH)/formal/bus_formal.sv \
$(RTL_PATH)/top/rib2apb.sv \
$(RTL_PATH)/top/ribp2rib.sv \
$(ROOT_PATH)/rtl/ip/ribp/interconnect/ribp_regslice.sv \
$(ROOT_PATH)/rtl/ip/ribp/peripheral/gpio.sv \
$(ROOT_PATH)/rtl/ip/ribp/peripheral/gpio_if.sv \
$(ROOT_PATH)/rtl/ip/ribp/peripheral/user_gpio_if.sv \
$(ROOT_PATH)/rtl/ip/ribp/peripheral/gpio_core.sv \
$(ROOT_PATH)/rtl/ip/ribp/peripheral/gpio_reg.sv \
$(ROOT_PATH)/rtl/ip/ribp/peripheral/ribp_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 \
@@ -146,7 +150,7 @@ formal-sysctrl: $(FORMAL_DIR)/sysctrl/.stamp | manifest
formal-pll-rcu: $(FORMAL_DIR)/pll_rcu/.stamp | manifest
formal-gpio-user: $(FORMAL_DIR)/gpio_user/.stamp | manifest
formal-gpio: $(FORMAL_DIR)/gpio/.stamp | manifest
formal-ws2812: $(FORMAL_DIR)/ws2812/.stamp | manifest
@@ -160,4 +164,4 @@ formal-doctor:
formal-clean:
python3 $(ROOT_PATH)/scripts/clean.py --root $(ROOT_PATH) --path $(FORMAL_DIR)
.PHONY: formal formal-bus formal-rib-adapter formal-rib2apb formal-sysctrl formal-pll-rcu formal-gpio-user formal-ws2812 formal-timer formal-clint formal-doctor formal-clean
.PHONY: formal formal-bus formal-rib-adapter formal-rib2apb formal-sysctrl formal-pll-rcu formal-gpio formal-ws2812 formal-timer formal-clint formal-doctor formal-clean
+4 -2
View File
@@ -68,6 +68,7 @@ CRT_SRCS := $(ROOT_PATH)/crt/arch/riscv/startup.S \
$(ROOT_PATH)/crt/src/hal/clint.c \
$(ROOT_PATH)/crt/src/hal/clock.c \
$(ROOT_PATH)/crt/src/hal/uart.c \
$(ROOT_PATH)/crt/src/hal/gpio_math.c \
$(ROOT_PATH)/crt/src/hal/gpio.c \
$(ROOT_PATH)/crt/src/hal/timer_math.c \
$(ROOT_PATH)/crt/src/hal/timer.c \
@@ -142,9 +143,10 @@ FORCE_VERSION:
upd_ver_info: $(VERSION_HEADER)
asm: $(MPW_VARIANT_STAMP)
asm: $(MPW_VARIANT_STAMP) $(MEMORY_MAP_STAMP)
@mkdir -p $(SW_BUILD_DIR)/asm
$(MAKE) -C $(ROOT_PATH)/app/asm OUT_DIR=$(SW_BUILD_DIR)/asm
$(MAKE) -C $(ROOT_PATH)/app/asm OUT_DIR=$(SW_BUILD_DIR)/asm \
GENERATED_INCLUDE=$(MEMORY_MAP_C_DIR)
cp $(SW_BUILD_DIR)/asm/hello-asm.flash $(SW_BUILD_DIR)/$(ASM_FIRMWARE_NAME).hex
cp $(SW_BUILD_DIR)/asm/hello-asm.bin $(SW_BUILD_DIR)/$(ASM_FIRMWARE_NAME).bin
cp $(SW_BUILD_DIR)/asm/hello-asm.txt $(SW_BUILD_DIR)/$(ASM_FIRMWARE_NAME)_all.txt
+30 -6
View File
@@ -61,6 +61,24 @@ module ip_ribp_wrapper (
logic s_dma_xfer_done;
logic s_tim0_irq, s_tim1_irq;
`ifdef PDK_GF180
localparam bit GPIO_HAS_INPUT_CMOS = 1'b1;
localparam bit GPIO_HAS_PULL_UP = 1'b1;
localparam bit GPIO_HAS_PULL_DOWN = 1'b1;
`elsif PDK_ICS55
localparam bit GPIO_HAS_INPUT_CMOS = 1'b1;
localparam bit GPIO_HAS_PULL_UP = 1'b1;
localparam bit GPIO_HAS_PULL_DOWN = 1'b1;
`elsif PDK_SKY130
localparam bit GPIO_HAS_INPUT_CMOS = 1'b1;
localparam bit GPIO_HAS_PULL_UP = 1'b0;
localparam bit GPIO_HAS_PULL_DOWN = 1'b0;
`else
localparam bit GPIO_HAS_INPUT_CMOS = 1'b0;
localparam bit GPIO_HAS_PULL_UP = 1'b0;
localparam bit GPIO_HAS_PULL_DOWN = 1'b0;
`endif
rib2ribp u_rib2ribp (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
@@ -83,12 +101,18 @@ module ip_ribp_wrapper (
// Generated IRQ ownership and core-vector bit assignments are topology checked.
`include "ribp_irq_bindings.svh"
ribp_gpio u_rib_gpio (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.ribp(u_gpio_ribp_if),
.gpio (gpio),
.user_gpio(user_gpio)
ribp_gpio #(
.USER_BASE_ADDR (`SOC_ADDR_RIBP_GPIO_BASE),
.ADMIN_BASE_ADDR(`SOC_ADDR_RIBP_GPIO_ADMIN_BASE),
.HAS_INPUT_CMOS (GPIO_HAS_INPUT_CMOS),
.HAS_PULL_UP (GPIO_HAS_PULL_UP),
.HAS_PULL_DOWN (GPIO_HAS_PULL_DOWN)
) u_rib_gpio (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.ribp (u_gpio_ribp_if),
.gpio (gpio),
.user_gpio (user_gpio)
);
// verilog_format: on
+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/gpio_math.c",
"crt/src/hal/ws2812_math.c",
"crt/src/hal/timer_math.c",
"app/media/src/video_player.c",
+57 -64
View File
@@ -3,6 +3,7 @@
#include <retrosoc/core/status.h>
#include <retrosoc/core/wait.h>
#include <retrosoc/hal/gpio.h>
#include <retrosoc/hal/lcd.h>
#include <retrosoc/hal/spisd.h>
#include <retrosoc/hal/timer.h>
@@ -24,17 +25,12 @@ unsigned long long __umoddi3(unsigned long long dividend, unsigned long long div
static uint8_t storage[32];
static uint32_t image_call_count;
void putch(char ch)
{
void putch(char ch) {
(void)ch;
}
void lcd_fill_image(uint16_t x_start,
uint16_t y_start,
uint16_t x_end,
uint16_t y_end,
uint32_t *data)
{
void lcd_fill_image(uint16_t x_start, uint16_t y_start, uint16_t x_end, uint16_t y_end,
uint32_t *data) {
(void)x_start;
(void)y_start;
(void)x_end;
@@ -43,8 +39,7 @@ void lcd_fill_image(uint16_t x_start,
++image_call_count;
}
rs_status_t rs_spisd_read_bytes(void *buffer, size_t byte_count, uintptr_t address)
{
rs_status_t rs_spisd_read_bytes(void *buffer, size_t byte_count, uintptr_t address) {
size_t offset;
if ((buffer == NULL) || (address < TEST_STORAGE_ADDRESS)) {
@@ -60,11 +55,8 @@ rs_status_t rs_spisd_read_bytes(void *buffer, size_t byte_count, uintptr_t addre
return RS_OK;
}
static rs_status_t test_reader_read(void *context,
uint32_t offset,
void *buffer,
size_t byte_count)
{
static rs_status_t test_reader_read(void *context, uint32_t offset, void *buffer,
size_t byte_count) {
const uint8_t *bytes = context;
if ((bytes == NULL) || (buffer == NULL)) {
@@ -76,8 +68,7 @@ static rs_status_t test_reader_read(void *context,
return RS_OK;
}
static int test_string_helpers(void)
{
static int test_string_helpers(void) {
char buffer[8] = {'x', '\0'};
int32_t value;
@@ -87,12 +78,10 @@ static int test_string_helpers(void)
if (strncmp("left", "right", 0U) != 0) {
return 2;
}
if (rs_strlcpy(buffer, "abcdefghi", sizeof(buffer)) != 9U ||
strcmp(buffer, "abcdefg") != 0) {
if (rs_strlcpy(buffer, "abcdefghi", sizeof(buffer)) != 9U || strcmp(buffer, "abcdefg") != 0) {
return 3;
}
if (rs_strlcat(buffer, "x", sizeof(buffer)) != 8U ||
strcmp(buffer, "abcdefg") != 0) {
if (rs_strlcat(buffer, "x", sizeof(buffer)) != 8U || strcmp(buffer, "abcdefg") != 0) {
return 4;
}
if (rs_strtoi32("-2147483648", &value) != RS_OK || value != INT32_MIN ||
@@ -103,12 +92,11 @@ static int test_string_helpers(void)
return 0;
}
static int test_formatter(void)
{
static int test_formatter(void) {
char buffer[32];
if (rs_snprintf(buffer, sizeof(buffer), "%#08x:%lld:%zu", 0x2AU, -42LL,
(size_t)7U) != 14 || strcmp(buffer, "0x00002a:-42:7") != 0) {
if (rs_snprintf(buffer, sizeof(buffer), "%#08x:%lld:%zu", 0x2AU, -42LL, (size_t)7U) != 14 ||
strcmp(buffer, "0x00002a:-42:7") != 0) {
return 1;
}
if (rs_snprintf(buffer, 4U, "abcdef") != 6 || strcmp(buffer, "abc") != 0) {
@@ -124,29 +112,25 @@ static int test_formatter(void)
return 0;
}
static int test_compiler_helpers(void)
{
static int test_compiler_helpers(void) {
if ((__clzsi2(0U) != 32) || (__clzsi2(1U) != 31) || (__ffssi2(0U) != 0) ||
(__ffssi2(1U) != 1) || (__ffssi2(0x10U) != 5)) {
return 1;
}
if ((__udivdi3(UINT64_MAX, 3U) != 6148914691236517205ULL) ||
(__umoddi3(UINT64_MAX, 3U) != 0U) ||
(__udivdi3(UINT64_MAX, UINT64_MAX) != 1U) ||
(__umoddi3(UINT64_MAX, 3U) != 0U) || (__udivdi3(UINT64_MAX, UINT64_MAX) != 1U) ||
(__umoddi3(100U, 7U) != 2U) || (__divdi3(-100LL, 7LL) != -14LL)) {
return 2;
}
return 0;
}
static int test_wait_helper(void)
{
static int test_wait_helper(void) {
volatile uint32_t value = 0x30U;
if (rs_wait_mask(&value, 0x30U, 0x30U, 1U) != RS_OK ||
rs_wait_mask(&value, 0x40U, 0x40U, 1U) != RS_ETIMEOUT ||
rs_wait_mask(NULL, 1U, 1U, 1U) != RS_EINVAL ||
rs_wait_value(&value, 0x30U, 1U) != RS_OK ||
rs_wait_mask(NULL, 1U, 1U, 1U) != RS_EINVAL || rs_wait_value(&value, 0x30U, 1U) != RS_OK ||
rs_wait_not_value(&value, 0U, 1U) != RS_OK ||
rs_wait_not_value(&value, 0x30U, 1U) != RS_ETIMEOUT) {
return 1;
@@ -154,8 +138,7 @@ static int test_wait_helper(void)
return 0;
}
static int test_ws2812_helpers(void)
{
static int test_ws2812_helpers(void) {
const rs_ws2812_config_t config = {
72000000U,
RS_WS2812_DEFAULT_BIT_PERIOD_NS,
@@ -168,9 +151,9 @@ static int test_ws2812_helpers(void)
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)) {
(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;
@@ -187,16 +170,15 @@ static int test_ws2812_helpers(void)
return 0;
}
static int test_timer_helpers(void)
{
static int test_timer_helpers(void) {
rs_timer_period_t period;
if ((rs_timer_period_from_ms(72000000U, 1U, &period) != RS_OK) ||
(period.prescale != 0U) || (period.load != 71999U)) {
if ((rs_timer_period_from_ms(72000000U, 1U, &period) != RS_OK) || (period.prescale != 0U) ||
(period.load != 71999U)) {
return 1;
}
if ((rs_timer_period_from_ms(72000000U, 1000U, &period) != RS_OK) ||
(period.prescale != 0U) || (period.load != 71999999U)) {
if ((rs_timer_period_from_ms(72000000U, 1000U, &period) != RS_OK) || (period.prescale != 0U) ||
(period.load != 71999999U)) {
return 2;
}
if ((rs_timer_period_from_ms(0U, 1U, &period) != RS_EINVAL) ||
@@ -208,13 +190,32 @@ static int test_timer_helpers(void)
return 0;
}
static int test_wav_parser(void)
{
static int test_gpio_helpers(void) {
rs_gpio_filter_timing_t timing;
if ((rs_gpio_filter_timing_from_us(72000000U, 2U, 3U, &timing) != RS_OK) ||
(timing.divider != 143U) || (timing.stable_samples != 3U)) {
return 1;
}
if ((rs_gpio_filter_timing_from_us(1U, 1U, 1U, &timing) != RS_OK) || (timing.divider != 0U)) {
return 2;
}
if ((rs_gpio_filter_timing_from_us(0U, 1U, 1U, &timing) != RS_EINVAL) ||
(rs_gpio_filter_timing_from_us(72000000U, 0U, 1U, &timing) != RS_EINVAL) ||
(rs_gpio_filter_timing_from_us(72000000U, 2U, 0U, &timing) != RS_EINVAL) ||
(rs_gpio_filter_timing_from_us(72000000U, 2U, 16U, &timing) != RS_EINVAL) ||
(rs_gpio_filter_timing_from_us(72000000U, 1000U, 3U, &timing) != RS_EINVAL) ||
(rs_gpio_filter_timing_from_us(72000000U, 2U, 3U, NULL) != RS_EINVAL)) {
return 3;
}
return 0;
}
static int test_wav_parser(void) {
static const uint8_t wav[] = {
'R', 'I', 'F', 'F', 40U, 0U, 0U, 0U, 'W', 'A', 'V', 'E',
'f', 'm', 't', ' ', 16U, 0U, 0U, 0U, 1U, 0U, 1U, 0U,
0x40U, 0x1FU, 0U, 0U, 0x80U, 0x3EU, 0U, 0U, 2U, 0U, 16U, 0U,
'd', 'a', 't', 'a', 4U, 0U, 0U, 0U, 0U, 1U, 2U, 3U,
'R', 'I', 'F', 'F', 40U, 0U, 0U, 0U, 'W', 'A', 'V', 'E', 'f', 'm', 't', ' ',
16U, 0U, 0U, 0U, 1U, 0U, 1U, 0U, 0x40U, 0x1FU, 0U, 0U, 0x80U, 0x3EU, 0U, 0U,
2U, 0U, 16U, 0U, 'd', 'a', 't', 'a', 4U, 0U, 0U, 0U, 0U, 1U, 2U, 3U,
};
rs_wav_info_t info;
const rs_wav_reader_t reader = {test_reader_read, (void *)wav, sizeof(wav)};
@@ -223,15 +224,13 @@ static int test_wav_parser(void)
info.block_align != 2U || info.data_offset != 44U || info.data_size != 4U) {
return 1;
}
if (rs_wav_parse(&(rs_wav_reader_t){test_reader_read, (void *)wav, 12U}, &info) !=
RS_EFORMAT) {
if (rs_wav_parse(&(rs_wav_reader_t){test_reader_read, (void *)wav, 12U}, &info) != RS_EFORMAT) {
return 2;
}
return 0;
}
static int test_video_parser(void)
{
static int test_video_parser(void) {
rs_video_info_t info;
(void)memset(storage, 0, sizeof(storage));
@@ -249,17 +248,11 @@ static int test_video_parser(void)
return 0;
}
int main(void)
{
int main(void) {
const int results[] = {
test_string_helpers(),
test_formatter(),
test_compiler_helpers(),
test_wait_helper(),
test_ws2812_helpers(),
test_timer_helpers(),
test_wav_parser(),
test_video_parser(),
test_string_helpers(), test_formatter(), test_compiler_helpers(),
test_wait_helper(), test_ws2812_helpers(), test_timer_helpers(),
test_gpio_helpers(), test_wav_parser(), test_video_parser(),
};
for (size_t index = 0U; index < (sizeof(results) / sizeof(results[0])); ++index) {
+174
View File
@@ -0,0 +1,174 @@
`timescale 1ns / 1ps
`include "gpio_define.svh"
module gpio_tb;
localparam int PIN_NUM = 4;
localparam logic [31:0] USER_BASE = 32'h1000_0000;
localparam logic [31:0] ADMIN_BASE = 32'h1001_4000;
logic clk_i = 1'b0;
logic rst_n_i = 1'b0;
ribp_if ribp ();
gpio_if #(PIN_NUM) gpio ();
user_gpio_if #(PIN_NUM) user_gpio ();
logic [31:0] read_data;
always #5 clk_i = ~clk_i;
ribp_gpio #(
.PIN_NUM (PIN_NUM),
.USER_BASE_ADDR (USER_BASE),
.ADMIN_BASE_ADDR(ADMIN_BASE),
.HAS_INPUT_CMOS (1'b1),
.HAS_PULL_UP (1'b1),
.HAS_PULL_DOWN (1'b1)
) dut (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.ribp (ribp),
.gpio (gpio),
.user_gpio(user_gpio)
);
task automatic write_register(input logic [31:0] address, input logic [31:0] data,
input logic expected_error);
begin
@(negedge clk_i);
ribp.addr = address;
ribp.wdata = data;
ribp.wstrb = 4'hF;
ribp.valid = 1'b1;
@(posedge clk_i);
#1;
if (!ribp.ready || (ribp.resp_err !== expected_error)) begin
$fatal(1, "write response mismatch at %08x", address);
end
@(negedge clk_i);
ribp.valid = 1'b0;
@(posedge clk_i);
#1;
end
endtask
task automatic read_register(input logic [31:0] address, output logic [31:0] data,
input logic expected_error);
begin
@(negedge clk_i);
ribp.addr = address;
ribp.wdata = '0;
ribp.wstrb = '0;
ribp.valid = 1'b1;
@(posedge clk_i);
#1;
if (!ribp.ready || (ribp.resp_err !== expected_error)) begin
$fatal(1, "read response mismatch at %08x", address);
end
data = ribp.rdata;
@(negedge clk_i);
ribp.valid = 1'b0;
@(posedge clk_i);
#1;
end
endtask
initial begin
ribp.valid = 1'b0;
ribp.addr = '0;
ribp.wdata = '0;
ribp.wstrb = '0;
gpio.di_i = '0;
gpio.alt0_do_i = '0;
gpio.alt0_oe_i = '0;
gpio.alt1_do_i = '0;
gpio.alt1_oe_i = '0;
user_gpio.do_o = '0;
user_gpio.oe_o = '0;
repeat (3) @(posedge clk_i);
rst_n_i = 1'b1;
repeat (3) @(posedge clk_i);
read_register(ADMIN_BASE + `RIBP_GPIO_CAPABILITY, read_data, 1'b0);
if (read_data !== 32'h007F_4204) $fatal(1, "capability register mismatch");
read_register(ADMIN_BASE + `RIBP_GPIO_PAD_CAPABILITY, read_data, 1'b0);
if (read_data !== 32'h0000_0007) $fatal(1, "pad capability register mismatch");
read_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_OUTPUT_ENABLE, read_data, 1'b0);
if (read_data !== 32'd0) $fatal(1, "output enable reset value mismatch");
write_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_OUT_SET, 32'h1, 1'b0);
write_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_OE_SET, 32'h1, 1'b0);
if ({gpio.oe_o[0], gpio.do_o[0]} !== 2'b11) $fatal(1, "software output failed");
write_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_OUT_TOGGLE, 32'h1, 1'b0);
if (gpio.do_o[0] !== 1'b0) $fatal(1, "atomic output toggle failed");
read_register(ADMIN_BASE + 32'h2, read_data, 1'b1);
write_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_USER_ACCESS_MASK, 32'h2, 1'b0);
write_register(USER_BASE + `RIBP_GPIO_USER_OUT_SET, 32'h6, 1'b0);
read_register(USER_BASE + `RIBP_GPIO_USER_DATA_OUT, read_data, 1'b0);
if (read_data !== 32'h2) $fatal(1, "user access mask did not isolate output data");
user_gpio.do_o[2] = 1'b1;
user_gpio.oe_o[2] = 1'b1;
@(negedge clk_i);
ribp.addr = ADMIN_BASE + `RIBP_GPIO_ADMIN_USER_SELECT;
ribp.wdata = 32'h4;
ribp.wstrb = 4'hF;
ribp.valid = 1'b1;
@(posedge clk_i);
#1;
if (gpio.oe_o[2] !== 1'b0) $fatal(1, "user handoff did not force high impedance");
@(negedge clk_i);
ribp.valid = 1'b0;
@(posedge clk_i);
#1;
if ({gpio.oe_o[2], gpio.do_o[2]} !== 2'b11) $fatal(1, "user output was not selected");
write_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_OE_SET, 32'h2, 1'b0);
write_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_OPEN_DRAIN, 32'h2, 1'b0);
write_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_OUT_SET, 32'h2, 1'b0);
if ({gpio.oe_o[1], gpio.do_o[1]} !== 2'b00) $fatal(1, "open-drain release failed");
write_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_OUT_CLEAR, 32'h2, 1'b0);
if ({gpio.oe_o[1], gpio.do_o[1]} !== 2'b10) $fatal(1, "open-drain low drive failed");
write_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_PULL_UP, 32'h8, 1'b0);
write_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_PULL_DOWN, 32'h8, 1'b1);
write_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_INTR_RISE_ENABLE, 32'h1, 1'b0);
write_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_INTR_ENABLE, 32'h1, 1'b0);
gpio.di_i[0] = 1'b1;
repeat (5) @(posedge clk_i);
read_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_INTR_STATE, read_data, 1'b0);
if ((read_data & 32'h1) == 0) $fatal(1, "rising edge interrupt was not latched");
if (!gpio.irq_o) $fatal(1, "combined GPIO interrupt was not asserted");
write_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_INTR_STATE, 32'h1, 1'b0);
if (gpio.irq_o) $fatal(1, "W1C did not clear the interrupt");
write_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_INTR_TEST, 32'h1, 1'b0);
if (!gpio.irq_o) $fatal(1, "software interrupt test failed");
write_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_INTR_STATE, 32'h1, 1'b0);
write_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_INTR_RISE_ENABLE, 32'h9, 1'b0);
write_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_INTR_HIGH_ENABLE, 32'h8, 1'b1);
write_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_FILTER_DIV, 32'h0, 1'b0);
write_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_FILTER_COUNT, 32'h4, 1'b0);
write_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_FILTER_ENABLE, 32'h8, 1'b0);
gpio.di_i[3] = 1'b1;
@(posedge clk_i);
gpio.di_i[3] = 1'b0;
repeat (5) @(posedge clk_i);
read_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_DATA_IN, read_data, 1'b0);
if ((read_data & 32'h8) != 0) $fatal(1, "input filter accepted a short pulse");
gpio.di_i[3] = 1'b1;
repeat (8) @(posedge clk_i);
read_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_DATA_IN, read_data, 1'b0);
if ((read_data & 32'h8) == 0) $fatal(1, "input filter rejected a stable input");
write_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_CONFIG_LOCK, 32'h1, 1'b0);
write_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_OE_CLEAR, 32'h1, 1'b1);
if (gpio.oe_o[0] !== 1'b1) $fatal(1, "configuration lock did not protect OE");
$display("GPIO V2 directed test passed");
$finish;
end
endmodule
-127
View File
@@ -1,127 +0,0 @@
`timescale 1ns / 1ps
module gpio_user_mux_tb;
logic clk_i = 1'b0;
logic rst_n_i = 1'b0;
ribp_if rib ();
gpio_if gpio ();
user_gpio_if user_gpio ();
logic [31:0] read_data;
always #5 clk_i = ~clk_i;
ribp_gpio dut (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.ribp (rib),
.gpio (gpio),
.user_gpio(user_gpio)
);
task automatic write_register(input logic [7:0] address, input logic [31:0] data);
begin
@(negedge clk_i);
rib.addr = {24'd0, address};
rib.wdata = data;
rib.wstrb = 4'hF;
rib.valid = 1'b1;
@(posedge clk_i);
#1;
@(negedge clk_i);
rib.valid = 1'b0;
@(posedge clk_i);
#1;
end
endtask
task automatic read_register(input logic [7:0] address, output logic [31:0] data);
begin
@(negedge clk_i);
rib.addr = {24'd0, address};
rib.wdata = '0;
rib.wstrb = '0;
rib.valid = 1'b1;
@(posedge clk_i);
#1;
data = rib.rdata;
@(negedge clk_i);
rib.valid = 1'b0;
@(posedge clk_i);
#1;
end
endtask
task automatic select_user_gpio(input logic [31:0] selection);
begin
@(negedge clk_i);
rib.addr = 32'h0000_0030;
rib.wdata = selection;
rib.wstrb = 4'hF;
rib.valid = 1'b1;
@(posedge clk_i);
#1;
if (gpio.oe_o[0] !== 1'b0) $fatal(1, "GPIO handoff did not drive high impedance");
@(negedge clk_i);
rib.valid = 1'b0;
@(posedge clk_i);
#1;
end
endtask
initial begin
rib.valid = 1'b0;
rib.addr = '0;
rib.wdata = '0;
rib.wstrb = '0;
gpio.di_i = '0;
gpio.alt0_do_i = '0;
gpio.alt0_oe_i = '0;
gpio.alt1_do_i = '0;
gpio.alt1_oe_i = '0;
user_gpio.do_o = '0;
user_gpio.oe_o = '0;
repeat (2) @(posedge clk_i);
rst_n_i = 1'b1;
repeat (2) @(posedge clk_i);
write_register(8'h00, 32'h0000_0001);
write_register(8'h10, 32'h0000_0001);
if ({gpio.oe_o[0], gpio.do_o[0]} !== 2'b11) $fatal(1, "rib software GPIO is not active");
user_gpio.do_o[0] = 1'b0;
user_gpio.oe_o[0] = 1'b1;
select_user_gpio(32'h0000_0001);
if ({gpio.oe_o[0], gpio.do_o[0]} !== 2'b10) $fatal(1, "user GPIO was not selected");
read_register(8'h30, read_data);
if (read_data !== 32'h0000_0001) $fatal(1, "USER_SEL readback is incorrect");
read_register(8'h38, read_data);
if (read_data !== 32'h0000_0001) $fatal(1, "USER_STATUS is not active after handoff");
gpio.di_i[0] = 1'b1;
#1;
if (user_gpio.di_i[0] !== 1'b1) $fatal(1, "user GPIO input path is disconnected");
write_register(8'h34, 32'h0000_0001);
read_register(8'h34, read_data);
if (read_data !== 32'h0000_0001) $fatal(1, "USER_LOCK is not write-one-set");
write_register(8'h30, '0);
if ({gpio.oe_o[0], gpio.do_o[0]} !== 2'b10) $fatal(1, "USER_LOCK did not preserve ownership");
read_register(8'h30, read_data);
if (read_data !== 32'h0000_0001) $fatal(1, "locked USER_SEL changed");
gpio.alt0_do_i[1] = 1'b1;
gpio.alt0_oe_i[1] = 1'b1;
write_register(8'h28, 32'h0000_0002);
if ({gpio.oe_o[1], gpio.do_o[1]} !== 2'b11) $fatal(1, "alternate GPIO mode regressed");
write_register(8'h04, 32'h0000_0001);
write_register(8'h08, 32'h0000_0001);
write_register(8'h0C, 32'h0000_0001);
if ({gpio.cs_o[0], gpio.pu_o[0], gpio.pd_o[0]} !== 3'b111)
$fatal(1, "rib GPIO electrical controls changed under user ownership");
$display("GPIO user ownership test passed");
$finish;
end
endmodule
+1
View File
@@ -140,6 +140,7 @@ module ribp_topology_tb;
rst_n_i = 1'b1;
expect_route(`SOC_ADDR_RIBP_GPIO_BASE, 1, 32'h1111_1111);
expect_route(`SOC_ADDR_RIBP_GPIO_ADMIN_BASE, 1, 32'h1111_1111);
expect_route(`SOC_ADDR_PSRAM_BASE, 4, 32'h4444_4444);
expect_route(`SOC_ADDR_FLASH_BASE, 9, 32'h9999_9999);
expect_route(`SOC_ADDR_RIBP_I2C1_BASE, 17, 32'h1234_5678);
+65
View File
@@ -0,0 +1,65 @@
"""Directed RTL tests for the GPIO V2 register and pad-control contract."""
from __future__ import annotations
import shutil
import subprocess
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
def test_gpio_v2_register_pad_interrupt_and_filter_contract(tmp_path: Path) -> None:
iverilog = shutil.which("iverilog")
sv2v = shutil.which("sv2v")
vvp = shutil.which("vvp")
if iverilog is None or sv2v is None or vvp is None:
return
peripheral = ROOT / "rtl/ip/ribp/peripheral"
common = ROOT / "rtl/managed/clusterip/common/rtl"
source_list = tmp_path / "gpio.fl"
source_list.write_text(
"\n".join(
[
"+define+SV_ASSRT_DISABLE",
f"+incdir+{peripheral}",
f"+incdir+{common}",
str(common / "interface/ribp_if.sv"),
str(common / "utils/register.sv"),
str(common / "clkrst/counter.sv"),
str(common / "cdc/cdc_sync.sv"),
str(common / "utils/edge_det.sv"),
str(peripheral / "gpio_if.sv"),
str(peripheral / "user_gpio_if.sv"),
str(peripheral / "gpio_core.sv"),
str(peripheral / "gpio_reg.sv"),
str(peripheral / "ribp_gpio.sv"),
str(ROOT / "tests/rtl/gpio_tb.sv"),
"",
]
),
encoding="utf-8",
)
converted = tmp_path / "gpio_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 / "gpio_tb"
subprocess.run(
[iverilog, "-g2012", "-s", "gpio_tb", "-o", str(simulation), str(converted)],
check=True,
)
result = subprocess.run([vvp, str(simulation)], text=True, capture_output=True, check=True)
assert "GPIO V2 directed test passed" in result.stdout
-58
View File
@@ -1,58 +0,0 @@
"""RTL tests for user-IP ownership of the rib GPIO pad bank."""
from __future__ import annotations
import shutil
import subprocess
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
def test_user_ip_can_own_rib_gpio_pads_with_a_safe_handoff(tmp_path: Path) -> None:
iverilog = shutil.which("iverilog")
sv2v = shutil.which("sv2v")
vvp = shutil.which("vvp")
if iverilog is None or sv2v is None or vvp is None:
return
source_list = tmp_path / "gpio_user_mux.fl"
source_list.write_text(
"\n".join(
[
"+define+SV_ASSRT_DISABLE",
f"+incdir+{ROOT / 'rtl/ip/ribp/peripheral'}",
f"+incdir+{ROOT / 'rtl/managed/clusterip/common/rtl'}",
str(ROOT / "rtl/managed/clusterip/common/rtl/interface/ribp_if.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/utils/register.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/cdc/cdc_sync.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/utils/edge_det.sv"),
str(ROOT / "rtl/ip/ribp/peripheral/gpio.sv"),
str(ROOT / "tests/rtl/gpio_user_mux_tb.sv"),
"",
]
),
encoding="utf-8",
)
converted = tmp_path / "gpio_user_mux_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 / "gpio_user_mux_tb"
subprocess.run(
[iverilog, "-g2012", "-s", "gpio_user_mux_tb", "-o", str(simulation), str(converted)],
check=True,
)
result = subprocess.run([vvp, str(simulation)], text=True, capture_output=True, check=True)
assert "GPIO user ownership test passed" in result.stdout
+13
View File
@@ -58,6 +58,8 @@ def test_generated_artifacts_share_the_capacity_baseline(tmp_path: Path) -> None
assert "RS_SOC_PSRAM_SIZE UINT32_C(0x00800000)" in header
assert "RS_SOC_SDRAM_SIZE UINT32_C(0x02000000)" in header
assert "RS_SOC_SPISD_SIZE UINT32_C(0x40000000)" in header
assert "#ifdef __ASSEMBLER__" in header
assert "#define UINT32_C(value) value" in header
assert "RS_SOC_RIBP_SDIO_BASE" not in header
assert "RS_SOC_NMI_" not in header
assert "RS_SOC_OPIPSRAM_BASE" not in header
@@ -83,6 +85,17 @@ def test_user_ip_is_always_emitted_for_the_fixed_platform(tmp_path: Path) -> Non
assert "RS_SOC_APB_USER_IP_BASE" in header
def test_bootstrap_assembly_uses_the_generated_gpio_admin_base() -> None:
for source in (ROOT / "crt/arch/riscv/startup.S", ROOT / "app/asm/hello.s"):
text = source.read_text(encoding="utf-8")
assert '#include "retrosoc/generated/memory_map.h"' in text
assert "RS_SOC_RIBP_GPIO_ADMIN_BASE + 0x34" in text
assert "RS_SOC_RIBP_GPIO_ADMIN_BASE + 0x38" in text
assert "RS_SOC_RIBP_PSRAM_BASE + 0x08" in text
assert "0x10000028" not in text
assert "0x1000002c" not in text
def test_map_validation_rejects_overlaps(tmp_path: Path) -> None:
document = json.loads(MEMORY_MAP.read_text(encoding="utf-8"))
document["regions"][1]["base"] = document["regions"][0]["base"]
+11 -8
View File
@@ -203,7 +203,7 @@ def test_formal_filelists_are_scoped_to_the_protocol_duts(tmp_path: Path) -> Non
rib2apb_filelist = tmp_path / "rib2apb.fl"
sysctrl_filelist = tmp_path / "sysctrl.fl"
pll_rcu_filelist = tmp_path / "pll_rcu.fl"
gpio_user_filelist = tmp_path / "gpio_user.fl"
gpio_filelist = tmp_path / "gpio.fl"
ws2812_filelist = tmp_path / "ws2812.fl"
clint_filelist = tmp_path / "clint.fl"
assert generate_formal_filelist("bus", bus_filelist, memory_map, topology, user_extensions)
@@ -220,7 +220,7 @@ def test_formal_filelists_are_scoped_to_the_protocol_duts(tmp_path: Path) -> Non
"pll_rcu", pll_rcu_filelist, memory_map, topology, user_extensions
)
assert generate_formal_filelist(
"gpio_user", gpio_user_filelist, memory_map, topology, user_extensions
"gpio", gpio_filelist, memory_map, topology, user_extensions
)
assert generate_formal_filelist(
"ws2812", ws2812_filelist, memory_map, topology, user_extensions
@@ -234,7 +234,7 @@ def test_formal_filelists_are_scoped_to_the_protocol_duts(tmp_path: Path) -> Non
rib2apb = parse_filelists([rib2apb_filelist], require_files=False)
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)
gpio = parse_filelists([gpio_filelist], require_files=False)
ws2812 = parse_filelists([ws2812_filelist], require_files=False)
clint = parse_filelists([clint_filelist], require_files=False)
assert "+define+SV_ASSRT_DISABLE" in bus.defines
@@ -253,9 +253,12 @@ def test_formal_filelists_are_scoped_to_the_protocol_duts(tmp_path: Path) -> Non
assert ROOT / "rtl/mini/top/rcu.sv" in pll_rcu.files
assert ROOT / "rtl/managed/clusterip/common/rtl/cdc/cdc_2phase.sv" in pll_rcu.files
assert ROOT / "rtl/managed/clusterip/common/rtl/clkrst/rst_sync.sv" in pll_rcu.files
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/peripheral/gpio_core.sv" in gpio.files
assert ROOT / "rtl/ip/ribp/peripheral/gpio_reg.sv" in gpio.files
assert ROOT / "rtl/ip/ribp/peripheral/ribp_gpio.sv" in gpio.files
assert ROOT / "rtl/ip/ribp/peripheral/user_gpio_if.sv" in gpio.files
assert ROOT / "rtl/managed/clusterip/common/rtl/cdc/cdc_sync.sv" in gpio.files
assert ROOT / "rtl/mini/formal/gpio_formal.sv" in gpio.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
@@ -306,7 +309,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", "ws2812")
proofs = ("bus", "rib2apb", "sysctrl", "pll_rcu", "gpio", "ws2812")
for proof in proofs:
directory = tmp_path / proof
directory.mkdir()
@@ -334,7 +337,7 @@ def test_formal_result_summary_requires_every_passing_step(tmp_path: Path) -> No
"--proof",
f"pll_rcu={tmp_path / 'pll_rcu'}",
"--proof",
f"gpio_user={tmp_path / 'gpio_user'}",
f"gpio={tmp_path / 'gpio'}",
"--proof",
f"ws2812={tmp_path / 'ws2812'}",
)
+6 -4
View File
@@ -97,11 +97,12 @@ def test_topology_generates_complete_rib_apb_and_gpio_bindings(tmp_path: Path) -
assert ".apb_rib(u_apb_rib_if)" in bus_fabric
assert ".rib(u_rib_if)" in bus_fabric
assert "`define SOC_IRQ_VECTOR_WIDTH 32" in irq_config
assert "`define SOC_USER_IRQ_MASK 32'h0003FFFC" in irq_config
assert "`define SOC_IRQ_RIBP_WIDTH 11" in irq_config
assert "`define SOC_USER_IRQ_MASK 32'h0007FFFC" in irq_config
assert "`define SOC_IRQ_RIBP_WIDTH 12" in irq_config
assert "`define SOC_IRQ_APB_WIDTH 7" in irq_config
assert "assign irq_o[0] = u_clint_if.software_irq_o[0];" in rib_irq
assert "assign irq_o[10] = ws2812.irq_o;" in rib_irq
assert "assign irq_o[11] = gpio.irq_o;" in rib_irq
assert "assign irq_o[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
@@ -122,7 +123,7 @@ def test_topology_preserves_default_irq_compatibility_mapping() -> None:
)
for interrupt in document["interrupts"]
]
assert mappings[:18] == [
assert mappings[:19] == [
("clint_software", "ribp", 0, 0, "u_clint_if.software_irq_o[0]"),
("clint_timer", "ribp", 1, 1, "u_clint_if.timer_irq_o[0]"),
("uart0", "ribp", 2, 2, "uart.irq_o"),
@@ -141,8 +142,9 @@ def test_topology_preserves_default_irq_compatibility_mapping() -> None:
("advanced_timer", "apb", 5, 15, "u_tmr_if.irq_o"),
("reserved", "apb", 6, 16, "1'b0"),
("ws2812", "ribp", 10, 17, "ws2812.irq_o"),
("gpio", "ribp", 11, 18, "gpio.irq_o"),
]
assert all(mapping[3] >= 18 for mapping in mappings[18:])
assert all(mapping[3] >= 19 for mapping in mappings[19:])
def test_topology_always_adds_the_user_apb_target(tmp_path: Path) -> None: