Merge branch 'dev' into feat/mini-hp-core

This commit is contained in:
maksyuki committed 2026-10-14 18:23:57 +08:00
commit 16f2a8118a
178 files changed
+19338 -1507

No files matched your search

+1 -1
View File
@@ -132,7 +132,7 @@ jobs:
- name: Upload reports and structured results
if: always()
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: ${{ inputs.suite }}-regression-${{ github.run_id }}-${{ github.run_attempt }}
path: |
@@ -110,7 +110,7 @@ jobs:
- name: Upload reports and structured results
if: always()
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: environment-${{ matrix.environment }}-ihp130-${{ github.run_id }}-${{ github.run_attempt }}
path: |
+1 -1
View File
@@ -46,7 +46,7 @@ jobs:
run: make CONFIG=configs/ci/ihp130.mk package
- name: Upload workflow artifact
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: retrosoc-${{ github.ref_name }}
path: dist/**
+14 -2
View File
@@ -56,7 +56,7 @@ JTAG_IDCODE ?= DEADBEEF
EXT_CLK_HZ ?= 72000000
AUD_CLK_HZ ?= 18432000
CLINT_TIMEBASE_HZ ?= 1000000
MGMT_CPU_CLK_HZ := $(if $(filter PRODUCT,$(MINI_MODE)),24000000,$(EXT_CLK_HZ))
MGMT_CPU_CLK_HZ := $(if $(filter TINY,$(SOC)),24000000,$(if $(filter PRODUCT,$(MINI_MODE)),24000000,$(EXT_CLK_HZ)))
# Reset-state peripheral clock: the PCLK divider in the clock/reset subsystem
# resets to passthrough on the LP root clock, so PCLK starts at the management
# CPU frequency until software programs a different divider.
@@ -84,6 +84,7 @@ SW_ISA_PROFILE ?= COMPAT
SW_OPT ?= APP
SW_LTO ?= NO
WS2812_P3_ACCEPTANCE ?= NO
TINY_P7_ACCEPTANCE ?= NO
COREMARK_MODE ?= quick
NPU_P5_ACCEPTANCE ?= NO
# Select the HP (OpenC906) JTAG target before the LP releases it; debug aid
@@ -132,6 +133,9 @@ endif
ifeq ($(WS2812_P3_ACCEPTANCE),YES)
CONFIG_KEY_VARS += WS2812_P3_ACCEPTANCE
endif
ifeq ($(TINY_P7_ACCEPTANCE),YES)
CONFIG_KEY_VARS += TINY_P7_ACCEPTANCE
endif
VARIANT_ID := $(strip $(shell $(VCS_SHELL_PYTHON) $(ROOT_PATH)/scripts/config_key.py \
--lock $(LOCK_FILE) --profile $(PROFILE_NAME) --timestamp $(BUILD_TIMESTAMP) \
$(foreach var,$(CONFIG_KEY_VARS),--value $(var)=$($(var))) | tail -n 1))
@@ -291,6 +295,12 @@ $(call validate_value,SW_ISA_PROFILE,COMPAT TINY_PERF)
$(call validate_value,SW_OPT,APP O2 O3 Os)
$(call validate_value,SW_LTO,$(VALID_BOOL))
$(call validate_value,WS2812_P3_ACCEPTANCE,$(VALID_BOOL))
$(call validate_value,TINY_P7_ACCEPTANCE,$(VALID_BOOL))
ifeq ($(TINY_P7_ACCEPTANCE),YES)
ifneq ($(SOC)/$(APP)/$(HAVE_CSR),TINY/ci_smoke/YES)
$(error TINY_P7_ACCEPTANCE requires SOC=TINY APP=ci_smoke HAVE_CSR=YES)
endif
endif
ifeq ($(WS2812_P3_ACCEPTANCE),YES)
ifneq ($(SOC)/$(APP)/$(HAVE_CSR),MINI/ci_smoke/YES)
$(error WS2812_P3_ACCEPTANCE requires SOC=MINI APP=ci_smoke HAVE_CSR=YES)
@@ -387,7 +397,7 @@ endif
ifeq ($(STA),OPENSTA)
ifeq ($(HAVE_PLL),YES)
ifneq ($(SOC)/$(PDK)/$(TINY_SAFE24_PLL_OFF),TINY/ICS55/YES)
ifneq ($(SOC)/$(PDK)/$(TINY_R2_P7),TINY/ICS55/YES)
$(error STA=OPENSTA requires a qualified PDK PLL timing profile; HAVE_PLL=YES is unsupported)
endif
endif
@@ -424,6 +434,8 @@ endif
ifeq ($(HAVE_SVA), NO)
DEF_LIST += +define+SV_ASSRT_DISABLE
else ifeq ($(SOC),TINY)
DEF_LIST += +define+HAVE_SVA
endif
ifeq ($(HAVE_HP), YES)
+4 -2
View File
@@ -138,8 +138,10 @@ points, not the complete profile or application inventory.
Tiny's [2026-10-07 target refreeze](docs/ip/tiny-soc.md#ics55-default-platform-and-final-timing-gate-2026-10-07)
selects ICS55 with `HAVE_PLL=YES` and SAFE24 startup as the default.
The profile instantiates a parked PLL; dynamic SYS switching remains R2-P7 and
physical qualification is pending. IHP130 remains explicit compatibility.
The PLL starts parked off. The [R2-P7 implementation](docs/ip/tiny-soc-r2-clock-reset.md)
adds software-controlled SYS transitions; acceptance evidence is tracked in
the Tiny ledger and physical qualification remains pending. IHP130 remains
explicit compatibility.
Locked PLL views can be acquired
with `python3 physical/pdk/setup.py --pdk ICS55 --component pll` independently
of platform implementation or timing qualification.
+6 -2
View File
@@ -176,7 +176,11 @@ configurations, and build artifact locations.
For `SOC=TINY`, `bringup` and `ci_smoke` use the MCU acceptance composition. It
boots through XPI into SRAM, checks CPU C/A behavior and bus exceptions, DMA
(including UART requests), interrupts, RTC, GPIO, UART1 and I2C error completion,
then verifies watchdog reset by completing after the next NOR boot. It uses
(including UART requests), interrupts, RTC, GPIO and I2C0 error completion.
The P6 composition additionally checks RNG qualification rejection, CRC DMA,
Crypto initialization/vectors/erasure, audio plus finite WS refill and background
DMA, and SDIO transfers/recovery with verification-board models. Missing external
devices fail boundedly; they do not hold CPU boot reset. The image then verifies
watchdog reset by completing after the next NOR boot. It uses
SYSCTRL terminal status. The small `asm/tiny_boot.S` image separately qualifies
the synthesized SRAM/pad path; it is not a replacement for the full RTL test.
+1
View File
@@ -1,3 +1,4 @@
APP_SRCS += $(ROOT_PATH)/app/apps/ci_smoke/tiny.c
APP_CFLAGS += -Os
APP_SRCS += $(ROOT_PATH)/app/apps/ci_smoke/mcu_acceptance.S
APP_SRCS += $(ROOT_PATH)/app/apps/ci_smoke/tiny_r2_p6.c
+12
View File
@@ -34,3 +34,15 @@ rs_mcu_unmapped_probe:
.option pop
ret
.size rs_mcu_unmapped_probe, .-rs_mcu_unmapped_probe
.balign 4
.global rs_mcu_execute_probe
.type rs_mcu_execute_probe,@function
rs_mcu_execute_probe:
mv t0, a0
.option push
.option arch, +zifencei
fence.i
.option pop
jr t0
.size rs_mcu_execute_probe, .-rs_mcu_execute_probe
+1
View File
@@ -6,5 +6,6 @@
uint32_t rs_mcu_compressed_probe(void);
void rs_mcu_atomic_probe(uint32_t *address);
void rs_mcu_unmapped_probe(void);
uint32_t rs_mcu_execute_probe(uint32_t address);
#endif
@@ -0,0 +1,9 @@
#ifndef RETROSOC_APP_MCU_SHARED_ACCEPTANCE_H
#define RETROSOC_APP_MCU_SHARED_ACCEPTANCE_H
#include <stdint.h>
/* Zero means every P6 acceptance case completed; other values identify failure. */
uint8_t rs_tiny_p6_acceptance(void);
#endif
+73 -36
View File
@@ -12,6 +12,7 @@
#include <retrosoc/hal/i2c.h>
#include <retrosoc/hal/onchip_sram.h>
#include <retrosoc/hal/pwm.h>
#include <retrosoc/hal/rcu.h>
#include <retrosoc/hal/rtc.h>
#include <retrosoc/hal/sysctrl.h>
#include <retrosoc/hal/timer.h>
@@ -20,12 +21,16 @@
#include <retrosoc/lib/printf.h>
#include "mcu_acceptance.h"
#include "mcu_shared_acceptance.h"
static _Alignas(64) uint32_t rs_mcu_source[80];
static _Alignas(64) uint32_t rs_mcu_destination[80];
static volatile uint32_t rs_mcu_irq_count;
static volatile uint32_t rs_mcu_exception_count;
static volatile bool rs_mcu_handler_failed;
static volatile bool rs_mcu_probes_active;
static uint8_t rs_mcu_io_code;
static volatile uint32_t rs_mcu_watchdog_cookie __attribute__((section(".noinit.rs_mcu_wdg")));
static void rs_mcu_finish(bool pass, uint8_t code) {
printf("Tiny acceptance %s code=%u\n", pass ? "passed" : "failed", code);
@@ -39,8 +44,8 @@ static void rs_mcu_finish(bool pass, uint8_t code) {
static void rs_mcu_exception(uintptr_t mcause, uintptr_t stack_pointer) {
volatile uint32_t *const frame = (volatile uint32_t *)stack_pointer;
if ((mcause != UINT32_C(2)) && (mcause != UINT32_C(5))) {
rs_mcu_handler_failed = true;
if (!rs_mcu_probes_active || ((mcause != UINT32_C(2)) && (mcause != UINT32_C(5)))) {
rs_mcu_finish(false, 11U);
}
++rs_mcu_exception_count;
/* system_irq.S saves MEPC in slot 11 of the RV32 trap frame. Both test
@@ -75,7 +80,7 @@ static bool rs_mcu_dma(void) {
rs_mcu_source[index] = UINT32_C(0xa519c300) ^ index;
rs_mcu_destination[index] = UINT32_C(0xcccccccc);
}
if ((rs_dma_configure(4U, &config) != RS_EINVAL) ||
if ((rs_dma_configure(8U, &config) != RS_EINVAL) ||
(rs_dma_configure(RS_DMA_CHANNEL_BULK, &config) != RS_OK) ||
(rs_dma_start(RS_DMA_CHANNEL_BULK) != RS_OK) ||
(rs_dma_wait(RS_DMA_CHANNEL_BULK, RS_TIMEOUT_DEFAULT) != RS_OK)) {
@@ -91,8 +96,26 @@ static bool rs_mcu_dma(void) {
return false;
}
config.kind = RS_DMA_KIND_MM_TO_STREAM;
config.request = RS_DMA_REQUEST_I2S_TX;
return rs_dma_config_validate(0U, &config) != RS_OK;
config.request = RS_DMA_REQUEST_DVP_RX;
if (rs_dma_config_validate(0U, &config) == RS_OK) {
return false;
}
/* DMA owns the destination until completion. FENCE.I is executed by the
* assembly helper before entering either the DMA-written or patched code. */
rs_mcu_source[0] = UINT32_C(0x02a00513); /* addi a0,zero,42 */
rs_mcu_source[1] = UINT32_C(0x00008067); /* jalr zero,ra,0 */
config.kind = RS_DMA_KIND_MM_TO_MM;
config.request = RS_DMA_REQUEST_SOFTWARE;
config.byte_count = UINT32_C(8);
config.burst_beats = 1U;
if ((rs_dma_configure(RS_DMA_CHANNEL_BULK, &config) != RS_OK) ||
(rs_dma_start(RS_DMA_CHANNEL_BULK) != RS_OK) ||
(rs_dma_wait(RS_DMA_CHANNEL_BULK, RS_TIMEOUT_DEFAULT) != RS_OK) ||
(rs_mcu_execute_probe((uint32_t)(uintptr_t)rs_mcu_destination) != UINT32_C(42))) {
return false;
}
rs_mcu_destination[0] = UINT32_C(0x02b00513); /* addi a0,zero,43 */
return rs_mcu_execute_probe((uint32_t)(uintptr_t)rs_mcu_destination) == UINT32_C(43);
}
static bool rs_mcu_interrupts(void) {
@@ -146,9 +169,7 @@ static bool rs_mcu_io(void) {
const uint32_t transmit[4] = {UINT32_C(0x54), UINT32_C(0x49), UINT32_C(0x4e), UINT32_C(0x59)};
uint32_t receive[4] = {0U};
rs_gpio_capabilities_t capabilities;
rs_uart_timing_t timing;
bool high;
uint32_t timeout = UINT32_C(10000);
uint8_t payload = UINT8_C(0x55);
const rs_i2c_transfer_t transfer = {
.address = UINT16_C(0x50),
@@ -157,54 +178,51 @@ static bool rs_mcu_io(void) {
};
rs_i2c_status_t status;
rs_mcu_io_code = 40U;
if ((rs_gpio_get_capabilities(&capabilities) != RS_OK) ||
((capabilities.pad_features & (RS_GPIO_PAD_CAP_PULL_UP | RS_GPIO_PAD_CAP_PULL_DOWN)) !=
((RS_SOC_GPIO_HAS_PULLS != 0U) ? (RS_GPIO_PAD_CAP_PULL_UP | RS_GPIO_PAD_CAP_PULL_DOWN)
: 0U)) ||
(rs_gpio_configure(15U, &pull_up) !=
(rs_gpio_configure(27U, &pull_up) !=
((RS_SOC_GPIO_HAS_PULLS != 0U) ? RS_OK : RS_ENOTSUP)) ||
(rs_gpio_configure(15U, &pull_down) !=
(rs_gpio_configure(27U, &pull_down) !=
((RS_SOC_GPIO_HAS_PULLS != 0U) ? RS_OK : RS_ENOTSUP)) ||
(rs_gpio_configure(15U, &output) != RS_OK) || (rs_gpio_write(15U, true) != RS_OK) ||
(rs_gpio_read(15U, &high) != RS_OK) || !high || (rs_gpio_write(15U, false) != RS_OK) ||
(rs_gpio_read(15U, &high) != RS_OK) || high) {
(rs_gpio_configure(27U, &output) != RS_OK) || (rs_gpio_write(27U, true) != RS_OK) ||
(rs_gpio_read(27U, &high) != RS_OK) || !high || (rs_gpio_write(27U, false) != RS_OK) ||
(rs_gpio_read(27U, &high) != RS_OK) || high) {
return false;
}
rs_mcu_io_code = 41U;
if ((rs_uart_configure(&loopback, RS_TIMEOUT_DEFAULT) != RS_OK) ||
(rs_uart_write_dma(transmit, 4U, RS_TIMEOUT_DEFAULT) != RS_OK) ||
(rs_uart_read_dma(receive, 4U, RS_TIMEOUT_DEFAULT) != RS_OK)) {
return false;
}
rs_mcu_io_code = 42U;
for (uint32_t index = 0U; index < 4U; ++index) {
if (transmit[index] != receive[index]) {
return false;
}
}
rs_mcu_io_code = 43U;
if ((rs_uart_init(RS_CPU_CLOCK_HZ, UART_BPS) != RS_OK) ||
(rs_uart_timing_calculate(RS_CPU_CLOCK_HZ, UART_BPS, &timing) != RS_OK)) {
return false;
}
/* UART1 uses the same register ABI; the current shared high-level UART
* API selects UART0. Exercise UART1 PIO and its internal loopback here. */
RS_SOC_REG32(RS_SOC_APB4_UART1_BASE, UINT32_C(0x00)) = timing.baud_integer;
RS_SOC_REG32(RS_SOC_APB4_UART1_BASE, UINT32_C(0x04)) = timing.baud_fraction;
RS_SOC_REG32(RS_SOC_APB4_UART1_BASE, UINT32_C(0x08)) = UINT32_C(3);
RS_SOC_REG32(RS_SOC_APB4_UART1_BASE, UINT32_C(0x0c)) = UINT32_C(7);
RS_SOC_REG32(RS_SOC_APB4_UART1_BASE, UINT32_C(0x10)) = UINT32_C(0x5a);
while (((RS_SOC_REG32(RS_SOC_APB4_UART1_BASE, UINT32_C(0x18)) & UINT32_C(0x40)) != 0U) &&
(timeout != 0U)) {
--timeout;
}
if ((timeout == 0U) || ((RS_SOC_REG32(RS_SOC_APB4_UART1_BASE, UINT32_C(0x14)) &
UINT32_C(0xff)) != UINT32_C(0x5a))) {
(rs_i2c_init(RS_I2C_BUS_1, RS_CPU_CLOCK_HZ, UINT32_C(100000)) != RS_ENOTSUP)) {
return false;
}
/* There is no slave on the pulled-up acceptance board: a real address
* transaction must terminate as NACK rather than stick waiting on the bus. */
for (uint32_t bus = 0U; bus < 2U; ++bus) {
for (uint32_t bus = 0U; bus < 1U; ++bus) {
rs_mcu_io_code = 44U;
const rs_i2c_bus_t instance = (bus == 0U) ? RS_I2C_BUS_0 : RS_I2C_BUS_1;
if ((rs_i2c_init(instance, RS_CPU_CLOCK_HZ, UINT32_C(100000)) != RS_OK) ||
(rs_i2c_transfer(instance, &transfer, RS_TIMEOUT_DEFAULT) != RS_EIO) ||
rs_status_t init_result = rs_i2c_init(instance, RS_CPU_CLOCK_HZ, UINT32_C(100000));
rs_status_t transfer_result = init_result == RS_OK
? rs_i2c_transfer(instance, &transfer, RS_TIMEOUT_DEFAULT)
: init_result;
printf("R2_P6_IO i2c_init=%d i2c_transfer=%d\n", (int)init_result, (int)transfer_result);
if (rs_i2c_get_status(instance, &status) == RS_OK) {
printf("R2_P6_IO i2c_errors=%lx\n", (unsigned long)status.errors);
}
if ((init_result != RS_OK) || (transfer_result != RS_EIO) ||
(rs_i2c_get_status(instance, &status) != RS_OK) ||
((status.errors & RS_I2C_ERROR_ADDR_NACK) == 0U)) {
return false;
@@ -235,8 +253,10 @@ int main(void) {
rs_onchip_sram_info_t memory;
rs_sysctrl_fault_status_t fault;
rs_watchdog_status_t watchdog = {0};
rs_tiny_rcu_snapshot_t reset_snapshot;
uint64_t before;
uint64_t after;
uint8_t p6_result;
const rs_watchdog_config_t watchdog_config = {
.prescale_divider = 1U,
.timeout_ticks = UINT32_C(20000),
@@ -251,12 +271,21 @@ int main(void) {
rs_mcu_finish(false, 1U);
}
printf("Hello retroSoC! Tiny MCU\n");
if (rs_watchdog_get_status(&watchdog, RS_TIMEOUT_DEFAULT) != RS_OK) {
if (rs_watchdog_get_status(&watchdog, RS_TIMEOUT_DEFAULT) != RS_OK ||
rs_tiny_rcu_get_snapshot(&reset_snapshot) != RS_OK) {
rs_mcu_finish(false, 2U);
}
if (watchdog.reset_count != 0U) {
rs_mcu_finish(true, 0U);
if ((reset_snapshot.reset_cause & UINT32_C(4)) != 0U) {
/* P7 resets WDG functional state. The AON RCU owns the retained cause;
* the SRAM cookie proves all pre-reset acceptance work completed. */
const bool reset_valid =
rs_mcu_watchdog_cookie == UINT32_C(0x57375037) && watchdog.reset_count == 0U &&
reset_snapshot.current_profile == 0U && reset_snapshot.sys_hz == UINT32_C(24000000) &&
reset_snapshot.gate_status == 0U;
rs_mcu_watchdog_cookie = 0U;
rs_mcu_finish(reset_valid, reset_valid ? 0U : 2U);
}
rs_mcu_watchdog_cookie = 0U;
if ((rs_archinfo_read(&info) != RS_OK) || (rs_archinfo_validate_build(&info) != RS_OK) ||
(info.soc_id != RS_SOC_ID) || (info.topology != UINT32_C(0x20200001)) ||
(info.technology != RS_SOC_TECHNOLOGY_ID) ||
@@ -267,11 +296,14 @@ int main(void) {
}
if ((rs_mcu_compressed_probe() != 42U) || ((__RV_CSR_READ(CSR_MISA) & UINT32_C(1)) != 0U) ||
(rs_irq_register_exception(2U, rs_mcu_exception) != RS_OK) ||
(rs_irq_register_exception(5U, rs_mcu_exception) != RS_OK)) {
(rs_irq_register_exception(5U, rs_mcu_exception) != RS_OK) ||
(rs_irq_register_exception(7U, rs_mcu_exception) != RS_OK)) {
rs_mcu_finish(false, 4U);
}
rs_mcu_probes_active = true;
rs_mcu_atomic_probe(rs_mcu_source);
rs_mcu_unmapped_probe();
rs_mcu_probes_active = false;
if ((rs_mcu_exception_count != 2U) || rs_mcu_handler_failed ||
(rs_sysctrl_get_fault_status(&fault) != RS_OK) || !fault.pending ||
(fault.address != UINT32_C(0x38000000)) || (rs_sysctrl_clear_fault() != RS_OK)) {
@@ -289,9 +321,14 @@ int main(void) {
rs_mcu_finish(false, 8U);
}
if (!rs_mcu_io()) {
rs_mcu_finish(false, 10U);
rs_mcu_finish(false, rs_mcu_io_code);
}
p6_result = rs_tiny_p6_acceptance();
if (p6_result != 0U) {
rs_mcu_finish(false, p6_result);
}
printf("Tiny CPU/DMA/IRQ/RTC/I/O passed; exercising watchdog reset\n");
rs_mcu_watchdog_cookie = UINT32_C(0x57375037);
if ((rs_watchdog_configure(&watchdog_config) != RS_OK) ||
(rs_watchdog_start(RS_TIMEOUT_DEFAULT) != RS_OK)) {
rs_mcu_finish(false, 9U);
+4
View File
@@ -1,3 +1,7 @@
APP_SRCS += $(ROOT_PATH)/app/apps/ci_smoke/tiny.c
APP_CFLAGS += -Os
APP_SRCS += $(ROOT_PATH)/app/apps/ci_smoke/mcu_acceptance.S
APP_SRCS += $(ROOT_PATH)/app/apps/ci_smoke/tiny_r2_p6.c
ifeq ($(TINY_P7_ACCEPTANCE),YES)
APP_SRCS := $(ROOT_PATH)/app/apps/ci_smoke/tiny_r2_p7.c
endif
+801
View File
@@ -0,0 +1,801 @@
#include <stddef.h>
#include <stdint.h>
#include <retrosoc/arch/riscv/system_base.h>
#include <retrosoc/core/irq.h>
#include <retrosoc/core/soc.h>
#include <retrosoc/generated/irq_metadata.h>
#include <retrosoc/hal/clint.h>
#include <retrosoc/hal/crc.h>
#include <retrosoc/hal/crypto.h>
#include <retrosoc/hal/dma.h>
#include <retrosoc/hal/gpio.h>
#include <retrosoc/hal/i2s.h>
#include <retrosoc/hal/rng.h>
#include <retrosoc/hal/sdio.h>
#include <retrosoc/hal/uart.h>
#include <retrosoc/hal/ws2812.h>
#include <retrosoc/lib/printf.h>
#include "mcu_shared_acceptance.h"
static volatile uint32_t rs_p6_irq_count;
static uint32_t rs_p6_irq_selected;
static volatile bool rs_p6_irq_error;
static _Alignas(64) uint32_t rs_p6_source[256];
static _Alignas(64) uint32_t rs_p6_destination[258];
static _Alignas(64) uint32_t rs_p6_audio_tx[768];
static _Alignas(64) uint32_t rs_p6_audio_rx[562];
static _Alignas(64) rs_dma_tcd_t rs_p6_descriptors[4];
static rs_dma_session_t rs_p6_audio_tx_owner;
static rs_dma_session_t rs_p6_audio_rx_owner;
static rs_dma_session_t rs_p6_background_owner;
static uint32_t rs_p6_pixels[65];
static void rs_p6_irq_handler(uintptr_t cause, uintptr_t stack) {
(void)cause;
(void)stack;
++rs_p6_irq_count;
if (rs_irq_disable_external(rs_p6_irq_selected) != RS_OK) {
rs_p6_irq_error = true;
}
}
static bool rs_p6_irq_arm(uint32_t irq) {
rs_p6_irq_selected = irq;
rs_p6_irq_count = 0U;
rs_p6_irq_error = false;
if ((rs_irq_register_external(irq, rs_p6_irq_handler) != RS_OK) ||
(rs_irq_enable_external(irq, rs_p6_irq_handler) != RS_OK)) {
return false;
}
__RV_CSR_SET(CSR_MSTATUS, UINT32_C(8));
return true;
}
static bool rs_p6_irq_received(void) {
uint32_t timeout = UINT32_C(100000);
while ((rs_p6_irq_count == 0U) && (timeout != 0U)) {
--timeout;
}
return (rs_p6_irq_count == 1U) && !rs_p6_irq_error;
}
static bool rs_p6_route(uint32_t pin, bool open_drain) {
const rs_gpio_config_t config = {
.mode = RS_GPIO_MODE_ALT0,
.pull = RS_GPIO_PULL_NONE,
.trigger = RS_GPIO_TRIGGER_NONE,
.open_drain = open_drain,
.filter_enable = false,
};
return rs_gpio_configure(pin, &config) == RS_OK;
}
static bool rs_p6_uart(void) {
const uint8_t transmit[4] = {0xA5U, 0x5AU, 0x96U, 0x69U};
rs_uart_rx_data_t receive[4];
const rs_uart_config_t unsupported = {
.source_clock_hz = RS_PCLK_CLOCK_HZ,
.baud_rate = UART_BPS,
.data_bits = 8U,
.stop_bits = 1U,
.parity = RS_UART_PARITY_NONE,
.rx_watermark = 1U,
.rx_timeout_bits = 16U,
.tx_enable = true,
.rx_enable = true,
.auto_cts_enable = true,
};
if ((rs_uart_configure(&unsupported, RS_TIMEOUT_DEFAULT) != RS_ENOTSUP) ||
(rs_uart_write(transmit, sizeof(transmit), RS_TIMEOUT_DEFAULT) != RS_OK) ||
(rs_uart_read(receive, 4U, RS_TIMEOUT_DEFAULT) != RS_OK)) {
return false;
}
for (uint32_t index = 0U; index < 4U; ++index) {
if (receive[index].data != transmit[index] || receive[index].errors != 0U) {
return false;
}
}
printf("\nR2_P6_UART pads=1 echo_bytes=4 flow_rejected=1\n");
return true;
}
static bool rs_p6_rng(void) {
const rs_rng_config_t config = {.fifo_watermark = 1U};
rs_rng_snapshot_t snapshot;
uint32_t word = 0U;
if ((rs_rng_init(&config) != RS_OK) ||
(rs_rng_read_entropy(&word, RS_TIMEOUT_DEFAULT) != RS_ENOTSUP) ||
(rs_rng_read_diagnostic(&word, RS_TIMEOUT_DEFAULT) != RS_OK) ||
(rs_rng_get_status(&snapshot) != RS_OK) || snapshot.source_qualified ||
!rs_p6_irq_arm(RS_SOC_EXT_IRQ_RNG) ||
(rs_rng_interrupt_enable(RS_RNG_INTERRUPT_SOURCE_FAULT) != RS_OK) ||
(rs_rng_interrupt_test(RS_RNG_INTERRUPT_SOURCE_FAULT) != RS_OK) || !rs_p6_irq_received() ||
(rs_rng_interrupt_enable(0U) != RS_OK) ||
(rs_rng_interrupt_clear(RS_RNG_INTERRUPT_ALL) != RS_OK)) {
return false;
}
printf("R2_P6_RNG unqualified=1 diagnostic=1 irq=1\n");
return true;
}
static bool rs_p6_dma_capabilities(void) {
const uint32_t capability = RS_DMA_REG(RS_DMA_REG_CAPABILITY);
rs_dma_config_t config = {
.kind = RS_DMA_KIND_MM_TO_MM,
.request = RS_DMA_REQUEST_SOFTWARE,
.source = (uintptr_t)rs_p6_source,
.destination = (uintptr_t)rs_p6_destination,
.byte_count = 4U,
.width = RS_DMA_WIDTH_32,
.source_increment = true,
.destination_increment = true,
.burst_beats = 1U,
};
rs_dma_status_t status;
const uint32_t unsupported[] = {9U, 10U, 11U, 14U, 15U};
if (((capability >> 8U) & UINT32_C(0xFF)) != 8U) {
return false;
}
for (size_t index = 0U; index < sizeof(unsupported) / sizeof(unsupported[0]); ++index) {
rs_p6_destination[0] = UINT32_C(0x12345678);
if (rs_dma_configure(RS_DMA_CHANNEL_DVP, &config) != RS_OK) {
return false;
}
/* Exercise hardware rejection independently of the SDK's earlier gate. */
RS_DMA_CH_REG(RS_DMA_CHANNEL_DVP, RS_DMA_CH_REG_REQUEST_SEL) = unsupported[index];
if ((rs_dma_start(RS_DMA_CHANNEL_DVP) != RS_OK) ||
(rs_dma_wait(RS_DMA_CHANNEL_DVP, RS_TIMEOUT_DEFAULT) != RS_EIO) ||
(rs_dma_get_status(RS_DMA_CHANNEL_DVP, &status) != RS_OK) || !status.error ||
status.busy || (status.bytes_done != 0U) ||
(rs_p6_destination[0] != UINT32_C(0x12345678))) {
return false;
}
}
printf("R2_P6_DMA channels=8 rejected_requests=5\n");
return true;
}
static uint32_t rs_p6_crc_reference(const uint8_t *data, size_t bytes) {
uint32_t value = UINT32_MAX;
for (size_t index = 0U; index < bytes; ++index) {
value ^= (uint32_t)data[index];
for (uint32_t bit = 0U; bit < 8U; ++bit) {
value = (value >> 1U) ^ (((value & 1U) != 0U) ? UINT32_C(0xEDB88320) : 0U);
}
}
return ~value;
}
static bool rs_p6_crc(void) {
rs_crc_config_t crc;
static rs_dma_session_t lease;
uint32_t result;
rs_dma_config_t transfer = {
.kind = RS_DMA_KIND_MM_TO_MM,
.request = RS_DMA_REQUEST_SOFTWARE,
.source = (uintptr_t)rs_p6_source,
.destination = RS_SOC_APB4_CRC_BASE + CRC_DATA_OFFSET,
.byte_count = UINT32_C(64),
.width = RS_DMA_WIDTH_32,
.source_increment = true,
.burst_beats = 1U,
};
for (uint32_t index = 0U; index < 256U; ++index) {
rs_p6_source[index] = UINT32_C(0x12345678) ^ (index * UINT32_C(0x01020408));
}
if ((rs_crc_get_profile(RS_CRC_PROFILE_CRC32_ISO_HDLC, &crc) != RS_OK) ||
(rs_dma_session_acquire(&lease, RS_DMA_CHANNEL_BULK) != RS_OK)) {
return false;
}
for (size_t tail = 0U; tail < 4U; ++tail) {
if ((rs_crc_init(&crc) != RS_OK) || (rs_crc_start() != RS_OK) ||
(rs_dma_session_configure(&lease, &transfer) != RS_OK) ||
(rs_dma_session_start(&lease) != RS_OK) ||
(rs_dma_wait(RS_DMA_CHANNEL_BULK, RS_TIMEOUT_DEFAULT) != RS_OK) ||
(rs_crc_update((const uint8_t *)rs_p6_source + 64U, tail) != RS_OK) ||
(rs_crc_finish(&result) != RS_OK) ||
(result != rs_p6_crc_reference((const uint8_t *)rs_p6_source, 64U + tail))) {
return false;
}
}
if ((rs_dma_session_release(&lease) != RS_OK) ||
(rs_crc_compute(&crc, rs_p6_source, 0U, &result) != RS_OK) || (result != 0U)) {
return false;
}
printf("R2_P6_CRC dma=1 tails=4 empty=1\n");
return true;
}
static bool rs_p6_crypto(void) {
static uint32_t modulus[RS_CRYPTO_RSA_WORDS];
static uint32_t base[RS_CRYPTO_RSA_WORDS];
static uint32_t exponent[RS_CRYPTO_RSA_WORDS];
static uint32_t rsa_result[RS_CRYPTO_RSA_WORDS];
static const uint8_t key[16] = {
0x00U, 0x01U, 0x02U, 0x03U, 0x04U, 0x05U, 0x06U, 0x07U,
0x08U, 0x09U, 0x0AU, 0x0BU, 0x0CU, 0x0DU, 0x0EU, 0x0FU,
};
static const uint32_t input[4] = {
UINT32_C(0x33221100),
UINT32_C(0x77665544),
UINT32_C(0xBBAA9988),
UINT32_C(0xFFEEDDCC),
};
static const uint32_t expected[4] = {
UINT32_C(0xD8E0C469),
UINT32_C(0x30047B6A),
UINT32_C(0x80B7CDD8),
UINT32_C(0x5AC5B470),
};
if ((rs_crypto_zeroize_wait(1U) != RS_ETIMEOUT) ||
(rs_crypto_zeroize_wait(RS_TIMEOUT_DEFAULT) != RS_OK) ||
(rs_crypto_init(2050U) != RS_ETIMEOUT) ||
(rs_crypto_selftest(RS_TIMEOUT_DEFAULT) != RS_OK) ||
(rs_crypto_aes_set_key(key, sizeof(key), RS_TIMEOUT_DEFAULT) != RS_OK)) {
return false;
}
for (uint32_t index = 0U; index < 16U; ++index) {
rs_p6_source[index] = input[index % 4U];
}
rs_p6_destination[0] = UINT32_C(0x5A5AA5A5);
rs_p6_destination[17] = UINT32_C(0xA5A55A5A);
if (rs_crypto_aes_crypt_dma(RS_CRYPTO_AES_ECB, false, NULL, rs_p6_source, &rs_p6_destination[1],
64U, RS_TIMEOUT_DEFAULT) != RS_OK) {
return false;
}
for (uint32_t index = 0U; index < 16U; ++index) {
if (rs_p6_destination[index + 1U] != expected[index % 4U]) {
return false;
}
}
/* A small public-operation integration vector exercises the full 2048-bit
* storage/control path. Full private vectors remain in the shared suite. */
for (uint32_t index = 0U; index < RS_CRYPTO_RSA_WORDS; ++index) {
modulus[index] = UINT32_MAX;
}
base[0] = 2U;
exponent[0] = 3U;
if ((rs_crypto_rsa_prepare(modulus, RS_TIMEOUT_DEFAULT) != RS_OK) ||
(rs_crypto_rsa_modexp(base, exponent, 2U, false, rsa_result, RS_TIMEOUT_DEFAULT) !=
RS_OK)) {
return false;
}
for (uint32_t index = 0U; index < RS_CRYPTO_RSA_WORDS; ++index) {
if (rsa_result[index] != ((index == 0U) ? 8U : 0U)) {
return false;
}
}
if ((rs_p6_destination[0] != UINT32_C(0x5A5AA5A5)) ||
(rs_p6_destination[17] != UINT32_C(0xA5A55A5A)) || !rs_p6_irq_arm(RS_SOC_EXT_IRQ_CRYPTO)) {
return false;
}
RS_CRYPTO_REG(RS_CRYPTO_REG_IRQ_STATE) = RS_CRYPTO_IRQ_ALL;
RS_CRYPTO_REG(RS_CRYPTO_REG_IRQ_ENABLE) = RS_CRYPTO_IRQ_ZEROIZED;
if ((rs_crypto_zeroize_wait(0U) != RS_ETIMEOUT) ||
(rs_crypto_zeroize_wait(RS_TIMEOUT_DEFAULT) != RS_OK) || !rs_p6_irq_received()) {
return false;
}
RS_CRYPTO_REG(RS_CRYPTO_REG_IRQ_ENABLE) = 0U;
RS_CRYPTO_REG(RS_CRYPTO_REG_IRQ_STATE) = RS_CRYPTO_IRQ_ALL;
printf("R2_P6_CRYPTO init=1 aes_sha=1 dma45=1 rsa_public=1 zeroize=1\n");
return true;
}
static bool rs_p6_stop_session(rs_dma_session_t *owner) {
rs_dma_status_t status;
uint32_t timeout = UINT32_C(100000);
if ((rs_dma_get_status(owner->channel, &status) != RS_OK) ||
(status.busy && (rs_dma_session_abort(owner) != RS_OK))) {
return false;
}
do {
if ((rs_dma_get_status(owner->channel, &status) != RS_OK) || (timeout == 0U)) {
return false;
}
--timeout;
} while (status.busy);
return rs_dma_session_release(owner) == RS_OK;
}
static uint32_t rs_p6_audio_sample(bool wide, uint32_t index) {
const uint32_t word = rs_p6_audio_rx[1U + (wide ? index : index / 2U)];
return wide ? word : ((word >> ((index % 2U) * 16U)) & UINT32_C(0xFFFF));
}
static bool rs_p6_audio_words(bool wide, uint32_t received_words) {
const uint32_t samples = wide ? received_words : received_words * 2U;
const uint32_t first = wide ? UINT32_C(0x120020) : UINT32_C(0x2020);
uint32_t offset = 0U;
/* Ignore only the bounded PHY startup preamble, never a missing payload.
* Exactly 256 stereo frames must then match consecutive independent samples. */
while ((offset < 40U) && (rs_p6_audio_sample(wide, offset) != first)) {
++offset;
}
if ((offset >= 40U) || ((offset + 512U) > samples)) {
return false;
}
for (uint32_t sample = 0U; sample < 512U; ++sample) {
if (rs_p6_audio_sample(wide, offset + sample) != (first + sample)) {
printf("R2_P6_AUDIO mismatch wide=%u sample=%lu got=%lx expected=%lx\n", wide ? 1U : 0U,
(unsigned long)sample, (unsigned long)rs_p6_audio_sample(wide, offset + sample),
(unsigned long)(first + sample));
return false;
}
}
return (rs_p6_audio_rx[0] == UINT32_C(0x5A5AA5A5)) &&
(rs_p6_audio_rx[received_words + 1U] == UINT32_C(0xA5A55A5A));
}
static bool rs_p6_audio_ws(bool wide) {
printf("R2_P6_STAGE audio_bits=%u cycles=%lu\n", wide ? 24U : 16U,
(unsigned long)__RV_CSR_READ(CSR_MCYCLE));
const uint32_t rx_words = wide ? 548U : 280U;
const rs_i2s_config_t audio = {
.stream_tx = true,
.stream_rx = true,
.preset = wide ? RS_I2S_PRESET_24B_48K : RS_I2S_PRESET_16B_48K,
.upbound = 96U,
.lowbound = 32U,
};
const rs_ws2812_config_t ws = {
.source_clock_hz = RS_PCLK_CLOCK_HZ,
.bit_period_ns = 1250U,
.t0h_ns = 350U,
.t1h_ns = 700U,
.reset_ns = 300000U,
.fifo_watermark = 8U,
};
rs_dma_config_t tx = {
.kind = RS_DMA_KIND_MM_TO_STREAM,
.request = RS_DMA_REQUEST_I2S_TX,
.source = (uintptr_t)rs_p6_audio_tx,
.byte_count = sizeof(rs_p6_audio_tx),
.width = RS_DMA_WIDTH_32,
.source_increment = true,
.priority = 3U,
.burst_beats = 16U,
};
rs_dma_config_t rx = {
.kind = RS_DMA_KIND_STREAM_TO_MM,
.request = RS_DMA_REQUEST_I2S_RX,
.destination = (uintptr_t)&rs_p6_audio_rx[1],
.byte_count = rx_words * 4U,
.width = RS_DMA_WIDTH_32,
.destination_increment = true,
.priority = 3U,
.burst_beats = 16U,
};
rs_dma_config_t background = {
.kind = RS_DMA_KIND_MM_TO_MM,
.request = RS_DMA_REQUEST_SOFTWARE,
.source = (uintptr_t)rs_p6_source,
.destination = (uintptr_t)&rs_p6_destination[1],
.byte_count = 256U,
.width = RS_DMA_WIDTH_32,
.source_increment = true,
.destination_increment = true,
.burst_beats = 16U,
};
rs_i2s_status_t audio_status;
rs_dma_status_t rx_status, bg_status;
rs_ws2812_dma_status_t ws_status;
uint64_t now, deadline, start;
uint32_t cycles, instructions;
uint32_t frame = 0U, background_jobs = 0U;
for (uint32_t pin = 12U; pin <= 17U; ++pin) {
if (!rs_p6_route(pin, false)) {
return false;
}
}
if (!rs_p6_route(26U, false) || (rs_i2s_disable() != RS_OK) ||
(rs_i2s_flush(true, true, RS_TIMEOUT_DEFAULT) != RS_OK) ||
(rs_i2s_configure(&audio) != RS_OK) || (rs_ws2812_init(&ws) != RS_OK)) {
return false;
}
printf("R2_P6_STAGE audio_host_ready=1 cycles=%lu\n", (unsigned long)__RV_CSR_READ(CSR_MCYCLE));
for (uint32_t index = 0U; index < 768U; ++index) {
rs_p6_audio_tx[index] =
wide ? UINT32_C(0x120000) + index
: ((UINT32_C(0x2001) + 2U * index) << 16U) | (UINT32_C(0x2000) + 2U * index);
}
for (uint32_t index = 0U; index < 65U; ++index) {
rs_p6_pixels[index] = index * UINT32_C(0x010203);
}
for (uint32_t index = 0U; index < 256U; ++index) {
rs_p6_source[index] = UINT32_C(0x5A123456) ^ index;
rs_p6_destination[index + 1U] = 0U;
}
rs_p6_destination[0] = UINT32_C(0x5A5AA5A5);
rs_p6_destination[257] = UINT32_C(0xA5A55A5A);
rs_p6_audio_rx[0] = UINT32_C(0x5A5AA5A5);
rs_p6_audio_rx[rx_words + 1U] = UINT32_C(0xA5A55A5A);
for (uint32_t index = 0U; index < 4U; ++index) {
rs_p6_descriptors[index] = (rs_dma_tcd_t){
.source = (uint32_t)(uintptr_t)&rs_p6_source[index * 64U],
.destination = (uint32_t)(uintptr_t)&rs_p6_destination[1U + index * 64U],
.byte_count = 256U,
.next_ptr = index == 3U ? 0U : (uint32_t)(uintptr_t)&rs_p6_descriptors[index + 1U],
.control = RS_DMA_TCD_VALID | RS_DMA_TCD_SRC_INC | RS_DMA_TCD_DST_INC |
(UINT32_C(16) << RS_DMA_TCD_BURST_SHIFT),
};
}
if (!rs_p6_irq_arm(RS_SOC_EXT_IRQ_I2S) ||
(rs_i2s_interrupt_enable(RS_I2S_INTERRUPT_TX_LOW) != RS_OK) ||
(rs_i2s_interrupt_test(RS_I2S_INTERRUPT_TX_LOW) != RS_OK) || !rs_p6_irq_received() ||
(rs_i2s_interrupt_enable(0U) != RS_OK) ||
(rs_i2s_interrupt_clear(RS_I2S_INTERRUPT_ALL) != RS_OK) ||
(rs_dma_session_acquire(&rs_p6_audio_tx_owner, RS_DMA_CHANNEL_I2S_TX) != RS_OK) ||
(rs_dma_session_acquire(&rs_p6_audio_rx_owner, RS_DMA_CHANNEL_I2S_RX) != RS_OK) ||
(rs_dma_session_acquire(&rs_p6_background_owner, RS_DMA_CHANNEL_DVP) != RS_OK) ||
(rs_dma_session_configure(&rs_p6_audio_tx_owner, &tx) != RS_OK) ||
(rs_dma_session_configure(&rs_p6_audio_rx_owner, &rx) != RS_OK) ||
(rs_dma_session_start(&rs_p6_audio_tx_owner) != RS_OK) ||
(rs_dma_session_start(&rs_p6_audio_rx_owner) != RS_OK) ||
(rs_clint_get_time(&start) != RS_OK)) {
return false;
}
deadline = start + UINT64_C(50000);
printf("R2_P6_STAGE audio_dma_started=1 cycles=%lu\n",
(unsigned long)__RV_CSR_READ(CSR_MCYCLE));
do {
if ((rs_i2s_get_status(&audio_status) != RS_OK) || (rs_clint_get_time(&now) != RS_OK) ||
(now >= deadline)) {
return false;
}
} while (audio_status.tx_level < 96U);
printf("R2_P6_STAGE audio_prefilled=1 cycles=%lu\n", (unsigned long)__RV_CSR_READ(CSR_MCYCLE));
cycles = (uint32_t)__RV_CSR_READ(CSR_MCYCLE);
instructions = (uint32_t)__RV_CSR_READ(CSR_MINSTRET);
if (!rs_p6_irq_arm(RS_SOC_EXT_IRQ_WS2812) ||
(rs_ws2812_irq_enable(RS_WS2812_INTR_FIFO_LOW | RS_WS2812_INTR_DONE) != RS_OK) ||
(rs_i2s_enable(true, true) != RS_OK) ||
(rs_ws2812_dma_begin(rs_p6_pixels, 33U, now, deadline) != RS_OK)) {
return false;
}
do {
if ((rs_clint_get_time(&now) != RS_OK) || (now >= deadline) ||
(rs_ws2812_dma_service(now) != RS_OK) || (rs_ws2812_dma_status(&ws_status) != RS_OK) ||
(rs_dma_get_status(RS_DMA_CHANNEL_I2S_RX, &rx_status) != RS_OK) ||
(rs_dma_get_status(RS_DMA_CHANNEL_DVP, &bg_status) != RS_OK) || rx_status.error ||
bg_status.error) {
return false;
}
if (!bg_status.busy && (background_jobs < 64U)) {
if (rs_dma_session_configure(&rs_p6_background_owner, &background) != RS_OK) {
return false;
}
/* This application exclusively leases channel 2; the ordinary TCD
* helper is blocking, so publish the same finite hardware chain. */
RS_DMA_CH_REG(RS_DMA_CHANNEL_DVP, RS_DMA_CH_REG_TCD_HEAD) =
wide ? (uint32_t)(uintptr_t)rs_p6_descriptors : 0U;
RS_DMA_CH_REG(RS_DMA_CHANNEL_DVP, RS_DMA_CH_REG_TCD_COUNT) = wide ? 4U : 0U;
if (rs_dma_session_start(&rs_p6_background_owner) != RS_OK) {
return false;
}
++background_jobs;
}
if (!ws_status.active && (frame == 0U)) {
if ((ws_status.result != RS_OK) ||
(rs_ws2812_dma_begin(rs_p6_pixels, 65U, now, deadline) != RS_OK)) {
return false;
}
frame = 1U;
ws_status.active = true;
}
} while (rx_status.busy || ws_status.active);
cycles = (uint32_t)__RV_CSR_READ(CSR_MCYCLE) - cycles;
instructions = (uint32_t)__RV_CSR_READ(CSR_MINSTRET) - instructions;
if ((rx_status.bytes_done != rx_words * 4U) || !rx_status.done || (ws_status.result != RS_OK) ||
(rs_p6_irq_count != 1U) || (rs_i2s_get_status(&audio_status) != RS_OK) ||
((audio_status.interrupt_state &
(RS_I2S_INTERRUPT_TX_UNDERRUN | RS_I2S_INTERRUPT_RX_OVERRUN)) != 0U) ||
!rs_p6_stop_session(&rs_p6_audio_tx_owner) || !rs_p6_stop_session(&rs_p6_audio_rx_owner) ||
(rs_i2s_disable() != RS_OK) ||
(rs_dma_wait(RS_DMA_CHANNEL_DVP, RS_TIMEOUT_DEFAULT) != RS_OK) ||
(rs_dma_session_release(&rs_p6_background_owner) != RS_OK) ||
(rs_i2s_flush(true, true, RS_TIMEOUT_DEFAULT) != RS_OK) ||
(rs_ws2812_irq_enable(0U) != RS_OK) || !rs_p6_audio_words(wide, rx_words)) {
return false;
}
for (uint32_t index = 0U; index < (wide ? 256U : 64U); ++index) {
if (rs_p6_destination[index + 1U] != rs_p6_source[index]) {
return false;
}
}
if ((rs_p6_destination[0] != UINT32_C(0x5A5AA5A5)) ||
(rs_p6_destination[257] != UINT32_C(0xA5A55A5A))) {
return false;
}
if (!wide) {
for (uint32_t index = 65U; index <= 256U; ++index) {
if (rs_p6_destination[index] != 0U) {
return false;
}
}
}
printf("R2_P6_AUDIO bits=%u frames=256 ws_frames=2 background_jobs=%lu tcd=%u elapsed_us=%lu "
"cycles=%lu instructions=%lu\n",
wide ? 24U : 16U, (unsigned long)background_jobs, wide ? 1U : 0U,
(unsigned long)(now - start), (unsigned long)cycles, (unsigned long)instructions);
return background_jobs != 0U;
}
static bool rs_p6_audio_ws_recovery(void) {
const rs_ws2812_config_t config = {
.source_clock_hz = RS_PCLK_CLOCK_HZ,
.bit_period_ns = 1250U,
.t0h_ns = 350U,
.t1h_ns = 700U,
.reset_ns = 300000U,
.fifo_watermark = 8U,
};
uint64_t now, began;
rs_i2s_status_t audio;
rs_ws2812_status_t ws;
for (uint32_t phase = 0U; phase < 2U; ++phase) {
if ((rs_gpio_write(27U, true) != RS_OK) || (rs_clint_get_time(&began) != RS_OK)) {
return false;
}
do {
if (rs_clint_get_time(&now) != RS_OK) {
return false;
}
} while ((now - began) < 10U);
if ((rs_i2s_flush(true, true, 16U) != RS_ETIMEOUT) ||
(rs_i2s_get_status(&audio) != RS_OK)) {
return false;
}
/* A timed-out command remains owned by the controller. Do not issue
* another COMMAND while BUSY: that write is correctly rejected. */
do {
if ((rs_i2s_get_status(&audio) != RS_OK) || (rs_clint_get_time(&now) != RS_OK) ||
((now - began) > 1000U)) {
return false;
}
} while ((audio.status & (RS_I2S_STATUS_TX_FLUSH_BUSY | RS_I2S_STATUS_RX_FLUSH_BUSY)) !=
0U);
if (rs_gpio_write(27U, false) != RS_OK) {
return false;
}
}
if ((rs_ws2812_init(&config) != RS_OK) || (rs_ws2812_push(0U, RS_TIMEOUT_DEFAULT) != RS_OK) ||
(rs_ws2812_start(2U) != RS_OK) || (rs_ws2812_wait(RS_TIMEOUT_DEFAULT) != RS_EIO) ||
(rs_ws2812_get_status(&ws) != RS_OK) ||
((ws.error_status & RS_WS2812_ERROR_UNDERFLOW) == 0U) ||
(rs_clint_get_time(&began) != RS_OK)) {
return false;
}
do {
if ((rs_ws2812_get_status(&ws) != RS_OK) || (rs_clint_get_time(&now) != RS_OK) ||
((now - began) > 1000U)) {
return false;
}
} while (ws.busy);
if ((rs_ws2812_init(&config) != RS_OK) || (rs_ws2812_push(0U, RS_TIMEOUT_DEFAULT) != RS_OK) ||
(rs_ws2812_start(65U) != RS_OK) || (rs_ws2812_abort(RS_TIMEOUT_DEFAULT) != RS_OK)) {
return false;
}
printf("R2_P6_RECOVERY audio_stops=2 ws_underflow=1 ws_abort=1\n");
return true;
}
static bool rs_p6_sdio_step(rs_status_t result, uint32_t step) {
rs_sdio_status_t status;
if (result == RS_OK) {
return true;
}
if (rs_sdio_status_get(RS_SDIO_INSTANCE_0, &status) == RS_OK) {
printf("R2_P6_SDIO_ERROR step=%lu result=%ld command=%lx data=%lx dma=%lx errors=%lx\n",
(unsigned long)step, (long)result, (unsigned long)status.command_status,
(unsigned long)status.data_status, (unsigned long)status.dma_status,
(unsigned long)status.error_status);
}
return false;
}
static bool rs_p6_sdio(void) {
static _Alignas(16) rs_sdio_descriptor_t descriptor;
rs_sd_memory_info_t card;
rs_sdio_clock_t clock;
rs_sdio_response_t response;
rs_dma_config_t background = {
.kind = RS_DMA_KIND_MM_TO_MM,
.request = RS_DMA_REQUEST_SOFTWARE,
.source = (uintptr_t)rs_p6_audio_tx,
.destination = (uintptr_t)&rs_p6_audio_rx[1],
.byte_count = 1024U,
.width = RS_DMA_WIDTH_32,
.source_increment = true,
.destination_increment = true,
.burst_beats = 16U,
};
rs_sdio_command_t command = {
.index = 17U,
.argument = 1U,
.response = RS_SDIO_RESPONSE_R1,
.crc_check = true,
.index_check = true,
};
const rs_sdio_data_config_t pio = {
.block_size = 512U,
.block_count = 1U,
.direction = RS_SDIO_DATA_FROM_CARD,
.dma = false,
.block_mode = true,
};
const rs_sdio_data_config_t dma_write = {
.block_size = 512U,
.block_count = 2U,
.direction = RS_SDIO_DATA_TO_CARD,
.dma = true,
.block_mode = true,
};
uint32_t id, version, capability;
rs_status_t initialized;
printf("R2_P6_STAGE sdio_begin=1\n");
for (uint32_t pin = 0U; pin < 6U; ++pin) {
if (!rs_p6_route(pin, false)) {
return false;
}
}
if ((rs_sdio_probe(RS_SDIO_INSTANCE_1, &id, &version, &capability) != RS_ENOTSUP) ||
(rs_sdio_controller_selftest(RS_SDIO_INSTANCE_0) != RS_OK)) {
return false;
}
initialized =
rs_sd_memory_initialize(RS_SDIO_INSTANCE_0, RS_CPU_CLOCK_HZ, &card, RS_TIMEOUT_DEFAULT);
printf("R2_P6_STAGE sdio_init=%ld clock=%lu width=%u rca=%u\n", (long)initialized,
(unsigned long)card.actual_clock_hz, (unsigned)card.bus_width, (unsigned)card.rca);
if ((initialized != RS_OK) || (card.actual_clock_hz != UINT32_C(12000000)) ||
!card.high_capacity || (card.rca != 1U) || (card.bus_width != RS_SDIO_BUS_WIDTH_4)) {
return false;
}
for (uint32_t index = 0U; index < 256U; ++index) {
rs_p6_source[index] = UINT32_C(0x13579BDF) ^ index;
rs_p6_destination[index + 1U] = 0U;
rs_p6_audio_tx[index] = UINT32_C(0x55AA0000) + index;
rs_p6_audio_rx[index + 1U] = ~rs_p6_audio_tx[index];
}
rs_p6_destination[0] = UINT32_C(0x5A5AA5A5);
rs_p6_destination[257] = UINT32_C(0xA5A55A5A);
command.index = 25U;
if (!rs_p6_sdio_step(rs_dma_session_acquire(&rs_p6_background_owner, RS_DMA_CHANNEL_DVP), 1U) ||
!rs_p6_sdio_step(rs_dma_session_configure(&rs_p6_background_owner, &background), 2U) ||
!rs_p6_sdio_step(
rs_sdio_command_execute(RS_SDIO_INSTANCE_0, &command, &response, RS_TIMEOUT_DEFAULT),
3U) ||
!rs_p6_sdio_step(rs_sdio_descriptor_prepare(&descriptor, (uintptr_t)rs_p6_source, 1024U,
(uintptr_t)0U, true, false),
4U) ||
!rs_p6_sdio_step(rs_sdio_dma_setup(RS_SDIO_INSTANCE_0, &descriptor, 1U, 1024U), 5U) ||
!rs_p6_sdio_step(rs_sdio_data_configure(RS_SDIO_INSTANCE_0, &dma_write), 6U) ||
!rs_p6_sdio_step(rs_sdio_data_start(RS_SDIO_INSTANCE_0), 7U) ||
!rs_p6_sdio_step(rs_dma_session_start(&rs_p6_background_owner), 8U) ||
!rs_p6_sdio_step(rs_sdio_data_wait(RS_SDIO_INSTANCE_0, RS_TIMEOUT_DEFAULT), 9U) ||
!rs_p6_sdio_step(rs_sdio_dma_wait(RS_SDIO_INSTANCE_0, RS_TIMEOUT_DEFAULT), 10U)) {
return false;
}
printf("R2_P6_STAGE sdio_write=1\n");
command.index = 12U;
command.argument = 0U;
command.response = RS_SDIO_RESPONSE_R1B;
if (!rs_p6_sdio_step(
rs_sdio_command_execute(RS_SDIO_INSTANCE_0, &command, &response, RS_TIMEOUT_DEFAULT),
11U) ||
!rs_p6_sdio_step(rs_sd_memory_read_blocks(RS_SDIO_INSTANCE_0, &card, 1U, 2U,
&rs_p6_destination[1], RS_TIMEOUT_DEFAULT),
12U) ||
!rs_p6_sdio_step(rs_dma_wait(RS_DMA_CHANNEL_DVP, RS_TIMEOUT_DEFAULT), 13U) ||
!rs_p6_sdio_step(rs_dma_session_release(&rs_p6_background_owner), 14U)) {
return false;
}
printf("R2_P6_STAGE sdio_read=1\n");
for (uint32_t index = 0U; index < 256U; ++index) {
if ((rs_p6_destination[index + 1U] != rs_p6_source[index]) ||
(rs_p6_audio_rx[index + 1U] != rs_p6_audio_tx[index])) {
return false;
}
}
if ((rs_p6_destination[0] != UINT32_C(0x5A5AA5A5)) ||
(rs_p6_destination[257] != UINT32_C(0xA5A55A5A)) ||
(rs_sdio_clock_set(RS_SDIO_INSTANCE_0, RS_CPU_CLOCK_HZ, 400000U, &clock) != RS_OK) ||
(clock.actual_hz != 400000U)) {
return false;
}
/* Select one-bit card and host consistently before the PIO read. */
command.index = 55U;
command.argument = (uint32_t)card.rca << 16U;
command.response = RS_SDIO_RESPONSE_R1;
if (rs_sdio_command_execute(RS_SDIO_INSTANCE_0, &command, &response, RS_TIMEOUT_DEFAULT) !=
RS_OK) {
return false;
}
command.index = 6U;
command.argument = 0U;
if ((rs_sdio_command_execute(RS_SDIO_INSTANCE_0, &command, &response, RS_TIMEOUT_DEFAULT) !=
RS_OK) ||
(rs_sdio_bus_width_set(RS_SDIO_INSTANCE_0, RS_SDIO_BUS_WIDTH_1) != RS_OK)) {
return false;
}
command.index = 17U;
command.argument = 1U;
if (!rs_p6_sdio_step(
rs_sdio_command_execute(RS_SDIO_INSTANCE_0, &command, &response, RS_TIMEOUT_DEFAULT),
15U) ||
!rs_p6_sdio_step(rs_sdio_data_configure(RS_SDIO_INSTANCE_0, &pio), 16U) ||
!rs_p6_sdio_step(rs_sdio_data_start(RS_SDIO_INSTANCE_0), 17U) ||
!rs_p6_sdio_step(
rs_sdio_pio_read(RS_SDIO_INSTANCE_0, &rs_p6_destination[1], 512U, RS_TIMEOUT_DEFAULT),
18U) ||
!rs_p6_sdio_step(rs_sdio_data_wait(RS_SDIO_INSTANCE_0, RS_TIMEOUT_DEFAULT), 19U)) {
return false;
}
printf("R2_P6_STAGE sdio_pio=1\n");
for (uint32_t index = 0U; index < 128U; ++index) {
if (rs_p6_destination[index + 1U] != rs_p6_source[index]) {
return false;
}
}
if ((rs_sdio_irq_clear(RS_SDIO_INSTANCE_0, RS_SDIO_IRQ_ALL) != RS_OK) ||
!rs_p6_irq_arm(RS_SOC_EXT_IRQ_SDIO) ||
(rs_sdio_irq_enable(RS_SDIO_INSTANCE_0, RS_SDIO_IRQ_ALL) != RS_OK) ||
(rs_sdio_irq_test(RS_SDIO_INSTANCE_0, 1U) != RS_OK) || !rs_p6_irq_received() ||
(rs_sdio_irq_enable(RS_SDIO_INSTANCE_0, 0U) != RS_OK) ||
(rs_sdio_irq_clear(RS_SDIO_INSTANCE_0, RS_SDIO_IRQ_ALL) != RS_OK) ||
(rs_sdio_timeouts_set(RS_SDIO_INSTANCE_0, 24000U, 24000U, 24000U) != RS_OK)) {
return false;
}
/* Verification card injects faults on these documented CMD13 arguments. */
command.index = 13U;
command.argument = UINT32_C(0xBADC0DE0);
if (rs_sdio_command_execute(RS_SDIO_INSTANCE_0, &command, &response, RS_TIMEOUT_DEFAULT) !=
RS_EFORMAT) {
return false;
}
command.argument = UINT32_C(0xBADC0DE1);
if (rs_sdio_command_execute(RS_SDIO_INSTANCE_0, &command, &response, RS_TIMEOUT_DEFAULT) !=
RS_ETIMEOUT) {
return false;
}
command.argument = (uint32_t)card.rca << 16U;
if ((rs_sdio_error_clear(RS_SDIO_INSTANCE_0, RS_SDIO_ERROR_ALL) != RS_OK) ||
(rs_sdio_command_execute(RS_SDIO_INSTANCE_0, &command, &response, RS_TIMEOUT_DEFAULT) !=
RS_OK)) {
return false;
}
printf("R2_P6_SDIO dma_bytes=2048 pio_bytes=512 width1=1 width4=1 irq=1 errors=2\n");
return true;
}
uint8_t rs_tiny_p6_acceptance(void) {
if (!rs_p6_uart()) {
return 27U;
}
if (!rs_p6_dma_capabilities()) {
return 19U;
}
if (!rs_p6_rng()) {
return 20U;
}
if (!rs_p6_crc()) {
return 21U;
}
if (!rs_p6_crypto()) {
return 22U;
}
if (!rs_p6_audio_ws(false)) {
return 23U;
}
if (!rs_p6_audio_ws(true)) {
return 24U;
}
if (!rs_p6_audio_ws_recovery()) {
return 26U;
}
if (!rs_p6_sdio()) {
return 25U;
}
return 0U;
}
+171
View File
@@ -0,0 +1,171 @@
#include <stdbool.h>
#include <stdint.h>
#include <retrosoc/core/soc.h>
#include <retrosoc/arch/riscv/system_base.h>
#include <retrosoc/hal/clint.h>
#include <retrosoc/hal/dma.h>
#include <retrosoc/hal/rcu.h>
#include <retrosoc/hal/rcu_regs.h>
#include <retrosoc/hal/sysctrl.h>
#include <retrosoc/hal/timer.h>
#include <pwm_regs.h>
/* Short P7 firmware: execute in real main SRAM and use real SYSCTRL/APB
* crossings. The full peripheral firmware remains a separate regression. */
static volatile uint32_t rs_p7_words[128];
static _Alignas(64) uint32_t rs_p7_source[1024];
static _Alignas(64) uint32_t rs_p7_destination[1024];
static volatile uint32_t rs_p7_retained __attribute__((section(".noinit.rs_p7")));
static bool rs_p7_peripheral_rate(uint32_t pclk_hz) {
const rs_timer_config_t config = {.mode = RS_TIMER_MODE_FREE_RUNNING,
.direction = RS_TIMER_DIRECTION_UP};
const volatile uint32_t *pwm_rate =
(const volatile uint32_t *)(RS_SOC_APB4_PWM_BASE + PWM_CLOCK_HZ_OFFSET);
uint32_t first, last;
uint64_t start, end;
uint64_t expected;
rs_timeout_t budget = RS_TIMEOUT_DEFAULT;
if (*pwm_rate != pclk_hz || rs_timer_configure(RS_TIMER_0, &config) != RS_OK ||
rs_timer_start(RS_TIMER_0) != RS_OK || rs_clint_get_time(&start) != RS_OK ||
rs_timer_get_value(RS_TIMER_0, &first) != RS_OK) {
return false;
}
do {
if (rs_clint_get_time(&end) != RS_OK || budget-- == 0U) {
return false;
}
} while (end - start < 200U);
if (rs_timer_get_value(RS_TIMER_0, &last) != RS_OK || rs_timer_stop(RS_TIMER_0) != RS_OK) {
return false;
}
expected = (end - start) * (pclk_hz / 1000000U);
/* Independent CLINT reference; allow the bounded APB/software read skew.
* A wrong /2 or /4 domain cannot fit this +/-25% interval. */
return (uint64_t)(last - first) > expected * 3U / 4U &&
(uint64_t)(last - first) < expected * 5U / 4U;
}
static bool rs_p7_sleep(void) {
rs_dma_session_t session;
rs_dma_config_t config = {.kind = RS_DMA_KIND_MM_TO_MM,
.request = RS_DMA_REQUEST_SOFTWARE,
.source = (uintptr_t)rs_p7_source,
.destination = (uintptr_t)rs_p7_destination,
.byte_count = sizeof(rs_p7_source),
.width = RS_DMA_WIDTH_32,
.source_increment = true,
.destination_increment = true,
.burst_beats = 16U};
uint64_t now;
uint32_t index;
__disable_irq();
for (index = 0U; index < 1024U; index++) {
rs_p7_source[index] = index ^ UINT32_C(0x13572468);
rs_p7_destination[index] = 0U;
}
if (rs_tiny_rcu_apply_gate(1U, RS_TIMEOUT_DEFAULT) != RS_OK ||
rs_dma_session_acquire(&session, RS_DMA_CHANNEL_BULK) != RS_OK ||
rs_dma_session_configure(&session, &config) != RS_OK || rs_clint_get_time(&now) != RS_OK ||
rs_clint_set_compare(0U, now + 3000U) != RS_OK || rs_dma_session_start(&session) != RS_OK) {
return false;
}
__RV_CSR_SET(CSR_MIE, MIE_MTIE);
__WFI();
__RV_CSR_CLEAR(CSR_MIE, MIE_MTIE);
if (rs_clint_set_compare(0U, UINT64_MAX) != RS_OK ||
rs_dma_wait(RS_DMA_CHANNEL_BULK, RS_TIMEOUT_DEFAULT) != RS_OK ||
rs_dma_session_release(&session) != RS_OK ||
rs_tiny_rcu_apply_gate(0U, RS_TIMEOUT_DEFAULT) != RS_OK) {
return false;
}
for (index = 0U; index < 1024U; index++) {
if (rs_p7_destination[index] != rs_p7_source[index]) {
return false;
}
}
return true;
}
static bool rs_p7_profile(rs_tiny_rcu_profile_t profile, uint32_t sys_hz, uint32_t mem_hz,
uint32_t pclk_hz) {
rs_tiny_rcu_snapshot_t snapshot;
uint32_t index;
uint64_t before, after;
if (rs_clint_get_time(&before) != RS_OK ||
rs_tiny_rcu_request_profile(profile, RS_TIMEOUT_DEFAULT) != RS_OK ||
rs_clint_get_time(&after) != RS_OK || after <= before) {
return false;
}
if (rs_tiny_rcu_get_snapshot(&snapshot) != RS_OK || snapshot.current_profile != profile ||
snapshot.sys_hz != sys_hz || snapshot.mem_hz != mem_hz || snapshot.pclk_hz != pclk_hz) {
return false;
}
for (index = 0U; index < 128U; index++) {
if (rs_p7_words[index] != (UINT32_C(0xABCDEF00) ^ index)) {
return false;
}
}
return rs_p7_peripheral_rate(pclk_hz);
}
int main(void) {
rs_tiny_rcu_snapshot_t snapshot;
uint32_t index;
uint8_t code = 0U;
if (rs_tiny_rcu_get_snapshot(&snapshot) == RS_OK && (snapshot.reset_cause & 2U) != 0U) {
code = (rs_p7_retained == UINT32_C(0x7037CAFE) && snapshot.current_profile == 0U &&
snapshot.sys_hz == 24000000U && snapshot.gate_status == 0U)
? 0U
: 11U;
rs_sysctrl_write_test_status(code == 0U, code);
for (;;) {
}
}
for (index = 0U; index < 128U; index++) {
rs_p7_words[index] = UINT32_C(0xABCDEF00) ^ index;
}
if (rs_tiny_rcu_get_snapshot(&snapshot) != RS_OK || snapshot.sys_hz != 24000000U ||
snapshot.target_capability != UINT32_C(0x3FFF)) {
code = 1U;
} else if ((snapshot.capability & UINT32_C(0xC)) == UINT32_C(0xC)) {
if (!rs_p7_profile(RS_TINY_RCU_PROFILE_PLL192, 192000000U, 96000000U, 48000000U)) {
code = 2U;
} else if (!rs_p7_profile(RS_TINY_RCU_PROFILE_PLL240, 240000000U, 120000000U, 60000000U)) {
code = 3U;
}
} else if (!rs_p7_profile(RS_TINY_RCU_PROFILE_XIN, snapshot.xin_hz, snapshot.xin_hz,
snapshot.xin_hz > 60000000U ? snapshot.xin_hz / 2U
: snapshot.xin_hz)) {
code = 4U;
} else {
/* No-PLL compatibility has no PLL profile bit. */
}
if (code == 0U && !rs_p7_profile(RS_TINY_RCU_PROFILE_SAFE24, 24000000U, 24000000U, 24000000U)) {
code = 5U;
}
if (code == 0U && rs_tiny_rcu_apply_gate(UINT32_C(1) << 4U, RS_TIMEOUT_DEFAULT) != RS_OK) {
code = 6U;
}
if (code == 0U && rs_tiny_rcu_apply_gate(0U, RS_TIMEOUT_DEFAULT) != RS_OK) {
code = 7U;
}
if (code == 0U && rs_tiny_rcu_apply_reset(UINT32_C(1) << 4U, RS_TIMEOUT_DEFAULT) != RS_OK) {
code = 8U;
}
if (code == 0U && !rs_p7_sleep()) {
code = 9U;
}
if (code == 0U) {
rs_p7_retained = UINT32_C(0x7037CAFE);
(void)rs_tiny_rcu_system_reset(RS_TIMEOUT_DEFAULT);
code = 10U;
}
rs_sysctrl_write_test_status(code == 0U, code);
for (;;) {
}
}
+12 -5
View File
@@ -78,15 +78,22 @@ for the matrix and its pending/current evidence boundary.
The [2026-10-07 Tiny refreeze](../docs/ip/tiny-soc.md#ics55-default-platform-and-final-timing-gate-2026-10-07)
sets the default to ICS55 with `HAVE_PLL=YES` and SAFE24 boot.
`ci/ics55-tiny.mk` implements that entrypoint with the PLL held off; standalone
PLL backend tests do not enable RCU/SYS switching. `make SOC=TINY` selects this
`ci/ics55-tiny.mk` boots with the PLL held off; R2-P7 adds explicit software
requests for functional PLL192/240 transitions, subject to separate acceptance.
`ci/ihp130-tiny-xin48.mk` and `ci/ihp130-tiny-xin96.mk` normalize their declared
external input to REF24 at boot and permit no-PLL XIN transitions. See the
[P7 runbook](../docs/ip/tiny-soc-r2-clock-reset.md) for the evidence boundary.
`TINY_P7_ACCEPTANCE=YES APP=ci_smoke` selects the distinct RCU acceptance image
and configuration digest; ordinary Tiny acceptance firmware remains the default.
`make SOC=TINY` selects this
committed profile, and `make SOC=TINY PDK=IHP130` selects explicit compatibility.
Do not substitute Mini's `ci/ics55.mk` for Tiny. Acceptance evidence belongs in
the Tiny ledger, and physical qualification remains pending.
`ci/ihp130-tiny.mk` selects `SOC=TINY`, one RV32IMC Hazard3 (A disabled),
128 KiB SRAM, AXI32/APB4, four DMA channels and the wired peripheral subset.
128 KiB SRAM and AXI32/APB4. The P6 candidate adds eight-channel DMA and the
frozen shared-peripheral integration; source presence is not phase acceptance.
The profile uses 24 MHz, `HAVE_HP=NO`, `MINI_MODE=NONE`, CSR-enabled RV32IM
firmware and `ld2_all_sram`. Both `bringup` and `ci_smoke` select Tiny acceptance
firmware. Icarus and Verilator use the same pin-level Flash model. Other PDKs,
wireless, external RAM and HP applications are rejected by the Tiny config gate.
firmware. Icarus and Verilator use the same pin-level Flash model. PDKs other
than IHP130/ICS55, external-RAM boot layouts and HP applications remain rejected.
+19
View File
@@ -0,0 +1,19 @@
# Functional external-input profile; physical qualification is pending.
SOC := TINY
MINI_MODE := NONE
PDK := IHP130
SIMU := VERILATOR
HAVE_PLL := NO
HAVE_SRAM_IF := YES
HAVE_SRAM_MACRO := YES
SRAM_SIZE_KIB := 128
HAVE_HP := NO
HAVE_SVA := NO
EXT_CLK_HZ := 48000000
AUD_CLK_HZ := 24000000
CLINT_TIMEBASE_HZ := 1000000
ISA := RV32IM
HAVE_CSR := YES
APP := bringup
LINK_TYPE := ld2_all_sram
RTL_TOP := retrosoc_tiny_tb
+19
View File
@@ -0,0 +1,19 @@
# Functional external-input profile; physical qualification is pending.
SOC := TINY
MINI_MODE := NONE
PDK := IHP130
SIMU := VERILATOR
HAVE_PLL := NO
HAVE_SRAM_IF := YES
HAVE_SRAM_MACRO := YES
SRAM_SIZE_KIB := 128
HAVE_HP := NO
HAVE_SVA := NO
EXT_CLK_HZ := 96000000
AUD_CLK_HZ := 24000000
CLINT_TIMEBASE_HZ := 1000000
ISA := RV32IM
HAVE_CSR := YES
APP := bringup
LINK_TYPE := ld2_all_sram
RTL_TOP := retrosoc_tiny_tb
+6 -1
View File
@@ -239,11 +239,16 @@ For the complete firmware, simulation, synthesis, and timing flow, see the
Tiny uses the same `rs_` SDK and selects its generated memory, IRQ and capability
headers through the committed profile. The generated memory-map header includes
Tiny capability metadata, so public DMA headers report four channels without
Tiny capability metadata, so public DMA headers report eight channels without
requiring an application to reproduce private compiler flags. Unavailable IP
drivers are omitted; Tiny SYSCTRL rejects unsupported lifecycle/PLL operations
before MMIO. Startup skips external-RAM initialization and loads into SRAM.
P6 selects the existing RNG/CRC/WS2812/Crypto/I2S/SDIO drivers. Crypto uses 4/5,
audio uses 6/7 and channel 3 remains exclusively leased for a WS frame.
I2C1, SDIO1, DVP and future DMA endpoints remain unsupported on Tiny. Shared
Mini defaults are unchanged; Tiny does not acquire Mini's HP channel reservation.
`<retrosoc/hal/watchdog.h>` provides `rs_watchdog_configure`, `rs_watchdog_start`,
`rs_watchdog_service`, `rs_watchdog_get_status` and `rs_watchdog_clear_reset_cause`,
with SDK status values and bounded command waits. These wrappers preserve the
+1
View File
@@ -12,6 +12,7 @@ typedef enum {
RS_EIO = -5,
RS_ENOTSUP = -6,
RS_EFORMAT = -7,
RS_EBUSY = -8,
} rs_status_t;
typedef uint32_t rs_timeout_t;
+9
View File
@@ -50,6 +50,15 @@ typedef enum {
RS_DMA_CHANNEL_BULK = 3,
RS_DMA_CHANNEL_CRYPTO_IN = 4,
RS_DMA_CHANNEL_CRYPTO_OUT = 5,
#ifdef RS_SOC_TINY
RS_DMA_CHANNEL_DVP = 2,
RS_DMA_CHANNEL_I2S_TX = 6,
RS_DMA_CHANNEL_I2S_RX = 7,
#else
RS_DMA_CHANNEL_DVP = RS_DMA_CHANNEL_BULK,
RS_DMA_CHANNEL_I2S_TX = RS_DMA_CHANNEL_BULK,
RS_DMA_CHANNEL_I2S_RX = RS_DMA_CHANNEL_BULK,
#endif
} rs_dma_channel_t;
typedef struct {
+2 -4
View File
@@ -77,13 +77,11 @@
#define RS_DMA_ERROR_READ UINT32_C(0x00000040)
#define RS_DMA_ERROR_STREAM_LAST UINT32_C(0x00000100)
#ifdef RS_SOC_TINY
#define RS_DMA_CHANNEL_COUNT 4U
#else
#define RS_DMA_CHANNEL_COUNT 8U
#endif
#ifndef RS_SOC_TINY
#define RS_DMA_CHANNEL_HP 6U
#define RS_DMA_CHANNEL_RESERVED 7U
#endif
#define RS_DMA_MAX_BURST_BEATS 16U
#define RS_DMA_DATA_WIDTH_BITS 32U
+42
View File
@@ -0,0 +1,42 @@
#ifndef RETROSOC_HAL_RCU_H
#define RETROSOC_HAL_RCU_H
#include <stdbool.h>
#include <stdint.h>
#include <retrosoc/core/status.h>
typedef enum {
RS_TINY_RCU_PROFILE_SAFE24 = 0,
RS_TINY_RCU_PROFILE_XIN = 1,
RS_TINY_RCU_PROFILE_PLL192 = 2,
RS_TINY_RCU_PROFILE_PLL240 = 3,
} rs_tiny_rcu_profile_t;
typedef struct {
uint32_t capability;
uint32_t target_capability;
uint32_t status;
uint32_t fault;
uint32_t reset_cause;
uint32_t gate_status;
uint32_t xin_hz;
uint32_t ref_hz;
uint32_t sys_hz;
uint32_t mem_hz;
uint32_t pclk_hz;
uint32_t clint_hz;
rs_tiny_rcu_profile_t current_profile;
} rs_tiny_rcu_snapshot_t;
rs_status_t rs_tiny_rcu_get_snapshot(rs_tiny_rcu_snapshot_t *snapshot);
rs_status_t rs_tiny_rcu_profile_hz(rs_tiny_rcu_profile_t profile, uint32_t *hz);
rs_status_t rs_tiny_rcu_request_profile(rs_tiny_rcu_profile_t profile, rs_timeout_t timeout);
rs_status_t rs_tiny_rcu_apply_gate(uint32_t target_mask, rs_timeout_t timeout);
rs_status_t rs_tiny_rcu_apply_reset(uint32_t target_mask, rs_timeout_t timeout);
rs_status_t rs_tiny_rcu_clear_fault(uint32_t mask);
rs_status_t rs_tiny_rcu_clear_reset_cause(uint32_t mask);
rs_status_t rs_tiny_rcu_system_reset(rs_timeout_t timeout);
rs_status_t rs_tiny_rcu_set_clkout(uint32_t source, uint16_t half_period);
#endif
+46
View File
@@ -0,0 +1,46 @@
#ifndef RETROSOC_HAL_RCU_REGS_H
#define RETROSOC_HAL_RCU_REGS_H
#include <stdint.h>
#define RS_TINY_RCU_ID_OFFSET UINT32_C(0x100)
#define RS_TINY_RCU_VERSION_OFFSET UINT32_C(0x104)
#define RS_TINY_RCU_CAPABILITY_OFFSET UINT32_C(0x108)
#define RS_TINY_RCU_CLOCK_REQUEST_OFFSET UINT32_C(0x10C)
#define RS_TINY_RCU_CLOCK_CURRENT_OFFSET UINT32_C(0x110)
#define RS_TINY_RCU_COMMAND_OFFSET UINT32_C(0x114)
#define RS_TINY_RCU_STATUS_OFFSET UINT32_C(0x118)
#define RS_TINY_RCU_TIMEOUT_OFFSET UINT32_C(0x11C)
#define RS_TINY_RCU_GATE_REQUEST_OFFSET UINT32_C(0x120)
#define RS_TINY_RCU_GATE_STATUS_OFFSET UINT32_C(0x124)
#define RS_TINY_RCU_RESET_REQUEST_OFFSET UINT32_C(0x128)
#define RS_TINY_RCU_RESET_DONE_OFFSET UINT32_C(0x12C)
#define RS_TINY_RCU_RESET_CAUSE_OFFSET UINT32_C(0x130)
#define RS_TINY_RCU_FAULT_OFFSET UINT32_C(0x134)
#define RS_TINY_RCU_IRQ_STATE_OFFSET UINT32_C(0x138)
#define RS_TINY_RCU_IRQ_ENABLE_OFFSET UINT32_C(0x13C)
#define RS_TINY_RCU_XIN_HZ_OFFSET UINT32_C(0x140)
#define RS_TINY_RCU_REF_HZ_OFFSET UINT32_C(0x144)
#define RS_TINY_RCU_SYS_HZ_OFFSET UINT32_C(0x148)
#define RS_TINY_RCU_MEM_HZ_OFFSET UINT32_C(0x14C)
#define RS_TINY_RCU_PCLK_HZ_OFFSET UINT32_C(0x150)
#define RS_TINY_RCU_CLINT_HZ_OFFSET UINT32_C(0x154)
#define RS_TINY_RCU_TARGET_CAP_OFFSET UINT32_C(0x158)
#define RS_TINY_RCU_CLKOUT_OFFSET UINT32_C(0x15C)
#define RS_TINY_RCU_COMMAND_CLOCK_APPLY UINT32_C(1U << 0)
#define RS_TINY_RCU_COMMAND_GATE_APPLY UINT32_C(1U << 1)
#define RS_TINY_RCU_COMMAND_RESET_APPLY UINT32_C(1U << 2)
#define RS_TINY_RCU_COMMAND_SYSTEM_RESET UINT32_C(1U << 3)
#define RS_TINY_RCU_STATUS_BUSY UINT32_C(1U << 0)
#define RS_TINY_RCU_STATUS_SAFE_SOURCE UINT32_C(1U << 1)
#define RS_TINY_RCU_STATUS_PLL_LOCKED UINT32_C(1U << 2)
#define RS_TINY_RCU_STATUS_PLL_SELECTED UINT32_C(1U << 3)
#define RS_TINY_RCU_STATUS_FAULT UINT32_C(1U << 4)
#define RS_TINY_RCU_CAP_PLL_PRESENT UINT32_C(1U << 8)
#define RS_TINY_RCU_CAP_TARGET_MASK UINT32_C(0x0003FFFF)
#define RS_TINY_RCU_FAULT_MASK UINT32_C(0x0000001F)
#define RS_TINY_RCU_RESET_CAUSE_MASK UINT32_C(0x0000003F)
#define RS_TINY_RCU_CSR_MSLEEP 0xBF0
#endif
+23 -7
View File
@@ -5,16 +5,25 @@
static const rs_dma_session_t *rs_dma_owners[RS_DMA_CHANNEL_COUNT];
#ifdef RS_DMA_TEST_MMIO
uint32_t rs_dma_test_irq_save(void);
void rs_dma_test_irq_restore(uint32_t saved);
#endif
static uint32_t rs_dma_lock(void) {
uint32_t saved = 0U;
#if defined(__riscv) && defined(CSR_ENABLE)
#ifdef RS_DMA_TEST_MMIO
saved = rs_dma_test_irq_save();
#elif defined(__riscv) && defined(CSR_ENABLE)
__asm__ volatile("csrrci %0, mstatus, 8" : "=r"(saved)::"memory");
#endif
return saved;
}
static void rs_dma_unlock(uint32_t saved) {
#if defined(__riscv) && defined(CSR_ENABLE)
#ifdef RS_DMA_TEST_MMIO
rs_dma_test_irq_restore(saved);
#elif defined(__riscv) && defined(CSR_ENABLE)
if ((saved & UINT32_C(8)) != 0U) {
__asm__ volatile("csrsi mstatus, 8" ::: "memory");
}
@@ -185,6 +194,7 @@ rs_status_t rs_dma_submit_tcd_chain(uint32_t channel, rs_dma_tcd_t *first, uint3
rs_status_t result;
uint32_t control;
uint32_t burst;
uint32_t saved;
result = rs_dma_tcd_validate(channel, current);
if (result != RS_OK) {
@@ -208,14 +218,20 @@ rs_status_t rs_dma_submit_tcd_chain(uint32_t channel, rs_dma_tcd_t *first, uint3
config.crc_expected = current->crc_expected;
config.priority = (uint8_t)((control >> RS_DMA_TCD_PRIORITY_SHIFT) & UINT32_C(0x3));
config.burst_beats = (uint8_t)burst;
/* Publish configuration, TCD and START before an ISR can lease the
* idle channel. Nested helpers preserve this outer interrupt mask.
* Restore interrupts before the bounded completion wait. */
saved = rs_dma_lock();
result = rs_dma_configure(channel, &config);
if (result != RS_OK) {
rs_dma_unlock(saved);
return result;
}
RS_DMA_CH_REG(channel, RS_DMA_CH_REG_TCD_HEAD) = (uint32_t)(uintptr_t)current;
RS_DMA_CH_REG(channel, RS_DMA_CH_REG_TCD_COUNT) = UINT32_C(1);
rs_dma_fence();
result = rs_dma_start(channel);
rs_dma_unlock(saved);
if (result != RS_OK) {
return result;
}
@@ -411,9 +427,9 @@ void ip_dma_test(void) {
.lowbound = 32U,
}) != RS_OK) ||
(rs_i2s_enable(true, false) != RS_OK) ||
(rs_dma_configure(RS_DMA_CHANNEL_BULK, &tx_config) != RS_OK) ||
(rs_dma_configure(RS_DMA_CHANNEL_I2S_TX, &tx_config) != RS_OK) ||
(rs_dma_start(RS_DMA_CHANNEL_BULK) != RS_OK) ||
(rs_dma_wait(RS_DMA_CHANNEL_BULK, RS_TIMEOUT_DEFAULT) != RS_OK)) {
(rs_dma_wait(RS_DMA_CHANNEL_I2S_TX, RS_TIMEOUT_DEFAULT) != RS_OK)) {
printf("dma transmit failed\n");
return;
}
@@ -432,9 +448,9 @@ void ip_dma_test(void) {
.lowbound = 32U,
}) != RS_OK) ||
(rs_i2s_enable(false, true) != RS_OK) ||
(rs_dma_configure(RS_DMA_CHANNEL_BULK, &rx_config) != RS_OK) ||
(rs_dma_start(RS_DMA_CHANNEL_BULK) != RS_OK) ||
(rs_dma_wait(RS_DMA_CHANNEL_BULK, RS_TIMEOUT_DEFAULT) != RS_OK)) {
(rs_dma_configure(RS_DMA_CHANNEL_I2S_RX, &rx_config) != RS_OK) ||
(rs_dma_start(RS_DMA_CHANNEL_I2S_RX) != RS_OK) ||
(rs_dma_wait(RS_DMA_CHANNEL_I2S_RX, RS_TIMEOUT_DEFAULT) != RS_OK)) {
printf("dma receive failed\n");
(void)rs_i2s_disable();
return;
+3 -2
View File
@@ -19,7 +19,8 @@ static bool rs_dma_address_valid(uintptr_t address, uint32_t byte_count, bool in
static bool rs_dma_request_valid(const rs_dma_config_t *config) {
#ifdef RS_SOC_TINY
if (config->kind != RS_DMA_KIND_MM_TO_MM) {
if (((uint32_t)config->request >= 16U) ||
((UINT32_C(0x31FF) & (UINT32_C(1) << (uint32_t)config->request)) == 0U)) {
return false;
}
#endif
@@ -97,7 +98,7 @@ rs_status_t rs_dma_tcd_validate(uint32_t channel, const rs_dma_tcd_t *tcd) {
request = (tcd->control >> RS_DMA_TCD_REQUEST_SHIFT) & UINT32_C(0xF);
burst = (tcd->control >> RS_DMA_TCD_BURST_SHIFT) & UINT32_C(0x1F);
#ifdef RS_SOC_TINY
if (kind != (uint32_t)RS_DMA_KIND_MM_TO_MM) {
if ((UINT32_C(0x31FF) & (UINT32_C(1) << request)) == 0U) {
return RS_ENOTSUP;
}
#endif
+74
View File
@@ -49,8 +49,12 @@
#define RS_I2C_TIMEOUT_MAX_CYCLES UINT32_C(0xFFFFFF)
static uintptr_t rs_i2c_base(rs_i2c_bus_t bus) {
#ifdef RS_SOC_TINY
return (bus == RS_I2C_BUS_0) ? (uintptr_t)RS_SOC_APB4_I2C0_BASE : (uintptr_t)0U;
#else
return (bus == RS_I2C_BUS_0) ? (uintptr_t)RS_SOC_APB4_I2C0_BASE
: (uintptr_t)RS_SOC_APB4_I2C1_BASE;
#endif
}
static bool rs_i2c_bus_valid(rs_i2c_bus_t bus) {
@@ -92,8 +96,13 @@ static rs_status_t rs_i2c_gpio_configure(rs_i2c_bus_t bus) {
.filter_enable = false,
.interrupt_enable = false,
};
#ifdef RS_SOC_TINY
const uint32_t scl_pin = UINT32_C(10);
const uint32_t sda_pin = UINT32_C(11);
#else
const uint32_t scl_pin = (bus == RS_I2C_BUS_0) ? UINT32_C(7) : UINT32_C(3);
const uint32_t sda_pin = (bus == RS_I2C_BUS_0) ? UINT32_C(8) : UINT32_C(4);
#endif
rs_status_t status = rs_gpio_configure(scl_pin, &pin_config);
if (status == RS_OK) {
@@ -228,6 +237,11 @@ static rs_status_t rs_i2c_wait_done(rs_i2c_bus_t bus, rs_timeout_t timeout) {
rs_status_t rs_i2c_configure(rs_i2c_bus_t bus, const rs_i2c_config_t *config,
rs_timeout_t timeout) {
#ifdef RS_SOC_TINY
if (bus == RS_I2C_BUS_1) {
return RS_ENOTSUP;
}
#endif
rs_i2c_timing_t timing;
uint32_t stretch_timeout;
uint32_t bus_idle_timeout;
@@ -287,6 +301,11 @@ rs_status_t rs_i2c_configure(rs_i2c_bus_t bus, const rs_i2c_config_t *config,
}
rs_status_t rs_i2c_init(rs_i2c_bus_t bus, uint32_t source_clock_hz, uint32_t bus_hz) {
#ifdef RS_SOC_TINY
if (bus == RS_I2C_BUS_1) {
return RS_ENOTSUP;
}
#endif
const rs_i2c_config_t config = {
.source_clock_hz = source_clock_hz,
.bus_hz = bus_hz,
@@ -303,6 +322,11 @@ rs_status_t rs_i2c_init(rs_i2c_bus_t bus, uint32_t source_clock_hz, uint32_t bus
rs_status_t rs_i2c_transfer(rs_i2c_bus_t bus, const rs_i2c_transfer_t *transfer,
rs_timeout_t timeout) {
#ifdef RS_SOC_TINY
if (bus == RS_I2C_BUS_1) {
return RS_ENOTSUP;
}
#endif
if (transfer == NULL) {
return RS_EINVAL;
}
@@ -338,6 +362,11 @@ static rs_status_t rs_i2c_register_prefix(const rs_i2c_register_access_t *access
rs_status_t rs_i2c_register_write(rs_i2c_bus_t bus, const rs_i2c_register_access_t *access,
const uint8_t *data, size_t length, rs_timeout_t timeout) {
#ifdef RS_SOC_TINY
if (bus == RS_I2C_BUS_1) {
return RS_ENOTSUP;
}
#endif
uint8_t prefix[2];
size_t prefix_length;
rs_status_t status;
@@ -355,6 +384,11 @@ rs_status_t rs_i2c_register_write(rs_i2c_bus_t bus, const rs_i2c_register_access
rs_status_t rs_i2c_register_read(rs_i2c_bus_t bus, const rs_i2c_register_access_t *access,
uint8_t *data, size_t length, rs_timeout_t timeout) {
#ifdef RS_SOC_TINY
if (bus == RS_I2C_BUS_1) {
return RS_ENOTSUP;
}
#endif
uint8_t prefix[2];
size_t prefix_length;
rs_status_t status;
@@ -371,6 +405,11 @@ rs_status_t rs_i2c_register_read(rs_i2c_bus_t bus, const rs_i2c_register_access_
}
rs_status_t rs_i2c_abort(rs_i2c_bus_t bus, rs_timeout_t timeout) {
#ifdef RS_SOC_TINY
if (bus == RS_I2C_BUS_1) {
return RS_ENOTSUP;
}
#endif
if (!rs_i2c_bus_valid(bus)) {
return RS_EINVAL;
}
@@ -382,6 +421,11 @@ rs_status_t rs_i2c_abort(rs_i2c_bus_t bus, rs_timeout_t timeout) {
}
rs_status_t rs_i2c_recover(rs_i2c_bus_t bus, rs_timeout_t timeout) {
#ifdef RS_SOC_TINY
if (bus == RS_I2C_BUS_1) {
return RS_ENOTSUP;
}
#endif
rs_status_t status;
if (!rs_i2c_bus_valid(bus)) {
@@ -408,6 +452,11 @@ rs_status_t rs_i2c_recover(rs_i2c_bus_t bus, rs_timeout_t timeout) {
}
rs_status_t rs_i2c_get_status(rs_i2c_bus_t bus, rs_i2c_status_t *status) {
#ifdef RS_SOC_TINY
if (bus == RS_I2C_BUS_1) {
return RS_ENOTSUP;
}
#endif
uint32_t levels;
if (!rs_i2c_bus_valid(bus) || (status == NULL)) {
@@ -423,6 +472,11 @@ rs_status_t rs_i2c_get_status(rs_i2c_bus_t bus, rs_i2c_status_t *status) {
}
rs_status_t rs_i2c_irq_enable(rs_i2c_bus_t bus, uint32_t mask) {
#ifdef RS_SOC_TINY
if (bus == RS_I2C_BUS_1) {
return RS_ENOTSUP;
}
#endif
if (!rs_i2c_bus_valid(bus) || ((mask & ~RS_I2C_INTR_ALL) != 0U)) {
return RS_EINVAL;
}
@@ -431,6 +485,11 @@ rs_status_t rs_i2c_irq_enable(rs_i2c_bus_t bus, uint32_t mask) {
}
rs_status_t rs_i2c_irq_ack(rs_i2c_bus_t bus, uint32_t mask) {
#ifdef RS_SOC_TINY
if (bus == RS_I2C_BUS_1) {
return RS_ENOTSUP;
}
#endif
if (!rs_i2c_bus_valid(bus) || (mask == 0U) || ((mask & ~RS_I2C_INTR_ALL) != 0U)) {
return RS_EINVAL;
}
@@ -439,6 +498,11 @@ rs_status_t rs_i2c_irq_ack(rs_i2c_bus_t bus, uint32_t mask) {
}
rs_status_t rs_i2c_irq_test(rs_i2c_bus_t bus, uint32_t mask) {
#ifdef RS_SOC_TINY
if (bus == RS_I2C_BUS_1) {
return RS_ENOTSUP;
}
#endif
if (!rs_i2c_bus_valid(bus) || (mask == 0U) || ((mask & ~RS_I2C_INTR_ALL) != 0U)) {
return RS_EINVAL;
}
@@ -449,6 +513,11 @@ rs_status_t rs_i2c_irq_test(rs_i2c_bus_t bus, uint32_t mask) {
rs_status_t rs_i2c_write_dma(rs_i2c_bus_t bus, uint16_t address, bool ten_bit_address,
const uint8_t *data, size_t length, rs_i2c_dma_workspace_t *workspace,
rs_timeout_t timeout) {
#ifdef RS_SOC_TINY
if (bus == RS_I2C_BUS_1) {
return RS_ENOTSUP;
}
#endif
rs_status_t status;
size_t index;
const uint32_t channel = (bus == RS_I2C_BUS_0) ? RS_DMA_CHANNEL_I2C0 : RS_DMA_CHANNEL_I2C1;
@@ -494,6 +563,11 @@ rs_status_t rs_i2c_write_dma(rs_i2c_bus_t bus, uint16_t address, bool ten_bit_ad
rs_status_t rs_i2c_read_dma(rs_i2c_bus_t bus, uint16_t address, bool ten_bit_address, uint8_t *data,
size_t length, rs_i2c_dma_workspace_t *workspace,
rs_timeout_t timeout) {
#ifdef RS_SOC_TINY
if (bus == RS_I2C_BUS_1) {
return RS_ENOTSUP;
}
#endif
rs_status_t status;
size_t index;
const uint32_t channel = (bus == RS_I2C_BUS_0) ? RS_DMA_CHANNEL_I2C0 : RS_DMA_CHANNEL_I2C1;
+261
View File
@@ -0,0 +1,261 @@
#include <stddef.h>
#include <retrosoc/core/soc.h>
#include <retrosoc/hal/rcu.h>
#include <retrosoc/hal/rcu_regs.h>
#if defined(__riscv) && defined(RS_SOC_TINY)
#include <retrosoc/arch/riscv/system_base.h>
#endif
static volatile uint32_t *rs_tiny_rcu_register(uint32_t offset) {
#if defined(RS_TINY_RCU_TEST_MMIO)
extern volatile uint32_t rs_tiny_rcu_test_mmio[1024];
return &rs_tiny_rcu_test_mmio[offset / 4U];
#else
return (volatile uint32_t *)(RS_SOC_APB4_SYSCTRL_BASE + (uintptr_t)offset);
#endif
}
static rs_status_t rs_tiny_rcu_fault_status(void) {
const uint32_t fault = *rs_tiny_rcu_register(RS_TINY_RCU_FAULT_OFFSET);
if ((fault & UINT32_C(0x09)) != 0U) {
return RS_EIO;
}
return (fault != 0U) ? RS_ETIMEOUT : RS_OK;
}
static rs_status_t rs_tiny_rcu_prepare(rs_timeout_t timeout) {
if (timeout == 0U) {
return RS_EINVAL;
}
if ((*rs_tiny_rcu_register(RS_TINY_RCU_STATUS_OFFSET) & RS_TINY_RCU_STATUS_BUSY) != 0U) {
return RS_EBUSY;
}
/* The caller owns clearing sticky faults. Do not start an operation whose
* result could be confused with a previous failure. */
return rs_tiny_rcu_fault_status();
}
static rs_status_t rs_tiny_rcu_wait(rs_timeout_t timeout, uint32_t *status) {
uint32_t value = 0U;
while (timeout-- != 0U) {
value = *rs_tiny_rcu_register(RS_TINY_RCU_STATUS_OFFSET);
if ((value & RS_TINY_RCU_STATUS_BUSY) == 0U) {
if (status != NULL) {
*status = value;
}
return RS_OK;
}
}
return RS_ETIMEOUT;
}
rs_status_t rs_tiny_rcu_get_snapshot(rs_tiny_rcu_snapshot_t *snapshot) {
uint32_t initial_profile;
if (snapshot == NULL) {
return RS_EINVAL;
}
if ((*rs_tiny_rcu_register(RS_TINY_RCU_STATUS_OFFSET) & RS_TINY_RCU_STATUS_BUSY) != 0U) {
return RS_EBUSY;
}
initial_profile = *rs_tiny_rcu_register(RS_TINY_RCU_CLOCK_CURRENT_OFFSET);
snapshot->capability = *rs_tiny_rcu_register(RS_TINY_RCU_CAPABILITY_OFFSET);
snapshot->target_capability = *rs_tiny_rcu_register(RS_TINY_RCU_TARGET_CAP_OFFSET);
snapshot->status = *rs_tiny_rcu_register(RS_TINY_RCU_STATUS_OFFSET);
snapshot->fault = *rs_tiny_rcu_register(RS_TINY_RCU_FAULT_OFFSET);
snapshot->reset_cause = *rs_tiny_rcu_register(RS_TINY_RCU_RESET_CAUSE_OFFSET);
snapshot->gate_status = *rs_tiny_rcu_register(RS_TINY_RCU_GATE_STATUS_OFFSET);
snapshot->xin_hz = *rs_tiny_rcu_register(RS_TINY_RCU_XIN_HZ_OFFSET);
snapshot->ref_hz = *rs_tiny_rcu_register(RS_TINY_RCU_REF_HZ_OFFSET);
snapshot->sys_hz = *rs_tiny_rcu_register(RS_TINY_RCU_SYS_HZ_OFFSET);
snapshot->mem_hz = *rs_tiny_rcu_register(RS_TINY_RCU_MEM_HZ_OFFSET);
snapshot->pclk_hz = *rs_tiny_rcu_register(RS_TINY_RCU_PCLK_HZ_OFFSET);
snapshot->clint_hz = *rs_tiny_rcu_register(RS_TINY_RCU_CLINT_HZ_OFFSET);
snapshot->current_profile =
(rs_tiny_rcu_profile_t)(*rs_tiny_rcu_register(RS_TINY_RCU_CLOCK_CURRENT_OFFSET) &
UINT32_C(3));
if (((*rs_tiny_rcu_register(RS_TINY_RCU_STATUS_OFFSET) & RS_TINY_RCU_STATUS_BUSY) != 0U) ||
((uint32_t)snapshot->current_profile != initial_profile)) {
return RS_EBUSY;
}
return RS_OK;
}
rs_status_t rs_tiny_rcu_profile_hz(rs_tiny_rcu_profile_t profile, uint32_t *hz) {
if (hz == NULL) {
return RS_EINVAL;
}
switch (profile) {
case RS_TINY_RCU_PROFILE_SAFE24:
*hz = UINT32_C(24000000);
break;
case RS_TINY_RCU_PROFILE_XIN:
*hz = *rs_tiny_rcu_register(RS_TINY_RCU_XIN_HZ_OFFSET);
break;
case RS_TINY_RCU_PROFILE_PLL192:
*hz = UINT32_C(192000000);
break;
case RS_TINY_RCU_PROFILE_PLL240:
*hz = UINT32_C(240000000);
break;
default:
return RS_EINVAL;
}
return RS_OK;
}
rs_status_t rs_tiny_rcu_request_profile(rs_tiny_rcu_profile_t profile, rs_timeout_t timeout) {
uint32_t capability;
uint32_t status;
uint32_t hz;
rs_status_t result;
if (timeout == 0U || rs_tiny_rcu_profile_hz(profile, &hz) != RS_OK) {
return RS_EINVAL;
}
(void)hz;
capability = *rs_tiny_rcu_register(RS_TINY_RCU_CAPABILITY_OFFSET);
if ((capability & (UINT32_C(1) << (uint32_t)profile)) == 0U) {
return RS_ENOTSUP;
}
result = rs_tiny_rcu_prepare(timeout);
if (result != RS_OK) {
return result;
}
*rs_tiny_rcu_register(RS_TINY_RCU_CLOCK_REQUEST_OFFSET) = (uint32_t)profile;
*rs_tiny_rcu_register(RS_TINY_RCU_COMMAND_OFFSET) = RS_TINY_RCU_COMMAND_CLOCK_APPLY;
if (rs_tiny_rcu_wait(timeout, &status) != RS_OK) {
return RS_ETIMEOUT;
}
if ((status & RS_TINY_RCU_STATUS_FAULT) != 0U) {
return rs_tiny_rcu_fault_status();
}
if (*rs_tiny_rcu_register(RS_TINY_RCU_CLOCK_CURRENT_OFFSET) != (uint32_t)profile) {
return RS_EIO;
}
return RS_OK;
}
rs_status_t rs_tiny_rcu_apply_gate(uint32_t target_mask, rs_timeout_t timeout) {
uint32_t status;
rs_status_t result;
if (timeout == 0U || (target_mask & ~RS_TINY_RCU_CAP_TARGET_MASK) != 0U) {
return RS_EINVAL;
}
if ((target_mask & ~(*rs_tiny_rcu_register(RS_TINY_RCU_TARGET_CAP_OFFSET))) != 0U) {
return RS_ENOTSUP;
}
result = rs_tiny_rcu_prepare(timeout);
if (result != RS_OK) {
return result;
}
*rs_tiny_rcu_register(RS_TINY_RCU_GATE_REQUEST_OFFSET) = target_mask;
*rs_tiny_rcu_register(RS_TINY_RCU_COMMAND_OFFSET) = RS_TINY_RCU_COMMAND_GATE_APPLY;
if (rs_tiny_rcu_wait(timeout, &status) != RS_OK) {
return RS_ETIMEOUT;
}
result = rs_tiny_rcu_fault_status();
if (result != RS_OK) {
return result;
}
#if defined(__riscv) && defined(RS_SOC_TINY)
/* Hazard3's existing sleep permission complements the RCU leaf gate.
* Keep power-down clear: the fabric, SRAM and DMA remain available. */
if ((target_mask & 1U) != 0U) {
__RV_CSR_CLEAR(RS_TINY_RCU_CSR_MSLEEP, 2U);
__RV_CSR_SET(RS_TINY_RCU_CSR_MSLEEP, 1U);
} else {
__RV_CSR_CLEAR(RS_TINY_RCU_CSR_MSLEEP, 1U);
}
#endif
return RS_OK;
}
rs_status_t rs_tiny_rcu_apply_reset(uint32_t target_mask, rs_timeout_t timeout) {
uint32_t status;
rs_status_t result;
if (timeout == 0U || target_mask == 0U || (target_mask & ~RS_TINY_RCU_CAP_TARGET_MASK) != 0U ||
(target_mask & 1U) != 0U) {
return RS_EINVAL;
}
if ((target_mask & ~(*rs_tiny_rcu_register(RS_TINY_RCU_TARGET_CAP_OFFSET))) != 0U) {
return RS_ENOTSUP;
}
result = rs_tiny_rcu_prepare(timeout);
if (result != RS_OK) {
return result;
}
if ((*rs_tiny_rcu_register(RS_TINY_RCU_GATE_STATUS_OFFSET) & target_mask) != 0U) {
return RS_EBUSY;
}
*rs_tiny_rcu_register(RS_TINY_RCU_RESET_REQUEST_OFFSET) = target_mask;
*rs_tiny_rcu_register(RS_TINY_RCU_COMMAND_OFFSET) = RS_TINY_RCU_COMMAND_RESET_APPLY;
if (rs_tiny_rcu_wait(timeout, &status) != RS_OK) {
return RS_ETIMEOUT;
}
return rs_tiny_rcu_fault_status();
}
rs_status_t rs_tiny_rcu_clear_fault(uint32_t mask) {
if ((mask & ~RS_TINY_RCU_FAULT_MASK) != 0U) {
return RS_EINVAL;
}
*rs_tiny_rcu_register(RS_TINY_RCU_FAULT_OFFSET) = mask;
return RS_OK;
}
rs_status_t rs_tiny_rcu_clear_reset_cause(uint32_t mask) {
if ((mask & ~RS_TINY_RCU_RESET_CAUSE_MASK) != 0U) {
return RS_EINVAL;
}
*rs_tiny_rcu_register(RS_TINY_RCU_RESET_CAUSE_OFFSET) = mask;
return RS_OK;
}
rs_status_t rs_tiny_rcu_system_reset(rs_timeout_t timeout) {
uint32_t status;
if (timeout == 0U) {
return RS_EINVAL;
}
status = *rs_tiny_rcu_register(RS_TINY_RCU_STATUS_OFFSET);
if ((status & RS_TINY_RCU_STATUS_BUSY) != 0U) {
return RS_EBUSY;
}
*rs_tiny_rcu_register(RS_TINY_RCU_COMMAND_OFFSET) = RS_TINY_RCU_COMMAND_SYSTEM_RESET;
/* Success restarts execution at the reset vector. Returning means failure. */
while (timeout-- != 0U) {
status = *rs_tiny_rcu_register(RS_TINY_RCU_STATUS_OFFSET);
if ((status & RS_TINY_RCU_STATUS_FAULT) != 0U) {
return RS_EIO;
}
}
return RS_ETIMEOUT;
}
rs_status_t rs_tiny_rcu_set_clkout(uint32_t source, uint16_t half_period) {
rs_tiny_rcu_snapshot_t snapshot;
rs_status_t result;
uint32_t hz;
if (source > 4U || (source != 0U && half_period == 0U)) {
return RS_EINVAL;
}
result = rs_tiny_rcu_get_snapshot(&snapshot);
if (result != RS_OK) {
return result;
}
hz = source == 2U ? snapshot.sys_hz
: source == 3U ? snapshot.mem_hz
: source == 4U ? snapshot.pclk_hz
: snapshot.ref_hz;
if (source != 0U && (uint64_t)hz > (UINT64_C(48000000) * (uint64_t)half_period)) {
return RS_EINVAL;
}
if (source != 0U && (*rs_tiny_rcu_register(RS_TINY_RCU_CLKOUT_OFFSET) & 7U) != 0U) {
return RS_EBUSY;
}
*rs_tiny_rcu_register(RS_TINY_RCU_CLKOUT_OFFSET) = source | ((uint32_t)half_period << 8U);
return (*rs_tiny_rcu_register(RS_TINY_RCU_FAULT_OFFSET) != 0U) ? RS_EIO : RS_OK;
}
+215 -5
View File
@@ -53,6 +53,12 @@
RS_SD_R1_CARD_ECC_FAILED | RS_SD_R1_CC_ERROR | RS_SD_R1_ERROR | RS_SD_R1_CSD_OVERWRITE | \
RS_SD_R1_WP_ERASE_SKIP)
/* Preserve protocol ceilings while supporting slower product input clocks. */
static uint32_t rs_sdio_transfer_clock(uint32_t source_hz, uint32_t ceiling_hz) {
const uint32_t maximum_hz = source_hz / UINT32_C(2);
return (maximum_hz < ceiling_hz) ? maximum_hz : ceiling_hz;
}
static bool rs_sdio_instance_valid(rs_sdio_instance_t instance) {
return (instance == RS_SDIO_INSTANCE_0) || (instance == RS_SDIO_INSTANCE_1);
}
@@ -61,8 +67,10 @@ static uintptr_t rs_sdio_base(rs_sdio_instance_t instance) {
switch (instance) {
case RS_SDIO_INSTANCE_0:
return (uintptr_t)RS_SOC_APB4_SDIO0_BASE;
#ifndef RS_SOC_TINY
case RS_SDIO_INSTANCE_1:
return (uintptr_t)RS_SOC_APB4_SDIO1_BASE;
#endif
default:
return (uintptr_t)0U;
}
@@ -257,6 +265,11 @@ static rs_status_t rs_sdio_send_app_command(rs_sdio_instance_t instance, uint16_
rs_status_t rs_sdio_probe(rs_sdio_instance_t instance, uint32_t *ip_id, uint32_t *version,
uint32_t *capability) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
if (!rs_sdio_instance_valid(instance) || (ip_id == NULL) || (version == NULL) ||
(capability == NULL) || (rs_sdio_read(instance, RS_SDIO_REG_IP_ID, ip_id) != RS_OK) ||
(rs_sdio_read(instance, RS_SDIO_REG_IP_VERSION, version) != RS_OK) ||
@@ -270,6 +283,11 @@ rs_status_t rs_sdio_probe(rs_sdio_instance_t instance, uint32_t *ip_id, uint32_t
}
rs_status_t rs_sdio_reset(rs_sdio_instance_t instance, rs_timeout_t timeout) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
volatile uint32_t *status_reg;
if (!rs_sdio_instance_valid(instance)) {
@@ -294,6 +312,11 @@ rs_status_t rs_sdio_reset(rs_sdio_instance_t instance, rs_timeout_t timeout) {
rs_status_t rs_sdio_clock_set(rs_sdio_instance_t instance, uint32_t source_clock_hz,
uint32_t target_clock_hz, rs_sdio_clock_t *clock) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
rs_sdio_clock_t calculated;
uint32_t value;
uint32_t enabled;
@@ -318,6 +341,11 @@ rs_status_t rs_sdio_clock_set(rs_sdio_instance_t instance, uint32_t source_clock
}
rs_status_t rs_sdio_bus_width_set(rs_sdio_instance_t instance, rs_sdio_bus_width_t width) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
uint32_t value;
if (!rs_sdio_instance_valid(instance) || rs_sdio_busy(instance)) {
@@ -338,6 +366,11 @@ rs_status_t rs_sdio_bus_width_set(rs_sdio_instance_t instance, rs_sdio_bus_width
rs_status_t rs_sdio_timeouts_set(rs_sdio_instance_t instance, uint32_t command_timeout,
uint32_t data_timeout, uint32_t busy_timeout) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
if (!rs_sdio_instance_valid(instance) || rs_sdio_busy(instance) || (command_timeout == 0U) ||
(data_timeout == 0U) || (busy_timeout == 0U)) {
return RS_EINVAL;
@@ -351,6 +384,11 @@ rs_status_t rs_sdio_timeouts_set(rs_sdio_instance_t instance, uint32_t command_t
}
rs_status_t rs_sdio_enable(rs_sdio_instance_t instance, bool enable) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
uint32_t value;
if (!rs_sdio_instance_valid(instance) ||
@@ -366,6 +404,11 @@ rs_status_t rs_sdio_enable(rs_sdio_instance_t instance, bool enable) {
}
rs_status_t rs_sdio_configure(rs_sdio_instance_t instance, const rs_sdio_config_t *config) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
rs_sdio_clock_t clock;
rs_status_t status;
@@ -393,6 +436,11 @@ rs_status_t rs_sdio_configure(rs_sdio_instance_t instance, const rs_sdio_config_
}
rs_status_t rs_sdio_status_get(rs_sdio_instance_t instance, rs_sdio_status_t *status) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
uint32_t present;
if (!rs_sdio_instance_valid(instance) || (status == NULL)) {
@@ -421,6 +469,11 @@ rs_status_t rs_sdio_status_get(rs_sdio_instance_t instance, rs_sdio_status_t *st
}
rs_status_t rs_sdio_command_start(rs_sdio_instance_t instance, const rs_sdio_command_t *command) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
uint32_t cfg;
if (!rs_sdio_instance_valid(instance) || (command == NULL) || (command->index > 63U) ||
@@ -449,6 +502,11 @@ rs_status_t rs_sdio_command_start(rs_sdio_instance_t instance, const rs_sdio_com
}
rs_status_t rs_sdio_response_get(rs_sdio_instance_t instance, rs_sdio_response_t *response) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
if (!rs_sdio_instance_valid(instance) || (response == NULL) ||
(rs_sdio_read(instance, RS_SDIO_REG_RESP0, &response->words[0]) != RS_OK) ||
(rs_sdio_read(instance, RS_SDIO_REG_RESP1, &response->words[1]) != RS_OK) ||
@@ -462,6 +520,11 @@ rs_status_t rs_sdio_response_get(rs_sdio_instance_t instance, rs_sdio_response_t
rs_status_t rs_sdio_command_wait(rs_sdio_instance_t instance, rs_sdio_response_t *response,
rs_timeout_t timeout) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
rs_status_t status;
if (!rs_sdio_instance_valid(instance) || (response == NULL)) {
@@ -485,6 +548,11 @@ rs_status_t rs_sdio_command_wait(rs_sdio_instance_t instance, rs_sdio_response_t
rs_status_t rs_sdio_command_execute(rs_sdio_instance_t instance, const rs_sdio_command_t *command,
rs_sdio_response_t *response, rs_timeout_t timeout) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
rs_status_t status;
status = rs_sdio_command_start(instance, command);
@@ -500,6 +568,11 @@ rs_status_t rs_sdio_command_execute(rs_sdio_instance_t instance, const rs_sdio_c
rs_status_t rs_sdio_data_configure(rs_sdio_instance_t instance,
const rs_sdio_data_config_t *config) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
uint32_t value;
if (!rs_sdio_instance_valid(instance) || (config == NULL) || (config->block_size == 0U) ||
@@ -532,6 +605,11 @@ rs_status_t rs_sdio_data_configure(rs_sdio_instance_t instance,
}
rs_status_t rs_sdio_data_start(rs_sdio_instance_t instance) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
if (!rs_sdio_instance_valid(instance) || rs_sdio_busy(instance) ||
(rs_sdio_irq_clear(instance, RS_SDIO_IRQ_DATA_DONE | RS_SDIO_IRQ_DATA_ERROR) != RS_OK) ||
(rs_sdio_error_clear(instance, RS_SDIO_ERROR_DATA_CRC | RS_SDIO_ERROR_DATA_TIMEOUT |
@@ -582,6 +660,11 @@ static rs_status_t rs_sdio_pio_store(rs_sdio_instance_t instance, const uint8_t
rs_status_t rs_sdio_pio_write(rs_sdio_instance_t instance, const void *buffer, size_t byte_count,
rs_timeout_t timeout) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
const uint8_t *bytes = (const uint8_t *)buffer;
rs_status_t status;
size_t offset = 0U;
@@ -612,6 +695,11 @@ rs_status_t rs_sdio_pio_write(rs_sdio_instance_t instance, const void *buffer, s
rs_status_t rs_sdio_pio_read(rs_sdio_instance_t instance, void *buffer, size_t byte_count,
rs_timeout_t timeout) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
uint8_t *bytes = (uint8_t *)buffer;
size_t offset = 0U;
@@ -645,6 +733,11 @@ rs_status_t rs_sdio_pio_read(rs_sdio_instance_t instance, void *buffer, size_t b
}
rs_status_t rs_sdio_data_wait(rs_sdio_instance_t instance, rs_timeout_t timeout) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
rs_status_t status;
if (!rs_sdio_instance_valid(instance)) {
@@ -664,6 +757,11 @@ rs_status_t rs_sdio_data_wait(rs_sdio_instance_t instance, rs_timeout_t timeout)
rs_status_t rs_sdio_dma_setup(rs_sdio_instance_t instance, rs_sdio_descriptor_t *descriptors,
uint16_t count, uint32_t total_bytes) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
uintptr_t address;
if (!rs_sdio_instance_valid(instance) || (total_bytes == 0U) ||
@@ -686,6 +784,11 @@ rs_status_t rs_sdio_dma_setup(rs_sdio_instance_t instance, rs_sdio_descriptor_t
}
rs_status_t rs_sdio_dma_start(rs_sdio_instance_t instance) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
/*
* DATA_START is the coordinated DMA/data-engine launch. Keep this
* compatibility entry point as an alias so software cannot start a DMA
@@ -695,6 +798,11 @@ rs_status_t rs_sdio_dma_start(rs_sdio_instance_t instance) {
}
rs_status_t rs_sdio_dma_abort(rs_sdio_instance_t instance) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
if (!rs_sdio_instance_valid(instance)) {
return RS_EINVAL;
}
@@ -702,6 +810,11 @@ rs_status_t rs_sdio_dma_abort(rs_sdio_instance_t instance) {
}
rs_status_t rs_sdio_dma_status_get(rs_sdio_instance_t instance, rs_sdio_dma_status_t *status) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
uint32_t dma_error;
if (!rs_sdio_instance_valid(instance) || (status == NULL) ||
@@ -720,6 +833,11 @@ rs_status_t rs_sdio_dma_status_get(rs_sdio_instance_t instance, rs_sdio_dma_stat
}
rs_status_t rs_sdio_dma_wait(rs_sdio_instance_t instance, rs_timeout_t timeout) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
rs_status_t status;
if (!rs_sdio_instance_valid(instance)) {
@@ -738,6 +856,11 @@ rs_status_t rs_sdio_dma_wait(rs_sdio_instance_t instance, rs_timeout_t timeout)
}
rs_status_t rs_sdio_irq_enable(rs_sdio_instance_t instance, uint32_t mask) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
if (!rs_sdio_instance_valid(instance) || ((mask & ~RS_SDIO_IRQ_ALL) != 0U)) {
return RS_EINVAL;
}
@@ -745,6 +868,11 @@ rs_status_t rs_sdio_irq_enable(rs_sdio_instance_t instance, uint32_t mask) {
}
rs_status_t rs_sdio_irq_pending(rs_sdio_instance_t instance, uint32_t *mask) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
if (!rs_sdio_instance_valid(instance) || (mask == NULL)) {
return RS_EINVAL;
}
@@ -752,6 +880,11 @@ rs_status_t rs_sdio_irq_pending(rs_sdio_instance_t instance, uint32_t *mask) {
}
rs_status_t rs_sdio_irq_clear(rs_sdio_instance_t instance, uint32_t mask) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
if (!rs_sdio_instance_valid(instance) || ((mask & ~RS_SDIO_IRQ_ALL) != 0U)) {
return RS_EINVAL;
}
@@ -759,6 +892,11 @@ rs_status_t rs_sdio_irq_clear(rs_sdio_instance_t instance, uint32_t mask) {
}
rs_status_t rs_sdio_irq_test(rs_sdio_instance_t instance, uint32_t mask) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
if (!rs_sdio_instance_valid(instance) || ((mask & ~RS_SDIO_IRQ_ALL) != 0U)) {
return RS_EINVAL;
}
@@ -766,6 +904,11 @@ rs_status_t rs_sdio_irq_test(rs_sdio_instance_t instance, uint32_t mask) {
}
rs_status_t rs_sdio_error_clear(rs_sdio_instance_t instance, uint32_t mask) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
if (!rs_sdio_instance_valid(instance) || ((mask & ~RS_SDIO_ERROR_ALL) != 0U)) {
return RS_EINVAL;
}
@@ -773,6 +916,11 @@ rs_status_t rs_sdio_error_clear(rs_sdio_instance_t instance, uint32_t mask) {
}
rs_status_t rs_sdio_controller_selftest(rs_sdio_instance_t instance) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
rs_sdio_status_t status;
uint32_t ip_id;
uint32_t version;
@@ -916,7 +1064,9 @@ static rs_status_t rs_sd_memory_try_high_speed(rs_sdio_instance_t instance,
return RS_EIO;
}
if (rs_sdio_clock_set(instance, source_clock_hz,
(source_clock_hz >= 100000000U) ? 50000000U : 36000000U,
rs_sdio_transfer_clock(source_clock_hz, (source_clock_hz >= 100000000U)
? 50000000U
: 36000000U),
&clock) == RS_OK) {
info->actual_clock_hz = clock.actual_hz;
info->high_speed = true;
@@ -928,6 +1078,11 @@ static rs_status_t rs_sd_memory_try_high_speed(rs_sdio_instance_t instance,
rs_status_t rs_sd_memory_initialize(rs_sdio_instance_t instance, uint32_t source_clock_hz,
rs_sd_memory_info_t *info, rs_timeout_t timeout) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
rs_sdio_response_t response;
rs_sdio_clock_t clock;
uint32_t payload;
@@ -1008,7 +1163,9 @@ rs_status_t rs_sd_memory_initialize(rs_sdio_instance_t instance, uint32_t source
if (status != RS_OK) {
return status;
}
if ((rs_sdio_response_field(&response, 31U, 16U, &payload) != RS_OK) ||
/* RESP retains the complete 48-bit frame: the R6 payload is [39:8],
* so its upper sixteen RCA bits are [39:24], above the status/CRC. */
if ((rs_sdio_response_field(&response, 39U, 24U, &payload) != RS_OK) ||
(payload > UINT16_MAX)) {
return RS_EFORMAT;
}
@@ -1046,7 +1203,8 @@ rs_status_t rs_sd_memory_initialize(rs_sdio_instance_t instance, uint32_t source
info->bus_width_fallback = true;
info->bus_width = RS_SDIO_BUS_WIDTH_1;
}
status = rs_sdio_clock_set(instance, source_clock_hz, 36000000U, &clock);
status = rs_sdio_clock_set(instance, source_clock_hz,
rs_sdio_transfer_clock(source_clock_hz, 36000000U), &clock);
if (status != RS_OK) {
return status;
}
@@ -1150,12 +1308,22 @@ static rs_status_t rs_sd_memory_transfer(rs_sdio_instance_t instance,
rs_status_t rs_sd_memory_read_blocks(rs_sdio_instance_t instance, const rs_sd_memory_info_t *info,
uint32_t block, uint32_t count, void *buffer,
rs_timeout_t timeout) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
return rs_sd_memory_transfer(instance, info, block, count, buffer, false, timeout);
}
rs_status_t rs_sd_memory_write_blocks(rs_sdio_instance_t instance, const rs_sd_memory_info_t *info,
uint32_t block, uint32_t count, const void *buffer,
rs_timeout_t timeout) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
return rs_sd_memory_transfer(instance, info, block, count, (void *)buffer, true, timeout);
}
@@ -1175,6 +1343,11 @@ static rs_status_t rs_sdio_function_command(rs_sdio_instance_t instance, uint8_t
rs_status_t rs_sdio_function_cmd52_read(rs_sdio_instance_t instance, uint8_t function,
uint32_t address, uint8_t *data, rs_timeout_t timeout) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
rs_sdio_cmd52_t command = {
.function = function,
.address = address,
@@ -1203,6 +1376,11 @@ rs_status_t rs_sdio_function_cmd52_read(rs_sdio_instance_t instance, uint8_t fun
rs_status_t rs_sdio_function_cmd52_write(rs_sdio_instance_t instance, uint8_t function,
uint32_t address, uint8_t data, rs_timeout_t timeout) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
const rs_sdio_cmd52_t command = {
.function = function,
.address = address,
@@ -1223,6 +1401,11 @@ rs_status_t rs_sdio_function_cmd52_write(rs_sdio_instance_t instance, uint8_t fu
rs_status_t rs_sdio_function_initialize(rs_sdio_instance_t instance, uint32_t source_clock_hz,
rs_sdio_function_info_t *info, rs_timeout_t timeout) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
rs_sdio_response_t response;
rs_sdio_clock_t clock;
uint32_t payload;
@@ -1345,8 +1528,10 @@ rs_status_t rs_sdio_function_initialize(rs_sdio_instance_t instance, uint32_t so
} else {
info->high_speed_fallback = true;
}
status = rs_sdio_clock_set(instance, source_clock_hz,
high_speed_enabled ? 24000000U : 25000000U, &clock);
status = rs_sdio_clock_set(
instance, source_clock_hz,
rs_sdio_transfer_clock(source_clock_hz, high_speed_enabled ? 24000000U : 25000000U),
&clock);
if (status != RS_OK) {
return status;
}
@@ -1356,6 +1541,11 @@ rs_status_t rs_sdio_function_initialize(rs_sdio_instance_t instance, uint32_t so
rs_status_t rs_sdio_function_enable(rs_sdio_instance_t instance, rs_sdio_function_info_t *info,
uint8_t function, rs_timeout_t timeout) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
uint8_t enable;
uint8_t ready;
uint32_t attempts;
@@ -1394,6 +1584,11 @@ rs_status_t rs_sdio_function_cmd53_transfer(rs_sdio_instance_t instance, uint8_t
uint32_t address, void *buffer, size_t byte_count,
bool write, bool block_mode, bool fixed_address,
uint16_t block_size, rs_timeout_t timeout) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
rs_sdio_cmd53_t command;
rs_sdio_data_config_t data_config;
rs_sdio_response_t response;
@@ -1482,6 +1677,11 @@ rs_status_t rs_sdio_function_cmd53_transfer(rs_sdio_instance_t instance, uint8_t
}
rs_status_t rs_sdio_function_irq_enable(rs_sdio_instance_t instance, bool enable) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
uint32_t mask;
if (!rs_sdio_instance_valid(instance) ||
@@ -1501,6 +1701,11 @@ rs_status_t rs_sdio_function_irq_enable(rs_sdio_instance_t instance, bool enable
}
rs_status_t rs_sdio_function_irq_pending(rs_sdio_instance_t instance, bool *pending) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
uint32_t mask;
if ((pending == NULL) || (rs_sdio_irq_pending(instance, &mask) != RS_OK)) {
@@ -1511,5 +1716,10 @@ rs_status_t rs_sdio_function_irq_pending(rs_sdio_instance_t instance, bool *pend
}
rs_status_t rs_sdio_function_irq_ack(rs_sdio_instance_t instance) {
#ifdef RS_SOC_TINY
if (instance == RS_SDIO_INSTANCE_1) {
return RS_ENOTSUP;
}
#endif
return rs_sdio_irq_clear(instance, RS_SDIO_IRQ_DAT1);
}
+3 -1
View File
@@ -136,7 +136,9 @@ rs_status_t rs_sd_memory_parse_csd(const rs_sdio_response_t *response, rs_sd_mem
return RS_EINVAL;
}
block_length = UINT32_C(1) << read_bl_len;
capacity_bytes = ((uint64_t)c_size + UINT64_C(1)) * (UINT64_C(1) << (c_size_mult + 2U)) *
/* C_SIZE_MULT is a three-bit CSD field: the shift is at most nine.
* Keep the factor RV32-native; the enclosing product remains 64-bit. */
capacity_bytes = ((uint64_t)c_size + UINT64_C(1)) * (UINT32_C(1) << (c_size_mult + 2U)) *
(uint64_t)block_length;
capacity_blocks = capacity_bytes / (uint64_t)RS_SD_MEMORY_BLOCK_SIZE;
info->card_type = RS_SD_MEMORY_SDSC;
+11
View File
@@ -84,6 +84,12 @@ static rs_status_t rs_uart_flow_gpio_configure(const rs_uart_config_t *config) {
};
rs_status_t status = RS_OK;
#ifdef RS_SOC_TINY
status = rs_gpio_configure(8U, &pin_config);
if (status == RS_OK) {
status = rs_gpio_configure(9U, &pin_config);
}
#endif
if (config->auto_cts_enable) {
status = rs_gpio_configure(RS_UART_CTS_GPIO, &pin_config);
}
@@ -106,6 +112,11 @@ rs_status_t rs_uart_configure(const rs_uart_config_t *config, rs_timeout_t timeo
return RS_EINVAL;
}
#ifdef RS_SOC_TINY
if (config->auto_cts_enable || config->auto_rts_enable) {
return RS_ENOTSUP;
}
#endif
RS_UART_CTRL = 0U;
while (((RS_UART_STATUS & (RS_UART_STATUS_TX_BUSY | RS_UART_STATUS_RX_BUSY)) != 0U) &&
(timeout != 0U)) {
+5 -1
View File
@@ -419,7 +419,11 @@ rs_status_t rs_ws2812_dma_service(uint64_t now) {
return rs_ws2812_dma_finish();
}
if ((transmitter_flags & RS_WS2812_STATUS_BUSY) == 0U) {
return rs_ws2812_dma_fail(RS_EIO);
/* Completion may occur between the interrupt and status reads, and
* sticky DONE can lag the core's idle transition. Observe terminal
* events on the next bounded service call; never refill an idle core.
* Errors, aborts and the deadline remain checked on every call. */
return RS_OK;
}
if (rs_ws2812_transfer.remaining == 0U) {
rs_ws2812_write_register(RS_WS2812_INTR_STATE_OFFSET, RS_WS2812_INTR_FIFO_LOW);
+1 -1
View File
@@ -158,7 +158,7 @@
"cluster_common": {"url": "https://github.com/retroSoC/common.git", "revision": "964a54d70cb78e394ba9a25c05f2aa2b02941fb7", "destination": "rtl/managed/clusterip/common", "license": "NOASSERTION"},
"cluster_crc": {"url": "https://github.com/retroSoC/crc.git", "revision": "8c1a5664c4d23d005c1afad432372a13ca967cc8", "destination": "rtl/managed/clusterip/crc", "license": "MulanPSL-2.0"},
"cluster_ps2": {"url": "https://github.com/retroSoC/ps2.git", "revision": "2097a0d41b07573ff0453d65ba9a1670b3df7b9c", "destination": "rtl/managed/clusterip/ps2", "license": "MulanPSL-2.0"},
"cluster_pwm": {"url": "https://github.com/retroSoC/pwm.git", "revision": "79ae32f725e848a1e7f6134847b3fb032ed64006", "destination": "rtl/managed/clusterip/pwm", "license": "MulanPSL-2.0"},
"cluster_pwm": {"url": "https://github.com/retroSoC/pwm.git", "revision": "38efaf5e20f084a00148920087c60cadab61839b", "destination": "rtl/managed/clusterip/pwm", "license": "MulanPSL-2.0"},
"cluster_rng": {"url": "https://github.com/retroSoC/rng.git", "revision": "36f3e0774d8f3c4b597d3db8a96ca3992532b61e", "destination": "rtl/managed/clusterip/rng", "license": "MulanPSL-2.0"},
"cluster_rtc": {"url": "https://github.com/retroSoC/rtc.git", "revision": "32548c5cbcdf39e09d0880fb611f9dd47940ea88", "destination": "rtl/managed/clusterip/rtc", "license": "NOASSERTION"},
"cluster_wdg": {"url": "https://github.com/retroSoC/wdg.git", "revision": "f5d39fa246054d678d34195824d943367dfcce44", "destination": "rtl/managed/clusterip/wdg", "license": "MulanPSL-2.0"}
+4 -2
View File
@@ -3,8 +3,10 @@
## Supported Profiles
Tiny's [2026-10-07 specification default](ip/tiny-soc.md#ics55-default-platform-and-final-timing-gate-2026-10-07)
is ICS55/PLL with SAFE24 boot. The executable default keeps the PLL parked off;
PLL-driven SYS and physical qualification remain later work. The table
is ICS55/PLL with SAFE24 boot. The PLL starts parked off; the
[R2-P7 implementation](ip/tiny-soc-r2-clock-reset.md) adds explicit software
transitions with separate functional evidence. Physical qualification remains
pending. The table
describes executable profiles, not qualified operating rates.
For Tiny and its SPI/PIO-lite/PPALite extensions only, pre-final post-synthesis
timing closure is observational. Retain STA attempts/failures and all required
+9
View File
@@ -105,6 +105,15 @@ change.
in `[23:0]`. `TKEEP` and `TSTRB` are `4'hF`. I2S RX does not assert `TLAST`.
DMA asserts `TLAST` on the final programmed TX word.
The P6 integration regression uses alternating sample least-significant bits
in both 16- and 24-bit pin-level loopback. It exposed a receiver defect that
combined the preceding sample's last bit with the next sample. The corrected
receiver appends the bit sampled at the LRCK transition to the preceding
sample's shifted bits and registers completion after that value is available.
This preserves the host register/stream ABI and does not add slave mode.
The register frontend uses PWRITE for transfer direction. In particular, a
write with zero byte strobes cannot be reinterpreted as a side-effecting RX read.
## HAL Sequence
Mini applications should use `rs_i2s_configure()`, `rs_i2s_enable()`, and either
+44
View File
@@ -180,3 +180,47 @@ The standalone IP does not allocate memory, perform cache maintenance,
implement an IOMMU/firewall, or provide a hosted/runtime C API. The SoC
integration only supplies the two native AXI/APB/IRQ/pad paths; the SDK driver
is maintained by a separate workstream.
## Tiny SAFE24 integration
Tiny R2-P6 connects instance 0 to external AXI owner 2, its independent APB
branch and IRQ10, and GPIO0..5. Instance 1 remains absent and its public SDK
operations return `RS_ENOTSUP` before MMIO. These bindings do not replace the
Mini mappings above. The 24 MHz input permits at most 12 MHz SDCLK; enumeration
still starts at 400 kHz and the SDK caps later protocol requests at input/2.
R6 is retained as the complete 48-bit frame in RESP. RCA therefore occupies
bits 39:24, above the payload status and CRC/end bits. The shared initialization
helper uses this field rather than bits 31:16; no register or public API changes.
The command sender enables its first output bit at its scheduled launch. A
command admitted on an already running SDCLK rising edge must not expose an
extra start bit. Directed pad sampling covers repeated command admission at
both half-cycle phases in Icarus and Verilator. The card model collects the
complete 48-bit frame after its start bit, including the pulled-high end bit
after host output release; host output-enable is not the frame length oracle.
The transmit wait indication also covers the last bit of a word when its
successor is unavailable. The core pauses SDCLK before the next word's launch,
while retaining the current driven bit and draining the high half-cycle. This
prevents an idle bit from becoming the next word's MSB under memory backpressure.
A 1024-byte four-lane DMA case inserts 24 system cycles between read beats and
checks every card byte, CRC status and descriptor retirement through real
three-state lines. Inactive lanes stay pulled high in the card's token/tail checks.
Card-to-memory DMA retains descriptor ownership across every AXI burst, rather
than publishing DONE after the first 64 bytes. Receive backpressure stops the
next SDCLK launch as soon as a complete word is held; a registered stop loses
the next card bit. The roundtrip fixture delays descriptor fetch as well as
payload supply, checks all 1024 received bytes and successful final writeback.
Its card emits all sixteen CRC bits in order; earlier manual-model tests did
not reject the skipped CRC bit. These fixes preserve the native one-read/one-write
capacity, descriptor layout, register ABI and interrupt/error lifecycle.
The existing verification card keeps its manually scheduled standalone mode.
Tiny selects its optional SDHC enumeration responses with 1024 512-byte blocks
and default-speed-only capability. Its board fixture checks the observed wire
command, direction, width and length, then calls the existing data-stimulus API
at DATA_START. All payload and response bits cross the real pads. This checks
the repository controller/SDK contract; it does not qualify arbitrary card
response timing, SD protocol compliance, a physical card or an electrical rate.
+385
View File
@@ -0,0 +1,385 @@
# Tiny R2-P7 clock/reset implementation and evidence
This phase starts at `43a3c8d453b6d41df3536719ebc6d1f4521bc7cd`, against frozen
specification blob `7e19de49d49efaafacccfa85671e314d53dd15a4`. Implementation
is authorized; phase acceptance still requires review of the completed gates.
No P8 implementation, commit, push, or physical frequency qualification follows.
## Product and ownership
`configs/ci/ics55-tiny.mk` remains the default: TINY, ICS55, PLL present,
SAFE24 boot. The single RCU-owned PLL starts off. Software may request the
functional PLL192/PLL240 profiles after draining clients. These are digital
simulation capabilities, not characterized silicon operating points.
`configs/ci/ihp130-tiny.mk` remains the explicit no-PLL 24 MHz compatibility
profile. `ihp130-tiny-xin48.mk` and `ihp130-tiny-xin96.mk` add declared external
input variants, both normalizing the boot/control reference to 24 MHz.
CPU, all 32 main SRAM macros, bank frontends, fabric and SDIO share active SYS.
Only the CPU leaf stops for WFI. XPI data/control use MEM. DMA, shared serial
and security IP and the six private Crypto macros use PCLK. Crypto capacity
and erasure policy are unchanged. AON owns RCU, CLINT and RTC/WDG functional
timekeeping; AUDIO is optional at boot. The historical ASIC wrapper and frozen
QFN64/pad contract are retained under the existing P6 qualification boundary.
Tiny owns the clock backend, admission holders, APB/AXI crossings, peripheral
lifecycle controller, CPU gate, CLKOUT and SYSCTRL ABI. Common clock mux,
divider, reset barrier, synchronizers and CDC primitives are reused without
editing managed sources. New optional shared-IP lifecycle outputs preserve
Mini defaults; the debug wrapper's separate system reset is opt-in. Managed
PWM/CRC lifecycle mirrors observe successful writes at their existing ABI.
The already approved PWM lock remains
`38efaf5e20f084a00148920087c60cadab61839b`.
RCU occupies `SYSCTRL+0x100..0x15c`, IRQ31. Handwritten SVH/C definitions
have a 24-register parity test. `rcu.h` supplies bounded Tiny-specific profile,
snapshot, gate, local/system reset, cause/fault and CLKOUT operations. Invalid,
unsupported, busy, timeout and hardware-error results are distinct. No new
MISRA Required-rule deviation is introduced. Future targets 14–17 remain
absent; no SPI, PIO-lite, DVP, PPALite or PSRAM rollout is included.
## Lifecycle and failure boundaries
Clock requests block new admissions after command acceptance, drain accepted
CPU/local/AXI/APB traffic, park on REF24, configure conservative divisors,
qualify the source, then commit rate snapshots before unblocking. Stalled
addresses already accepted into admission holders retain VALID/payload.
Unaccepted requests may remain pending and resume after switching. An
insufficient transition budget fails before changing the committed profile.
PLL lock timeout publishes the complete SAFE24 fallback. PLL stuck-low/high
requires a system/CDC reset before forced mux recovery; XIN loss needs source
restoration and external reset.
Local resets hold for five actual leaf edges and release through five-edge
synchronizers and bus barriers. I2S additionally requires actual AUDIO-side
reset readiness; missing AUDIO cannot manufacture RESET_DONE. Gate changes
wait for idle; CPU-only WFI permission does not pause unrelated DMA. Current
non-CPU lifecycle commands conservatively drain all central-DMA work before
changing the selected targets. This can reject a request while an unrelated
DMA stream remains active; it does not stop that stream by gating its clock.
Warm reset retains cause/fault/debug state, aborts interrupted debug commands,
restores SAFE24, resets functional RTC/WDG state and preserves main SRAM data.
The 2026-10-14 P7 review correction keeps the XPI fabric target and AXI bridge
source endpoint on SYS reset while the XPI/MEM endpoint uses its MEM leaf reset.
The later response-conservation correction uses source-owned response holders:
already presented B/R responses remain stable and retire once, while missing
read beats receive DECERR and aborted writes drain W before B. Admission allows
one outstanding request in each direction. Payload source reset uses the
five-edge link barrier; the response holders reset only with the source.
An unfinished warm clear holds isolation until destination reset so that late
responses cannot enter another transaction. Production DMA/XPI clear inputs
remain tied low. A failed drain does not trigger an invented autonomous AXI
timeout or peripheral reset. The focused tests now cover these protocol
boundaries in both simulators; earlier 36-case results did not establish them.
Source-only reset has a separate production precondition. The DMA source
may reset locally only after its engine and accepted admission/fabric work
drain. The controller blocks new admissions, waits at least 16 REF24 cycles
for the synchronized observations, and leaves the source running on drain
timeout. XPI has no independent SYS-source reset: the common system reset
also asserts its MEM endpoint reset. The standalone AXI bridge does not
cancel a live remote slave's work on an arbitrary source-only reset and must
not be reused with that assumption.
The focused `tiny_p7_source_reset_tb` extracts the production controller,
admission, bridge, synchronizers and reset bindings into its fixture and uses
the real DMA AXI master. It checks stalled addresses, partial writes, late
responses, held read responses, timeout, recovery and common reset at all five
declared domain ratios. A scoreboard independent of local reset detects the
deliberately broken-drain negative case. Unrelated clients and the completion
mailbox are idle; the target is a behavioral slave. This supplies executable
integration-contract coverage, not a general arbitrary-reset proof or physical
qualification. The new evidence is under `06-00.../meta/tiny-r2-p7-source-reset/`
as detailed in the verification ledger; production RTL and backend inputs are
unchanged by this follow-up.
The follow-up validation commands are:
```sh
source .cache/retrosoc/development/tiny-r2-p1/activate.sh
p7_reset_evidence=build/ics55-tiny-2026-10-14-06-00-ff4b5cef6eb5/meta/tiny-r2-p7-source-reset
PYTHONDONTWRITEBYTECODE=1 python3 scripts/run_flow.py --tool pytest \
--log "$p7_reset_evidence/directed.log" \
--result "$p7_reset_evidence/result-directed.json" -- \
python3 -m pytest -q tests/test_tiny_r2_p7.py \
--basetemp="$p7_reset_evidence/directed-tests"
make --silent CONFIG=configs/ci/ics55-tiny.mk SOC=TINY PDK=ICS55 \
rtl-format-check rtl-style-check-all check-clock-reset-domains
ruff check .
python3 scripts/regress.py --root . --suite pr --soc TINY --pdk ICS55 --dry-run
python3 scripts/regress.py --root . --suite pr --soc TINY --pdk IHP130 --dry-run
git diff --check
```
These paths identify the retained execution, not an instruction to overwrite
it: use a fresh evidence directory for any retry. `source.json` binds the new
tests and `verification-update.json` compares them to the original `06-00`
snapshot. Only the two verification files differ; all 1728 other technical
inputs and tool identities match. No firmware, full-SoC, synthesis, netlist or
STA flow is repeated for this verification-only correction. The earlier
full-suite Pytest and full-Icarus limitations below remain explicit.
The first post-fix physical smoke campaign below is historical. Each PDK has
its own profile/configuration digest. These results precede the final response
holders; current source evidence is recorded in the verification ledger.
| PDK | Variant | Synthesis | Netlist boot | STA |
| --- | --- | --- | --- | --- |
| ICS55 | `build/ics55-tiny-2026-10-14-01-20-ff4b5cef6eb5` | PASS, 2122.685 s | PASS, 1656.024 s, `SIM_TEST_PASS Tiny netlist boot` | PASS observation; WNS max/min `-144.40/-0.31 ns`, TNS max/min `-4859636.50/-45.04` |
| IHP130 | `build/ihp130-tiny-2026-10-14-01-20-8232fa11e7f3` | PASS, 1925.337 s | PASS, 586.861 s, `SIM_TEST_PASS Tiny netlist boot` | PASS observation; WNS max/min `-257.81/-0.47 ns`, TNS max/min `-9807803.00/-100.68` |
The current frozen-source `06-00` campaign supersedes the historical table for
post-fix qualification:
| PDK | Variant | Synthesis | Netlist boot | STA |
| --- | --- | --- | --- | --- |
| ICS55 | `build/ics55-tiny-2026-10-14-06-00-ff4b5cef6eb5` | PASS, 2108.388 s | PASS, 1655.980 s, `SIM_TEST_PASS Tiny netlist boot` | PASS observation; WNS max/min `-152.03/-0.31 ns`, TNS max/min `-5622052.50/-44.14` |
| IHP130 | `build/ihp130-tiny-2026-10-14-06-00-8232fa11e7f3` | PASS, 1934.515 s | PASS, 578.869 s, `SIM_TEST_PASS Tiny netlist boot` | PASS observation; WNS max/min `-253.43/-0.49 ns`, TNS max/min `-10035324.00/-153.37` |
The STA markers explicitly identify SAFE24/observational analysis; neither
variant qualifies an active PLL, routed implementation, PVT corner or product
operating point. The `04-00` source candidate was later rejected by the
response-conservation tests. The `06-00` source-bound post-fix campaign is the
current qualification run and all requested synthesis, netlist and STA gates
pass. Its measured results are recorded in the ledger. Full firmware Icarus
remains deferred to the final Tiny phase.
Source/divisor/enable for CLKOUT are committed together, cross to the selected
source, and finish the current output high pulse before disabling. GPIO28 ALT0
and GPIO23 ALT1 consume the same generator. Illegal/live changes return
PSLVERR and leave the previous configuration unchanged.
## Validation sequence and commands
Activate the locked environment before these commands:
```sh
source .cache/retrosoc/development/tiny-r2-p1/activate.sh
python3 scripts/regress.py --root . --suite pr --soc TINY --pdk ICS55 --dry-run
python3 scripts/regress.py --root . --suite pr --soc TINY --pdk IHP130 --dry-run
python3 -m pytest -q tests/test_tiny_r2_p7.py tests/test_tiny.py tests/test_tiny_r2_p4.py tests/test_tiny_r2_p5.py
make sw-format-check sw-policy-check sw-host-test
make rtl-format-check rtl-style-check-all rtl-readiness-check check-clock-reset-domains
ruff check .
python3 scripts/dependency_lock.py --lock dependencies/dependencies.lock.json
python3 -m pytest -q
git diff --check
```
The direct long-flow commands use the selected regression's targets with
larger explicit wall-time budgets. The identical Tiny nightly matrix does not
need a second execution. Every row below runs once per PDK, with
`CONFIG=configs/ci/<pdk>-tiny.mk SOC=TINY PDK=<PDK> APP=ci_smoke HAVE_SVA=YES`:
| Purpose | BUILD_TIMESTAMP | Remaining Make arguments |
| --- | --- | --- |
| P7 Verilator | `2026-10-13-17-05` | `TINY_P7_ACCEPTANCE=YES SIMU=VERILATOR SOC_SIM_TIME=3600 TINY_SIM_TIMEOUT=6000000 TINY_SIM_ARGS=+p7_monitor sim` |
| P7 Icarus | `2026-10-13-17-06` | `TINY_P7_ACCEPTANCE=YES SIMU=IVERILOG TINY_SIM_TIMEOUT=6000000 TINY_SIM_ARGS=+p7_monitor 'TINY_RUN=timeout --foreground 14400s $(VVP) $(SIM_BUILD_ROOT)/simv' sim` |
| Physical macro synthesis | `2026-10-13-17-15` | `HAVE_SVA=NO SYNTH=YOSYS YOSYS_TIMEOUT=10800 synth` |
| Full firmware/JTAG, P7 watchdog-cause adaptation, Verilator | `2026-10-13-18-25` | `SIMU=VERILATOR SOC_SIM_TIME=7200 TINY_SIM_TIMEOUT=30000000 TINY_SIM_ARGS=+tiny_jtag_smoke sim` |
| Full firmware/JTAG, P7 watchdog-cause adaptation, Icarus | `2026-10-13-18-26` | `SIMU=IVERILOG TINY_SIM_TIMEOUT=30000000 TINY_SIM_ARGS=+tiny_jtag_smoke 'TINY_RUN=timeout --foreground 36000s $(VVP) $(SIM_BUILD_ROOT)/simv' sim` |
| Netlist boot, after synthesis | `2026-10-13-17-15` | `HAVE_SVA=NO SIMU=IVERILOG netsim-boot` |
| Observational core STA, after synthesis | `2026-10-13-17-15` | `HAVE_SVA=NO 'STA_BUILD_ROOT=$(VARIANT_ROOT)/sta/opensta-p7-v3' STA=OPENSTA sta` |
Warning/metric collection uses each corresponding variant. New EDA signatures
remain observations; no warning baseline or metric policy is edited. Compiler
warning policy and Tiny functional verdicts remain required.
After correcting the external-input POR reference, the actual-SoC P7 image is
also run with `CONFIG=configs/ci/ihp130-tiny-xin48.mk` and
`CONFIG=configs/ci/ihp130-tiny-xin96.mk`, `SOC=TINY PDK=IHP130 APP=ci_smoke
TINY_P7_ACCEPTANCE=YES HAVE_SVA=YES TINY_SIM_ARGS=+p7_monitor
TINY_SIM_TIMEOUT=24000000`. Verilator uses timestamp `2026-10-13-18-40`,
`SIMU=VERILATOR SOC_SIM_TIME=7200 sim`; final Icarus uses `2026-10-13-20-05`,
`SIMU=IVERILOG 'TINY_RUN=timeout --foreground 21600s $(VVP) $(SIM_BUILD_ROOT)/simv' sim`.
The larger cycle budget counts raw XIN edges, not SYS/REF24 cycles. These are
additional source configurations, not repetitions of a single benchmark.
The initial XIN48/96 Icarus attempts (`18-41`) produced a premature terminal
write but failed the independent P7 lifecycle coverage checks, so they remain
FAIL. A short actual-SoC comparison reproduced unknown RCU cause/fault state
with the old reset declaration initialized low at time zero. REF24 has no edges
while its input divider is reset; an explicit external RESET_N assertion at
1 ns, before the first XIN edge, initializes the asynchronous state correctly.
Both old-stimulus negative probes fail and both asserted-reset probes pass,
under `20-05.../meta/cold-reset/`. The testbench now checks exact known cold
state and rejects an unknown terminal pass/code. This is a verification-stimulus
repair, with no forced DUT state or synthesizable RTL change. The Verilator
source cases and existing 24 MHz runs are retained separately; only the two
failed Icarus source cases are replayed, with the same firmware hashes.
## Workloads and provenance
The separate `TINY_P7_ACCEPTANCE=YES` configuration key selects the short P7
firmware without replacing the original acceptance application. It exercises
all supported profile selections for the chosen backend, 128-word SRAM
retention, atomic rate readback, PWM rate readback, CLINT monotonicity and an
independent PCLK timer measurement over at least 200 CLINT ticks. The timer
check allows bounded APB read skew but rejects /2 or /4 domain mistakes.
It gates/ungates/resets timer0, copies 1024 words through DMA while CPU WFI is
armed, wakes on CLINT, and requests software reset with a retained `.noinit`
sentinel. The observer must see actual CPU gated cycles, DMA responses during
that interval, supported profile commits and exactly one software reset.
Tiny PASS requires command success, TEST_STATUS/SIM_TEST_PASS, and absence of
FAILED/FATAL/assertion failed/%Error/SIM_TEST_FAIL/SIM_TEST_TIMEOUT. P7 also
requires `P7_LIFECYCLE_PASS`. The old full firmware additionally retains its
P6 peripheral/wire/AXI records and JTAG checks. UART output is diagnostic only.
The completed P7 firmware results below match between Verilator and Icarus.
All eight images use the same retained binary. Cycles count raw XIN edges and
must not be compared as CPU cycles across different XIN configurations.
| Source/profile | Terminal cycles in each simulator | Actual gated SYS cycles | DMA responses while gated |
| --- | ---: | ---: | ---: |
| ICS55 XIN24, PLL192/240/SAFE24 | 3,525,681 | 71,614 | 64 |
| IHP130 XIN24, external/SAFE24 | 3,521,913 | 71,623 | 64 |
| IHP130 XIN48, external/SAFE24 | 7,038,466 | 71,635 | 64 |
| IHP130 XIN96, external/SAFE24 | 14,071,429 | 71,615 | 64 |
Each run also records exactly one software reset and verifies SRAM retention.
The source record is
`build/ics55-tiny-2026-10-13-17-05-f3e42d54c18b/meta/tiny-r2-p7/source.json`.
It retains repository state, the exact specification, locked tool identities,
source hashes, patch and a source archive, including untracked implementation
files. `scripts/tiny_r2_p7.py verify-source` checks the retained source set.
The Make formatter subsequently changed only the terminal newline in eight
Make/configuration files. `make-format-equivalence.json` checks every changed
file byte-for-byte except that final newline; RTL, firmware, tests and tool
scripts are identical. `formatted/source.json` retains the final formatting
snapshot separately, without overwriting the source consumed by running jobs.
Subsequent validation corrections update the commercial six-domain SAFE24
adapter/test and connect the top-level POR counter to normalized REF24. The
external-source fixture now requires five actual REF24 edges at 24/48/96 MHz
and compares old/new reset-tree outputs at 24 MHz in both simulators (seven
checks pass). For every retained long-flow configuration, XIN is 24 MHz and
the backend directly wires XIN to REF24, so that one port reconnection is an
exact alias substitution. The original source remains the provenance of those
runs; the documented equivalence and focused higher-input-rate tests cover
the correction without repeating default-profile simulations.
Build variants retain effective configuration, generated filelists, firmware
ELF/bin/hex/map, simulator binaries, command logs and structured terminal
results. Match binaries by SHA-256 across simulators, and retain flags and
plusargs with results; do not mix these images with compiler/layout experiments.
The four P7 binaries (both PDKs and both simulators) have SHA-256
`0a536fbbae9832de7ba5a82f7688a147b96a57435a66249b5e8de18bad1c01c5`.
The initial full ICS55 firmware matched between simulators at
`e4cb5965b9b9ddb1135a0a672327113ed8b378ace56e1a4ee771374309c18a15`;
the initial full IHP130 firmware matched at
`048bd15221fcbd25986abaa2d973e74d101f3a8ac8f328a6169442ba8acabacb`.
The corrected watchdog-acceptance full images match between simulators at
ICS55 `69b8582099c8735e81014b9c3f5685d9212a58c18b820a8c9a611a6f64a2f6c4`
and IHP130 `efc0b403f32669332e942cb63ee9bb2b035a6e462e68e0a5a1524dea01c6c298`.
P7 has its own structural netlist audit. It requires 32 main and six private
Crypto physical SRAM macros, main-SRAM/CPU SYS equality, the CPU-only gate,
Crypto PCLK binding and exactly one real ICS55 PLL driving the SYS mux.
The audit binds configuration digest, netlist/JSON and audit-script hashes.
The strict historical P1 parked-PLL/direct-SYS audit is unchanged.
Core STA analyzes SAFE24 boot, with explicit mux/PLL case analysis. Retain
clocks, setup/hold, coverage, recovery/removal and constraint-violation reports.
It excludes pad/board/routed/PVT and active-PLL qualification. Negative slack
is observational before the final combined product campaign, not evidence of
qualification and not permission to omit synthesis or netlist functionality.
The commercial adapter mirrors the same six-domain SAFE24 inventory and
requires explicit mux/PLL case selections; its host tests pass. Internal /2,
/4 and CLKOUT-source clocks supplement the six root domains. Settled mux gate
state is constrained because a select request alone does not propagate through
the mux's sequential handshake in static analysis. The initial and second STA
attempts remain under `sta/opensta` and `sta/opensta-p7-v2`; the final observation
is under `sta/opensta-p7-v3`. No licensed
commercial execution, active-PLL SDC, routed extraction, board IO or package
qualification is supplied by those host tests.
Historical `2026-10-13-17-15` SAFE24 observations, in ns, are ICS55 setup/hold WNS −147.48/−0.31,
TNS −4758121.50/−45.30; IHP130 WNS −275.45/−0.50,
TNS −9958118.00/−102.84. The worst setup cone begins at the fabric reset leaf,
whose direct fanout is over 10,000; no reset false path was added to hide it.
The v3 coverage reports retain excluded pad IO budgets, 42 unclocked ICS55 PLL
monitor/mux pins while that source is off, and 5 ICS55 / 6 IHP130 control pins
with multiple clock tags. These are explicit limits of this settled SAFE24
observation, not complete-mode timing coverage or signoff. These values and
coverage counts belong to that historical netlist; the current `06-00` WNS/TNS
are in the table above, with their own retained coverage reports.
The single full Pytest attempt records 1696 passed, 6 failed, 60 setup errors
and one optional `pypdf` skip. Two failures were the commercial domain policy;
60 setup errors and one failure shared the missing `RS_EBUSY=-8` SDK return
entry in the publication's source-checked table. Both are corrected; the
commercial/STA tests pass and the publication subset records 110 passed/one
skip. The remaining three Icarus failures used a test module collected before
the external-reset fixture acquired its `tiny_reset_tree` dependency; the
updated test command explicitly supplies it and all seven external-source/reset
checks pass. Keep that original full-suite result and these focused reruns;
do not report an unexecuted clean full-suite pass. Only the shared return-code
table changes in the publication; no source identity, PDF, edition or release
qualification is advanced.
## Interrupted and historical attempts
Earlier October 13 build directories are development snapshots. In particular,
the `15-45-f3e42d54c18b` Verilator attempt passed at 3,281,032 cycles and observed
71,612 gated cycles, 64 DMA responses while gated and one warm reset. Later
corrections mean that result is not final-source acceptance. Earlier synthesis
attempts failed obsolete P1 checks; a temporary weakened P1 audit is invalid
evidence and was restored. No current physical result may be inferred from it.
The first final-campaign synthesis attempt (`17-07`, SVA enabled) failed during
Slang elaboration on unsupported temporal assertion syntax. It did not run
mapping. The `17-15` retry uses the existing regression's `HAVE_SVA=NO` synthesis
configuration; behavioral/SVA verification remains separate and unchanged.
The `17-08` complete Verilator runs passed all first-boot peripherals, then
incorrectly repeated the firmware after watchdog reset and failed recovery
code 26 on the second pass. The old firmware inferred warm boot from the
watchdog's functional `reset_count`; P7 intentionally resets that state. The
corrected firmware checks retained RCU cause bit 2, verifies that watchdog
functional state was cleared and SAFE24 restored, and requires an SRAM `.noinit`
cookie set only after all first-boot tests. This preserves the complete test
sequence and watchdog acceptance rather than restoring obsolete retention.
The corresponding `17-09` Icarus runs were cancelled as obsolete; controller
exit 130 is retained in `controller-cancellation.json`, separately from any
unfinished child result. Corrected full runs use `18-25` / `18-26` and source
evidence under `build/ics55-tiny-2026-10-13-18-25-f3e42d54c18b/meta/tiny-r2-p7/`.
The corrected full Icarus attempts under `18-26` reached the watchdog-reset
stage after reporting all first-boot peripherals, but both exhausted the
36,000-second wall-clock budget with exit 124 and no terminal PASS. Those
results remain FAIL. A focused test uses the real WDG APB wrapper, command
mailboxes, Tiny APB bridge, clock backend, reset tree and RCU, and verifies two
successive hardware watchdog resets, retained RCU cause and cleared functional
state in both simulators. It does not replace the full firmware gate.
The exact retained `simv` and firmware were replayed under
`sim/iverilog/attempt-2026-10-14-04-50/` with `timeout --foreground 72000s vvp -i`,
the same `+max_cycles=30000000 +tiny_jtag_smoke` and firmware path. The
maintainer then intentionally interrupted both replays with exit 130 and moved
this long-running full-Icarus gate to the final Tiny phase. The logs and
interrupted `result-sim.json` records are retained as unrun/partial evidence;
they cannot satisfy the P7 terminal verdict. `-i` enabled unbuffered standard
IO; it did not change the design or acceptance workload. No synthesis,
simulator compilation or firmware build was repeated for the replay.
PIDs are diagnostics in a particular execution namespace. Absence from another
tool invocation's process list does not prove termination. Keep long commands
attached to their execution sessions and use terminal result JSON plus strict
log checks. `run_flow.py` records child/parent PIDs and closes results on caught
exceptions; SIGKILL or host loss can still leave a running record. Such a record
never counts as PASS. NFS clock-skew warnings are retained in the build logs.
Cancellation now writes an interrupted checkpoint before process-group cleanup
and ignores a second termination signal during that cleanup. The final result
records the child's exit code and whether cleanup remains pending; the repeated
signal/TERM-ignoring-child regression passes. An external executor can still
kill a process before it handles a signal, so controller cancellation evidence
must not be mistaken for a fabricated child exit result.
The ledger records the final pass/fail/unrun matrix. Human review of that matrix,
the diff and remaining qualification boundaries is the P7 acceptance gate.
The generated `audit.py` under the corrected full-run evidence directory reads
all source configurations' terminal results, strict Tiny markers, P7 lifecycle
observations, unchanged P6 workload/wire records, JTAG, paired firmware hashes,
macro audit, netlist boot and STA artifacts. It launches no hardware flow and
writes `acceptance.json`; a pending/missing/failed input cannot produce PASS.
+402
View File
@@ -0,0 +1,402 @@
# TINY-R2-P5 concurrent fabric
This implements the approved P5 preflight on
`3131762b35e0ebfcd196395f955e7c1005bd18e7`, using frozen specification blob
`7e19de49d49efaafacccfa85671e314d53dd15a4`. The default is Tiny/ICS55 SAFE24
with its present PLL parked off; IHP130/no PLL remains compatibility.
The [ledger](tiny-soc-verification.md) separates phase evidence from approval.
Historical campaigns and their limitations remain below. The P1 correction
has its own source and validation scope; no P6, frequency or physical
qualification is implied.
## P1 protocol correction (2026-10-11)
This correction starts from committed `4e6ab1a6fb445521703f0bfa412d3a1e8a38cd69`
and the unchanged frozen specification. The preceding `09-30` campaign's
`final-evidence.json` records two reset protocol failures and stale publication
bindings even though ordinary regressions and matched-image replay passed.
Those results do not qualify this corrected RTL.
Target reset cancels the downstream session while preserving the live
upstream session. A reset sampled in Address/Response latches the abort path;
releasing the target cannot replay its cancelled address or data. Already
captured R/B responses remain in the Common spills with unchanged VALID,
payload, ID and order. Only fabric/system reset clears those buffers. The
downstream stability assertions follow the target's own reset; upstream
stability assertions remain enabled throughout target-only reset.
A nine-bit count tracks captured read beats, including buffered and already
retired beats. Missing DECERR beats append through the same spill until the
original ARLEN+1 count is met; RLAST belongs only to the final beat. AbortWrite
accepts/discards remaining upstream W beats before creating one DECERR B.
Captured-B state prevents duplicate responses and preserves an already held
successful completion. Fault metadata is captured from the first erroneous
response beat, preserving earlier SLVERR over a subsequent reset DECERR;
fault counting remains once per terminal transaction. Fully captured successful
responses do not become reset faults.
The reset fixture drives stimulus away from sampling edges and independently
checks R/B stability, exact beat counts, W-before-B ordering, one completion,
credit release and first-error metadata. Its 28 cases include 16-beat SRAM/XPI
traffic, a production APB bridge, three production DMA-master drain cases,
target reset held through completion and early target release. A separate
formal mode varies target reset arbitrarily and proves response/ownership,
beat-count and W/B ordering properties by induction. The normal formal mode
and its fixed-response expectations remain intact. Publication bindings retain
complete terminal/fault predicates, with negative tests for premature
completion and response flushing; the reviewed publication SHA is unchanged.
Evidence belongs under
`build/ics55-tiny-2026-10-11-23-35-b4aa59f3e5d9/meta/tiny-r2-p5-p1/`.
Final focused checks precede one full Pytest and one PR execution per Tiny
PDK. No matched-image replay is required to claim this bounded correctness
repair; historical workload measurements retain their source attribution.
Validation completed on 2026-10-12; the ledger retains human review pending.
Runtime peripheral-reset controls/quiescence remain P7, and the existing
five-edge reset leaves remain unchanged. No shared DMA, public ABI, profile,
dependency, warning baseline or metrics policy changes are included.
The final technical input digest is
`f670789a2379a8d2e6c59f5ecd714ca0237bb6f1cfe749a2f29b0794d356b5f0`.
`source.json` preserves the 2011 inputs and locked tools; `evidence.json`
rechecks their identity and binds all gate/fixture/flow artifacts. Focused
validation passes 101 tests; full Pytest passes 1685 with one optional `pypdf`
skip and no failures. Counts overlap. ICS55/IHP130 PR commands each ran once;
their Tiny nightly dry-run matrices match, so the same gates were not repeated.
All six ordinary simulations satisfy command success, the Tiny success marker
and forbidden-marker checks. The scoped reset fixture and formal checks are
additional protocol evidence, not physical or XPI-device qualification.
| PDK / balanced recipe | Cells | Library area | Max WNS / TNS (ns) | Min WNS / TNS (ns) |
| --- | ---: | ---: | ---: | ---: |
| ICS55 | 245037 | 1910664.100799 | -70.04 / -1151596.25 | 0 / 0 |
| IHP130 | 259394 | 9911158.202394 | -127.01 / -3120300.50 | 0 / 0 |
Current physical-flow observations are under
`build/ics55-tiny-2026-10-11-23-35-42d5beccb726/` and
`build/ihp130-tiny-2026-10-11-23-35-bff91e3529ba/`, in `meta/metrics.json`
and `sta/opensta/{timing_metrics.rpt,p4-clock-bindings.rpt,opensta.log}`.
Both mapping checks retain four groups, 32 macros and CPU/main SRAM on SYS.
Timing remains observational and unclosed; library area is not routed die area
and zero observed min violations do not constitute PVT/hold signoff. Lint
signature/count sets are identical to `09-30` (470/414 new against the unchanged
ICS55/IHP130 baselines, zero increased). Successful ABC Liberty-cache fallback
and NFS clock-skew observations remain logged. No baselines were regenerated.
Commands used the locked activation, `BUILD_TIMESTAMP=2026-10-11-23-35` and
`JOBS=3`. The `run_flow.py` JSON/log pairs in the correction evidence root retain
every exact command: `focused`, `quality`, `pytest-full`, `regress-ics55`,
`regress-ihp130`, `metrics-ics55`, `metrics-ihp130`, four `dry-run-*` records
and `audit-result`. The principal acceptance commands were:
```sh
source .cache/retrosoc/development/tiny-r2-p1/activate.sh
export BUILD_TIMESTAMP=2026-10-11-23-35 JOBS=3
make rtl-format-check rtl-style-check-all rtl-readiness-check-all
ruff check .
python3 -m pytest -q --tb=short --basetemp=build/ics55-tiny-2026-10-11-23-35-b4aa59f3e5d9/meta/tiny-r2-p5-p1/tests-full
python3 scripts/regress.py --root . --suite pr --soc TINY --pdk ICS55
python3 scripts/regress.py --root . --suite pr --soc TINY --pdk IHP130
make CONFIG=configs/ci/ics55-tiny.mk SOC=TINY PDK=ICS55 APP=ci_smoke SYNTH=YOSYS STA=OPENSTA metrics
make CONFIG=configs/ci/ihp130-tiny.mk SOC=TINY PDK=IHP130 APP=ci_smoke SYNTH=YOSYS STA=OPENSTA metrics
git diff --check
```
New executions must select a fresh timestamp/evidence directory. New matched
workload replay, compiler experiments, full-function C netlist qualification,
PDF build/visual promotion, CDC/RDC, extracted PVT/MMMC and place/route were
not run. No performance, new operating-frequency or complete-product claim is
made by this correction. Error completion does not roll back downstream writes
or peripheral side effects that committed before reset.
## Target-local reset repair (2026-10-11)
This is the superseded first attempt. Its focused tests passed but omitted
required protocol checks; the later `09-30` campaign and independent monitors
rejected it. The following description records that attempted behavior, not
the corrected implementation above.
The P5 review found that a target reset was previously used only as an
admission enable while an accepted controller session remained live. The
attempt carried the explicit per-target reset leaf into each controller.
Admission and downstream AXI VALID/W/RREADY are blocked while that leaf is
asserted. An accepted Address or Response session changes to an abort response
and returns one DECERR RLAST or B response with the saved owner and ID; the
response remains held until the upstream terminal handshake. Response spill
registers flush during the target reset, so an old downstream response cannot
be replayed. The normal terminal handshake releases the fabric owner credit
and records the reset termination as one fault event.
The focused reset-isolation fixture covers an accepted SRAM read held before
response and an accepted XPI/APB write held before W, under both Icarus and
Verilator. It checks DECERR termination, no stale response replay, and owner
credit release. These local reruns are separate from the earlier matched-image
campaign; the campaign evidence and PPA/STA attribution remain unchanged until
a new approved evidence capture is made. The retained focused test output is
under `build/ics55-tiny-2026-10-10-18-58-b4aa59f3e5d9/meta/tiny-r2-p5/reset-repair-tests/`.
## Ownership and bounds
The three ingress slots are CPU slow path 0, central DMA 1 and reserved SDIO 2.
Only slots 0/1 have product producers. CPU has one combined read/write credit;
DMA and the reserved interface each have one read and one write credit.
The real DMA already reserves complete write data before starting a burst;
its shared scheduler and all request/channel assignments are unchanged.
Targets 0–3 directly attach the existing SRAM group frontends through Common
AXI connectors. Target 4 is XPI memory. Targets 5–20 follow the existing APB
peripheral order, with GPIO's two windows sharing its one branch. Targets 21/22
are the unchanged DECERR/SLVERR responders. The generator emits the decoder
and sixteen single-peripheral bridge instances from canonical topology.
There is no product-wide APB or external transaction lock.
Each target admits one combined transaction and saves owner, direction and
address attributes on the upstream address handshake. Admission reserves both
target and ingress direction, even while the downstream address is stalled.
Owner round robin advances on admission; the chosen owner's direction
preference alternates when both directions compete. W waits for saved AW
ownership and downstream AW acceptance, and stops after the terminal data beat.
Bounded Common response spill registers isolate held R/B values. Ownership and
ingress credit last through the upstream B/RLAST handshake, including buffered
terminal responses. A continuously eligible owner can be bypassed by at most
two other admissions; progress requires clocks and peer completion.
Local CPU I/D, group arbitration, P4 response reservations and main SRAM macro
paths are unchanged. The local uncontended bound remains three SYS cycles,
and the P4 external SRAM streaming fixture still requires one beat per cycle.
All new logic uses SYS; APB bridges follow their existing peripheral reset
leaves. Target reset must be released for admission. System reset invalidates
all buffered sessions without clearing SRAM payload. Hart reset drains CPU
work only. Runtime peripheral resets still require P7's quiescence controls.
## Faults and public compatibility
Each target retains its first failed captured read beat's address/response
until terminal upstream completion; a successful final beat cannot erase it.
Write errors retain the accepted AW address. Six external direction events and
two local CPU events feed the collector. CPU frontend errors carry SLVERR;
external errors propagated through AHB are not counted again locally.
The collector increments the existing count by every simultaneous event,
using four-bit multiplicity and a 33-bit saturating sum. It selects metadata
by ascending owner, CPU instruction before data, then read before write.
Equal keys use stable input order. SYSCTRL preserves a valid first snapshot;
W1C clears validity only and a simultaneous hardware event wins over clear.
FAULT_MASTER adds reserved owner 2 without changing offset or owner 0/1 meanings.
FAULT_DETAIL remains raw AXI response 2/3. The existing HAL byte-sized owner
field needs no change. No SDK API, register generator, interrupt, DMA channel,
capability or pad assignment is added.
The old CPU/DMA wait registers retain their predicate: a requesting owner with
no address handshake in that cycle counts once. R2_P5_FABRIC records separate
active-target maxima, DMA simultaneous read/write ownership and simultaneous
read/write beat handshakes. These are testbench observations. The original
P3/P4 record formats and evidence retain their own identities.
## Verification and reproducibility
Focused protocol tests use all three owner slots, saved-address perturbations,
W-before-AW, held addresses/data/responses, independent control branches,
same-target contention and 96 deterministic randomized reads. Mixed-response
bursts align two terminal faults while their final responses are OKAY.
SYSCTRL tests cover multi-event count, owner 2, hardware-over-W1C and saturation.
The saturation test seeds the Common counter register, then exercises the
real adder and readback; it does not simulate billions of events.
The system fixture instantiates production retrosoc_tiny, CPU, DMA, SRAM,
bridges and SYSCTRL. Only the XPI boundary is replaced with a controllable
verification endpoint and a fixture-only reset ROM. Real DMA copies B2 to B3
with concurrent read/write beats and CPU readback checks. With an XPI mapped
response held, SRAM code and SYSCTRL access continue. A held XPI APB write
then verifies independent controls and hart-reset isolation. Finally CPU aborts
a held DMA read, checks that BUSY survives accepted work, releases the response
and verifies ABORTED. Ordinary regressions retain real pin-level XPI/NOR models.
This fixture is neither a product Boot ROM nor an XPI-device qualification.
The first fixture attempted hart reset while holding the XPI memory response;
an accepted CPU instruction also needed that target, so reset correctly waited
for drain. The corrected isolation case retains DMA at the independent APB
branch and permits CPU progress. Other development attempts preserve sv2v
diagnostics separately from converted RTL and observe declared interface
instances rather than module-modport aliases for Icarus compatibility.
Yosys induction checks production target ownership, response retention,
terminal-fault attribution and service-count fairness. A combinational proof
checks the collector's count and lexicographic metadata selection for arbitrary
event vectors. These are scoped control proofs, not full-chip formal signoff.
The P5 replay entrypoint uses the unchanged P3 flat/banked images referenced by
the repaired P4 report:
`build/ics55-tiny-2026-10-10-15-15-b4aa59f3e5d9/meta/tiny-r2-p4/report.json`.
The old reports retain their original 4db27524-based snapshot attribution;
preflight matched all 1972 technical inputs to the committed P5 starting point.
P5 report identity, source/configuration/tool hashes and observations are new.
Parameters stay 1024 words, four jobs, 4096 CPU iterations and seed 0x12345678.
Each Verilator image runs once and banked Icarus once; all banked observations
must agree. There is no frozen overall speedup requirement or compiler/layout
experiment. Different-bank concurrency is proved separately because the
retained banked placement concentrates DMA storage in B3.
Fresh development evidence begins at
`build/ics55-tiny-2026-10-10-18-25-b4aa59f3e5d9/meta/tiny-r2-p5/`.
The completed campaign and its monitor-only follow-up are recorded below.
Documentation remains here rather than in generated directories.
The Tiny draft/source extractor follows P5 routing and the fault field width;
its reviewed publication SHA and PDF status are not advanced.
## Completed campaign and monitor correction
The final evidence root is
`build/ics55-tiny-2026-10-10-18-58-b4aa59f3e5d9/meta/tiny-r2-p5/`.
`source.json` binds the initial 2010-input campaign to combined ICS55/IHP130
digest `e85f4245883bf35f152d607480adf48edcf49debadb46d8b0bde92050a792b11`.
Both Tiny PR regressions, full Pytest and the first three image executions
passed on that snapshot. The initial aggregation in `report-result.{json,log}`
correctly failed: Icarus reported active-target maxima one greater than
Verilator in all eight banked windows, while every other sample was identical.
The locked Icarus 13 installation miscounts the temporary expression
`$countones(~a_23_bit_vector)`. `countones_probe.sv` and its retained log
demonstrate an expected count 1 returning 3/6, while a named vector and explicit
bit counting return 1. The correction counts the declared 23 bits explicitly,
rejects unknown target states, and adds an opt-in count 0..23 self-check. No
postprocessing subtraction, relaxed comparison, tool upgrade or DUT fix is used.
`monitor-source.json` captures the final combined digest
`0f3ed21f6fd6e932ff2532aab8c9964e71cf4b972e4c27e2c8ca6ab1f22c1015`.
Its only technical difference from the initial capture is
`rtl/tiny/dv/retrosoc_tiny_tb.sv`, inside P5 opt-in observation/self-check code.
The DUT, firmware, verification fixtures, policies and tool inputs are identical.
The ordinary regression commands do not enable this branch, and netlist boot
excludes it. Their results, full-suite results and PPA retain their original
snapshot attribution; they were not rerun or relabeled as new executions.
The audit checks this exact single-file delta and the original command flags.
Both Verilator images and banked Icarus were rebuilt/replayed after the fix,
and the monitor self-check passed on both new models. The final ICS55-only
replay input digest is
`df14713ed7177d988d0e3fa0a038984589584788eda994dd7480f01a8dfcab7c`.
`report-final.{json,log}` and `report.json` confirm exact equality of all banked
observations across simulators, including the corrected active-target statistic.
`monitor-diagnosis.json` preserves the original discrepancy, logs and input
identity. Final model copies are retained beside their attempt records and
bound by `model-backup-*.json`; the initial Icarus model is separately retained.
| Gate | Retained result | Scope/outcome |
| --- | --- | --- |
| Focused development suite | Earlier `18-25` root, `focused-final.{json,log}` | 106 passed before final campaign |
| Initial quality | `quality.{json,log}` | Ruff, diff, RTL format/style/readiness pass; 11.524 s |
| Full Pytest | `pytest-full.{json,log}` | 1679 passed, optional `pypdf` skip; 3867.891 s; original snapshot |
| Tiny ICS55 PR | `regress-tiny-ics55.{json,log}` | Pass; 4273.573 s; original snapshot |
| Tiny IHP130 PR | `regress-tiny-ihp130.{json,log}` | Pass; 3028.340 s; original snapshot |
| Initial image executions | `replay-verilator` / `replay-iverilog` results | Commands pass; 609.465/5952.278 s; cross-simulator aggregation fails as above |
| Monitor follow-up quality | `monitor-quality.{json,log}` | Pass; 11.714 s |
| Count self-checks | `monitor-check-{verilator,iverilog}.{json,log}` | Pass; instrumentation-only, not firmware acceptance |
| Final image executions | `replay-verilator-monitor` / `replay-iverilog-monitor` results | Pass; 618.974/5978.016 s; one run per selected image |
| Final report and audit | `report-final.{json,log}`, `report.json`, `evidence.json`, `audit.py` | Measurement complete; source-bound functional validation complete, review pending |
All six ordinary simulations satisfy command success, configured SIM_TEST_PASS
and absence of forbidden error markers. In the final full suite, six P4 SRAM
fixtures preserve three-cycle local latency and one-cycle streaming spacing;
two P4 CPU fixtures, two P5 fabric fixtures, two real-CPU/DMA system fixtures
and three scoped formal logs pass. The system fixture reports 13 simultaneous
DMA read/write beat cycles and 27 independent control completions on both tools,
with hart-reset isolation and abort drain. SYSCTRL tests additionally check
multiple faults, reserved owner 2, raw detail, clear precedence and saturation.
These are functional results, not physical or XPI-device qualification.
## Measurements and qualification limits
The final runs preserve the immutable P3 image identities and compare them
with repaired P4 measurements, without changing compiler or placement:
| Workload | Flat P4 → P5 cycles | Banked P4 → P5 cycles |
| --- | ---: | ---: |
| Empty | 69 → 69 | 66 → 66 |
| CPU | 73911 → 73911 | 73914 → 73914 |
| CPU copy | 102568 → 102568 | 86214 → 86214 |
| DMA burst1 | 91306 → 65527 | 89365 → 65502 |
| DMA burst16 | 17897 → 16974 | 17317 → 15802 |
| CPU/DMA contention | 299881 → 299990 | 299633 → 299738 |
| TCD contention | 299878 → 300027 | 299645 → 299750 |
| Chunk256 | 361285 → 361514 | 359570 → 361355 |
The flat burst16 window records a maximum of three active targets, 124 cycles
with simultaneous DMA read/write ownership and 48 simultaneous read/write beat
cycles. Banked DMA stays in B3 and has zero DMA read/write overlap; up to two
targets are active because independent CPU control can overlap that bank.
The separate system fixture establishes different-bank DMA concurrency.
CPU/copy cycles are unchanged. Small contention/TCD increases and the 0.50%
banked chunk256 increase remain visible; no universal speedup is claimed.
Synthesis/STA observations for the unchanged DUT use
`build/ics55-tiny-2026-10-10-18-58-42d5beccb726/` and
`build/ihp130-tiny-2026-10-10-18-58-bff91e3529ba/`.
Both pass synthesis, netlist boot and the existing 32-macro/direct-SYS binding
checks. Reports are `meta/metrics.json`, `sta/opensta/timing_metrics.rpt`,
`sta/opensta/p4-clock-bindings.rpt` and `sta/opensta/retrosoc_sta.log`.
| PDK | Cells | Library area | Max WNS / TNS (ns) | Min WNS / TNS (ns) |
| --- | ---: | ---: | ---: | ---: |
| ICS55 | 241309 | 1906156.940799 | -64.75 / -932689.50 | 0 / 0 |
| IHP130 | 256995 | 9888378.712794 | -111.93 / -2594456.50 | 0 / 0 |
Library area grows 5.56%/6.95% versus repaired P4. Max timing margins worsen;
timing remains unclosed and observational before the final product campaign.
Library area is not routed die area, and zero observed min violations is not
PVT/hold signoff. No new timing exception or rate is introduced.
The unchanged warning baselines report 469/413 new signatures for ICS55/IHP130,
zero increased and zero resolved. Relative to repaired P4, two new signatures
are intentional empty `owner_idle_o`/`target_idle_o` connections; their future
lifecycle use and verification visibility are documented at the instances.
The former unused interface `aresetn` signature disappears because target
admission now consumes it. There is no new width/latch signature. The known
NFS clock-skew warning and successful ABC Liberty-cache fallback remain logged.
No warning baseline or metrics policy was changed.
Mini behavioral compatibility retains its earlier P4 evidence and source
identity. The audit rechecks the unchanged shared/Mini/SDK/configuration inputs;
there is no new Mini execution or physical claim. No C/H implementation or
MISRA deviation changed. The P3 compiler matrix and three cold repetitions,
full C netlist campaign, PDF build/visual checks, CDC/RDC/PVT/MMMC, place/route,
high-rate qualification and P6+ work were not performed. Only the concretely
affected image measurements were repeated for the monitor defect.
## Executed commands
Commands used the locked activation and `BUILD_TIMESTAMP=2026-10-10-18-58`,
`JOBS=3`. `scripts/run_flow.py` retains the exact outer commands and JSON/log
results named above. New evidence is under `meta/tiny-r2-p5`; source snapshots,
tool identities, input digests, models, images and raw logs remain separate.
```sh
source .cache/retrosoc/development/tiny-r2-p1/activate.sh
export BUILD_TIMESTAMP=2026-10-10-18-58 JOBS=3
make rtl-format-check rtl-style-check-all rtl-readiness-check-all
ruff check .
git diff --check
python3 -m pytest -q --basetemp=build/ics55-tiny-2026-10-10-18-58-b4aa59f3e5d9/meta/tiny-r2-p5/tests-full
python3 scripts/regress.py --root . --suite pr --soc TINY --pdk ICS55 --dry-run
python3 scripts/regress.py --root . --suite nightly --soc TINY --pdk ICS55 --dry-run
python3 scripts/regress.py --root . --suite pr --soc TINY --pdk IHP130 --dry-run
python3 scripts/regress.py --root . --suite nightly --soc TINY --pdk IHP130 --dry-run
python3 scripts/regress.py --root . --suite pr --soc TINY --pdk ICS55
python3 scripts/regress.py --root . --suite pr --soc TINY --pdk IHP130
make CONFIG=configs/ci/ics55-tiny.mk SOC=TINY PDK=ICS55 SIMU=VERILATOR SOC_SIM_TIME=21600 \
TINY_P5_REFERENCE=build/ics55-tiny-2026-10-10-15-15-b4aa59f3e5d9/meta/tiny-r2-p4/report.json \
TINY_P5_CASES=perf-o2-plain-flat,perf-o2-plain-banked tiny-r2-p5-replay
make CONFIG=configs/ci/ics55-tiny.mk SOC=TINY PDK=ICS55 SIMU=IVERILOG SOC_SIM_TIME=21600 \
TINY_P5_REFERENCE=build/ics55-tiny-2026-10-10-15-15-b4aa59f3e5d9/meta/tiny-r2-p4/report.json \
TINY_P5_CASES=perf-o2-plain-banked tiny-r2-p5-replay
make CONFIG=configs/ci/ics55-tiny.mk SOC=TINY PDK=ICS55 tiny-r2-p5-report
make CONFIG=configs/ci/ics55-tiny.mk SOC=TINY PDK=ICS55 APP=ci_smoke SYNTH=YOSYS STA=OPENSTA metrics
make CONFIG=configs/ci/ihp130-tiny.mk SOC=TINY PDK=IHP130 APP=ci_smoke SYNTH=YOSYS STA=OPENSTA metrics
PYTHONDONTWRITEBYTECODE=1 python3 build/ics55-tiny-2026-10-10-18-58-b4aa59f3e5d9/meta/tiny-r2-p5/audit.py
```
Tiny PR/nightly definitions matched and were not executed twice. The first
report attempt failed; only the monitor-corrected executions qualify the final
cross-simulator report. The original cold executions are not a three-repeat
qualification campaign. No commit, staging, push, PR or automatic next-phase
transition belongs to this implementation.
+387
View File
@@ -0,0 +1,387 @@
# TINY-R2-P4 local CPU/SRAM implementation
## Approved throughput repair (2026-10-10; validated, review pending)
The maintainer approved the eight-file P4 throughput-repair preflight after
the read-only review identified the external frontend's per-beat bubbles.
This follow-up remains P4; it does not enable P5 target concurrency.
The initial implementation and its measurements below are retained historical
evidence for the pre-repair snapshot, not qualification of the changed RTL.
Fresh evidence is under
`build/ics55-tiny-2026-10-10-15-15-b4aa59f3e5d9/meta/tiny-r2-p4/`.
The group now reserves two response slots for its external client, counting
both queued completions and a macro read still in flight. A response pop may
free capacity on the same edge as the next issue. The macro's saved owner/bank
still selects the captured read value; FIFO data cannot be overwritten by
later local accesses. A same-client pending read prevents a simultaneous write
commit into the single-push response FIFO. Local I/D keep one operation each
and their original completion pipeline.
The external frontend separates issued and retired read positions. W uses a
two-entry Common FIFO containing captured address, data and byte strobes.
Only captured legal beats enter arbitration. Invalid strobes or LAST retain
the existing error policy, including earlier committed beats and later legal
beats after an invalid strobe. B waits for all queued legal writes and their
commit responses. External transaction ownership still lasts through B/RLAST.
These are small register queues, not additional user SRAM or new macros.
Acceptance adds consecutive 16-beat read/write handshakes at one SYS cycle per
beat after startup, without local competition or bus backpressure. Logs retain
first-beat latency, total transfer time and final-W-to-B delay. Directed checks
fill/drain queues, stop READY, interleave local service, retain old read values
across local writes, exercise buffered malformed writes and drop stale queued
responses on system reset. Induction checks response conservation/capacity in
addition to held-response stability and the existing service-count fairness.
The unchanged CPU debug/reset, discovery/counters and both real-PDK macro
fixtures remain required.
Use the same immutable P3 flat/banked images and parameters. Each selected
Verilator image runs once; banked Icarus runs once. Compare both with their P3
samples and the pre-repair P4 report. The repair requires burst-16 cycles below
36713/36489 respectively, truthful reporting of all other workload deltas and
identical banked results across simulators. It does not promise P3 cycle parity
or change compiler/layout settings. Existing shared Mini debug inputs are
unchanged by this repair; matching prior compatibility evidence is retained.
### Repair results and provenance
The campaign is complete for review. `source.json`, `report.json`, `audit.py`
and `evidence.json` in the fresh evidence root bind 1972 technical inputs,
six fresh Tiny regression simulations, three matched-image executions,
six real/behavioral SRAM fixtures, two CPU fixtures and control induction.
The combined ICS55/IHP130 input digest is
`58daf68fbb9dee199b3085a73ef07d22eb3de6d00ba875c462e85b76b6091a0d`;
the ICS55 replay input digest is
`a96a129ffc02d0d38fd2eee7ed8fb62893828db3602bbd4459e0a71ffce0c35f`.
Only the approved six technical files differ from the preceding P4 snapshot;
the other two changes are this runbook and the ledger. No commit or staging
operation is part of this campaign.
All six SRAM runs record `read_gap_max=1 write_gap_max=1 beats=16`, with first
R at three cycles after AR and final R at cycle18; W begins one cycle after
AW, final B is at cycle20, four cycles after final W. These are standalone
uncontended protocol measurements, not end-to-end DMA or physical timing.
All six retain `local_latency_max=3`. The new queue checks cover full-response
backpressure, local writes to already captured read addresses, W contention,
buffered malformed writes, and both queued-R and accepted-but-uncommitted-W
system resets. CPU debug/patch/hart-reset and induction pass in the final suite.
| Gate | Result under the fresh evidence root | Outcome |
| --- | --- | --- |
| Final Ruff, diff, RTL format/style/readiness | `quality-pass.{json,log}` | Pass |
| Full Pytest | `pytest-full.{json,log}` | 1659 passed, one optional `pypdf` skip; 3903.061 s |
| Tiny ICS55 PR | `regress-tiny-ics55.{json,log}` | Pass; 3396.035 s |
| Tiny IHP130 PR | `regress-tiny-ihp130.{json,log}` | Pass; 2340.150 s |
| Verilator, both immutable images once | `replay-verilator.{json,log}` | Pass; 390.698 s |
| Icarus, immutable banked image once | `replay-iverilog.{json,log}` | Pass; 5154.223 s |
| Cross-simulator report | `report-result.{json,log}`, `report.json` | Complete; all banked observations identical |
| Source/artifact audit | `evidence.json`, `audit.py` | Pass; review/phase acceptance pending |
The initial `quality-final.{json,log}` is a retained failed development attempt:
the style gate required `s_req_accept`/`s_resp_accept` abbreviations. The names
were corrected before the final source snapshot and all long gates. The first
29 focused checks and additional queued-W-reset check also passed; final-suite
logs qualify the final technical snapshot. No failed attempt is relabeled.
| Workload | Flat pre-repair P4 → repaired P4 | Banked pre-repair P4 → repaired P4 |
| --- | ---: | ---: |
| Empty | 69 → 69 | 66 → 66 |
| CPU | 73911 → 73911 | 73914 → 73914 |
| CPU copy | 102568 → 102568 | 86214 → 86214 |
| DMA burst1 | 91306 → 91306 | 89365 → 89365 |
| DMA burst16 | 36713 → 17897 | 36489 → 17317 |
| CPU/DMA contention | 303493 → 299881 | 299633 → 299633 |
| TCD contention | 303702 → 299878 | 299645 → 299645 |
| Chunk256 | 377705 → 361285 | 376970 → 359570 |
Burst16 cycles decrease 51.25%/52.54%, satisfying the approved repair criterion.
They remain 8.34%/4.81% above the original P3 16520/16523 cycles. Burst1 still
retains the earlier P4 overhead versus P3; this repair does not claim universal
baseline parity. Flat local maximum is four cycles in contended windows, with
17697 eligible bank-conflict cycles; banked maximum is three with zero conflicts.
Uncontended local latency is still checked independently. Firmware checksums,
parameters and each image's compiler/layout identity remain unchanged.
Fresh physical variants are
`build/ics55-tiny-2026-10-10-15-15-42d5beccb726/` and
`build/ihp130-tiny-2026-10-10-15-15-bff91e3529ba/`.
Both pass synthesis, netlist boot and common-SYS/32-macro binding checks.
Their `meta/metrics.json`, `sta/opensta/timing_metrics.rpt`,
`sta/opensta/p4-clock-bindings.rpt` and `sta/opensta/retrosoc_sta.log` retain
the matching observations:
| PDK | Cells | Library area | Setup WNS / TNS (ns) | Hold WNS / TNS (ns) |
| --- | ---: | ---: | ---: | ---: |
| ICS55 | 207029 | 1805809.700799 | -36.88 / -489807.34 | 0 / 0 |
| IHP130 | 218773 | 9245382.228597 | -97.37 / -1640251.62 | 0 / 0 |
Setup remains unclosed; library area is not die area and zero observed hold
slack violations are not physical signoff. Lint comparisons to the unchanged
baselines report 468/412 new signatures for ICS55/IHP130, zero increased and
zero resolved. Relative to pre-repair P4 there are exactly two additional
signatures: intentionally unused FIFO `cnt_o`, and `s_full`, which is consumed
by the verification assertions but unused in synthesis-oriented lint. No new
width/latch warning is attributed to the repair. Warning and metrics policy
remain observational and no baseline was edited.
Commands below used the same timestamp, with `scripts/run_flow.py` retaining
outer command/result/log records. Standalone test artifacts use distinct
`tests-focused`, `tests-reset` and `tests-full` directories under this variant.
```sh
source .cache/retrosoc/development/tiny-r2-p1/activate.sh
export BUILD_TIMESTAMP=2026-10-10-15-15 JOBS=3
python3 -m pytest -q tests/test_tiny_r2_p4.py --basetemp=build/ics55-tiny-2026-10-10-15-15-b4aa59f3e5d9/meta/tiny-r2-p4/tests-focused
python3 -m pytest -q 'tests/test_tiny_r2_p4.py::test_four_group_local_memory[BEHAV-iverilog]' --basetemp=build/ics55-tiny-2026-10-10-15-15-b4aa59f3e5d9/meta/tiny-r2-p4/tests-reset
ruff check .
git diff --check
make rtl-format-check rtl-style-check-all rtl-readiness-check-all
python3 -m pytest -q --basetemp=build/ics55-tiny-2026-10-10-15-15-b4aa59f3e5d9/meta/tiny-r2-p4/tests-full
python3 scripts/regress.py --root . --suite pr --soc TINY --pdk ICS55 --dry-run
python3 scripts/regress.py --root . --suite nightly --soc TINY --pdk ICS55 --dry-run
python3 scripts/regress.py --root . --suite pr --soc TINY --pdk IHP130 --dry-run
python3 scripts/regress.py --root . --suite nightly --soc TINY --pdk IHP130 --dry-run
python3 scripts/regress.py --root . --suite pr --soc TINY --pdk ICS55
python3 scripts/regress.py --root . --suite pr --soc TINY --pdk IHP130
make CONFIG=configs/ci/ics55-tiny.mk SOC=TINY PDK=ICS55 SIMU=VERILATOR SOC_SIM_TIME=21600 \
TINY_P4_REFERENCE=build/ics55-tiny-performance-2026-10-09-17-10-53cc6674d532/meta/tiny-r2-p3/report.json \
TINY_P4_CASES=perf-o2-plain-flat,perf-o2-plain-banked tiny-r2-p4-replay
make CONFIG=configs/ci/ics55-tiny.mk SOC=TINY PDK=ICS55 SIMU=IVERILOG SOC_SIM_TIME=21600 \
TINY_P4_REFERENCE=build/ics55-tiny-performance-2026-10-09-17-10-53cc6674d532/meta/tiny-r2-p3/report.json \
TINY_P4_CASES=perf-o2-plain-banked tiny-r2-p4-replay
make CONFIG=configs/ci/ics55-tiny.mk SOC=TINY PDK=ICS55 tiny-r2-p4-report
make CONFIG=configs/ci/ics55-tiny.mk SOC=TINY PDK=ICS55 APP=ci_smoke SYNTH=YOSYS STA=OPENSTA metrics
make CONFIG=configs/ci/ihp130-tiny.mk SOC=TINY PDK=IHP130 APP=ci_smoke SYNTH=YOSYS STA=OPENSTA metrics
PYTHONDONTWRITEBYTECODE=1 python3 build/ics55-tiny-2026-10-10-15-15-b4aa59f3e5d9/meta/tiny-r2-p4/audit.py
```
Tiny PR/nightly dry-runs match; no duplicate nightly execution was needed.
The six fresh ordinary simulations all require `SIM_TEST_PASS`, including
Tiny netlist boot, together with command success and no forbidden error marker.
Mini's two prior behavioral results retain their original source attribution;
the audit verifies that changed technical inputs are confined to Tiny and its
tests. Mini synthesis/STA, the P3 compiler matrix, additional cold repetitions,
C format/policy/host reruns, PDF generation, PVT/MMMC, CDC/RDC and place/route
were not run for this repair. No C/H code or MISRA deviation changed. The
full-suite optional PDF retrieval check remains skipped for missing `pypdf`.
No new frequency, physical qualification or P5 authorization is claimed.
## Original implementation snapshot
This implements the approved [P4 contract](tiny-soc.md#tiny-r2-p4---dual-port-hazard3-and-four-bank-local-sram)
from `4db27524a6282ca40d76b935415b89f043d6ea83`, specification blob
`7e19de49d49efaafacccfa85671e314d53dd15a4`. The maintainer approved the preflight
and implementation. No P5 execution, dependency update, publication release or
physical-frequency qualification is implied. Historical phase evidence remains
in the [ledger](tiny-soc-verification.md).
## Architecture and lifecycle
Tiny uses locked `hazard3_cpu_2port`, preserving ISA, IRQ, debug, multiply and
branch-prediction settings; A remains disabled. Separate I/D AHB frontends decode
SRAM before the tagged slow-path merge. One accepted D operation spans all
targets. Write data is captured in its AHB data phase; local stores complete
after macro commit. Uncontended local completion is at most three SYS cycles.
Slow errors retain two-cycle AHB completion. Instruction origin reaches the
existing fabric fault path: only NOR boot/NSS0 may be fetched externally, and
peripheral fetches cannot trigger register reads.
Four 32 KiB groups cover `0x30000000..0x3001ffff`, each with eight existing
4 KiB macros and I/D/external round robin advancing only on macro issue.
Per-client registers capture read responses before macro outputs change.
External W waits and R/B backpressure do not lock local macro service.
Continuously eligible requests wait for at most two other issues, conditional
on progress rather than a wall-clock bound for stopped peers.
Each external frontend retains one transaction through B/RLAST and alternates
read/write preference. A direction-tagged demux preserves the existing external
fabric attachment. Whole-product different-target DMA read/write concurrency
remains P5; standalone frontend concurrency is not that product qualification.
Hart reset requests debug halt, prevents new debug instruction injection, drains
accepted local/slow operations and responses, then resets the hart/frontends.
A terminal-cycle AHB overlap must still drain. SRAM/external frontends and
unrelated DMA stay active. System reset invalidates pending responses without
clearing SRAM payload. Five-edge release and Mini's default single-port reset
behavior are preserved; shared debug only adds a pending-state output.
## ABI, physical bindings and observations
Shared SRAM register definitions are unchanged: 128 KiB, `BANK_COUNT=32`,
`BANK_BYTES=4096`. Public counters count external AXI events, saturating sums
of simultaneous events; stall cycles use OR across groups. Local CPU traffic
is not silently added. `R2_AHB` observes the separate I/D ports. `R2_P4_LOCAL`
records local completions, latency and address-admission waits; `R2_P4_BANK`
records macro issues, eligible-client waits and conflict cycles (two or more
eligible clients in a group). These are verification-only observations.
Macro `g,b` is `u_soc.u_sram.gen_group[g].u_group.gen_bank[b].u_ram.u_mem`,
physical index `8*g+b`. IHP130 placement coordinates stay fixed while instance
and supply hooks track both indices. CPU and all 32 macros remain directly on
SYS24; ICS55 PLL stays off and IHP130 remains no-PLL compatibility. Mapping
checks reject stale single-port/flat-bank hierarchies. STA remains observational,
with no 96/192/240 MHz or physical signoff claim.
The Tiny publication extractor and affected draft prose/diagram follow the new
bindings. No reviewed publication SHA, delivered PDF or release status advances.
## Acceptance and matched images
`tests/test_tiny_r2_p4.py` uses real IHP130/ICS55 macro models and a behavioral
model on both simulators: all macro/group boundaries, byte masks, back-to-back
AHB, independent groups, held responses, W-before-AW, bursts/malformed writes,
discovery/counter semantics and reset payload retention. Deterministic readback
uses seed `0x12345678`, 4096 write/read pairs per group. The actual CPU fixture
checks mixed 16/32-bit cross-group fetch, FENCE.I, instruction faults, JTAG
patch/resume, and reset with a held slow response plus an unrelated held B.
Yosys induction proves group control fairness and retained responses with only
macro payload abstracted. Normal Tiny firmware additionally executes code
copied through the real DMA, then CPU-patched code, after completion/FENCE.I.
Its helper is private acceptance assembly, not a new SDK API.
`tiny-r2-p4-replay` validates retained P3 images/logs, builds a fresh P4 model,
and executes each selected image once. Image source/compiler/layout and new
RTL/model identities are separate; hardware settings must match. Original P3
same-source/three-cold-start rules are unchanged. `tiny-r2-p4-report` revalidates
artifacts and requires identical banked-image results across the two simulators.
The reference is
`build/ics55-tiny-performance-2026-10-09-17-10-53cc6674d532/meta/tiny-r2-p3/report.json`.
Select `perf-o2-plain-flat` and `perf-o2-plain-banked` for Verilator and the banked
image for Icarus. Parameters remain 1024 words, four jobs, 4096 CPU iterations,
seed `0x12345678`, burst-1/16 DMA, TCD and chunk-256. Compare each unchanged
binary with its own historical samples, not with a different compiler/layout.
No fixed speedup is predeclared; latency/fairness requirements remain gates.
Development evidence begins under
`build/ics55-tiny-2026-10-10-10-05-b4aa59f3e5d9/meta/tiny-r2-p4/`.
Early fixture failures and corrected runs are retained separately. The final
campaign below completes planned functional validation and measurements; human
review/phase acceptance remain pending, including the DMA throughput tradeoff.
## Executed campaign and provenance
The evidence root above contains `source.json`, `evidence.json`, `audit.py` and
`report.json`. The campaign snapshot contains 1972 unchanged technical inputs,
base commit `4db27524a6282ca40d76b935415b89f043d6ea83` plus the retained patch and
new-file archive, and locked tool/executable/archive identities. Its combined
ICS55/IHP130 digest is
`9fa311a5500711c99b006eacd95cdff04a74659d52b5b46640c42c49773a7f66`.
The ICS55-only replay source set has digest
`2f0d285186f84d41422d8381d09b0a01a2afc066dbd011a11ec7a8a85433ad0d`;
these are different declared input sets, not conflicting identities.
The final audit rechecks all captured inputs, eight ordinary regression
simulations, the three matched-image executions, six standalone memory runs,
two actual-CPU runs and the group-control induction log. Ordinary firmware and
model artifacts are hashed post-run; the campaign source hashes were captured
before execution and rechecked afterward. Image/model identities and original
P3 phase attribution are retained separately.
| Gate | Retained result under the evidence root | Outcome |
| --- | --- | --- |
| Format/style/readiness, C policy/host, Ruff/diff | `quality-final.{json,log}` | Pass; no new MISRA deviation; partial checks, not MISRA certification |
| Full Pytest | `pytest-full.{json,log}` | 1659 passed, one optional `pypdf` environment skip; 3856.630 s |
| Tiny ICS55 PR | `regress-tiny-ics55.{json,log}` | Pass; 3295.064 s |
| Tiny IHP130 PR | `regress-tiny-ihp130.{json,log}` | Pass; 2297.373 s |
| Mini ICS55 behavioral-only PR | `regress-mini-ics55.{json,log}` | Pass; 3093.379 s; no Mini physical evidence |
| P4 Verilator replay | `replay-verilator.{json,log}` | Both selected images once; 357.537 s including build/audit |
| P4 Icarus replay | `replay-iverilog.json`, `replay-iverilog.json.log` | Banked image once; 5214.736 s including build/audit |
| Matched-image aggregation | `report-result.json`, `report.log`, `report.json` | `measurement_complete`; all banked samples match across simulators |
The six standalone memory logs report `local_latency_max=3`, with real
simultaneous group issues. Those protocol fixtures use a 10 ns verification
clock; product replays and timing checks use SAFE24. The banked product replay
also observes three-cycle maximum local latency and zero eligible bank-conflict
cycles. No physical frequency qualification follows from functional models.
Commands were run through `scripts/run_flow.py` for retained logs/results:
```sh
source .cache/retrosoc/development/tiny-r2-p1/activate.sh
export BUILD_TIMESTAMP=2026-10-10-10-05
export JOBS=3
make rtl-format-check rtl-style-check-all rtl-readiness-check-all
make sw-format-check sw-policy-check sw-host-test
ruff check .
git diff --check
python3 -m pytest -q --basetemp=build/ics55-tiny-2026-10-10-10-05-b4aa59f3e5d9/meta/tiny-r2-p4/tests-full
python3 scripts/regress.py --root . --suite pr --soc TINY --pdk ICS55 --dry-run
python3 scripts/regress.py --root . --suite nightly --soc TINY --pdk ICS55 --dry-run
python3 scripts/regress.py --root . --suite pr --soc TINY --pdk IHP130 --dry-run
python3 scripts/regress.py --root . --suite pr --soc MINI --pdk ICS55 --behavioral-only --dry-run
python3 scripts/regress.py --root . --suite pr --soc TINY --pdk ICS55
python3 scripts/regress.py --root . --suite pr --soc TINY --pdk IHP130
JAVA_TOOL_OPTIONS=-Dsbt.server.forcestart=true python3 scripts/regress.py \
--root . --suite pr --soc MINI --pdk ICS55 --behavioral-only
make CONFIG=configs/ci/ics55-tiny.mk SOC=TINY PDK=ICS55 SIMU=VERILATOR SOC_SIM_TIME=21600 \
TINY_P4_REFERENCE=build/ics55-tiny-performance-2026-10-09-17-10-53cc6674d532/meta/tiny-r2-p3/report.json \
TINY_P4_CASES=perf-o2-plain-flat,perf-o2-plain-banked tiny-r2-p4-replay
make CONFIG=configs/ci/ics55-tiny.mk SOC=TINY PDK=ICS55 SIMU=IVERILOG SOC_SIM_TIME=21600 \
TINY_P4_REFERENCE=build/ics55-tiny-performance-2026-10-09-17-10-53cc6674d532/meta/tiny-r2-p3/report.json \
TINY_P4_CASES=perf-o2-plain-banked tiny-r2-p4-replay
make CONFIG=configs/ci/ics55-tiny.mk SOC=TINY PDK=ICS55 tiny-r2-p4-report
make CONFIG=configs/ci/ics55-tiny.mk SOC=TINY PDK=ICS55 APP=ci_smoke SYNTH=YOSYS STA=OPENSTA metrics
make CONFIG=configs/ci/ihp130-tiny.mk SOC=TINY PDK=IHP130 APP=ci_smoke SYNTH=YOSYS STA=OPENSTA metrics
```
Tiny PR and nightly expand to the same commands; nightly was not duplicated.
Each simulation requires command success, its configured terminal marker and
no forbidden-error marker. All selected ordinary targets require SIM_TEST_PASS,
including Tiny netlist boot. UART text alone is insufficient. Development
attempts are not counted as extra qualifying cold starts.
## Measurements and remaining qualification
Raw cycles below compare each immutable binary with its own P3 baseline.
The report also retains retired instructions, CPI inputs, empty calibration,
local waits, macro issues/conflicts and external traffic accounting.
| Workload | Flat P3 → P4 cycles | Banked P3 → P4 cycles |
| --- | ---: | ---: |
| CPU | 73951 → 73911 | 73954 → 73914 |
| CPU copy | 127157 → 102568 | 114875 → 86214 |
| DMA burst 1 | 64577 → 91306 | 64583 → 89365 |
| DMA burst 16 | 16520 → 36713 | 16523 → 36489 |
| CPU/DMA contention | 310368 → 303493 | 310383 → 299633 |
| TCD contention | 310665 → 303702 | 310683 → 299645 |
| Chunk 256 | 373812 → 377705 | 374016 → 376970 |
CPU-copy cycles decrease by about 19%/25%, while isolated burst-16 DMA takes
about 2.2 times the baseline cycles. The current external frontend processes
issue, completion and bus response separately for each beat; preserving local
response isolation costs streaming throughput. This regression is retained for
review, not hidden by the CPU-copy result or a changed compiler/layout. P5's
external target concurrency alone does not remove these per-beat waits. No
fixed speedup was an acceptance gate, but the tradeoff needs explicit assessment
before phase acceptance or a throughput claim.
Actual synthesis/STA variants are
`build/ics55-tiny-2026-10-10-10-05-42d5beccb726/` and
`build/ihp130-tiny-2026-10-10-10-05-bff91e3529ba/`.
Both emit `TINY_R2_P4_BINDING_PASS` and pass netlist boot. Their raw reports are
`sta/opensta/timing_metrics.rpt`, `p4-clock-bindings.rpt`, `retrosoc_sta.log` and
`meta/metrics.json`.
| PDK | Cells | Reported library area | Max WNS / TNS (ns) | Min WNS / TNS (ns) |
| --- | ---: | ---: | ---: | ---: |
| ICS55 | 206311 | 1803352.980799 | -40.55 / -518700.31 | -0.02 / -11.85 |
| IHP130 | 216216 | 9218095.882797 | -97.46 / -1626503.88 | -0.17 / -142.67 |
Timing is not closed. Library area is not placed/routed die area. Lint baseline
comparisons remain non-blocking observations: Tiny ICS55 466 new signatures,
Tiny IHP130 410 new, Mini ICS55 1537 new / 118 resolved, all with zero increased.
Compared with the preceding P3 lint runs, newly observed signatures are intentional
empty named output connections: Tiny's unused master IDs, exclusive/power/fence
outputs, plus Mini's ignored `reset_pending_o`. Tiny is cacheless with committed
stores, no A/SBA and no Xh3Power, so these outputs do not add interfaces or clock
gating. Baselines and metric policy were not edited. The logs retain the known
generated-file clock skew and successful ABC Liberty fallback observations.
The P3 eight-configuration matrix and three-cold-start campaign were not rerun.
No place/route, extracted/PVT/CDC/RDC signoff, high-rate qualification, new PDK
rollout or PDF build/visual check was performed. Publication snapshot enforcement
remains enabled; the reviewed PDF source revision was not advanced. Mini
DMA-to-WS2812 stays unsupported under option A, and product DMA/IRQ budgets
remain for a supported integration. P5 and later features are not implemented.
+284
View File
@@ -3,6 +3,9 @@
This record covers the two P1 findings approved for follow-up on
`bab01352f8f7c80e39546b04e3ed37a3d927f475` plus the existing P3 worktree.
It does not approve Mini fabric enablement or advance TINY-R2-P4.
Earlier sections retain their original development status and attribution;
the source-bound campaign below records completed validation on `3e46cc8`.
The final section separately records the subsequent two HAL race fixes.
## Retained Tiny Icarus finding: closed
@@ -182,6 +185,10 @@ STA observations qualify a new physical operating point.
## Compiler-matrix execution corrections (2026-10-09)
The intermediate snapshots below are historical. The final source-bound results
later in this record supersede their in-progress status without relabeling any
failed attempt or earlier artifact.
The initial flat-image collector could not read `_stack_point` from its symbol
listing. Flat placement now falls back to the retained linker map's explicit
`PROVIDE (_stack_point = .)` value; the exact SRAM endpoint and 4 KiB stack
@@ -286,3 +293,280 @@ the selected target's successful simulation checks. This behavioral-only run
provides no Mini synthesis, netlist, STA or physical evidence. Mini lint baseline
differences remain non-blocking observations, separately retained in the
`56f28cd9230c` variant's `meta/rtl-lint-warnings.json`.
## Final source-bound P3 matrix (2026-10-10)
After the external style commit and the publication/configuration corrections
settled, the final campaign was run against commit
`3e46cc80e2d2243233d284b7cd9583714b0773bb`, source digest
`a7fc5112cd6f20df956243c4d081a6a31ede6192cd270e3c13340557b0e58776`.
Its master record is
`build/ics55-tiny-performance-2026-10-09-17-10-53cc6674d532/meta/tiny-r2-p3/matrix/attempt-3jy5g5vo/record.json`;
the final report is in the same root at `meta/tiny-r2-p3/report.json`.
All eight cases pass image collection, normal Tiny acceptance and three cold
Verilator runs. The selected native Icarus cases also pass three cold runs:
- `compat-os-flat`: 7464.762, 7891.159 and 7428.430 seconds;
- `perf-o2-lto-flat`: 5841.742, 5890.497 and 6149.375 seconds;
- `perf-o2-plain-banked`: 5951.447, 6345.716 and 5964.039 seconds.
The collector validates Tiny `TEST_STATUS`/`SIM_TEST_PASS`, forbidden markers,
matched retained image/model/tool artifacts, source digests, three repetitions,
and cross-simulator sample identity. `report.json` remains
`measurement_complete`; the ledger's human phase gate is still required.
The earlier 09-25, 15-10 and 16-45 campaigns remain historical: source changes
during execution included the `352b200` style commit, the configuration/publication
repairs, and the performance profile's trailing-newline change. Their raw passing
samples and provenance failures are retained and are not relabeled as final
evidence. The final campaign uses the complete committed corrections in `3e46cc8`.
### Current validation and commands
The tested primary platform is TINY/ICS55, external SAFE24 with HAVE_PLL=YES
but the PLL parked off. CPU and the existing 128 KiB main SRAM remain at SYS24.
The IHP130 compatibility profile keeps no PLL. Dependencies, warning baselines,
metrics policy, hardware integration and the frozen specification blob
`7e19de49d49efaafacccfa85671e314d53dd15a4` are unchanged by this validation.
After activating `.cache/retrosoc/development/tiny-r2-p1/activate.sh`, the matrix
and final report commands were:
```sh
make CONFIG=configs/benchmark/ics55-tiny-performance.mk SOC=TINY PDK=ICS55 \
BUILD_TIMESTAMP=2026-10-09-17-10 SOC_SIM_TIME=21600 JOBS=3 \
TINY_P3_RESUME=NO tiny-r2-p3-matrix
make CONFIG=configs/benchmark/ics55-tiny-performance.mk SOC=TINY PDK=ICS55 \
BUILD_TIMESTAMP=2026-10-09-17-10 tiny-r2-p3-report
```
The current full Pytest run reports **1629 passed, 1 skipped** in 4200.10 seconds.
The one skipped PDF inspection test requires `pypdf`, absent from this environment;
it is not a hardware or simulation pass. Its command, complete log and result are
in the matrix root under `meta/tiny-r2-p3/quality/pytest.{log,json}`. The previous
4 failures and 94 fixture errors remain in their old logs; they are resolved by
the configuration declaration and publication/source-fixture corrections in
`3e46cc8`. The focused correction check also passed 149 tests with the same one
PDF skip. No PDF or publication source revision was advanced by these checks.
`ruff check .`, dependency-lock validation and `git diff --check` pass. The
current C format/policy/host and RTL format/style/readiness checks pass; their
exact Make command and outputs are retained under
`meta/tiny-r2-p3/quality/policy.{log,json}`. These are the repository's mechanical
quality checks, not complete MISRA certification; no new deviation is recorded.
Regression dry-runs selected TINY explicitly for both PDKs and MINI with
`--behavioral-only`. Tiny nightly resolves to the same commands as PR, so it was
not executed a second time. The final executions were:
```sh
BUILD_TIMESTAMP=2026-10-09-19-55 python3 scripts/regress.py --root . --suite pr --soc TINY --pdk ICS55
BUILD_TIMESTAMP=2026-10-09-19-56 python3 scripts/regress.py --root . --suite pr --soc TINY --pdk IHP130
JAVA_TOOL_OPTIONS=-Dsbt.server.forcestart=true BUILD_TIMESTAMP=2026-10-09-19-57 \
python3 scripts/regress.py --root . --suite pr --soc MINI --pdk ICS55 --behavioral-only
```
All three exit zero. Current evidence roots and results are:
| Scope | Evidence root below `build/` | Result |
| --- | --- | --- |
| Tiny ICS55 PR | `ics55-tiny-2026-10-09-19-55-42d5beccb726/` | `meta/tiny-r2-p3/regress-ics55.{log,json}`; behavioral/JTAG, netlist boot, synthesis and STA commands pass |
| Tiny IHP130 PR | `ihp130-tiny-2026-10-09-19-56-bff91e3529ba/` | `meta/tiny-r2-p3/regress-ihp130.{log,json}`; same target-specific gates pass |
| Mini ICS55 behavioral-only | `ics55-2026-10-09-19-57-d1cacf6d3b62/` | `meta/tiny-r2-p3/regress-mini.{log,json}`; SDRAM Verilator and Icarus assembly boot pass |
Each Tiny PR's HAVE_SVA=YES Verilator acceptance has its separate configuration
variant, identified in the retained command/log. Mini's SDRAM run uses
`dc24d22ad53d` and assembly boot uses `ec9e1f2e9c2c` at the same 19-57 timestamp.
Mini behavioral-only supplies no synthesis, netlist, STA or physical evidence.
The additional IHP130 performance/banked normal-firmware check also passes:
```sh
make CONFIG=configs/ci/ihp130-tiny.mk SOC=TINY PDK=IHP130 \
BUILD_TIMESTAMP=2026-10-10-00-13 SW_ISA_PROFILE=TINY_PERF SW_OPT=O2 SW_LTO=NO \
LINK_TYPE=ld2_tiny_banked SIMU=VERILATOR firmware sim
```
Its root is `build/ihp130-tiny-2026-10-10-00-13-8648aa4845d9/`, with the exact
command/result at `meta/tiny-r2-p3/performance-banked.{log,json}` and strict Tiny
simulation check at `sim/verilator/result-sim-check.json`. This is normal
firmware compatibility; it is not an additional IHP130 compiler measurement matrix.
The ICS55 platform auditor captured inputs before this execution and successfully
produced `meta/tiny-ics55-p1/report.json` in the actual APP=ci_smoke variant. This
reuses the platform audit under P3; its original phase label is unchanged. Metrics
were explicitly collected in each actual synthesis/STA variant rather than the
regression runner's final default-bringup observation directory:
| TINY PDK | Cells | Reported library area | Max WNS (ns) | Max TNS (ns) |
| --- | ---: | ---: | ---: | ---: |
| ICS55 | 197677 | 1786686.260799 | -41.14 | -508669.53 |
| IHP130 | 208547 | 9109320.296998 | -94.28 | -1594880.00 |
Both min WNS/TNS values are zero in these smoke reports. Full reports are at
`sta/opensta/timing_metrics.rpt` and `meta/metrics.json` within the corresponding
APP=ci_smoke roots. Library area is not placed/routed die area; negative margins
remain unclosed. No 96/192/240 MHz operation, routed/PVT signoff or physical
qualification is claimed, and no half-rate main-SRAM fallback is introduced.
Lint comparisons remain non-blocking observations: ICS55 Tiny has 459 new
signatures relative to its baseline, IHP130 Tiny 403, and Mini ICS55 1536 new /
118 resolved; all three have zero increased signatures. Counts are not attribution
of every warning to P3. The reports are in each timestamp's lint variant
(`303f95040df3`, `2dc2e5c747e5`, and `56f28cd9230c` respectively), under
`meta/rtl-lint-warnings.json`. Baselines were not edited.
### Software measurements and remaining boundaries
Each row below uses the retained P3 workload (1024 words, four jobs, 4096 CPU
iterations, seed `0x12345678`). Cycle counts are raw measured windows; the report
also retains the separate empty-window calibration, retired instructions, bus
observations and CPI. These are different compiler/layout binaries, not a
matched-binary architectural speedup claim.
| Case | BIN bytes | CPU cycles | Copy cycles | Burst-16 DMA cycles | Chunk-256 cycles | Observed stack bytes |
| --- | ---: | ---: | ---: | ---: | ---: | ---: |
| compat-os-flat | 11132 | 98682 | 164096 | 18881 | 511389 | 692 |
| perf-o2-plain-flat | 10148 | 73951 | 127157 | 16520 | 373812 | 648 |
| perf-o2-lto-flat | 8592 | 73974 | 127180 | 13980 | 339167 | 552 |
| perf-o3-plain-flat | 10404 | 73951 | 127157 | 16058 | 372690 | 628 |
| perf-o3-lto-flat | 9872 | 73974 | 127180 | 13980 | 339167 | 564 |
| perf-os-plain-flat | 8000 | 74064 | 139487 | 18421 | 394248 | 692 |
| perf-os-lto-flat | 6360 | 74064 | 139484 | 14169 | 343769 | 584 |
| perf-o2-plain-banked | 10184 | 73954 | 114875 | 16523 | 374016 | 648 |
Compare compatible Os with performance Os to hold optimization/layout fixed;
compare performance O2 with/without LTO to isolate that selection; compare the
two O2/no-LTO rows for placement. BIN bytes exclude BSS, and observed stack use
does not prove a static upper bound. Each image reserves at least 4096 stack
bytes. Bank conflicts remain unavailable because P4 hardware is not implemented.
The original P1 binaries and their historical phase/source identities are retained.
Current Mini PIO compatibility is additionally retained at
`build/ics55-2026-10-09-20-00-d0c568d0142e/meta/tiny-r2-p3/pio-retry/evidence.json`.
The initial default 180-second attempt emits SIM_TEST_TIMEOUT despite emulator
exit zero, and fails the simulation checker. A retry of the identical model and
BIN with `--fast-flash -t 1800` passes in 323.930 seconds, including 4/16/33/65-word
frames, IRQ count 5, exact waveform/reset-low checks, underflow/abort recovery,
and observer totals `frames=6 completed=4 underflows=1 aborts=2 dma_starts=0`.
The evidence records the before/after model and BIN hashes, command, manifest,
compile/run logs and a post-build recheck of 487 compiled input sizes/mtimes.
It is explicitly a post-build audit; neither the failed timeout nor the original
unsupported Mini DMA attempt is relabeled as a passing run.
Product-integrated WS2812 DMA/IRQ service budgets remain deferred under approved
option A to an actual supported route, including Tiny R2-P6. Full-product,
physical, silicon and publication-PDF qualification remain outside these results.
Human review and acceptance of TINY-R2-P3 are required before authorizing any
R2-P4 implementation; this execution does not automatically advance a phase.
## Subsequent P1 HAL race fixes (2026-10-10)
This approved follow-up starts from `3e46cc80e2d2243233d284b7cd9583714b0773bb`
and preserves the preceding uncommitted documentation/evidence updates. The
specification remains blob `7e19de49d49efaafacccfa85671e314d53dd15a4`.
Only `crt/src/hal/dma.c`, `crt/src/hal/ws2812.c` and
`tests/c/test_runtime.c` change executable/test source. No public interface,
register, RTL, profile, dependency, warning baseline or metric policy changes.
- WS2812: completing between INTR_STATE and STATUS reads no longer produces a
false I/O error. Idle without an observed sticky DONE retains ownership and
returns to the caller; the next bounded service checks error, abort, deadline
and completion again. No refill is admitted to an idle core. DMA response
draining and source-buffer lifetime rules are unchanged.
- DMA: one outer interrupt save/restore spans legacy TCD configuration,
descriptor publication and START, including ownership rejection. Nested HAL
helpers preserve the saved mask; the blocking completion wait is outside this
critical section. Test-only IRQ hooks model a pending ISR at mask restoration
and do not appear in target builds.
- Deterministic production-HAL host tests cover an ISR trying to lease the
channel during TCD publication, rejection without modifying an existing lease,
an initially masked caller, completion between WS2812 register reads, delayed
sticky DONE, genuine error/abort and expiry while idle. Existing final-B drain,
timeout ownership and 16-word FIFO admission tests remain enabled.
New evidence is rooted at
`build/ics55-tiny-2026-10-10-09-00-b4aa59f3e5d9/meta/tiny-r2-p3-races/`.
The timestamp is a fixed build label; each structured result records actual UTC
start/end times. `source.json` records the base revision, dirty state, locked
tool identities, input hashes, source archive and worktree patch. Its combined
ICS55/IHP130 source/test/spec digest is
`dfa171fc1adbdfc5c7c9d31ef4ac7ba1a66710caac0fb13b869ddac89186b2f7`.
This digest's input set differs from the earlier compiler-matrix input set.
Before fixes, `host-red.{log,json}` records host test exit 10 (TCD race).
After the TCD fix alone, `host-ws-red.{log,json}` records exit 11 (WS2812 race).
Both fail the enclosing Make command. `host-green.{log,json}` passes after both
fixes; `policy.{log,json}` additionally covers the final error/abort/deadline and
interrupt-mask cases, C formatting and policy checks. `focused.{log,json}` has
156 passing tests, including P3, Tiny baseline, DMA/register parity, WS2812 RTL
and publication API behavior. No new MISRA deviation is introduced. These are
partial project checks, not complete MISRA certification.
Commands run with the existing locked environment:
```sh
source .cache/retrosoc/development/tiny-r2-p1/activate.sh
make sw-format-check sw-policy-check sw-host-test
python3 -m pytest -q tests/test_tiny_r2_p3.py tests/test_tiny.py \
tests/test_tiny_r2_baseline.py tests/test_dma.py \
tests/test_dma_register_parity.py tests/test_ws2812.py \
tests/test_publication_api_bundle.py
ruff check .
git diff --check
python3 scripts/regress.py --root . --suite pr --soc TINY --pdk ICS55 --dry-run
python3 scripts/regress.py --root . --suite nightly --soc TINY --pdk ICS55 --dry-run
python3 scripts/regress.py --root . --suite pr --soc TINY --pdk IHP130 --dry-run
python3 scripts/regress.py --root . --suite pr --soc MINI --pdk ICS55 --behavioral-only --dry-run
BUILD_TIMESTAMP=2026-10-10-09-00 JOBS=3 JAVA_TOOL_OPTIONS=-Dsbt.server.forcestart=true \
python3 scripts/regress.py --root . --suite pr --soc TINY --pdk ICS55
BUILD_TIMESTAMP=2026-10-10-09-00 JOBS=3 JAVA_TOOL_OPTIONS=-Dsbt.server.forcestart=true \
python3 scripts/regress.py --root . --suite pr --soc TINY --pdk IHP130
BUILD_TIMESTAMP=2026-10-10-09-00 JOBS=3 JAVA_TOOL_OPTIONS=-Dsbt.server.forcestart=true \
python3 scripts/regress.py --root . --suite pr --soc MINI --pdk ICS55 --behavioral-only
JAVA_TOOL_OPTIONS=-Dsbt.server.forcestart=true make CONFIG=configs/ci/ics55.mk \
SOC=MINI PDK=ICS55 APP=ci_smoke WS2812_P3_ACCEPTANCE=YES HAVE_CSR=YES \
LINK_TYPE=ld2_all_sram SIMU=VERILATOR VERILATOR_SIM_ARGS=--fast-flash \
SOC_SIM_TIME=3600 BUILD_TIMESTAMP=2026-10-10-09-00 JOBS=3 firmware sim
```
Regression dry-runs passed; Tiny PR and nightly select identical commands, so
the long run is not duplicated. All three regression executions passed once;
their exact commands and verdicts are retained as
`regress-{tiny-ics55,tiny-ihp130,mini-ics55}.{log,json}` in the new evidence root.
Durations are 3210.491 seconds (Tiny ICS55), 2205.505 seconds (Tiny IHP130),
and 3170.591 seconds (Mini ICS55 behavioral-only). Mini PIO passes once in
423.766 seconds including build, with 4/16/33/65-word frames, five IRQs and
observer totals `frames=6 completed=4 underflows=1 aborts=2 dma_starts=0`.
Its variant is `build/ics55-2026-10-10-09-00-d0c568d0142e/`.
`audit.py` and `evidence.json` retain the final artifact audit: all 1287 captured
source/test/spec inputs are unchanged, 487 Mini PIO compiled inputs retain their
recorded sizes/mtimes, and the firmware/model/log/result hashes are recorded.
The Mini model audit is explicitly post-build. Eight ordinary regression
simulations are independently checked against their executable success marker,
command exit and forbidden-error rules. All selected targets, including the
current Mini assembly fixture, require `SIM_TEST_PASS`; UART output is not used
to infer success. This evidence is in addition to the separate PIO audit.
Current Tiny synthesis/netlist/STA results are in
`build/ics55-tiny-2026-10-10-09-00-42d5beccb726/` and
`build/ihp130-tiny-2026-10-10-09-00-bff91e3529ba/`. Both netlist checks pass;
STA command success does not mean timing closure. Observed max WNS/TNS remain
-41.14 ns / -508669.53 ns (ICS55) and -94.28 ns / -1594880.00 ns (IHP130),
with min WNS/TNS zero. Raw reports are `sta/opensta/timing_metrics.rpt`.
Lint baseline comparisons still fail as non-blocking regression observations:
459 new signatures for ICS55 Tiny, 403 for IHP130 Tiny, and 1536 new / 118
resolved for Mini, with zero increased signatures. No baseline was changed.
Build logs also retain a roughly four-second generated-file clock-skew warning;
these were fresh variant builds, and the input audit passed.
The old compiler/layout matrix, full Pytest and earlier timing observations stay
bound to `3e46cc8`; they are not reattributed to this follow-up. The compiler
matrix and full Pytest are not rerun for these isolated C changes (no build or
Python implementation changes). Product-integrated WS2812 DMA/IRQ budgets remain
deferred under option A; no Mini DMA-to-MMIO route is added. Human P3 acceptance
and separate approval are still required before R2-P4. Per the maintainer's
instruction, long checks default to a single execution; additional repetitions
need a concrete failure/inconsistency or an explicit frozen acceptance rule.
+253
View File
@@ -0,0 +1,253 @@
# Tiny R2-P6 shared integration
Implementation in progress from `b3b30a72cd6ef5340c28e5044c96cd13258ec5a6`,
against specification blob `7e19de49d49efaafacccfa85671e314d53dd15a4`.
The maintainer approved the single-phase P6 preflight. No phase acceptance,
new operating-frequency claim, commit or publication promotion follows.
Default `configs/ci/ics55-tiny.mk` remains SAFE24 with its PLL parked off;
`configs/ci/ihp130-tiny.mk` remains no-PLL compatibility. CPU and all 32 main
SRAM macros share SYS. Crypto adds its six private 4 KiB macros on the current
SAFE24 PCLK alias, without reducing the 128 KiB main aperture.
## Product boundary
The eight-channel DMA enables requests 0-8 and 12/13 (`0x31ff`). I2C1 requests
9/10, DVP11 and future requests stay unsupported. Default owners are UART0=0,
I2C0=1, camera reservation/explicit memory borrowing=2, serialized bulk=3,
Crypto=4/5 and audio=6/7. The application lease on channel 3 persists across
all WS2812 refill batches; CRC and XPI must acquire it only after release.
SPI16/17 remain outside the existing V2.0 selector ABI and are rejected by
the SDK. This does not claim hardware rejection of arbitrary raw extended
selector writes: the common DMA specification explicitly records their legacy
truncation and requires V2.2 identity/capability guards before SPI integration.
The 20 independent APB branches occupy fabric targets 5-24. SRAM remains
0-3, XPI memory 4, and decode/protocol errors are 25/26. The original three
external owners remain; owner 2 is now the actual SDIO private DMA producer.
P5 response retention, write drain, fault routing and ownership are preserved.
The 21 system reset leaves exclude the independently released AUDIO reset;
neither an absent audio reference nor Crypto initialization holds CPU reset.
Gen1 default GPIO routes are SDIO0-5, UART8/9, I2C10/11, audio12-17,
PWM18-25 (plus its frozen ALT1 outputs), WS26 and XPI chip selects29-31.
Unfrozen UART/I2C alternate inputs, camera, SPI and CLKOUT remain inactive.
The historical ASIC wrapper still has legacy dedicated UART/XPI-CS ports and
its old power-pad inventory. It is not a QFN64 binding. This explicitly
approved compatibility boundary leaves the complete package/power wrapper
to final physical implementation, without changing the frozen package table.
## Shared correctness and software
Tiny's shared DMA validator now accepts integrated streams and eight channels.
Known absent I2C1/SDIO1 and UART flow-control operations fail before MMIO.
SD/SDIO enumeration caps a protocol clock request at half the actual input
frequency; the existing low-level clock calculator still rejects invalid
requests. The bounded SDSC CSD multiplier uses a 32-bit shift factor inside
the unchanged 64-bit capacity calculation, avoiding an absent RV32 runtime
helper without replacing the locked GNU toolchain.
The SD Memory RCA extraction uses raw R6 frame bits 39:24; the preceding
31:16 selection incorrectly shifted the assigned card address by eight bits.
The independent P6 I2S payload fixture reproduced an existing receive-bit
assembly defect in both simulators. Completion now follows capture of the
current sample's LSB at the LRCK boundary. Both widths preserve the existing
register ABI, FIFO/CDC topology and master-only integration. Shared Mini/APU
checks remain required; this is not an I2S slave extension.
Adding partial external GPIO drivers exposed a locked-Verilator simulation
limitation: a packed `tri1` bus loses pulls on its otherwise undriven bits.
The original I2C test consequently saw released SCL/SDA low and timed out.
A minimal reproduction fails in Verilator and passes in Icarus; explicit
independent scalar board nets retain both weak pulls and driven readback in
both tools. Tiny's generated testbench bindings use those scalar nets and an
observation-only packed view. ASIC ports, pad cells, power counts and I2C RTL
are unchanged by this verification repair.
The Tiny `HAVE_SVA=YES` flow now also emits `+define+HAVE_SVA`, which the
owned P4/P5 assertions use. Previously it only omitted the Common
`SV_ASSRT_DISABLE` define; focused fixtures explicitly enabled the owned
assertions but ordinary firmware flows did not. Mini flag behavior is unchanged.
The first full integration reached Crypto but timed out before audio completion.
A smaller actual-SoC diagnostic isolated an I2S status load fault: Tiny's
AXI-to-APB bridge retained the preceding write strobes on reads. Read PSTRB is
now zero as required by APB4, while writes retain their byte lanes. A directed
write/read sequence checks full/partial writes with a strict receiver.
The Tiny firmware limits exception skipping to its explicit CPU probes;
unexpected later load/store faults fail immediately instead of being masked.
Audio shutdown keeps the consumer running while aborting/draining its leased
TX DMA session. A held stream beat cannot retire into an already stopped/full
audio FIFO. Only after DMA retirement does the acceptance image disable I2S
and flush. After a missing-clock flush timeout it polls the outstanding command
through clock recovery instead of issuing another protected COMMAND write.
SDIO's reused card model originally required manual enumeration/data stimulus;
its default ACMD41 never reports READY. The command trace confirmed the
initialization loop. An opt-in SDHC response profile and the existing explicit
data API now support the Tiny board fixture while preserving standalone defaults.
The fixture checks wire-command/data-policy agreement and drives only external
pins. Card timing/protocol/electrical qualification remains excluded. The native
Icarus parser also needs an equivalent explicit enum branch in this model.
Tiny Icarus builds now retain separate testbench/header dependencies, including
the shared card model and P6 board include. Its serial observer emits measurement
records on ordinary runs; TEST_STATUS and strict error checks remain authoritative.
The 12 MHz command case exposed a running-clock start-bit duplication in the
shared SDIO sender. Output admission now waits for its first scheduled launch;
the independent pad sampler covers both admission phases. The card sampler
retains the complete frame across host OE release, including the pulled-high
end bit. This preserves the command/register ABI and does not extend card modes.
The full four-lane SDIO block exposed a separate transmit pause defect: when
memory delayed the next word, a late clock stop corrupted its first bit
(`0x57` became `0xD7`) and caused CRC rejection. The shared data engine now
requests the pause before that launch; the clock wrapper honors it while
draining the current high half-cycle. A full 1024-byte fixture with read-beat
gaps checks payload, CRC and descriptor retirement. Its scalar three-state
variant also caught card-model assumptions about unused lanes being zero;
token and partial-tail checks now account for their pulled-high state.
The receive roundtrip isolated premature descriptor completion after one
64-byte write burst. SDIO private DMA retains ownership until every payload
burst retires or an error is reported. A separately reproduced receive stop
delay shifted the next word (`0x13579BDE` to `0x26AE37BD`); immediate stop
qualification prevents the extra launch. The card's read-CRC sequencer also
skipped one bit and is corrected independently. Delayed descriptor/payload
tests check full readback, CRC, descriptor retirement and both simulators.
These repairs add no masters, channels, descriptor fields or operating modes.
## Validation and historical publication
Workloads keep the ordinary Tiny boot, SRAM/DMA tail, trap, interrupt, RTC,
I2C NACK, JTAG and watchdog-reset checks. P6 adds real GPIO8/9 UART echo;
eight-channel/unsupported-request checks; RNG qualification rejection and IRQ;
CRC DMA, empty input and byte tails; Crypto initialization, AES/SHA/public RSA,
channels 4/5 and zeroization. Audio uses the fixed 18.432 MHz external reference
and 48 kHz master presets, checks 256 stereo frames at both 16 and 24 bits,
and drives paced 33/65-word WS frames in each case. Channel 2 concurrently runs
bounded 256-byte jobs, then four-descriptor chains; channels 6/7 own audio.
The board independently decodes I2S/WS pins, checks FIFO/service bounds and
stops AUDIO low/high for recovery tests. SDIO uses 400 kHz enumeration/one-bit
PIO and 12 MHz four-bit DMA, transfers 1024 bytes in each DMA direction and
512 PIO bytes, overlaps a separate 1024-byte central-DMA job and checks IRQ,
CRC-error, timeout and recovery behavior.
MCYCLE/MINSTRET measure the audio/WS service loop after prefill; elapsed CLINT
time also includes its bounded preparation. The independent AXI observers count
handshakes over the complete firmware run, including boot/control accesses;
they are not payload-only bandwidth measurements. Write counts mean accepted
strobes, not rollback guarantees. Descriptor fetch bytes, per-owner AR/AW/W/R/B
backpressure, WS service gaps and actual SDIO/DMA overlap with CPU progress
remain explicit. `scripts/tiny_r2_p6.py` retains ELF/hex/bin/map, linker/generated
software inputs, simulator binaries/filelists, manifests, logs and their hashes.
Both simulators on both PDKs must have successful commands, Tiny's terminal
`SIM_TEST_PASS`, all workload/independent-observer records and no forbidden
error marker. Component diagnostics never satisfy that collector.
The current Tiny PR and nightly dry-run command matrices are identical for
each PDK. After focused/quality checks, execute full Pytest once and one full
PR per PDK in independent build directories; these broader gates may run in
parallel but must all finish before acceptance. Collect warnings/metrics with explicit `APP=ci_smoke`,
`SYNTH=YOSYS`, `STA=OPENSTA` and the same build timestamp. Do not rerun an
identical nightly matrix. Shared I2S and SDIO standalone/formal checks plus the
affected Mini firmware path remain separate compatibility evidence. Core STA
must execute and bind all 38 exact macros; its slack remains observational.
The legacy IHP130 LibreLane floorplan generator still places the 32 main macros
only. It has not placed/connected the six private Crypto macros or implemented
the final QFN64 wrapper. Full-chip APR/PDN/package work remains outside this
campaign; neither its old floorplan nor smoke STA establishes P6 physical closure.
Development artifacts begin at the manually selected `2026-10-12-12-00`
build label, with subsequent diagnostic labels recorded in each command.
Those labels are not wall-clock measurements; JSON start/end times are.
Some diagnostic firmware directories were reused while finding defects and
must not be rebound to later binaries. The `11-42` snapshot's Make-format gate
failed before long validation. The fresh `2026-10-12-11-50` campaign retains
its input snapshot under `build/ics55-tiny-2026-10-12-11-50-2eb98c0e1fae/meta/tiny-r2-p6/`.
Failed attempts retain their own verdicts and are not acceptance evidence.
Both original PR attempts passed complete Verilator/JTAG acceptance and then
stopped at native Icarus elaboration: `s_token_lanes` referenced `s_data_bytes`
before its declaration. The declaration relocation changes no expression or
other model byte. `verification-update.json` retains the second snapshot;
the collector checks that exact equivalence and rejects any DUT, firmware,
constraint, tool or other model-behavior change. Only the card model and its
collector/tests differ. Original Verilator and Mini results retain their
original source identity. Native Icarus and the remaining physical smoke stages
resume from the failed step; the original failed PR verdicts are not rewritten.
Full Pytest retains its original collection, with the equivalent model edit
during execution; the updated 48-case P6 suite separately covers the native
parser and fail-closed provenance checks. Counts overlap and must not be added.
The one full Pytest attempt reports 1634 passed, one optional `pypdf` skip,
five failures and 60 fixture errors. These reduce to two source-consumer issues:
Mini's publication parser counted inactive Tiny enum branches as Mini channel
allocations, and the commercial Tiny overlay retained its pre-P6 two-domain
allowlist. The parser now selects and checks Mini's actual bulk aliases; no book
identity, chapter, PDF or historical evidence changes. The commercial policy
and Tcl inventory now include the canonical AUDIO root; no licensed flow or
qualified I/O run is implied. `consumer-update.json` binds this separate scope
and rejects changes to RTL, firmware, models or smoke constraints.
Every one of the 65 original failed/error nodes passes in the explicit repair
selection. The complete affected publication/commercial suite reports 512 passed
and one optional PDF-library skip; 35 selected provenance tests also pass.
These counts overlap the full attempt and each other. There is no claimed clean
full-suite rerun: unaffected long tests were retained with their original scope.
### Completed Verilator observations (partial campaign)
`verilator-observations.json` and `report-functional-before-physical.json` bind
the following values to successful command/log artifacts. Each row verifies 256
stereo frames and two WS frames, with 64 completed background jobs. These values
are functional observations, not phase acceptance or timing closure.
| PDK | Audio bits | Service-loop cycles | Retired instructions | Elapsed including preparation (us) |
| --- | ---: | ---: | ---: | ---: |
| ICS55 | 16 | 141917 | 41351 | 8044 |
| ICS55 | 24 | 138353 | 40283 | 7892 |
| IHP130 | 16 | 141917 | 41351 | 8065 |
| IHP130 | 24 | 138350 | 40283 | 7903 |
The independent WS service-gap maxima are 2375/2374 SYS cycles
(98.96/98.92 us), below the 4800-cycle budget; FIFO peak occupancy is 15/16.
Both runs observe 4124 cycles of actual SDIO/private-DMA versus central-DMA
overlap, with 1185 CPU instruction-fetch handshakes in that interval. SDIO's
external owner reads 1056 bytes and accepts 1032 write bytes, including its
descriptor fetch/writeback. Central-DMA descriptor fetches total 16384 bytes.
No cross-architecture speedup or physical rate is inferred from these samples.
Both complete Icarus runs subsequently pass: ICS55 at 16036387 cycles and
IHP130 at 16035743 cycles, with the
same workload, WS, contention and bus observations as its Verilator run.
Its UART observer leaves a partial human-readable line before the testbench's
terminal `SIM_TEST_PASS Tiny cycles=...` record. `collection-update.json`
records a collector/test-only correction to accept that exact terminal token
without requiring a fresh line; JTAG-only/Mini markers, malformed cycle values
and forbidden errors remain rejected. Forty focused collection/provenance
checks pass. Raw logs, RTL, firmware, models and constraints are unchanged.
Both netlist boot checks pass at 30030 cycles. Yosys synthesis and OpenSTA run
for both PDKs and report the exact 32 main plus 6 private Crypto macro bindings.
Observed top-cell/area values are ICS55 412515/2461020.464702 and IHP130
433861/13320370.152627. Setup/hold remain negative: ICS55 WNSmax/WNSmin
−84.93/−0.31 ns, IHP130 −165.41/−0.51 ns. These are smoke-flow observations;
the frozen phase does not use synthesis slack as an intermediate blocking gate.
Warning checks retain new Yosys/Verilator/RTL-lint and IHP130 Icarus signatures
as non-promoted observations under the existing policy; no baseline was edited.
The old Tiny publication remains frozen at its advertised technical revision
`9c706ba0ff1da1028c299cfff573010a86dfd7ce`. Tests execute the original
publication adapter/test snapshot `2805ea91833a6bede32f3a82df3d17d20eb5ec77`
and, separately, the later P5 contract snapshot `b3b30a72cd6ef5340c28e5044c96cd13258ec5a6`.
Neither snapshot becomes P6 publication evidence. Current P6 source must be
rejected by the historical collector. The reset mutation checks still run
against the current target RTL. No frozen chapter, reviewed SHA or old PDF is
rewritten; unavailable PDF/media inputs remain a separate publication gap.
P7 RCU/variable clocks, PWM dependency upgrades, alternate-input ABI, slave
audio, PSRAM, DVP and later extensions remain outside this phase. Timing is
observational before complete-product qualification; exact macro/clock/source
binding and successful functional verdicts remain mandatory.
+483 -11
View File
@@ -1,14 +1,486 @@
# Tiny MCU Verification Record
## TINY-R2-P3 implementation in progress (2026-10-08–09)
## TINY-R2-P7 source-reset integration contract (2026-10-14)
The follow-up review identified a missing source-side reset proof, not a
reproduced product failure. The AXI bridge does not support arbitrarily
resetting an active source while leaving its slave session alive. Flushing
the FIFO cannot cancel a request already accepted by that slave. Production
Tiny prevents that sequence: DMA local reset requires engine, admission and
fabric drain before resetting the source; XPI's SYS source reset is a common
system reset that also resets its MEM endpoint. Neither production bridge
uses warm clear. These preconditions are part of the integration contract,
not a claim of unrestricted unilateral-reset support by the standalone bridge.
`tests/rtl/tiny_p7_source_reset_tb.sv` uses the production DMA master,
admission, CDC, lifecycle controller and reset tree. The test extracts the
actual top-level instances, drain/block synchronizers and generated DMA/XPI
reset bindings, so a hand-copied reset policy cannot silently replace them.
An independent outstanding scoreboard survives local reset and rejects
premature reset. The AXI target is a controllable behavioral slave; unrelated
CPU/APB/SDIO work and the DMA completion mailbox are idle. This is a focused
integration test, not a full-SoC firmware or formal CDC/RDC signoff result.
The scenarios cover stalled AR/AW, partial W, downstream W backpressure,
delayed R/B, source-held R, drain timeout without reset, post-reset reads and
writes, and common system reset during an outstanding read. The same binary
is reused at SAFE24, XIN48, XIN96, PLL192 and PLL240 domain ratios. They test
digital ordering only; the clock generators do not qualify a PLL or PDK rate.
A negative run falsifies both idle observations and must trip
`P7_SOURCE_RESET_UNDRAINED`; Icarus may return zero for sv2v's lowered fatal,
so the forbidden marker and missing PASS still make that simulation invalid.
Evidence for this correction is retained under
`build/ics55-tiny-2026-10-14-06-00-ff4b5cef6eb5/meta/tiny-r2-p7-source-reset/`.
The first focused attempt's test-driver failure is retained in `focused-1.log`;
it is not a DUT failure or acceptance result. The final `directed.log` and
`result-directed.json` record **54 passed in 191.71 seconds**. This includes
ten positive source-reset runs (five ratios in each simulator) and two
expected-negative broken-drain runs inside the two new Pytest cases.
RTL format/style, clock/reset inventory, Ruff, both Tiny PDK PR dry-runs and
`git diff --check` pass. The verification-only source comparison is retained
in `verification-update.json`: only the two test files differ; 1728 other
technical inputs and tool identities match. The design,
firmware, configuration, dependencies and physical inputs are unchanged from
the `06-00` source snapshot; the new tests do not relabel earlier simulations
as new executions. Full Icarus firmware remains deferred to final Tiny
qualification. Human P7 acceptance is still required.
## TINY-R2-P7 response conservation correction (2026-10-14)
The earlier review recommendation to give abort unconditional priority over
source response retirement was incorrect. A cycle with an already exposed
normal B/R handshake followed by an abort produced two completions for one
request. Four read/write tests reproduced this in Verilator and Icarus. The
failed candidate, diagnostics and source archive are retained below
`build/ics55-tiny-2026-10-14-04-00-ff4b5cef6eb5/meta/tiny-r2-p7-response/`.
The `04-00` synthesis/netlist/STA results belong to that rejected candidate;
their successful boot does not close the protocol finding.
The corrected bridge holds presented responses in source-owned registers.
Remote reset cannot revoke VALID or rewrite their payload under backpressure,
and a completed source handshake is never followed by a second completion.
Only missing read beats receive DECERR, with RLAST at the original burst end;
a reset write consumes the remaining W beats before emitting B. Outstanding
tracking is one transaction per direction, matching the existing Tiny paths.
The payload reset uses the five-edge source link barrier. An unfinished warm
clear quarantines the bridge until a real destination reset; a FIFO clear
cannot by itself cancel remote slave state or authorize re-use of late replies.
Production DMA/XPI clear inputs remain tied low; no new software reset API,
autonomous AXI timeout, DMA owner, or later-phase capability is introduced.
The new tests distinguish completed/held responses from missing responses,
cover simultaneous clear and response handshakes, remote reset with READY high,
backpressure, partial bursts and late-response quarantine. That campaign's
full directed selection passed 52 cases. Earlier 36-case and rejected-
candidate passes did not cover these failures and are not proof of the repair.
The failed same-cycle candidate and diagnostics remain in the response
evidence directory.
The final input snapshot is
`build/ics55-tiny-2026-10-14-06-00-ff4b5cef6eb5/meta/tiny-r2-p7-response/final/source-final.json`.
The `06-00` backend campaign is bound to the final source snapshot and all
requested post-fix gates pass. Its variants are
`build/ics55-tiny-2026-10-14-06-00-ff4b5cef6eb5/` and
`build/ihp130-tiny-2026-10-14-06-00-8232fa11e7f3/`. ICS55
synthesis/netlist/STA durations are `2108.388/1655.980/43.594 s`, with WNS
max/min `-152.03/-0.31 ns` and TNS max/min `-5622052.50/-44.14`; IHP130
durations are `1934.515/578.869/47.109 s`, with WNS max/min
`-253.43/-0.49 ns` and TNS max/min `-10035324.00/-153.37`. Netlist boot
emitted `SIM_TEST_PASS Tiny netlist boot` for both PDKs. These timing values
remain observational and do not qualify closure or an operating point.
Full Icarus firmware remains deferred by the maintainer to final Tiny
qualification. It is not a current P7 gate; protocol correctness and human
phase acceptance remain required.
## TINY-R2-P7 final verification campaign (2026-10-13)
The [P7 runbook](tiny-soc-r2-clock-reset.md) records the current implementation,
exact profile-specific commands, workload definitions and source snapshot.
The integrated design now drives actual SYS/MEM/PCLK consumers, CPU-only WFI
gating, local reset barriers, CLKOUT and the single ICS55 PLL backend. It keeps
the 32 main SRAM macros at CPU SYS and six private Crypto macros at PCLK.
Future target capabilities remain absent. No later phase is authorized here.
Development validation includes 68 passing Tiny P7/SYSCTRL/P5 directed tests
and a later complete 52-case P7 directed pass,
plus both simulators' real Hazard3 IRQ/debug wake, clock-pause and retained-debug
warm-reset/aborted-command cases. RCU validation now rejects invalid or BUSY
staging writes with PSLVERR, and the CLKOUT waveform test exposed and corrected
enable/divisor ordering. HAL tests cover invalid, absent, busy, gated-reset and
sticky-timeout/error boundaries. These short checks complement the final long
flows; they do not replace full firmware or netlist acceptance.
Source evidence is retained at
`build/ics55-tiny-2026-10-13-17-05-f3e42d54c18b/meta/tiny-r2-p7/source.json`.
The final command matrix and its retained corrections are listed in the runbook.
Default ICS55/IHP130 and external XIN48/96 P7 firmware pass in both simulators.
Each profile's cycle count and WFI/DMA/reset observations match across simulators.
Both watchdog-corrected full Verilator firmware runs pass the unchanged P6
peripheral/wire/AXI collector and JTAG. The two corresponding full Icarus
replays were intentionally interrupted with exit 130 after the maintainer
requested that this long-running gate move to the final Tiny phase; their
partial logs and interrupted result records are retained, but they are not
acceptance evidence.
Both PDKs pass synthesis, structural macro binding and Tiny netlist boot.
Current core STA remains observational and negative, with the coverage limits
explicit in the runbook. These are historical campaign results; current phase
acceptance requires the response correction above and human review. The full
Icarus firmware gate belongs to final Tiny qualification. No high-rate physical operating point is
qualified by these functional simulations or SAFE24 core STA.
## Historical TINY-R2-P7 CDC/reset correction follow-up (2026-10-14)
This checkpoint predates the response-conservation correction and is
superseded by the `06-00` campaign above. It is not current-source acceptance.
The P7 review identified two source-bound gaps. The XPI fabric target and bridge source side release with
the SYS reset endpoint, while the XPI/MEM side retains its MEM leaf reset. The
Tiny AXI CDC bridge synchronizes destination reset loss, invalidates its
protected request/response state and blocks new source admissions. Its original
single-DECERR completion claim was insufficient for held responses and bursts;
the later response-conservation correction supersedes that claim. The existing DMA/XPI
instances retain their explicit bridge boundaries and the XPI bridge now uses
the split SYS/MEM reset mapping.
The new `tests/rtl/tiny_axi4_async_bridge_tb.sv` passes under both Verilator
and Icarus for normal read response, reset-aborted read/write, DECERR,
no-replay and bridge recovery. The complete
`PYTHONDONTWRITEBYTECODE=1 python3 -m pytest -q tests/test_tiny_r2_p7.py`
selection eventually passed 52 tests after the later corrections; that count
does not belong to the `01-20` implementation. `make --silent rtl-format-check rtl-style-check-all`
and `git diff --check` pass. An ICS55 default-profile Verilator `comp` also
passes; its existing PDK and simulator warnings remain observations. The
checkpoint's synthesis, netlist boot and observational STA gates passed for
both PDKs. Its historical evidence is retained under
`build/ics55-tiny-2026-10-14-01-20-ff4b5cef6eb5/` and
`build/ihp130-tiny-2026-10-14-01-20-8232fa11e7f3/`. ICS55 reports WNS
max/min `-144.40/-0.31 ns` and TNS max/min `-4859636.50/-45.04`; IHP130
reports WNS max/min `-257.81/-0.47 ns` and TNS max/min `-9807803.00/-100.68`.
These remain observational negative timing results, not closure or operating
point qualification. The long-running full Icarus firmware gate remains
intentionally deferred to the final Tiny phase; no physical flow was run.
## Historical initial P7 checkpoint, superseded (2026-10-13)
The following incomplete checkpoint belongs to earlier development snapshots.
Its unconnected/missing-path statements do not describe the final campaign.
This checkpoint starts from committed source `43a3c8d453b6d41df3536719ebc6d1f4521bc7cd`,
specification blob `7e19de49d49efaafacccfa85671e314d53dd15a4`, Tiny/ICS55
profile `configs/ci/ics55-tiny.mk`, and the locked PWM revision
`38efaf5e20f084a00148920087c60cadab61839b`. The dependency lock validator
passes with digest `263402a34f2e315ca46ef4bf6366aab64935bd8de7abab3e469f513e988b0208`.
The implementation adds the Tiny-owned RCU register bank and handwritten
`rcu.h`/`rcu_regs.h`/`rcu.c` contract, reports SAFE24/domain-rate snapshots,
connects committed PCLK/SYS rate reporting to the upgraded PWM and SDIO
`CLOCK_ACTUAL` paths, and adds focused APB coverage for identity, capability,
profile rejection/commit, status and write-only command behavior. Verilator
and Icarus SYSCTRL focused tests pass; ICS55 Verilator and Icarus top-level
compilation, Tiny firmware linking, C format/policy/host tests and the SDIO
suite pass. Build logs are retained under the generated
`build/ics55-tiny-2026-10-13-09-00-f3e42d54c18b/` and
`build/ics55-tiny-2026-10-13-10-30-f3e42d54c18b/` roots.
A Tiny-owned `tiny_clock_reset_subsystem` backend is now source-bound and
filelisted. Its SAFE24/no-PLL focused test passes in both simulators and covers
five-stage SYS/MEM/PCLK reset release, external-profile handling and rejected
PLL requests. A second focused test exercises the behavioral PLL lock-timeout
path in both simulators; it does not claim successful macro lock or frequency
qualification. The backend is intentionally not advertised or wired into the
CPU/fabric yet; its SYSCTRL profile/apply handshake is now connected, while
the backend instance is now present in the Tiny top and its SYS output clocks
the CPU, fabric, SRAM, AXI interfaces and system reset tree. XPI now crosses
from SYS to the backend MEM clock through the Tiny-owned warm-flush AXI bridge.
The RCU command checks CPU/fabric/target idle observations and the XPI bridge
before accepting a transition. PCLK outputs are still not connected to all
peripheral consumers.
The focused command is
`PYTHONDONTWRITEBYTECODE=1 python3 -m pytest -q tests/test_tiny_r2_p7.py`;
its four cases passed. Supporting gates were
`make --silent rtl-format-check rtl-style-check-all`,
`make --silent sw-format-check sw-policy-check sw-host-test`,
`PYTHONDONTWRITEBYTECODE=1 python3 -m pytest -q tests/test_script_tools.py -k 'run_flow'`,
`ruff check scripts/run_flow.py tests/test_tiny_r2_p7.py`, and
`python3 scripts/dependency_lock.py --lock dependencies/dependencies.lock.json`.
The current ICS55 top-level compile used
`source .cache/retrosoc/development/tiny-r2-p1/activate.sh && CCACHE_DISABLE=1 make --silent CONFIG=configs/ci/ics55-tiny.mk APP=ci_smoke SIMU=VERILATOR HAVE_SVA=YES BUILD_TIMESTAMP=2026-10-13-16-30 comp`
and exited zero. These are source/compile/focused checks; none is a strict
full firmware terminal verdict or physical qualification.
The long-run diagnostic also confirmed that the emulator itself exits normally
with a Tiny timeout at a bounded cycle count and uses about 26 MiB RSS. The
earlier `status=running` records lacked a verified terminal result. The initial
diagnosis attributed this to sandbox cleanup without sufficient evidence;
that conclusion is withdrawn. Separate tool invocations can have different
PID namespaces, so process absence in one does not establish that another
command died. `run_flow.py` now records `parent_pid` and `child_pid`
in the running result and writes a failed terminal result for unexpected
Python exceptions; long runs must remain attached to one polling session.
This is not phase acceptance. The current RTL now uses the backend SYS clock
for the system/fabric path, but does not yet provide the required executable
MEM/PCLK clock consumers, full CDC bridges, WFI leaf gating, PLL lock/loss
recovery, or target-local five-cycle reset sequences. A full Tiny Verilator firmware
run was started but did not produce a terminal `TEST_STATUS`/`SIM_TEST_PASS`
verdict and was stopped; no physical, STA or netlist qualification is claimed.
The remaining P7 clock/reset implementation and strict terminal regressions
require a follow-up correction before human phase approval.
## TINY-R2-P6 implementation complete, human review pending (2026-10-12)
The maintainer approved the P6 preflight on
`b3b30a72cd6ef5340c28e5044c96cd13258ec5a6`, against unchanged specification blob
`7e19de49d49efaafacccfa85671e314d53dd15a4`. The
[P6 runbook](tiny-soc-r2-shared-integration.md) records eight-channel/shared-IP
integration, the explicitly retained historical ASIC wrapper and publication,
and the separate source-bound validation scope. Development diagnostics are
not final acceptance; no P7 or physical-frequency qualification is implied.
Initial integration exposed a shared I2S sample-LSB assembly defect and a
Verilator packed-tri1 board-model limitation. The runbook records their narrow
corrections. Directed SDIO integration additionally exposed command admission,
transmit/receive pause and multi-burst descriptor-retirement defects, plus
manual card-model gaps; their independent reproductions are recorded there.
Both Tiny PDKs have complete Verilator and Icarus functional verdicts and
netlist boots. Yosys synthesis and OpenSTA run for both PDKs with the exact
32-main plus 6-private-Crypto macro bindings. The final audit is
`build/ics55-tiny-2026-10-12-11-50-2eb98c0e1fae/meta/tiny-r2-p6/audit-result-v3.json`;
the source-bound evidence is `evidence.json` in the same directory. The
functional report retains four simulator records, all workload/AXI/device
observations and the Mini compatibility result.
Observed setup/hold slack remains negative: ICS55 WNSmax/WNSmin −84.93/−0.31 ns
and IHP130 −165.41/−0.51 ns. Warning checks report new signatures from the
existing policy's observation flows; no baseline or metrics policy was edited.
The full Pytest attempt remains recorded as 1634 passed, one optional `pypdf`
skip, five failures and 60 fixture errors; all 65 failed/error nodes pass in
the explicit repair selection, and the affected consumer suite passes 512 with
one optional skip. These overlapping counts are not a clean full-suite rerun.
Final human review, complete-product timing closure, package/QFN64 binding and
physical signoff remain pending. Earlier P5 results and failed P6 attempts
retain their original attribution.
## TINY-R2-P5 P1 protocol correction; validated, review pending (2026-10-12)
The maintainer requested correction of the reset write-drain/read-retention
failures and publication-binding mismatch. The implementation starts from
`4e6ab1a6fb445521703f0bfa412d3a1e8a38cd69`; the specification remains blob
`7e19de49d49efaafacccfa85671e314d53dd15a4`. The [P5 runbook](tiny-soc-r2-concurrent-fabric.md)
describes preserved upstream spill responses, complete read/error bursts,
write drain before B, latched downstream cancellation and first-error capture.
Profiles remain Tiny/ICS55 SAFE24 with parked PLL and Tiny/IHP130 compatibility.
Current evidence is isolated under
`build/ics55-tiny-2026-10-11-23-35-b4aa59f3e5d9/meta/tiny-r2-p5-p1/`.
The older `09-30` campaign failed overall: 1669 Pytest passes, one skip,
two failures and ten errors (publication), plus independent R stability and
W-before-B violations. Its passing ordinary regressions, PPA and replay are
historical observations, not acceptance of the repair.
Final focused validation passes 101 tests. Full Pytest passes 1685 tests,
with one optional `pypdf` skip and no failures; these counts overlap. Both
Tiny PDK PR regressions pass, including six strict simulation verdicts,
synthesis, netlist boot and the 32-macro/common-SYS binding check. Each
simulator passes all 28 reset scenarios, including three production DMA
drain cases; normal-target, arbitrary-target-reset and fault-collector
induction/SAT checks pass. Complete terminal/fault publication bindings and
three unsafe-completion mutation tests pass without changing the reviewed
publication SHA. RTL format/style/readiness, Ruff and diff checks pass.
`source.json` and `evidence.json` bind 2011 technical inputs to digest
`f670789a2379a8d2e6c59f5ecd714ca0237bb6f1cfe749a2f29b0794d356b5f0`,
locked tools, exact command results and retained artifacts. No warning
baseline, metric policy, shared DMA/SDK implementation or public ABI changed.
The lint signatures/counts match the preceding `09-30` campaign; the existing
baseline comparison remains a non-blocking observation. STA remains unclosed:
max WNS is -70.04 ns ICS55 and -127.01 ns IHP130. The runbook records full
area/timing observations and limitations. Matched-image replay, full-function
C netlist qualification, PDF promotion and physical qualification were not
repeated for this bounded correctness repair. P5 human review/acceptance and
all P6+ work remain separate.
## TINY-R2-P5 target-reset isolation repair; focused validation (2026-10-11)
Superseded attempt: later protocol checks rejected its response flushing and
early B/RLAST behavior. The four focused passes below remain historically
accurate but were insufficient to establish reset isolation.
Following the P5 review, the fabric now receives the explicit 23-entry target
reset vector. A reset blocks new admission and downstream handshakes, flushes
response spill state, and converts any accepted target session into a held
DECERR terminal response. The existing owner credit is released only by the
upstream RLAST/B handshake, preventing stale response replay or a permanently
busy direction. No target map, public register, DMA capacity, clock profile or
later phase was changed.
The focused command
`PYTHONDONTWRITEBYTECODE=1 python3 -m pytest -q tests/test_tiny_r2_p5.py -k 'target_reset_isolation or concurrent_fabric'` passed 4 tests: both
Icarus and Verilator reset-isolation tests and both existing concurrent-fabric
tests. `make rtl-format-check rtl-style-check-all rtl-readiness-check-all`
also passed. This is a focused implementation check; the prior P5 campaign
reports remain source-bound to their recorded snapshots and have not been
rewritten or relabeled. The retained focused logs are under
`build/ics55-tiny-2026-10-10-18-58-b4aa59f3e5d9/meta/tiny-r2-p5/reset-repair-tests/`.
## TINY-R2-P5 implementation; validated, review pending (2026-10-10)
The maintainer approved the single-phase P5 plan on clean commit
`3131762b35e0ebfcd196395f955e7c1005bd18e7`, specification blob
`7e19de49d49efaafacccfa85671e314d53dd15a4`. The P4 input hashes match this
starting source, but earlier reports retain their original snapshot attribution.
The [P5 runbook](tiny-soc-r2-concurrent-fabric.md) records target-local ownership,
sixteen APB branches, reserved inactive SDIO owner 2, concurrent-fault accounting
and the unchanged public ABI/clock/memory boundaries.
The completed campaign is under
`build/ics55-tiny-2026-10-10-18-58-b4aa59f3e5d9/meta/tiny-r2-p5/`;
the earlier `18-25` root retains development attempts. Full Pytest reports
1679 passed and one optional `pypdf` skip. Both Tiny PDK PR regressions pass,
including synthesis, netlist boot and clock/macro binding. The real CPU/DMA
fixture records 13 overlapping DMA read/write beat cycles, 27 independent control
completions, hart-reset isolation and abort drain on both simulators. Scoped
target-control/fault proofs and retained P4 latency/streaming checks pass.
Initial image executions passed but aggregation correctly failed on an Icarus 13
temporary-expression population-count defect. The only subsequent source change
is P5 opt-in testbench instrumentation. `source.json` retains the original
snapshot for the unchanged DUT/full-suite/ordinary-regression/PPA results;
`monitor-source.json` records the corrected monitor. Only affected image
measurements and monitor checks were repeated. `monitor-diagnosis.json` retains
the reproduction and original discrepancy; no comparison rule was relaxed.
Final `report.json` has identical banked observations across simulators. Against
repaired P4, burst1 cycles change 91306→65527 flat and 89365→65502 banked; burst16
changes 17897→16974 and 17317→15802. CPU/copy cycles are unchanged; small
contention/TCD/chunk256 increases remain explicit. The final combined input
digest is `0f3ed21f6fd6e932ff2532aab8c9964e71cf4b972e4c27e2c8ca6ab1f22c1015`.
`evidence.json` rechecks 2010 inputs and binds both snapshot scopes, six ordinary
simulations, final matched-image runs, 15 focused/formal artifacts and reused
unchanged Mini compatibility evidence. Library area grows 5.56%/6.95%; max WNS
is -64.75 ns ICS55 and -111.93 ns IHP130. Timing remains unclosed/observational.
The runbook records exact commands, source attribution, warning observations,
measurements and unrun qualifications. Human phase acceptance is pending.
Shared DMA/Mini/SDK behavior, dependencies and warning/metrics policy remain
unchanged. No P6 execution or physical-frequency qualification is authorized.
## TINY-R2-P4 throughput repair; validated, review pending (2026-10-10)
Following the read-only P4 review, the maintainer approved the bounded-buffer
repair plan for the P2 external-SRAM throughput regression. The changed Tiny
group/frontend keep local I/D single-operation semantics, reserve external
response capacity and overlap burst issuance/retirement; W is captured before
macro arbitration and B follows complete commit-response drain. Common FIFO
and register dependencies, ABI, clock/macro bindings and the pre-P5 external
attachment remain unchanged. No shared Mini debug code is changed by this fix.
Fresh evidence belongs under
`build/ics55-tiny-2026-10-10-15-15-b4aa59f3e5d9/meta/tiny-r2-p4/`.
The preceding P4 report below remains the pre-repair comparison, and does not
qualify these new changes. The final audit rechecks 1972 technical inputs,
six fresh regression simulations and three immutable-image executions. Full
Pytest reports 1659 passed and one optional `pypdf` skip. Both Tiny PDK PR
regressions pass, including synthesis, netlist boot and clock/macro bindings.
Streaming/backpressure/error/reset checks and response conservation/fairness
induction pass; all six SRAM fixtures retain three-cycle local completion and
one-cycle steady read/write beat spacing. Initial style failures and focused
development attempts remain separate from final qualifying results.
Matched burst16 DMA cycles fall from 36713 to 17897 (flat) and 36489 to 17317
(banked), reductions of 51.25% and 52.54%. All banked observations match across
Verilator and Icarus. CPU/copy/burst1 cycles remain unchanged from pre-repair
P4; burst1 overhead and 8.34%/4.81% burst16 overhead versus original P3 remain
explicit. The combined input digest is
`58daf68fbb9dee199b3085a73ef07d22eb3de6d00ba875c462e85b76b6091a0d`.
`evidence.json` binds the source, artifacts, comparison and reused unchanged
Mini behavioral evidence. All long gates ran once; Tiny nightly matched PR
and was not duplicated. Setup WNS remains negative (-36.88 ns ICS55,
-97.37 ns IHP130), with warning/metrics observations and baselines preserved.
The [runbook](tiny-soc-r2-local-memory.md) records exact commands, measurements
and unrun qualifications. Human review remains required; no P5 execution or
physical-frequency qualification is implied.
## TINY-R2-P4 implementation and validation; review pending (2026-10-10)
The maintainer approved P4 preflight and implementation on clean source
`4db27524a6282ca40d76b935415b89f043d6ea83`, specification blob
`7e19de49d49efaafacccfa85671e314d53dd15a4`. The [P4 runbook](tiny-soc-r2-local-memory.md)
records architecture, lifecycle, physical binding and matched-image boundaries.
P3 results retain their original source attribution; no historical phase or
physical qualification is automatically promoted.
Development evidence is under
`build/ics55-tiny-2026-10-10-10-05-b4aa59f3e5d9/meta/tiny-r2-p4/`.
All planned functional gates completed: 1659 Pytest passes and one optional
`pypdf` skip; Tiny ICS55/IHP130 PR and Mini ICS55 behavioral compatibility pass.
Both Tiny netlists pass boot and the four-group/32-macro/common-SYS binding
checks. Local uncontended latency is three cycles; group-control induction
passes. Setup and hold timing remain negative and observational.
`report.json` retains single-execution matched-binary results; the banked image
matches exactly across Verilator and Icarus. CPU-copy cycles improve about
19%/25% for flat/banked images, while isolated burst-16 DMA cycles increase
about 2.2 times. This external-frontend throughput regression requires review;
no blanket speedup or automatic phase acceptance is claimed. `evidence.json`
rechecks 1972 source inputs and binds the gate, image, model, log and report
artifacts. Exact commands, measurements and unrun qualifications are in the runbook.
External fabric stays serialized until P5, which is not authorized. Default
Tiny stays ICS55 SAFE24 with PLL off; IHP130 is explicit compatibility.
## TINY-R2-P3 implementation in progress (2026-10-08–10)
The retained source-bound compiler/layout matrix is complete for commit
`3e46cc80e2d2243233d284b7cd9583714b0773bb`, source digest
`a7fc5112cd6f20df956243c4d081a6a31ede6192cd270e3c13340557b0e58776`.
All eight configurations pass ordinary Tiny acceptance and three deterministic
Verilator cold starts. `compat-os-flat`, `perf-o2-lto-flat` and
`perf-o2-plain-banked` also pass three native Icarus cold starts, with exact
cross-simulator sample identity. The structured measurement report is
`build/ics55-tiny-performance-2026-10-09-17-10-53cc6674d532/meta/tiny-r2-p3/report.json`.
It records software measurements only; it does not close phase acceptance,
physical timing, hardware bank conflict or Mini integrated-DMA gates.
Quality evidence for that committed source is complete: full Pytest reports 1629 passed and
one environment skip for missing `pypdf`; focused P3 tests and the embedded/RTL
quality checks pass. Its Tiny PR regressions pass for ICS55 and IHP130, and
Mini ICS55 behavioral-only compatibility passes. Negative STA margins and
non-blocking lint observations remain recorded as observations.
Exact commands, measurements, current evidence roots and remaining qualification
limits are in the final section of the [P3 review-fix record](tiny-soc-r2-p3-review-fixes.md).
Human review and acceptance remain required before any R2-P4 work.
### Subsequent P1 race-fix follow-up
The approved follow-up fixes WS2812 completion between register reads and
legacy TCD publication across an ISR lease acquisition. Current source is
`3e46cc8` plus the two HAL fixes and deterministic host tests; the matrix above
remains evidence for the original commit. New evidence is under
`build/ics55-tiny-2026-10-10-09-00-b4aa59f3e5d9/meta/tiny-r2-p3-races/`.
The two new tests fail before their respective fixes and pass afterward;
C format/policy/host checks and 156 focused tests pass. Mini PIO compatibility
also passes once with strict SIM_TEST_PASS and no forbidden markers. Tiny
ICS55/IHP130 PR and Mini ICS55 behavioral regressions each pass once. Final
`evidence.json` checks all 1287 source inputs, 487 Mini PIO compiled inputs and
all eight ordinary regression simulations. Lint baseline differences and
negative STA remain observations; no timing closure is claimed. See the final
review-fix section for commands, hashes, exact paths and unrun gates.
Per the maintainer's follow-up instruction, long checks default to one execution;
repeat only for a concrete failure/inconsistency or an explicit acceptance
requirement. This follow-up does not repeat the three-cold-start compiler matrix.
It does not waive any frozen qualification requirement or reattribute old runs.
### Historical development snapshot
The 2026-10-09 execution snapshot in the [P3 review-fix record](tiny-soc-r2-p3-review-fixes.md)
records all eight ordinary Tiny/Verilator compiler experiments completed, with
selected Icarus groups still running. The current-source full Pytest result is
selected Icarus groups still running at that snapshot. The then-current full Pytest result was
1531 passed, 4 failed, 94 fixture errors and 1 skipped; configuration-declaration
and publication source/fixture drift remain open. IHP130 Tiny PR completes with
and publication source/fixture drift were still open. IHP130 Tiny PR completed with
non-blocking lint observations and negative STA margin. These results do not
close the remaining matrix, quality or human phase-acceptance gates.
replace the final results above or imply human phase acceptance.
The [P3 review-fix record](tiny-soc-r2-p3-review-fixes.md) closes the retained
banked O2 Icarus finding: all three runs completed successfully and match the
@@ -28,8 +500,8 @@ is bound at
underflow/abort recovery, zero DMA starts and strict TEST_STATUS/SIM_TEST_PASS.
Focused checks report 96 passed; C/RTL quality, Ruff and diff checks pass.
The old Mini DMA failure remains failed/unsupported evidence, not a PIO pass.
The full compiler/placement matrix, broader current-source regressions and human
phase acceptance remain pending. See the review-fix record for exact commands,
The full compiler/placement matrix and broader regressions were pending in this
initial record; human phase acceptance remains pending. See the review-fix record for exact commands,
artifacts, historical attribution and unrun gates.
The user explicitly approved the P3 preflight and implementation on source
@@ -42,9 +514,9 @@ Original phase IDs, historical evidence and qualification limits are retained.
The [P3 runbook](tiny-soc-r2-software-scheduling.md) defines compiler/layout
experiments, shared finite DMA/WS2812 service, ownership and exact commands.
Implementation and validation are in progress; this entry does not claim
phase completion or performance improvement. The existing GNU toolchain is
used first, as requested; no dependency version has been changed.
The initial entry recorded implementation and validation in progress; it did not
claim phase completion or performance improvement. The existing GNU toolchain
was used as requested; no dependency version has been changed.
Early development testing found a compressed-instruction acceptance bug:
`rs_mcu_unmapped_probe` assembled its faulting load as 16-bit `4108`, while
@@ -448,8 +920,8 @@ not a prerequisite to the architectural improvements in R2-P3 through R2-P5.
| TINY-R2-P0 - Performance Contract and Roadmap Freeze | Reviewable requirements, exact old/new phase mapping, invariant package/IO counts, same-frequency CPU/main-SRAM ownership, explicit performance budgets, links and commands. Record actual documentation checks separately. | Documentation freeze only; no hardware result |
| TINY-R2-P1 - Reproducible Baseline, Constraints and Measurements | Reviewed source/profile/lock/tool identity; current 24 MHz functional and workload baseline; clocks, reset endpoints, constraints and timing-exception audit; counters and comparison methodology. Historical WNS is a risk reference, not a refreshed measurement. | Functional/measurement baseline accepted by explicit P2 approval on 2026-10-06; timing, warning and physical gaps retained |
| TINY-R2-P2 - Reset Distribution and CPU/SRAM Clock Feasibility | Reset distribution and payload-reset semantics; locked IHP130 main-SRAM macro timing, candidate common CPU/SRAM periods and representative paths; early clock/reset feasibility with explicit gaps. No final routed/PVT pass is implied. | Implementation and evidence ready for review; all analyzed rates retain timing failures; phase acceptance pending human review |
| TINY-R2-P3 - Software and DMA Scheduling | Compiler and placement A/B results, finite DMA ownership, burst/chunk scheduling, WS2812 refill budget and workload correctness on the available platform. | Pending |
| TINY-R2-P4 - Dual-Port Hazard3 and Four-Bank Local SRAM | Actual separate I/D paths, four independent 32 KiB bank frontends, SYS-clocked physical main-SRAM macros, local latency/fairness, FENCE.I, debug/reset and early inventory/SDC/macro-binding updates. | Pending |
| TINY-R2-P3 - Software and DMA Scheduling | Compiler and placement A/B results, finite DMA ownership, burst/chunk scheduling and available-path correctness; approved option A retains product DMA/IRQ budget qualification for the platform that supplies the route. | Matrix retained on `3e46cc8`; subsequent two P1 HAL fixes and focused/compatibility checks complete in the worktree; human review/acceptance pending |
| TINY-R2-P4 - Dual-Port Hazard3 and Four-Bank Local SRAM | Actual separate I/D paths, four independent 32 KiB bank frontends, SYS-clocked physical main-SRAM macros, local latency/fairness, FENCE.I, debug/reset and early inventory/SDC/macro-binding updates. | Implementation and planned functional validation complete on `4db2752` plus retained worktree snapshot; matched-image DMA throughput regression and human phase acceptance pending review; timing remains observational |
| TINY-R2-P5 - Per-Target Concurrent Fabric | Cross-target concurrency, one combined transaction per target, central-DMA read/write overlap, AW/W binding, errors, simultaneous faults/counters and accepted-transfer drain. | Pending |
| TINY-R2-P6 - Shared IP and Eight-Channel DMA Integration | Legacy P7 obligations; shared PWM reporting, I2S slave and alternate-input-route prerequisites; common ABI/HAL parity; Crypto banks/lifecycle; truthful capabilities and affected Mini compatibility. | Pending |
| TINY-R2-P7 - Tiny RCU and Dual-Mode Clock/Reset Integration | Legacy P8 obligations with main SRAM in SYS and XPI in MEM; atomic rate changes, all actual CDC/reset barriers, clock failures, WFI leaf gating and reserved DVP controls. | Pending |
+9 -3
View File
@@ -158,11 +158,17 @@ false-paths the canonical reset ports. Product overlays
Qualified I/O covers JTAG, DVP (GPIO10-20), ULPI, SDRAM, SDIO, XPI, and
asynchronous pads (UART0/1, remaining GPIO); a reviewed
`TIMING_IO_MODE_HOOK` is mandatory in qualified mode.
- Tiny (`tiny.tcl`): SAFE24 boot — `clk_system` (external 24 MHz) and
`clk_jtag` only. The PLL_TOP macro is parked (EN=0), is not a clock source,
- Tiny (`tiny.tcl`): P7 SAFE24 boot — `clk_aon` (external 24 MHz), derived
`clk_system`, `clk_memory`, `clk_peripheral`, `clk_jtag`, and the independent
`clk_audio` (GPIO17, 18.432 MHz). Explicit case analysis selects REF24 in all
four RCU muxes and parks PLL_TOP (EN=0) for this scenario. The live design
supports software transitions, but active PLL timing is not modeled here.
In this scenario the PLL is not a clock source,
and has no characterized timing arcs yet (see `docs/ip/tiny-soc.md`
TINY-055); its DB/LEF/GDS/CDL views are still required for link, placement,
and LVS. Qualified I/O covers JTAG, XPI, and asynchronous pads.
and LVS. The legacy I/O template covers JTAG, XPI, and asynchronous pads;
this domain inventory update does not qualify P6 I2S/SDIO board timing or
the final QFN64 wrapper.
## Legacy-flow parity
+4 -4
View File
@@ -16,9 +16,9 @@ TOP := retrosoc_tiny_asic
# Canonical clock domains, mirrored by tcl/common/products/tiny.tcl and
# validated against rtl/tiny/integration/clock_reset_domains.json when the
# commercial timing contract is generated. Tiny boots SAFE24: the PLL_TOP
# macro is present in the netlist but parked off and is not a clock source.
COMMERCIAL_CLOCK_DOMAINS := system jtag
# commercial timing contract is generated. This adapter analyzes the SAFE24
# boot scenario, explicitly constraining RCU mux selections and PLL enable.
COMMERCIAL_CLOCK_DOMAINS := aon system memory peripheral jtag audio
# Qualified-I/O interface groups and the budget variables each group requires.
COMMERCIAL_IO_INTERFACES := JTAG XPI ASYNC
@@ -38,7 +38,7 @@ COMMERCIAL_IO_BUDGET_VARIABLES := \
# Tiny has no bidirectional pad-mode selection requirement.
PRODUCT_REQUIRES_IO_MODE_HOOK := NO
# The Tiny PLL_TOP macro is parked (EN=0) and has no characterized timing
# In this SAFE24 scenario PLL_TOP is parked (EN=0) and has no characterized timing
# arcs; doctor skips the qualified PLL-mode checks for this product. See
# docs/ip/tiny-soc.md (TINY-055) for the qualification boundary.
PRODUCT_PLL_MODE := parked
@@ -11,15 +11,14 @@
# retroSoC Tiny (retrosoc_tiny_asic) product overlay.
#
# Clock intent: the canonical contract (rtl/tiny/integration/
# clock_reset_domains.json) owns two domains, system (SAFE24 external clock)
# and jtag. The ICS55 PLL_TOP macro is present but parked off (EN=0) and is
# not a clock source; it carries no characterized timing arcs yet, so no PLL
# overlay clock is created here. See docs/ip/tiny-soc.md (TINY-055).
# clock_reset_domains.json) owns REF24, derived SYS/MEM/PCLK, JTAG and GPIO17
# AUDIO. This adapter covers SAFE24 boot only. RCU can enable the physical PLL
# in functional operation, but its active timing remains unqualified.
namespace eval product {}
proc product::expected_clock_domains {} {
return {system jtag}
return {aon system memory peripheral jtag audio}
}
proc product::custom_clock_domains {} {
@@ -27,7 +26,7 @@ proc product::custom_clock_domains {} {
}
proc product::clock_names {} {
return {clk_system clk_jtag}
return {clk_aon clk_system clk_memory clk_peripheral clk_jtag clk_audio clk_sys_div2 clk_sys_div4 clk_clkout_source}
}
proc product::io_interface_groups {} {
@@ -35,6 +34,31 @@ proc product::io_interface_groups {} {
}
proc product::apply_clock_overlays {} {
foreach divisor {2 4} {
create_generated_clock -name clk_sys_div$divisor \
-source [flow::domain_object system] -divide_by $divisor \
[flow::required_net_driver_pin "Tiny SYS /$divisor" \
"u_soc/u_clock_backend/s_div${divisor}_clk"]
flow::register_clock clk_sys_div$divisor core
}
foreach mux {u_sys_mux u_mem_mux u_pclk_div2_mux u_pclk_mux} {
set_case_analysis 0 [flow::required_pins "Tiny SAFE24 mux $mux" \
"u_soc/u_clock_backend/$mux/select_i"]
foreach {side enabled} {0 1 1 0} {
set_case_analysis $enabled [flow::required_net_driver_pin "Tiny settled clock gate" \
"u_soc/u_clock_backend/$mux/r_enable$side"]
}
}
set_case_analysis 0 [flow::required_pins "Tiny SAFE24 PLL enable" \
"u_soc/u_clock_backend/u_pll/gen_ecos/u_backend/u_PLL_TOP/EN"]
foreach bit {1 2} {
set_case_analysis 0 [flow::required_net_driver_pin "Tiny boot CLKOUT source" \
"u_soc/u_clock_output/s_cfg_q\[$bit\]"]
}
create_generated_clock -name clk_clkout_source -source [flow::domain_object aon] \
-divide_by 1 [flow::required_net_driver_pin "Tiny boot CLKOUT source clock" \
"u_soc/u_clock_output/s_src_clk"]
flow::register_clock clk_clkout_source core
}
proc product::apply_io_constraints {} {
@@ -27,7 +27,7 @@ SIDE_ORDER = ("south", "east", "north", "west")
EXPECTED_SIGNAL_PADS = 52
CLOCK_PORTS = ("extclk_i_pad", "jtag_tck_i_pad")
SRAM_MASTER = "RM_IHPSG13_1P_1024x32_c2_bm_bist"
SRAM_INSTANCE = "u_soc.u_sram.gen_memory.gen_bank[{index}].u_ram.u_mem"
SRAM_INSTANCE = "u_soc.u_sram.gen_group[{group}].u_group.gen_bank[{bank}].u_ram.u_mem"
SRAM_COLUMNS = 8
SRAM_ORIGIN = (400, 600)
# Detailed routing requires an M2 track inside each bank's 0.26 um bottom-edge
@@ -95,7 +95,7 @@ def sram_instances(capacity_kib: int) -> dict[str, tuple[list[int], str]]:
SRAM_ORIGIN[0] + column * SRAM_PITCH[0],
SRAM_ORIGIN[1] + row * SRAM_PITCH[1],
]
instances[SRAM_INSTANCE.format(index=index)] = (location, "N")
instances[SRAM_INSTANCE.format(group=index // 8, bank=index % 8)] = (location, "N")
return instances
@@ -122,18 +122,9 @@ def macro_config(instances: dict[str, tuple[list[int], str]], header: str) -> di
def macro_hooks(instances: dict[str, tuple[list[int], str]]) -> list[str]:
hooks: list[str] = []
for instance in instances:
bracket_start = instance.find("[")
if bracket_start >= 0:
bracket_end = instance.index("]", bracket_start)
instance_pattern = (
re.escape(instance[:bracket_start])
+ ".*"
+ instance[bracket_start + 1 : bracket_end]
+ ".*"
+ re.escape(instance[bracket_end + 1 :])
)
else:
instance_pattern = re.escape(instance)
# OpenDB and synthesis may escape array brackets differently. Handle
# every index (group and bank), retaining the exact non-index hierarchy.
instance_pattern = ".*".join(re.escape(part) for part in re.split(r"[\[\]]", instance))
hooks.extend(
[
f"{instance_pattern} VDD VSS VDDARRAY! VSS!",
+47 -2
View File
@@ -119,9 +119,12 @@ def render(domains: list[dict[str, Any]], reset_ports: list[str]) -> str:
if source_domain is None:
lines.append(f"create_clock -name clk_{name} -period {period} ${pin_variable}")
else:
divide_by = sta.get("divide_by", 1)
if not isinstance(divide_by, int) or isinstance(divide_by, bool) or divide_by < 1:
raise ValueError("generated clock division must be a positive integer")
lines.append(
f"create_generated_clock -name clk_{name} -source ${pin_variables[source_domain]} "
f"-divide_by 1 ${pin_variable}"
f"-divide_by {divide_by} ${pin_variable}"
)
lines.append("")
@@ -175,10 +178,52 @@ def main() -> int:
parser.add_argument("--domains", type=Path, required=True)
parser.add_argument("--pin-map", type=Path, required=True)
parser.add_argument("--output", type=Path, required=True)
parser.add_argument("--xin-hz", type=int, default=24000000)
args = parser.parse_args()
try:
domains, reset_ports = parse_document(args.domains, args.pin_map)
atomic_write(args.output, render(domains, reset_ports))
document = json.loads(args.domains.read_text())
p7 = document.get("clock_contract") == "TINY-R2-P7"
if p7 and args.xin_hz not in (24000000, 48000000, 96000000):
raise ValueError("unsupported Tiny external clock")
if p7 and args.xin_hz != 24000000:
aon = next(domain for domain in domains if domain["name"] == "aon")
external = {**aon, "name": "external", "sta": {**aon["sta"],
"period_ns": 1e9 / args.xin_hz}}
aon["sta"] = {"source_domain": "external", "period_ns": 1e9 / 24000000,
"divide_by": args.xin_hz // 24000000, "async_group": "core",
"pin": "u_soc.u_clock_backend.gen_ref_divided.u_ref_div/clk_o"}
domains.insert(0, external)
if p7:
# These sources run even while the muxes select their undivided
# inputs. Their handover synchronizers still require real clocks.
for divisor in (2, 4):
domains.append({"name": f"sys_div{divisor}", "sta": {
"source_domain": "system", "period_ns": 1e9 * divisor / 24000000,
"divide_by": divisor, "async_group": "core", "object_type": "net_driver",
"net": f"u_soc.u_clock_backend.s_div{divisor}_clk"}})
domains.append({"name": "clkout_source", "sta": {
"source_domain": "aon", "period_ns": 1e9 / 24000000,
"divide_by": 1, "async_group": "core", "object_type": "net_driver",
"net": "u_soc.u_clock_output.s_src_clk"}})
result = render(domains, reset_ports)
if p7:
result += "\n# P7 SAFE24 boot scenario; this does not qualify active PLL timing.\n"
for mux in ("u_sys_mux", "u_mem_mux", "u_pclk_div2_mux", "u_pclk_mux"):
result += (f"set_case_analysis 0 [require_pins {{P7 SAFE24 selection}} "
f"{{u_soc.u_clock_backend.{mux}/select_i}}]\n")
# select_i passes through sequential handshake state. A static
# mode must also select its settled gate state; setting only
# the request cannot prune the other live clock in STA.
for side, enabled in ((0, 1), (1, 0)):
result += (f"set_case_analysis {enabled} [require_net_driver_pin "
f"{{P7 SAFE24 settled gate}} "
f"{{u_soc.u_clock_backend.{mux}/r_enable{side}}}]\n")
result += "# CLKOUT retains its reset REF24 source in this boot scenario.\n"
for bit in (1, 2):
result += (f"set_case_analysis 0 [require_net_driver_pin {{P7 CLKOUT source}} "
f"{{u_soc.u_clock_output.s_cfg_q_{bit}_}}]\n")
atomic_write(args.output, result)
except (OSError, ValueError, json.JSONDecodeError) as error:
parser.error(str(error))
return 0
+5 -5
View File
@@ -16,14 +16,14 @@ include $(ROOT_PATH)/physical/smoke/sta/opensta/pdk_timing.mk
$(OPENSTA_SDC): $(OPENSTA_SDC_GEN) $(OPENSTA_DOMAIN_MAP) $(OPENSTA_PIN_MAP) | manifest
@mkdir -p $(dir $@)
python3 $(OPENSTA_SDC_GEN) --domains $(OPENSTA_DOMAIN_MAP) --pin-map $(OPENSTA_PIN_MAP) --output $@
python3 $(OPENSTA_SDC_GEN) --domains $(OPENSTA_DOMAIN_MAP) --pin-map $(OPENSTA_PIN_MAP) --output $@ --xin-hz $(EXT_CLK_HZ)
sta: $(OPENSTA_SDC) | manifest
@mkdir -p $(STA_BUILD_ROOT)
ifeq ($(TINY_SAFE24_PLL_OFF),YES)
python3 $(ROOT_PATH)/scripts/tiny_ics55_platform.py --variant-root $(VARIANT_ROOT) verify-netlist \
ifeq ($(SOC),TINY)
python3 $(ROOT_PATH)/scripts/tiny_r2_p7.py --variant-root $(VARIANT_ROOT) verify-netlist \
--netlist $(OPENSTA_NETLIST) --config $(OPENSTA_CONFIG) \
--output $(SYN_BUILD_ROOT)/tiny-ics55-netlist.json
--output $(SYN_BUILD_ROOT)/tiny-r2-p7-netlist.json
endif
@for input in $(OPENSTA_NETLIST) $(OPENSTA_LIBERTY) $(OPENSTA_LINK_LIBS) $(OPENSTA_SRAM_LIBS) $(OPENSTA_SDC) $(OPENSTA_CONFIG); do \
test -f "$$input" || { echo "OpenSTA input missing: $$input" >&2; exit 1; }; \
@@ -45,7 +45,7 @@ endif
--env OPENSTA_TOP=$(OPENSTA_TOP) --env OPENSTA_NETLIST=$(OPENSTA_NETLIST) --env OPENSTA_LIBERTY=$(OPENSTA_LIBERTY) \
--env 'OPENSTA_LINK_LIBS=$(OPENSTA_LINK_LIBS)' --env 'OPENSTA_SRAM_LIBS=$(OPENSTA_SRAM_LIBS)' \
--env OPENSTA_SDC=$(OPENSTA_SDC) --env OPENSTA_REPORT=$(OPENSTA_REPORT) \
--env OPENSTA_METRICS=$(OPENSTA_METRICS) --env OPENSTA_TINY_ICS55=$(TINY_SAFE24_PLL_OFF) -- \
--env OPENSTA_METRICS=$(OPENSTA_METRICS) --env OPENSTA_TINY_ICS55=$(TINY_SAFE24_PLL_OFF) --env OPENSTA_TINY_P7=$(TINY_R2_P7) -- \
$(OPENSTA) -no_init -exit -threads $(OPENSTA_THREADS) $(ROOT_PATH)/physical/smoke/sta/opensta/opensta.tcl
.PHONY: sta
+7
View File
@@ -20,6 +20,13 @@ if {[info exists ::env(OPENSTA_TOP)] && $::env(OPENSTA_TOP) ne ""} {
}
link_design $top_design
read_sdc $::env(OPENSTA_SDC)
if {$top_design eq "retrosoc_tiny_asic"} {
if {[info exists ::env(OPENSTA_TINY_P7)] && $::env(OPENSTA_TINY_P7) eq "YES"} {
source [file join [file dirname [info script]] tiny_r2_p7_checks.tcl]
} else {
source [file join [file dirname [info script]] tiny_r2_p4_checks.tcl]
}
}
if {[info exists ::env(OPENSTA_TINY_ICS55)] && $::env(OPENSTA_TINY_ICS55) eq "YES"} {
source [file join [file dirname [info script]] tiny_ics55_checks.tcl]
}
@@ -4,7 +4,7 @@
set audit_dir [file dirname $::env(OPENSTA_REPORT)]
set pll [get_cells -hierarchical -filter {ref_name == PLL_TOP}]
set ram [get_cells -hierarchical -filter {ref_name == ics55_ecos_sram_1024x32_m8}]
if {[llength $pll] != 1 || [llength $ram] != 32} { error "Tiny ICS55 macro binding mismatch" }
if {[llength $pll] != 1 || [llength $ram] != 38} { error "Tiny ICS55 macro binding mismatch" }
if {[llength [get_clocks clk_system]] != 1} { error "Tiny SYS clock missing" }
report_clock_properties > $audit_dir/clocks.rpt
check_setup -verbose > $audit_dir/coverage.rpt
@@ -0,0 +1,77 @@
# P4 structural timing prerequisites, common to both supported Tiny PDKs.
# Negative timing remains observational; missing/incorrect clock objects do not.
set p4_dir [file dirname $::env(OPENSTA_REPORT)]
set p4_ram [get_cells -hierarchical -filter {ref_name == RM_IHPSG13_1P_1024x32_c2_bm_bist}]
set p4_clock_pin A_CLK
if {[llength $p4_ram] == 0} {
set p4_ram [get_cells -hierarchical -filter {ref_name == ics55_ecos_sram_1024x32_m8}]
set p4_clock_pin CLK
}
if {[llength $p4_ram] != 38} { error "P6 requires 32 main SRAM and six private Crypto macros" }
set p6_all_ram $p4_ram
set p4_ram {}
set p6_crypto {}
set p6_crypto_expected {}
for {set bank 0} {$bank < 6} {incr bank} {
lappend p6_crypto_expected [format {u_soc.u_crypto.u_storage.gen_bank[%d].gen_macro.u_sram.u_mem} $bank]
}
set p6_crypto_seen {}
foreach cell $p6_all_ram {
set name [string map {/ .} [get_full_name $cell]]
set is_crypto 0
foreach expected $p6_crypto_expected {
if {[string equal [string range $name end-[expr {[string length $expected]-1}] end] $expected]} {
lappend p6_crypto_seen $expected
set is_crypto 1
}
}
if {$is_crypto} { lappend p6_crypto $cell } else { lappend p4_ram $cell }
}
if {[llength $p4_ram] != 32 || [lsort $p6_crypto_seen] ne [lsort $p6_crypto_expected]} {
error "P6 private/main SRAM hierarchy mismatch"
}
set p4_expected {}
for {set group 0} {$group < 4} {incr group} {
for {set bank 0} {$bank < 8} {incr bank} {
lappend p4_expected [format {u_soc.u_sram.gen_group[%d].u_group.gen_bank[%d].u_ram.u_mem} $group $bank]
}
}
set p4_seen {}
foreach cell $p4_ram {
set name [string map {/ .} [get_full_name $cell]]
foreach expected $p4_expected {
if {[string equal [string range $name end-[expr {[string length $expected]-1}] end] $expected]} {
lappend p4_seen $expected
}
}
}
if {[lsort $p4_seen] ne [lsort $p4_expected]} { error "P4 macro group hierarchy mismatch" }
set p4_source [get_pins -quiet {u_clock_buffer/clk_o}]
if {[llength $p4_source] != 1} { error "P4 SYS source missing" }
set p4_direct {}
foreach pin [get_fanout -from $p4_source -flat -pin_levels 1 -trace_arcs all] {
dict set p4_direct [get_full_name $pin] 1
}
set p4_clock_report [open $p4_dir/p4-clock-bindings.rpt w]
foreach cell [concat $p4_ram $p6_crypto] {
set clocks {}
foreach pin [get_pins -of_objects $cell] {
if {[string match "*/$p4_clock_pin" [get_full_name $pin]]} { lappend clocks $pin }
}
if {[llength $clocks] != 1} { error "P4 macro clock pin missing" }
set name [get_full_name [lindex $clocks 0]]
if {![dict exists $p4_direct $name]} { error "P4 SRAM clock is not direct SYS: $name" }
puts $p4_clock_report $name
}
set p4_cpu [get_pins -hierarchical -quiet {*u_hazard3_cpu_2port*/CLK *u_hazard3_cpu_2port*/CK *u_hazard3_cpu_2port*/CKN}]
set p4_front [get_pins -hierarchical -quiet {*u_cpu_mem*/CLK *u_cpu_mem*/CK *u_cpu_mem*/CKN}]
if {[llength $p4_cpu] == 0 || [llength $p4_front] == 0} { error "P4 CPU/frontend clock endpoints missing" }
foreach pin [concat $p4_cpu $p4_front] {
set name [get_full_name $pin]
if {![dict exists $p4_direct $name]} { error "P4 CPU/frontend does not share SYS: $name" }
}
puts $p4_clock_report "CPU clock endpoints: [llength $p4_cpu]"
puts $p4_clock_report "Frontend clock endpoints: [llength $p4_front]"
close $p4_clock_report
puts "TINY_R2_P4_BINDING_PASS: four groups, 32 macros, common CPU/SRAM SYS; not timing qualification"
puts "TINY_R2_P6_BINDING_PASS: six private Crypto macros on SAFE24 PCLK; no capacity alias"
@@ -0,0 +1,22 @@
# P7 uses a dedicated, provenance-checked Python macro/clock audit. Historical
# P1/P4 checks retain their direct-SYS/parked-PLL assumptions unchanged.
set p7_dir [file dirname $::env(OPENSTA_REPORT)]
foreach clock {clk_aon clk_system clk_memory clk_peripheral clk_audio clk_jtag clk_sys_div2 clk_sys_div4 clk_clkout_source} {
if {[llength [get_clocks -quiet $clock]] != 1} { error "P7 clock missing: $clock" }
}
set p7_pll [get_cells -hierarchical -quiet -filter {ref_name == PLL_TOP}]
if {[llength $p7_pll] > 1} { error "P7 contains multiple PLL macros" }
if {[llength $p7_pll] == 1} {
# The analysis is explicitly the cold SAFE24 boot operating state.
set p7_enable [get_pins -quiet "[get_full_name [lindex $p7_pll 0]]/EN"]
if {[llength $p7_enable] != 1} { error "P7 PLL enable pin missing" }
set_case_analysis 0 $p7_enable
}
report_clock_properties > $p7_dir/clocks.rpt
check_setup -verbose > $p7_dir/coverage.rpt
report_check_types -recovery -removal -min_pulse_width -min_period -max_slew -max_capacitance -max_fanout -violators -verbose > $p7_dir/constraint-violations.rpt
foreach domain {system memory peripheral aon} {
report_checks -path_group clk_$domain -path_delay max -group_path_count 20 -fields {capacitance slew fanout} > $p7_dir/$domain-setup.rpt
report_checks -path_group clk_$domain -path_delay min -group_path_count 20 -fields {capacitance slew fanout} > $p7_dir/$domain-hold.rpt
}
puts "TINY_R2_P7_STA_OBSERVATION: SAFE24 boot only; not active PLL, routed or PVT qualification"
+1 -1
View File
@@ -165,7 +165,7 @@ yosys tee -q -o "${report_dir}/${proj_name}_area_logic.rpt" stat -top $top_desig
# final netlist
yosys write_verilog -noattr -noexpr -nohex -nodec $netlist
if {$soc == "TINY" && $pdk == "ICS55"} {
if {$soc == "TINY"} {
yosys write_json ${netlist}.json
}
+4 -4
View File
@@ -59,7 +59,7 @@ gen_synth_filelist: $(SV_FLIST)
## Synthesize netlist using Yosys
synth: $(NETLIST) | manifest
$(NETLIST): $(SV_FLIST) $(YOSYS_SCRIPTS)
$(NETLIST): $(SV_FLIST) $(YOSYS_SCRIPTS) | manifest
@mkdir -p $(YOSYS_OUT)
@mkdir -p $(YOSYS_TMP)
@mkdir -p $(YOSYS_RPT)
@@ -74,9 +74,9 @@ $(NETLIST): $(SV_FLIST) $(YOSYS_SCRIPTS)
--env REPORTS=$(YOSYS_RPT) --env NETLIST=$(NETLIST) -- \
timeout --foreground --kill-after=5s $(YOSYS_TIMEOUT)s \
yosys -t --perffile $(YOSYS_BUILD)/yosys-perf.json -c $(YOSYS_DIR)/script/synth.tcl
ifeq ($(SOC)-$(PDK),TINY-ICS55)
python3 $(ROOT_PATH)/scripts/tiny_ics55_platform.py --variant-root $(VARIANT_ROOT) audit-netlist \
--netlist $(NETLIST) --config $(NETLIST_CONFIG) --output $(YOSYS_BUILD)/tiny-ics55-netlist.json
ifeq ($(SOC),TINY)
python3 $(ROOT_PATH)/scripts/tiny_r2_p7.py --variant-root $(VARIANT_ROOT) audit-netlist \
--netlist $(NETLIST) --config $(NETLIST_CONFIG) --output $(YOSYS_BUILD)/tiny-r2-p7-netlist.json
endif
$(NETLIST_DEBUG): $(NETLIST)
@@ -4459,7 +4459,8 @@
"RS_ETIMEOUT": -4,
"RS_EIO": -5,
"RS_ENOTSUP": -6,
"RS_EFORMAT": -7
"RS_EFORMAT": -7,
"RS_EBUSY": -8
},
"meanings": {
"RS_OK": "Operation completed under that API contract.",
@@ -4469,7 +4470,8 @@
"RS_ETIMEOUT": "Bounded operation timed out.",
"RS_EIO": "I/O operation failed.",
"RS_ENOTSUP": "Operation unsupported.",
"RS_EFORMAT": "Input format rejected."
"RS_EFORMAT": "Input format rejected.",
"RS_EBUSY": "Resource or lifecycle operation is busy; retry after the owning operation completes."
},
"observation": "Signed API return value. Exact triggering conditions depend on the called function; the enum definition does not make every code reachable from every API.",
"handling": "Check the return before using output values. No hardware W1C applies to a C return value; preserve the relevant peripheral status if diagnosis requires it.",
@@ -11,9 +11,9 @@ All **39 frozen entries / 14 IP chapters** are reviewed below. Register, address
| 3 | Configuration and Scope / 2 | Covered. IHP130, 128 KiB, 24 MHz target, four DMA channels; hardware RV32IMC versus default RV32IM firmware. | [ihp130-tiny.mk](../../../configs/ci/ihp130-tiny.mk), [retrosoc_tiny.sv](../../../rtl/tiny/top/retrosoc_tiny.sv) | Use the committed profile and identity; keep deferred interfaces and physical claims excluded. |
| 4 | Reading and Evidence Conventions / 2 | Covered. Product-specific organization, navigation or revision control; no additional hardware capability. | [datasheet.typ](../../../publications/datasheets/tiny/datasheet.typ), [structure-contract.json](../../../publications/datasheets/tiny/structure-contract.json) | Retain the declared scope, complete source links and independent Tiny identity when updating this section. |
| 5 | System Architecture / 17 | Covered. Product-specific organization, navigation or revision control; no additional hardware capability. | [datasheet.typ](../../../publications/datasheets/tiny/datasheet.typ), [structure-contract.json](../../../publications/datasheets/tiny/structure-contract.json) | Retain the declared scope, complete source links and independent Tiny identity when updating this section. |
| 6 | Processor and Integration / 17 | Covered. One RV32IMC MCU hart, A disabled, direct AHB-Lite/AXI32 adapter and debug reset boundary. | [retrosoc_tiny.sv](../../../rtl/tiny/top/retrosoc_tiny.sv), [mgmt_core_wrapper.sv](../../../rtl/ip/core/mgmt_core_wrapper.sv) | Retain actual wrapper parameters; do not infer firmware ISA flags from the hardware C extension. |
| 6 | Processor and Integration / 17 | Working-source P4 update: dual-port RV32IMC hart, A disabled, independent local I/D and tagged slow adapter. Page number belongs to the earlier delivered PDF. | [retrosoc_tiny.sv](../../../rtl/tiny/top/retrosoc_tiny.sv), [tiny_cpu_wrapper.sv](../../../rtl/tiny/top/tiny_cpu_wrapper.sv) | Retain actual wrapper parameters; revalidate publication pages separately before delivery. |
| 7 | Clock, Reset and Operating State / 18 | Covered. System and asynchronous JTAG domains; RTC/WDG share the system clock. | [clock_reset_domains.json](../../../rtl/tiny/integration/clock_reset_domains.json), [retrosoc_tiny.sv](../../../rtl/tiny/top/retrosoc_tiny.sv) | Obtain CDC/RDC and clock/power evidence separately; do not claim independent sleep timing. |
| 8 | Bus Transactions, Arbitration and Errors / 19 | Covered. One globally active transaction, round-robin acceptance, saved ownership, bounded legal bursts and error/drain behavior. | [tiny_axi4_fabric.sv](../../../rtl/tiny/top/tiny_axi4_fabric.sv), [axi4_error_slave.sv](../../../rtl/ip/interconnect/axi4_error_slave.sv) | Keep local IP capability separate from end-to-end fabric admission; no unlimited liveness guarantee. |
| 8 | Bus Transactions, Arbitration and Errors / 19 | P5 source draft updated: target-local ownership, bounded independent DMA R/W, separate APB branches and concurrent fault count. | [tiny_axi4_fabric.sv](../../../rtl/tiny/top/tiny_axi4_fabric.sv), [tiny_axi4_target.sv](../../../rtl/tiny/top/tiny_axi4_target.sv) | Page19 belongs to the prior PDF; no new PDF or unconditional liveness claim. SDIO owner2 is reserved only. |
| 9 | Memory Map and Access Conventions / 21 | Covered. Twenty canonical address windows, including alias versus physical-capacity distinctions. | [memory_map.json](../../../rtl/tiny/address_map/memory_map.json) | Reconcile every source-map change; do not use unmapped holes as scratch memory. |
| 10 | Interrupt and DMA Request Routing / 22 | Covered. Fourteen allocated core causes, thirty external positions, enabled DMA request map and channel conventions. | [soc_topology.json](../../../rtl/tiny/integration/soc_topology.json), [dma_math.c](../../../crt/src/hal/dma_math.c) | Keep core input bits, external ordinals, CSR positions and DMA selectors distinct. |
| 11 | Peripherals / 23 | Covered. Fourteen IP chapters and 337 source-extracted register definitions, with instance-specific routing. | [catalog.json](../../../publications/datasheets/tiny/catalog.json), [tiny_reference.py](../../../publications/tiny_reference.py) | Update source inventory, frozen register keys and rendered-coverage checks together after reviewed ABI changes. |
@@ -1,15 +1,21 @@
# Tiny Gen1 v0.1 DRAFT content review
The publication describes the independent IHP130 Tiny profile: one RV32IMC
Hazard3 with A disabled, RV32IM default firmware, 128 KiB SRAM, 24 MHz target,
The publication describes the independent IHP130 Tiny profile: one dual-port
RV32IMC Hazard3 with A disabled, RV32IM default firmware, 128 KiB SRAM in four
independently serviced 32 KiB groups, 24 MHz target,
four DMA channels and AXI32/APB4. Rill (清溪) is the selected prototype brand.
The font/palette/grid are inherited as design rules; product facts are not
inherited from Mini's descriptions or qualification records.
The P4 working-source update changes CPU/local-SRAM bindings and the affected
draft architecture prose/figure. It does not advance `tiny.json.source_revision`,
rebuild or relabel a delivered PDF, or add hardware qualification. A later
publication release still requires source review, snapshot refresh and PDF checks.
| Area | Review and publication decision | Follow-up boundary |
| --- | --- | --- |
| CPU and startup | Publish the actual C/M extensions, disabled A, CSR-enabled firmware and Tiny guard around PSRAM setup. | Keep compiler target separate from implemented ISA; a reset vector is not a completed boot. |
| Fabric | Document one globally active CPU/DMA read or write, saved ownership, naturally aligned bounded bursts and terminal response/drain. | Do not claim arbitrary AXI4 features, independent read/write concurrency or unrestricted liveness. |
| Fabric | P5 draft: independent target ownership, one combined CPU slow operation, DMA one read/one write, 16 APB branches and lossless concurrent-fault accounting. Owner2 is reserved, inactive SDIO. | No SDIO integration, arbitrary AXI4 features or unconditional liveness claim. Snapshot/PDF revision is not advanced. |
| Address/IRQ/pads | Derive all 20 regions, 14 allocated core causes and 52 logical signal pads; retain 32 GPIO alternate entries. | Logical pads are not package pin numbers or a board schematic. |
| IP/register scope | Fourteen chapters, thirteen unique register families; Tiny SYSCTRL is decoded independently and ARCHINFO overrides the SoC identity. | Reject missing/extra decode and defaults belonging to other products. |
| DMA | Four channels, request mask 0x07F9, no stream modes, 64-byte descriptors and source-derived field layout. | **Discovery discrepancy:** the four-bit maximum-burst field reads zero after truncating 16. Publish that value and the actual 16-beat limit separately; propose a future ABI/RTL review, do not change RTL here. |
+20 -9
View File
@@ -29,9 +29,9 @@
- JTAG debug with coordinated hart reset.
*Memory and interconnect*
- *128 KiB SRAM*, 32 banks of 4 KiB.
- *128 KiB SRAM*, four independently serviced 32 KiB groups backed by 32 banks of 4 KiB.
- Native AXI32 memory paths and one APB4 island.
- CPU and DMA share one active transaction globally.
- Independent local instruction/data ports and target-local external ownership.
- XPI NOR boot and SRAM-resident application layout.
*DMA and control*
@@ -103,16 +103,18 @@ The document uses the approved Mini visual language; all product facts and sourc
= System Architecture
== Processor and Integration <tiny-core>
Hazard3 is hart zero. The shared wrapper selects the C and M extensions and disables A for Tiny. Its AHB-Lite bus terminates
at a direct AXI32 adapter. JTAG supports the implemented halt/resume, register and system-bus debug path; debug hart reset
waits for the adapter to become idle instead of abandoning an accepted transaction.
Hazard3 is hart zero. The locked dual-port wrapper selects the C and M extensions and disables A for Tiny. Instruction fetch
and load/store use independent AHB ports through separate local SRAM frontends; non-SRAM accesses use the tagged slow path.
JTAG supports halt/resume, register access and halted-hart abstract-command memory access, without an independent system-bus master.
Debug hart reset first halts the hart and drains accepted local I/D and slow-path responses. SRAM and unrelated DMA work stay active.
The system has no application hart, MMU/Linux platform, hardware cache-coherent multicore organization or product extension
slots. CPU-visible SRAM, XPI and APB routes are the implemented interconnect. The single-hart architecture does not remove
the need for software/DMA buffer ownership and fences.
#architecture()
#source-note("rtl/tiny/top/retrosoc_tiny.sv",title:"Tiny product integration and parameters")
#source-note("rtl/ip/core/mgmt_core_wrapper.sv",title:"CPU ISA, AHB adapter and debug wrapper")
#source-note("rtl/tiny/top/tiny_cpu_wrapper.sv",title:"Dual-port CPU ISA, AHB ports and debug wrapper")
#source-note("rtl/tiny/top/tiny_sram.sv",title:"Four local SRAM groups and external attachment")
== Clock, Reset and Operating State <tiny-clock>
CPU, AXI, APB, RTC and watchdog share the configured 24 MHz system clock. CLINT derives a 1 MHz tick. JTAG has its own
@@ -130,9 +132,18 @@ advertised as a qualified voltage/frequency operating point.
#source-note("rtl/tiny/integration/clock_reset_domains.json",title:"Clock/reset inventory and debug crossings")
== Bus Transactions, Arbitration and Errors <tiny-bus>
The two initiators are CPU and DMA. They share *one active read or write transaction globally*. The fabric saves source,
direction, address and attributes, forwards the request, and retains ownership until the terminal B or RLAST handshake.
Read/write request arbitration is round-robin; it is not a throughput guarantee or an unrestricted liveness guarantee.
The two active external initiators are CPU slow path and DMA. A third ingress is reserved for future SDIO integration and
has no active producer. Each SRAM group, XPI memory endpoint and APB peripheral has independent transaction ownership.
CPU slow traffic admits one combined operation; DMA can have one read and one write outstanding across different targets.
Each target admits one combined read or write. CPU local instruction/data requests remain independently arbitrated within
each SRAM group. Saved ownership includes accepted address and response buffers and lasts through the upstream B/RLAST handshake.
Owner arbitration is round-robin with fair direction selection. Progress bounds require a running clock and peer progress.
Separate APB bridges keep independent controls outside a FIFO-waiting peripheral's transaction.
Fault accounting counts each failed transaction once, including errors before the final read beat. Simultaneous events
increment the saturating count individually; first-fault metadata uses ascending owner, instruction before data, then read
before write. CPU is owner0, DMA owner1, and owner2 is reserved. FAULT_DETAIL retains raw AXI response2/3. Clearing validity
does not clear the count, and a simultaneous hardware event wins over clear.
Memory transfers support naturally aligned 1/2/4-byte beats and up to 16 INCR beats. Single-beat FIXED is also admitted.
The complete address span must remain within one decoded target and one 4 KiB page without overflow. MMIO accepts only
+7 -6
View File
@@ -26,21 +26,22 @@
})
}
#let architecture() = diagram("tiny-architecture",schematic((
(5,4,65,19,[Hazard3 · hart 0 \ RV32IMC · A disabled]),
(5,4,65,19,[Hazard3 dual-port · hart 0 \ RV32IMC · A disabled]),
(104,4,63,19,[JTAG / debug transport \ External TCK domain]),
(5,37,65,15,[AHB-Lite → AXI32 adapter]),
(5,37,65,15,[I/D decode + slow adapter]),
(104,37,63,15,[Four-channel DMA \ AXI32 master]),
(30,69,112,19,[Tiny AXI32 fabric \ One active transaction globally]),
(0,110,40,22,[128 KiB SRAM \ 32 × 4 KiB]),
(30,69,112,19,[Tiny AXI32 fabric \ Independent target ownership]),
(0,110,40,22,[128 KiB SRAM \ 4 × 32 KiB groups]),
(44,110,40,22,[XPI read path \ Flash alias / NOR]),
(88,110,40,22,[APB4 bridge \ 16 targets]),
(88,110,40,22,[16 APB4 branches \ One per peripheral]),
(132,110,40,22,[Error responders \ DECERR / SLVERR]),
),(
((104,13),(70,13)),((37,23),(37,37)),
((5,44),(0,44),(0,103),(6,103),(6,110)),
((37,52),(37,61),(60,61),(60,69)),((135,52),(135,61),(112,61),(112,69)),
((45,88),(45,99),(20,99),(20,110)),((70,88),(70,103),(64,103),(64,110)),
((103,88),(103,103),(108,103),(108,110)),((127,88),(127,99),(152,99),(152,110)),
),height:134),[Tiny Gen1 integration. System logic runs from the configured 24 MHz clock; JTAG is asynchronous.])
),height:134),[Tiny Gen1 integration. The left branch represents independent local I/D paths bypassing the external fabric. System logic uses the configured 24 MHz clock; JTAG is asynchronous.])
#let ip-diagram(entry) = {
let children=entry.nodes.slice(1)
@@ -98,7 +98,12 @@
"uart1": 26
},
"sources": [
"rtl/ip/core/mgmt_core_wrapper.sv",
"rtl/tiny/top/tiny_cpu_wrapper.sv",
"rtl/tiny/top/tiny_cpu_mem.sv",
"rtl/tiny/top/tiny_sram.sv",
"rtl/tiny/top/tiny_sram_group.sv",
"rtl/tiny/top/tiny_sram_axi4.sv",
"rtl/tiny/top/tiny_sram_port_if.sv",
"rtl/ip/core/mgmt_debug_wrapper.sv",
"rtl/ip/core/mgmt_debug_reset.sv",
"rtl/ip/interconnect/ahbl2axi4.sv",
@@ -112,19 +117,19 @@
"app/apps/bringup/tiny.mk",
"app/apps/ci_smoke/tiny.mk",
"scripts/generate_tiny.py",
"tests/test_tiny.py"
"tests/test_tiny.py",
"rtl/tiny/top/tiny_axi4_target.sv",
"rtl/tiny/top/tiny_fault_collector.sv"
],
"bindings": [
{
"file": "rtl/tiny/top/tiny_axi4_fabric.sv",
"text": [
"(s_selected_addr.len <= 8'd15)",
"(s_last_addr[31:12] == s_selected_addr.addr[31:12])",
"s_accept = (s_state_q == Idle) && s_selected_valid;",
"Response: if (s_terminal) s_state_d = Idle;",
"s_tgt_d = s_protocol_legal ? s_decoded_target : 3'd4;",
"assign s_protocol_legal = !s_selected_addr.lock && (s_selected_addr.size <= 3'd2) &&\n ((s_selected_addr.addr & ((32'd1 << s_selected_addr.size) - 32'd1)) == 32'd0) &&\n ((s_selected_addr.burst == 2'b01) ||\n ((s_selected_addr.burst == 2'b00) && (s_selected_addr.len == 8'd0))) &&\n (s_selected_addr.len <= 8'd15) && !s_last_addr[32] &&\n (s_last_addr[31:12] == s_selected_addr.addr[31:12]) &&\n (decode_target(\n s_last_addr[31:0]\n ) == s_decoded_target) && ((s_decoded_target != 3'd2) || (s_selected_addr.len == 8'd0));",
"assign s_terminal = (s_state_q == Response) &&\n (s_write_q ? (s_target_bvalid[s_tgt_q] && s_bready[s_owner_q]) :\n (s_target_rvalid[s_tgt_q] && s_rready[s_owner_q] && s_target_rlast[s_tgt_q]));"
" function automatic logic [4:0] checked_target(\n input logic [31:0] addr_i, input logic [7:0] len_i,\n input logic [2:0] size_i, input logic [1:0] burst_i,\n input logic lock_i, input logic instruction_i);\n logic [32:0] last_addr;\n logic [4:0] decoded;\n decoded = decode_target(addr_i);\n last_addr = {1'b0, addr_i} + (({25'd0, len_i} + 33'd1) << size_i) - 33'd1;\n if (lock_i || (size_i > 3'd2) || (len_i > 8'd15) ||\n ((addr_i & ((32'd1 << size_i) - 32'd1)) != 32'd0) ||\n !((burst_i == 2'b01) || ((burst_i == 2'b00) && (len_i == 8'd0))) ||\n last_addr[32] || (last_addr[31:12] != addr_i[31:12]) ||\n (decode_target(last_addr[31:0]) != decoded) ||\n ((decoded >= 5'd5) && (decoded <= 5'd20) && (len_i != 8'd0)))\n return 5'd22;\n if (instruction_i && !(`SOC_ADDR_IS_FLASH(addr_i) ||\n ((addr_i >= 32'h5000_0000) && (addr_i < 32'h5400_0000))))\n return ((decoded >= 5'd5) && (decoded <= 5'd20)) ? 5'd22 : 5'd21;\n return decoded;\n endfunction",
"assign s_busy_d = (s_busy_q & ~s_done) | s_admit;",
"assign owner_idle_o[owner] = !(s_busy_q[2*owner] || s_busy_q[2*owner+1]);",
"assign s_eligible[2*owner] = rst_n_i && owner_idle_o[owner] && masters[owner].arvalid && (!masters[owner].awvalid || !s_cpu_prefer_write_q);",
"assign s_eligible[2*owner+1] = rst_n_i && owner_idle_o[owner] && masters[owner].awvalid && (!masters[owner].arvalid || s_cpu_prefer_write_q);"
]
},
{
@@ -177,6 +182,20 @@
"RS_DMA_CH_REG(channel, RS_DMA_CH_REG_TCD_COUNT) = UINT32_C(1);",
"current = (rs_dma_tcd_t *)(uintptr_t)current->next_ptr;"
]
},
{
"file": "rtl/tiny/top/tiny_axi4_target.sv",
"text": [
"assign s_accept = (s_req_q.state == Idle) && enable_i && s_valid;",
"assign s_terminal = s_resp_active && (s_req_q.write ? (s_b_valid && s_bready[s_req_q.owner]) : (s_r_valid && s_rready[s_req_q.owner] && s_r_data[1]));",
"assign fault_valid_o = s_terminal && s_req_q.error_seen;",
"if (!s_req_q.error_seen && (s_rin_data[3:2] != 2'b00)) begin s_req_d.error_seen = 1'b1; s_req_d.error_addr = s_req_q.read_addr; s_req_d.error_resp = s_rin_data[3:2]; end",
"assign s_abort_write_valid = (s_req_q.state == AbortResponse) && s_req_q.write && s_req_q.write_done && !s_req_q.b_captured;",
"assign s_abort_read_valid = (s_req_q.state == AbortResponse) && !s_req_q.write && (s_req_q.read_count <= {1'b0, s_saved_addr.len});",
".flush_i(1'b0)",
"AbortWrite: begin",
"if (s_terminal) s_req_d.state = Idle;"
]
}
]
}
+21 -1
View File
@@ -125,6 +125,26 @@ def clean_source(text: str) -> str:
return re.sub(r"\s+", "", re.sub(r"/\*.*?\*/|//[^\n]*", "", text, flags=re.S))
def mini_channel_enum(text: str) -> str:
"""Select Mini's channel roles, excluding the Tiny product branch.
Helper aliases share an allocated context; they do not allocate another
channel. Keep their Mini bulk binding checked instead of parsing Tiny's
literal 6/7 values from an inactive preprocessor branch.
"""
match = re.search(r"typedef enum\s*\{([^{}]*)\}\s*rs_dma_channel_t;", text, re.S)
if match is None:
raise ValueError("missing DMA channel enumeration")
body = re.sub(r"#ifdef RS_SOC_TINY\s*\n[^#]*#else\s*\n([^#]*)#endif", r"\1", match[1])
if "#" in body:
raise ValueError("unreviewed Mini DMA channel selection")
aliases = dict(re.findall(r"(RS_DMA_CHANNEL_\w+)\s*=\s*(RS_DMA_CHANNEL_\w+)\s*,", body))
expected = {"RS_DMA_CHANNEL_DVP", "RS_DMA_CHANNEL_I2S_TX", "RS_DMA_CHANNEL_I2S_RX"}
if any(name not in expected or value != "RS_DMA_CHANNEL_BULK" for name, value in aliases.items()):
raise ValueError("Mini DMA helper alias binding changed")
return body
def validate_programming(spec: dict, ids: set[str], root: Path, revision: str) -> None:
for relative in dependencies(spec):
path = root / relative
@@ -193,7 +213,7 @@ def collect_programming(root: Path, spec: dict, ids: set[str], revision: str) ->
result = copy.deepcopy(spec)
sdk_text = (root / "crt/include/retrosoc/hal/dma.h").read_text()
sdk = constants(sdk_text, "RS_DMA_REQUEST_")
channels = constants(sdk_text, "RS_DMA_CHANNEL_")
channels = constants(mini_channel_enum(sdk_text), "RS_DMA_CHANNEL_")
# Capacity is selected by product; it is not a named channel allocation.
# Keep the Mini publication's role inventory independent of Tiny's count.
channels.update(constants((root / "crt/include/retrosoc/hal/dma_regs.h").read_text(),
+23 -10
View File
@@ -26,6 +26,9 @@ TOP = "rtl/tiny/top/retrosoc_tiny.sv"
RESET_TREE = "rtl/tiny/top/tiny_reset_tree.sv"
SYSCTRL = "rtl/tiny/top/tiny_sysctrl.sv"
ARCHINFO = "rtl/tiny/top/tiny_archinfo.sv"
CPU = "rtl/tiny/top/tiny_cpu_wrapper.sv"
CPU_MEM = "rtl/tiny/top/tiny_cpu_mem.sv"
SRAM = "rtl/tiny/top/tiny_sram.sv"
FAMILIES = {"xpi", "sram", "gpio", "dma", "timer", "pwm", "rtc", "wdg", "uart", "i2c", "clint", "archinfo"}
MANAGED = {"publication_media", "hazard3", "cluster_common", "cluster_archinfo", "cluster_pwm", "cluster_rtc", "cluster_wdg"}
SUPPORTED_SYSCTRL = {
@@ -65,21 +68,30 @@ def facts(root: Path = ROOT) -> dict:
if any(profile.get(key) != value for key, value in required.items()):
raise ValueError("Tiny publication profile changed; review configuration and ISA boundary")
top = (root / TOP).read_text(encoding="utf-8")
ram = instance_parameters(top, "onchip_ram", "u_sram")
ram_reg = instance_parameters((root / SRAM).read_text(), "onchip_ram_reg", "u_reg")
ram = {"CapacityKiB": ram_reg.get("CapacityKiB"),
"DataWidth": 8 * ram_reg.get("DataBytes", 0), "IdWidth": 1}
dma = instance_parameters(top, "apb4_dma", "u_dma")
cpu = instance_parameters(top, "mgmt_core_wrapper", "u_cpu")
if ram != {"CapacityKiB": 128, "DataWidth": 32, "IdWidth": 1, "Ics55SmallBanks": 1}:
cpu = instance_parameters(top, "tiny_cpu_wrapper", "u_cpu")
if ram != {"CapacityKiB": 128, "DataWidth": 32, "IdWidth": 1} or ram_reg.get("Present") != 1:
raise ValueError("Tiny SRAM parameters changed")
# The publication remains the reviewed IHP130 compatibility profile;
# selecting ICS55's 4 KiB geometry does not alter its reported SRAM facts.
ram = {key: value for key, value in ram.items() if key != "Ics55SmallBanks"}
# P4 changes local routing, not physical geometry or the book's IHP130
# compatibility selection. Snapshot enforcement still precedes publication.
topology = read_topology(root / TOPOLOGY, root / MAP)
require_snippets(root, TOP, ["tiny_sram u_sram", "gen_sram_attachment", "axi4_connector u_connector",
".ID_WIDTH(1)", ".DATA_WIDTH(32)"])
require_snippets(root, SRAM, ["group < 4", "tiny_sram_group u_group"])
if dma != {"NumChannels": 4, "MaxBurstBeats": 16, "FifoDepth": 32, "RequestMask": 0x7F9, "EnableStreams": 0}:
raise ValueError("Tiny DMA parameters changed")
if cpu != {"ExternalIrqCount": 30, "EnableAtomics": 0, "TwoCycleBusErrors": 1, "ResetSyncStages": 5}:
if cpu != {"ExternalIrqCount": 30, "EnableAtomics": 0, "ResetSyncStages": 5}:
raise ValueError("Tiny CPU integration changed")
require_snippets(root, RESET_TREE, [".STAGE(5)", "assign cpu_ready_rst_n_o = &leaf_rst_n_o;"])
require_snippets(root, "rtl/ip/core/mgmt_core_wrapper.sv", [".EXTENSION_C(1)", ".EXTENSION_A(EnableAtomics)"])
require_snippets(root, TOP, ["u_masters_axi4_if[2]", ".axi4(u_masters_axi4_if[0])",
require_snippets(root, CPU, ["hazard3_cpu_2port", ".EXTENSION_C(1)", ".EXTENSION_A(EnableAtomics)"])
require_snippets(root, CPU_MEM, [".TwoCycleErrors(1'b1)", "ErrorFirst", "ErrorLast"])
require_snippets(root, TOP, ["u_masters_axi4_if[3]", "u_targets_axi4_if[23]",
"assign u_masters_axi4_if[2].arvalid = 1'b0;",
"assign u_masters_axi4_if[2].awvalid = 1'b0;",
".slow_axi4(u_masters_axi4_if[0])",
".axi4(u_masters_axi4_if[1])", ".cfg_apb4(u_sram_apb4_if)",
"assign s_tick = s_tick_count_q == 5'd23;"])
require_snippets(root, SYSCTRL, ["(apb4.paddr[1:0] != 2'd0)", "(apb4.pstrb != 4'hf)",
@@ -99,6 +111,7 @@ def facts(root: Path = ROOT) -> dict:
raise ValueError("Tiny discovery parameters changed")
require_snippets(root, ARCHINFO, ["32'h5449_4e59 : u_info_apb4_if.prdata", ".device_id_valid_i(1'b0)", ".device_id_read_enable_i(1'b0)"])
return {"profile": profile, "sram": ram, "dma": dma, "cpu": cpu, "archinfo": arch,
"fabric": topology["fabric"],
"hardware_isa": "RV32IMC", "firmware_isa": profile["ISA"], "soc_id": 0x54494E59,
"system_hz": int(profile["EXT_CLK_HZ"]), "timebase_hz": int(profile["CLINT_TIMEBASE_HZ"])}
@@ -130,7 +143,7 @@ def sysctrl_registers(root: Path) -> dict:
offsets = {r["symbol"]: int(r["offset"], 0) for r in read(root / MAP)["sysctrl_registers"]}
definitions = {
"FAULT_STATUS": ("RW1C", "Sticky first fault. Bit 0 clears pending; a new fault can win after clear.", [("PENDING", 0, 0, "A fault record is pending."), ("WRITE", 1, 1, "Faulting transaction was a write."), ("CLASS", 2, 4, "1: decode error; 2: protocol/target error.")]),
"FAULT_MASTER": ("RO", "Master of the captured first fault.", [("MASTER", 0, 0, "0 CPU, 1 DMA.")]),
"FAULT_MASTER": ("RO", "Master of the captured first fault.", [("MASTER", 0, 1, "0 CPU, 1 DMA, 2 reserved SDIO ingress; SDIO is not integrated.")]),
"FAULT_DETAIL": ("RO", "Captured AXI response code.", [("RESPONSE", 0, 1, "AXI RRESP/BRESP encoding.")]),
"PERF_CTRL": ("RW / command", "Enable counters, clear counters/snapshots or capture a coherent pair. Clear takes precedence over snapshot.", [("ENABLE", 0, 0, "Counter enable state."), ("CLEAR", 1, 1, "Write-one pulse; reads zero."), ("SNAPSHOT", 2, 2, "Write-one pulse; reads zero.")]),
"TEST_STATUS": ("RW sticky", "First full-word valid result is sticky until system reset; write mask 0x8000FF01.", [("PASS", 0, 0, "Pass when valid is set."), ("CODE", 8, 15, "Application-defined result code."), ("VALID", 31, 31, "The first valid write locks the result.")]),
+30 -29
View File
@@ -72,36 +72,37 @@ module mgmt_core_wrapper #(
.JtagIdcode (`SOC_JTAG_IDCODE),
.ResetSyncStages(ResetSyncStages)
) u_mgmt_debug_wrapper (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.bridge_idle_i (s_ahbl_idle),
.jtag_tck_i (jtag_tck_i),
.jtag_tms_i (jtag_tms_i),
.jtag_tdi_i (jtag_tdi_i),
.jtag_trst_n_i (jtag_trst_n_i),
.jtag_tdo_o (jtag_tdo_o),
.core_rst_n_o (s_core_rst_n),
.dbg_req_halt_o (s_dbg_req_halt),
.dbg_req_halt_on_reset_o (s_dbg_req_halt_on_reset),
.dbg_req_resume_o (s_dbg_req_resume),
.dbg_halted_i (s_dbg_halted),
.dbg_running_i (s_dbg_running),
.dbg_data0_rdata_o (s_dbg_data0_rdata),
.dbg_data0_wdata_i (s_dbg_data0_wdata),
.dbg_data0_wen_i (s_dbg_data0_wen),
.dbg_instr_data_o (s_dbg_instr_data),
.dbg_instr_data_vld_o (s_dbg_instr_data_vld),
.dbg_instr_data_rdy_i (s_dbg_instr_data_rdy),
.clk_i(clk_i),
.rst_n_i(rst_n_i),
.bridge_idle_i(s_ahbl_idle),
.jtag_tck_i(jtag_tck_i),
.jtag_tms_i(jtag_tms_i),
.jtag_tdi_i(jtag_tdi_i),
.jtag_trst_n_i(jtag_trst_n_i),
.jtag_tdo_o(jtag_tdo_o),
.core_rst_n_o(s_core_rst_n),
.reset_pending_o(), // Mini retains its existing reset policy.
.dbg_req_halt_o(s_dbg_req_halt),
.dbg_req_halt_on_reset_o(s_dbg_req_halt_on_reset),
.dbg_req_resume_o(s_dbg_req_resume),
.dbg_halted_i(s_dbg_halted),
.dbg_running_i(s_dbg_running),
.dbg_data0_rdata_o(s_dbg_data0_rdata),
.dbg_data0_wdata_i(s_dbg_data0_wdata),
.dbg_data0_wen_i(s_dbg_data0_wen),
.dbg_instr_data_o(s_dbg_instr_data),
.dbg_instr_data_vld_o(s_dbg_instr_data_vld),
.dbg_instr_data_rdy_i(s_dbg_instr_data_rdy),
.dbg_instr_caught_exception_i(s_dbg_instr_caught_exception),
.dbg_instr_caught_ebreak_i (s_dbg_instr_caught_ebreak),
.dbg_sbus_addr_o (s_dbg_sbus_addr),
.dbg_sbus_write_o (s_dbg_sbus_write),
.dbg_sbus_size_o (s_dbg_sbus_size),
.dbg_sbus_vld_o (s_dbg_sbus_vld),
.dbg_sbus_rdy_i (s_dbg_sbus_rdy),
.dbg_sbus_err_i (s_dbg_sbus_err),
.dbg_sbus_wdata_o (s_dbg_sbus_wdata),
.dbg_sbus_rdata_i (s_dbg_sbus_rdata)
.dbg_instr_caught_ebreak_i(s_dbg_instr_caught_ebreak),
.dbg_sbus_addr_o(s_dbg_sbus_addr),
.dbg_sbus_write_o(s_dbg_sbus_write),
.dbg_sbus_size_o(s_dbg_sbus_size),
.dbg_sbus_vld_o(s_dbg_sbus_vld),
.dbg_sbus_rdy_i(s_dbg_sbus_rdy),
.dbg_sbus_err_i(s_dbg_sbus_err),
.dbg_sbus_wdata_o(s_dbg_sbus_wdata),
.dbg_sbus_rdata_i(s_dbg_sbus_rdata)
);
hazard3_cpu_1port #(
+3
View File
@@ -19,12 +19,15 @@ module mgmt_debug_reset #(
input logic reset_req_i,
input logic bridge_idle_i,
output logic core_rst_n_o,
output logic reset_pending_o,
output logic reset_done_o
);
logic s_pending_d, s_pending_q;
logic s_reset_active_d, s_reset_active_q;
assign reset_pending_o = reset_req_i || s_pending_q || s_reset_active_q;
always_comb begin
s_pending_d = s_pending_q;
s_reset_active_d = s_reset_active_q;
+7 -3
View File
@@ -12,10 +12,12 @@
// management-core clock domain and exposes abstract commands only (no SBA).
module mgmt_debug_wrapper #(
parameter logic [31:0] JtagIdcode = 32'hDEAD_BEEF,
parameter int ResetSyncStages = 3
parameter int ResetSyncStages = 3,
parameter bit SeparateSystemReset = 1'b0
) (
input logic clk_i,
input logic rst_n_i,
input logic system_rst_n_i,
input logic bridge_idle_i,
input logic jtag_tck_i,
input logic jtag_tms_i,
@@ -23,6 +25,7 @@ module mgmt_debug_wrapper #(
input logic jtag_trst_n_i,
output logic jtag_tdo_o,
output logic core_rst_n_o,
output logic reset_pending_o,
output logic dbg_req_halt_o,
output logic dbg_req_halt_on_reset_o,
@@ -155,10 +158,11 @@ module mgmt_debug_wrapper #(
.ResetSyncStages(ResetSyncStages)
) u_mgmt_debug_reset (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.rst_n_i (SeparateSystemReset ? system_rst_n_i : rst_n_i),
.reset_req_i (s_sys_reset_req || s_hart_reset_req),
.bridge_idle_i(bridge_idle_i),
.bridge_idle_i (bridge_idle_i),
.core_rst_n_o (core_rst_n_o),
.reset_pending_o(reset_pending_o),
.reset_done_o (s_reset_done)
);
+4
View File
@@ -18,6 +18,7 @@ module apb4_dma #(
parameter bit EnableStreams = 1'b1
) (
// verilog_format: off -- integration ports retain established peripheral endpoint names.
output logic lifecycle_idle_o,
input logic clk_i,
input logic rst_n_i,
output logic dma_xfer_done_o,
@@ -179,4 +180,7 @@ module apb4_dma #(
.crypto_in_axis (crypto_in_axis),
.crypto_out_axis (crypto_out_axis)
);
logic s_bus_busy;
assign s_bus_busy = axi4.awvalid || axi4.arvalid || axi4.wvalid;
assign lifecycle_idle_o = !(|s_busy) && !(|s_start) && !s_bus_busy;
endmodule
+2
View File
@@ -10,6 +10,7 @@
module apb4_timer (
// verilog_format: off -- preserve reviewed column alignment
output logic lifecycle_idle_o,
input logic clk_i,
input logic rst_n_i,
input logic debug_halted_i,
@@ -89,4 +90,5 @@ module apb4_timer (
.one_shot_done_o (s_one_shot_done)
);
assign lifecycle_idle_o = !s_en;
endmodule
+5
View File
@@ -7,6 +7,7 @@ module apb4_crypto #(
parameter logic [5:0] DropResponseMask = 6'd0,
parameter bit DuplicateResponse = 1'b0
) (
output logic lifecycle_idle_o,
input logic clk_i,
rst_n_i,
output logic dma_input_proc_o,
@@ -43,6 +44,10 @@ module apb4_crypto #(
logic [11:0] s_offset;
logic [31:0] s_wdata;
logic [ 3:0] s_strb;
logic s_transport_busy;
assign s_transport_busy = (|s_fifo_busy) || (|s_engine_pending_q) || s_aes_key_busy;
assign lifecycle_idle_o = !(|s_busy) && !s_maintenance_busy && !s_transport_busy;
assign s_offset = s_regs_q.offset;
assign s_wdata = s_regs_q.data;
assign s_strb = s_regs_q.mask;
+3
View File
@@ -13,6 +13,7 @@ module apb4_i2c #(
parameter int RxFifoDepth = 16
) (
// verilog_format: off -- preserve reviewed column alignment
output logic lifecycle_idle_o,
input logic clk_i,
input logic rst_n_i,
output logic dma_tx_stall_o,
@@ -66,6 +67,8 @@ module apb4_i2c #(
logic s_recovery_failed_event;
logic s_core_cmd_flush;
assign lifecycle_idle_o = !s_en && !s_busy && !s_recovery_active;
assign i2c.scl_o = 1'b0;
assign i2c.sda_o = 1'b0;
assign i2c.scl_oe_o = s_scl_pull_low;
+33 -1
View File
@@ -41,6 +41,8 @@ module apb4_i2s (
output logic tx_underrun_evt_o,
output logic rx_overrun_evt_o,
output logic rx_flush_busy_o,
output logic lifecycle_idle_o,
output logic lifecycle_reset_ready_o,
apb4_if.slave apb4,
axi4_stream_if.sink tx_axis,
axi4_stream_if.source rx_axis,
@@ -120,6 +122,36 @@ module apb4_i2s (
logic s_tx_underrun_sys;
logic s_rx_overrun_sys;
logic s_tx_flush_sent_d, s_tx_flush_sent_q;
logic s_cfg_ready;
logic [31:0] s_cfg_sent_d, s_cfg_sent_q;
logic s_audio_used_d, s_audio_used_q;
// A disabled host which has never enabled its PHY may change host clock
// even when AUDIO is absent. After use, require the disabled configuration
// to reach AUDIO, empty FIFOs, and completed flush acknowledgements.
assign s_cfg_sent_d = s_cfg_ready ? s_cfg_sys : s_cfg_sent_q;
assign s_audio_used_d = s_audio_used_q || s_cfg_sys[`APB4_I2S__CFG_ENABLE];
assign lifecycle_reset_ready_o = s_tx_src_ready && !s_tx_flush_busy && !s_rx_flush_busy;
assign lifecycle_idle_o = !s_cfg_sys[`APB4_I2S__CFG_ENABLE] &&
!s_stream_tx_en && !s_stream_rx_en &&
(!s_audio_used_q || ((s_cfg_sent_q == s_cfg_sys) && s_cfg_ready &&
s_tx_level == 0 && s_rx_level == 0 && !s_tx_flush_busy && !s_rx_flush_busy));
dffr #(
.DATA_WIDTH(32)
) u_cfg_sent (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_cfg_sent_d),
.dat_o (s_cfg_sent_q)
);
dffr #(
.DATA_WIDTH(1)
) u_audio_used (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_audio_used_d),
.dat_o (s_audio_used_q)
);
assign s_cmd_sys = {s_cmd_rx_flush, s_cmd_tx_flush};
assign s_tx_src_clear = s_cmd_tx_flush && !s_tx_flush_sent_q && !s_tx_src_clear_busy;
@@ -191,7 +223,7 @@ module apb4_i2s (
.src_rst_n_i(rst_n_i),
.src_data_i (s_cfg_sys),
.src_valid_i(1'b1),
.src_ready_o(),
.src_ready_o(s_cfg_ready),
.dst_clk_i (clk_aud_i),
.dst_rst_n_i(rst_aud_n_i),
.dst_data_o (s_cfg_aud),
+3
View File
@@ -13,6 +13,7 @@ module apb4_uart #(
parameter int RxFifoDepth = 64
) (
// verilog_format: off -- preserve reviewed column alignment
output logic lifecycle_idle_o,
input logic clk_i,
input logic rst_n_i,
output logic dma_tx_stall_o,
@@ -53,6 +54,8 @@ module apb4_uart #(
logic s_tx_flow_blocked;
logic s_cts_change;
assign lifecycle_idle_o = !s_tx_busy && !s_rx_active && !s_tx_data_valid && !s_rx_en && !s_break;
assign uart.tx_o = s_tx;
assign uart.irq_o = s_irq;
+3
View File
@@ -12,6 +12,7 @@ module apb4_ws2812 #(
parameter int TxFifoDepth = 16
) (
// verilog_format: off -- preserve reviewed column alignment
output logic lifecycle_idle_o,
input logic clk_i,
input logic rst_n_i,
apb4_if.slave apb4,
@@ -33,6 +34,8 @@ module apb4_ws2812 #(
logic s_done, s_underflow, s_aborted;
logic s_irq, s_dat;
assign lifecycle_idle_o = !s_busy && !s_reset_active && !s_start && !s_data_valid;
assign ws2812.dat_o = s_dat;
assign ws2812.irq_o = s_irq;
+13 -14
View File
@@ -24,14 +24,14 @@ module i2s_recv (
logic s_lrck_trg;
logic [4:0] s_bit_cnt_d, s_bit_cnt_q;
logic [4:0] s_bit_num;
logic s_first_bit_d, s_first_bit_q;
logic s_done_d, s_done_q;
logic [23:0] s_recv_data_d, s_recv_data_q;
logic [23:0] s_data_d, s_data_q;
assign s_bit_num = bitmode_i ? 5'd23 : 5'd15;
assign data_o = s_data_q;
assign done_o = s_bit_cnt_q == s_bit_num;
assign done_o = s_done_q;
assign s_lrck_trg = lrck_i ^ s_lrck_dely_q;
@@ -89,26 +89,25 @@ module i2s_recv (
);
always_comb begin
s_first_bit_d = s_first_bit_q;
if (s_lrck_trg) s_first_bit_d = adcdat_i;
end
dffer #(
// I2S changes LRCK one bit before the next sample's MSB. The bit
// sampled on that transition is the current sample's LSB, not the next
// sample's first bit. Publish completion after data_o has captured it.
assign s_done_d = sclk_pos_i && s_lrck_trg;
dffr #(
.DATA_WIDTH(1)
) u_first_bit_dffer (
) u_done_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (sclk_pos_i),
.dat_i (s_first_bit_d),
.dat_o (s_first_bit_q)
.dat_i (s_done_d),
.dat_o (s_done_q)
);
always_comb begin
s_data_d = s_data_q;
if ((!s_lrck_trg) && (s_bit_cnt_q == (s_bit_num - 1'b1))) begin
if (bitmode_i) s_data_d = {s_recv_data_d[22:0], s_first_bit_q};
else s_data_d = {8'd0, s_recv_data_d[14:0], s_first_bit_q};
if (s_lrck_trg) begin
if (bitmode_i) s_data_d = {s_recv_data_q[22:0], adcdat_i};
else s_data_d = {8'd0, s_recv_data_q[14:0], adcdat_i};
end
end
dffer #(
+1 -1
View File
@@ -92,7 +92,7 @@ module i2s_reg (
endfunction
assign s_req = apb4.psel && apb4.penable && !s_ready_q;
assign s_write = |apb4.pstrb;
assign s_write = apb4.pwrite;
assign s_accept = s_req;
assign s_offset = apb4.paddr[11:0];
assign s_aligned = apb4.paddr[1:0] == 2'b00;
+11 -2
View File
@@ -12,16 +12,19 @@
module apb4_sdio #(
parameter int InputClockHz = 72_000_000,
parameter bit UseRuntimeClockHz = 1'b0,
parameter int AddrWidth = 32,
parameter int DataWidth = 32,
parameter int DescCount = 16
) (
// verilog_format: off -- preserve the APB, AXI, and pad column alignment
output logic lifecycle_idle_o,
input logic clk_i,
input logic rst_n_i,
apb4_if.slave apb4,
axi4_if.master dma_axi4,
sdio_if.dut sdio
sdio_if.dut sdio,
input logic [31:0] clock_hz_i
// verilog_format: on
);
logic s_busy;
@@ -80,6 +83,10 @@ module apb4_sdio #(
logic s_pio_read_consume;
sdio_if u_sdio_core_if ();
logic s_bus_busy;
assign s_bus_busy = dma_axi4.awvalid || dma_axi4.arvalid || dma_axi4.wvalid;
assign lifecycle_idle_o = !s_busy && !s_clock_enable && !s_bus_busy;
assign u_sdio_core_if.cmd_di_i = sdio.cmd_di_i;
assign u_sdio_core_if.dat_di_i = sdio.dat_di_i;
assign sdio.sck_o = u_sdio_core_if.sck_o;
@@ -149,7 +156,8 @@ module apb4_sdio #(
);
sdio_core #(
.InputClockHz(InputClockHz),
.InputClockHz (InputClockHz),
.UseRuntimeClockHz(UseRuntimeClockHz),
.AddrWidth (AddrWidth),
.DataWidth (DataWidth),
.DescCount (DescCount)
@@ -157,6 +165,7 @@ module apb4_sdio #(
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.host_enable_i (s_host_enable),
.clock_hz_i (clock_hz_i),
.host_irq_enable_i (s_host_irq_enable),
.clock_enable_i (s_clock_enable),
.half_period_i (s_half_period),
+6
View File
@@ -20,6 +20,7 @@
module apb4_xpi (
// verilog_format: off -- preserve reviewed port alignment
output logic lifecycle_idle_o,
input logic clk_i,
input logic rst_n_i,
input logic dma_xfer_done_i,
@@ -176,6 +177,11 @@ module apb4_xpi (
logic s_unused_dma_xfer_done;
logic s_unused_mm_perf_split;
logic s_protocol_idle;
assign s_protocol_idle = !s_mm_busy && !s_indirect_busy && !s_core_busy;
assign lifecycle_idle_o = s_protocol_idle &&
(s_owner_q == OwnerNone) && s_tx_fifo_empty && s_rx_fifo_empty;
assign s_owner_q = xpi_owner_e'(s_owner_bits_q);
assign s_selected_req_valid = s_mm_busy ? s_mm_req_valid :
(s_indirect_req_valid ? s_indirect_req_valid : s_mm_req_valid);
+4 -1
View File
@@ -88,7 +88,10 @@ module sdio_command #(
assign s_frame_crc_error = s_frame_crc != s_resp_shift_next[7:1];
assign s_frame_index_error = s_resp_shift_next[45:40] != s_cmd_index_q;
assign cmd_oe_o = (s_state_q == Send) || (s_tx_final_hold_q && !sample_tick_i);
// Admission can coincide with an SDCLK rising edge. Do not expose the
// start bit until the first scheduled launch, or the card sees it twice.
assign cmd_oe_o = ((s_state_q == Send) && (s_tx_started_q || launch_tick_i)) ||
(s_tx_final_hold_q && !sample_tick_i);
assign cmd_do_o = ((s_state_q == Send) || s_tx_final_hold_q) ? s_tx_shift_q[47] : 1'b1;
assign busy_o = (s_state_q != Idle) && (s_state_q != Complete);
assign done_o = s_done_q;
+11 -6
View File
@@ -12,6 +12,7 @@
module sdio_core #(
parameter int InputClockHz = 72_000_000,
parameter bit UseRuntimeClockHz = 1'b0,
parameter int AddrWidth = 32,
parameter int DataWidth = 32,
parameter int DescCount = 16
@@ -20,6 +21,7 @@ module sdio_core #(
input logic clk_i,
input logic rst_n_i,
input logic host_enable_i,
input logic [31:0] clock_hz_i,
input logic host_irq_enable_i,
input logic clock_enable_i,
input logic [15:0] half_period_i,
@@ -102,7 +104,7 @@ module sdio_core #(
logic s_data_tx_last;
logic s_data_rx_valid;
logic s_data_rx_ready;
logic s_data_rx_stall_q;
logic s_data_rx_stall;
logic [ 31:0] s_data_rx_data;
logic [ 3:0] s_data_rx_strb;
logic s_data_rx_last;
@@ -174,8 +176,8 @@ module sdio_core #(
assign s_clock_en = host_enable_i && clock_enable_i &&
(s_cmd_busy || s_data_busy || s_dma_busy || cmd_start_i ||
data_start_i) &&
!s_data_rx_stall_q &&
!(s_data_busy && s_data_tx_wait && (s_data_oe == 4'b0000));
!s_data_rx_stall &&
!(s_data_busy && s_data_tx_wait);
assign s_total_bytes = {16'd0, block_size_i} * {16'd0, block_count_i};
assign s_abort_request = host_abort_i || dma_abort_i ||
(s_dma_done && s_dma_err) ||
@@ -200,6 +202,9 @@ module sdio_core #(
assign s_dma_data_in_strb = s_data_rx_strb;
assign s_dma_data_in_last = s_data_rx_last;
assign s_data_rx_ready = data_dma_enable_i ? s_dma_data_in_ready : !s_pio_valid_q;
// ReadEmit begins on the sampling edge. Stop before the very next launch
// if its word cannot retire; registering this condition loses one card bit.
assign s_data_rx_stall = s_data_rx_valid && !s_data_rx_ready;
assign s_core_busy = s_cmd_busy || s_data_busy || s_dma_busy;
assign busy_o = s_core_busy;
assign s_pio_tx_pop = !data_dma_enable_i && s_data_tx_valid && s_data_tx_ready;
@@ -233,7 +238,9 @@ module sdio_core #(
assign bytes_done_o = s_dma_bytes_done;
assign dma_error_addr_o = s_dma_err_addr;
assign dma_error_o = {24'd0, s_dma_err_code};
assign clock_actual_o = (half_period_i == 16'd0) ? 32'd0 : InputClockHz / (2 * half_period_i);
logic [31:0] s_input_clock_hz;
assign s_input_clock_hz = UseRuntimeClockHz && (clock_hz_i != 32'd0) ? clock_hz_i : InputClockHz;
assign clock_actual_o = (half_period_i == 16'd0) ? 32'd0 : s_input_clock_hz / (2 * half_period_i);
assign status_o = {
23'd0,
s_clock_running,
@@ -401,7 +408,6 @@ module sdio_core #(
s_dat1_meta_q <= 1'b1;
s_dat1_sync_q <= 1'b1;
s_dat1_seen_q <= 1'b0;
s_data_rx_stall_q <= 1'b0;
s_pio_rdata_q <= '0;
s_pio_valid_q <= 1'b0;
s_pio_tx_rd_q <= '0;
@@ -421,7 +427,6 @@ module sdio_core #(
s_dat1_meta_q <= sdio.dat_di_i[1];
s_dat1_sync_q <= s_dat1_meta_q;
s_dat1_seen_q <= s_dat1_seen_d;
s_data_rx_stall_q <= s_data_rx_valid && !s_data_rx_ready;
if (data_start_i) begin
s_pio_tx_rd_q <= '0;
+6 -1
View File
@@ -160,7 +160,12 @@ module sdio_data #(
assign tx_ready_o = (s_state_q == WriteWaitWord) || (s_state_q == WriteNextWord) ||
((s_state_q == WritePayload) && launch_tick_i && s_tx_word_final &&
(s_tx_total_q < s_target_bytes_q));
assign tx_wait_o = (s_state_q == WriteWaitWord) || (s_state_q == WriteNextWord);
// Stop before launching the next word if memory has not supplied it.
// Waiting only after that launch exposes an idle bit to the card and
// corrupts the next word's MSB when the clock resumes.
assign tx_wait_o = (s_state_q == WriteWaitWord) || (s_state_q == WriteNextWord) ||
((s_state_q == WritePayload) && s_tx_word_final &&
(s_tx_total_q < s_target_bytes_q) && !tx_valid_i);
assign rx_valid_o = (s_state_q == ReadEmit);
assign rx_data_o = s_rx_pending_data_q;
assign rx_strb_o = s_rx_pending_strb_q;
+7
View File
@@ -516,11 +516,18 @@ module sdio_dma #(
s_err_addr_d = s_payload_addr + s_burst_offset;
end
if (s_write_final_sent_q) begin
// A retired AXI burst is not necessarily the whole descriptor.
// Keep ownership while subsequent bursts receive its remaining
// bytes, just as the memory-to-card direction does.
if (s_fault_d || (s_desc_progress_d >= s_desc_count_q)) begin
s_writeback_control_d = (s_desc_control_q &
~(32'd1 << sdio_pkg::SDIO_DESC_OWN)) |
(s_fault_d ? (32'd1 << sdio_pkg::SDIO_DESC_ERROR) :
(32'd1 << sdio_pkg::SDIO_DESC_DONE));
s_state_d = WritebackStart;
end else begin
s_state_d = PayloadWriteStart;
end
end
end
end
+6 -4
View File
@@ -123,6 +123,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/sysctrl.c \
$(ROOT_PATH)/crt/src/hal/rcu.c \
$(ROOT_PATH)/crt/src/hal/uart_math.c \
$(ROOT_PATH)/crt/src/hal/uart.c \
$(ROOT_PATH)/crt/src/hal/gpio_math.c \
@@ -182,7 +183,8 @@ CFLAGS += -DRS_SOC_TINY
TINY_CRT_NAMES := startup.S clzsi2.c divdi3.c ffssi2.c udivdi3.c umoddi3.c \
stdlib.c string.c console.c printf.c archinfo.c clint.c uart_math.c uart.c \
gpio_math.c gpio.c timer_math.c timer.c pwm.c rtc.c wdg.c watchdog.c i2c_math.c i2c.c \
dma_math.c dma.c onchip_sram.c xpi.c
dma_math.c dma.c onchip_sram.c xpi.c rng.c crc.c ws2812_math.c ws2812.c \
crypto.c crypto_lifecycle.c i2s_math.c i2s.c sdio_math.c sdio.c rcu.c
CRT_SRCS := $(foreach source,$(CRT_SRCS),$(if $(filter $(notdir $(source)),$(TINY_CRT_NAMES)),$(source)))
CRT_SRCS += $(ROOT_PATH)/crt/src/hal/sysctrl.c
endif
@@ -285,12 +287,12 @@ asm: $(MPW_VARIANT_DEP) $(MEMORY_MAP_STAMP)
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
endif
$(CRYPTO_CONSTANTS_HEADER): $(ROOT_PATH)/scripts/crypto_constants.py $(ROOT_PATH)/scripts/data/crypto_constants.json
$(PYTHON) $(ROOT_PATH)/scripts/crypto_constants.py --root $(ROOT_PATH) --output $(SW_BUILD_DIR)/include
endif
CRYPTO_CONSTANTS_DEP := $(if $(filter TINY,$(SOC)),,$(CRYPTO_CONSTANTS_HEADER))
CRYPTO_CONSTANTS_DEP := $(CRYPTO_CONSTANTS_HEADER)
$(FIRMWARE_ELF): $(MPW_VARIANT_DEP) $(MEMORY_MAP_STAMP) $(SOC_TOPOLOGY_STAMP) $(SOC_IRQ_METADATA) $(USER_EXTENSIONS_STAMP) $(ARCHINFO_METADATA_STAMP) $(VERSION_HEADER) $(CRYPTO_CONSTANTS_DEP) $(SRC_PATH) $(SW_HEADERS) $(LDS_PATH) \
$(ROOT_PATH)/rtl/mk/software.mk $(APP_MK)
+34
View File
@@ -1,6 +1,40 @@
{
"schema_version": 1,
"targets": [
{
"name": "tiny-r2-p5",
"paths": ["rtl/tiny/top"],
"status": "prototype",
"baseline_revision": "3131762b35e0ebfcd196395f955e7c1005bd18e7",
"configuration_digest": null,
"configuration_profiles": ["configs/ci/ics55-tiny.mk", "configs/ci/ihp130-tiny.mk"],
"required_evidence": [],
"synthesis_intent": {
"registers": "Common registers and bounded response spill registers per external target",
"reset": "accepted CPU work drains before hart reset; system reset invalidates all sessions",
"clock_enable": "all fabric, APB, CPU and SRAM logic remains on direct SAFE24 SYS",
"memory": "unchanged 32 SRAM macros; no global SRAM transaction lock",
"pipeline": "one combined transaction per target, CPU one combined, DMA/SDIO one per direction"
},
"waivers": []
},
{
"name": "tiny-r2-p4",
"paths": ["rtl/tiny/top"],
"status": "prototype",
"baseline_revision": "4db27524a6282ca40d76b935415b89f043d6ea83",
"configuration_digest": null,
"configuration_profiles": ["configs/ci/ics55-tiny.mk", "configs/ci/ihp130-tiny.mk"],
"required_evidence": [],
"synthesis_intent": {
"registers": "Common registers; independent I/D request state and per-group responses",
"reset": "five-edge release; hart halt/drain leaves SRAM and unrelated DMA active",
"clock_enable": "CPU and all 32 main-SRAM macros use direct SAFE24 SYS",
"memory": "four groups of eight tc_sram_1024x32 macros; no payload reset or extra SRAM",
"pipeline": "AHB capture, macro issue, registered response; uncontended completion within three cycles"
},
"waivers": []
},
{
"name": "owned-rtl",
"paths": [
+19 -1
View File
@@ -439,8 +439,26 @@
"tiny_archinfo.sv",
"tiny_axi42apb4.sv",
"tiny_axi4_fabric.sv",
"tiny_axi4_target.sv",
"tiny_cpu_mem.sv",
"tiny_cpu_wrapper.sv",
"tiny_fault_collector.sv",
"tiny_clock_reset_subsystem.sv",
"tiny_axi4_async_bridge.sv",
"tiny_apb_async_bridge.sv",
"tiny_axi4_admission.sv",
"tiny_managed_lifecycle.sv",
"tiny_peripheral_leaf.sv",
"tiny_peripheral_control.sv",
"tiny_clock_output.sv",
"tiny_clock_gate.sv",
"tiny_reset_tree.sv",
"tiny_sysctrl.sv"
"tiny_sram.sv",
"tiny_sram_axi4.sv",
"tiny_sram_group.sv",
"tiny_sram_port_if.sv",
"tiny_sysctrl.sv",
"tiny_rcu_regs.svh"
]
},
"reviewed_boundaries": [
+6 -5
View File
@@ -8,6 +8,7 @@
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
`timescale 1ns / 1ps
module tc_pll #(
parameter bit UseIcs55EcosPll = 1'b0,
parameter bit Ics55Parked = 1'b0
@@ -30,8 +31,8 @@ module tc_pll #(
logic s_apply_seen_d, s_apply_seen_q;
logic s_cfg_apply;
logic s_force_lock_fail;
time s_last_ref_edge;
time s_ref_period;
realtime s_last_ref_edge;
realtime s_ref_period;
function automatic int pll_multiplier(input logic [2:0] cfg_sel);
case (cfg_sel)
@@ -55,14 +56,14 @@ module tc_pll #(
always @(posedge fref_i) begin
if (s_last_ref_edge != 0) begin
s_ref_period = $time - s_last_ref_edge;
s_ref_period = $realtime - s_last_ref_edge;
end
s_last_ref_edge = $time;
s_last_ref_edge = $realtime;
end
always begin
@(posedge fref_i);
wait (pll_lock_o);
wait (pll_lock_o && s_ref_period > 0.0);
while (pll_lock_o) begin
#(s_ref_period / (2 * pll_multiplier(s_cfg_sel_q)));
if (pll_lock_o) begin
+36 -1
View File
@@ -60,6 +60,7 @@ OPENSTA_CONFIG := $(SYN_BUILD_ROOT)/out/$(RTL_NAME)_yosys.config
NETLIST_PATH := $(OPENSTA_NETLIST)
TINY_CONVERTED := $(SIM_BUILD_ROOT)/converted_soc.v
TINY_DEPFILE := $(SIM_BUILD_ROOT)/sources.d
TINY_TB_DEPFILE := $(SIM_BUILD_ROOT)/testbench.d
TINY_LINT_LOG := $(VARIANT_ROOT)/lint/verilator/lint.log
SOC_SIM_TIME ?= 1800
TINY_SIM_TIMEOUT ?= 20000000
@@ -69,7 +70,7 @@ IVERILOG ?= iverilog
VVP ?= vvp
include $(ROOT_PATH)/rtl/mk/software.mk
-include $(TINY_DEPFILE)
-include $(TINY_DEPFILE) $(TINY_TB_DEPFILE)
$(TINY_CONVERTED): $(FILELIST_STAMP)
@mkdir -p $(@D)
@@ -95,6 +96,9 @@ $(SIM_BUILD_ROOT)/simv: $(TINY_CONVERTED) $(wildcard $(RTL_PATH)/dv/*.sv)
--log $(SIM_BUILD_ROOT)/compile.log --result $(SIM_BUILD_ROOT)/result-compile.json -- \
$(IVERILOG) -g2012 -gno-specify -s $(RTL_TOP) -o $@ -f $(GENERATED_FL_DIR)/def.fl \
-f $(GENERATED_FL_DIR)/inc.fl -f $(PDK_FILELIST) $(TINY_TB_FLIST) $(TINY_CONVERTED)
$(PYTHON) $(ROOT_PATH)/scripts/rtl/filelist_deps.py -f $(GENERATED_FL_DIR)/def.fl \
-f $(GENERATED_FL_DIR)/inc.fl $(TINY_TB_FLIST) \
--target $@ --output $(TINY_TB_DEPFILE)
comp: $(SIM_BUILD_ROOT)/simv
TINY_RUN := $(VVP) $(SIM_BUILD_ROOT)/simv
endif
@@ -201,3 +205,34 @@ tiny-r2-p3-report:
$(PYTHON) $(TINY_P3_TOOL) --root $(ROOT_PATH) --variant-root $(VARIANT_ROOT) report
.PHONY: tiny-r2-p3-image tiny-r2-p3-sim tiny-r2-p3-matrix tiny-r2-p3-report
# P4 preserves old image identity and replays each selected image exactly once.
TINY_P4_REFERENCE ?=
TINY_P4_CASES ?= perf-o2-plain-flat,perf-o2-plain-banked
tiny-r2-p4-replay: | manifest
$(PYTHON) $(ROOT_PATH)/scripts/tiny_r2_p4.py --root $(ROOT_PATH) --variant-root $(VARIANT_ROOT) \
--reference $(TINY_P4_REFERENCE) --cases $(TINY_P4_CASES) --simulator $(SIM_TOOL_NAME) \
--vvp $(VVP) --timeout $(SOC_SIM_TIME) -- \
make --no-print-directory -j1 -B CONFIG=$(CONFIG_PATH) BUILD_TIMESTAMP=$(BUILD_TIMESTAMP) \
$(foreach var,$(CONFIG_KEY_VARS),$(var)=$($(var))) SIMU=$(SIMU) SYNTH=NONE STA=NONE comp
tiny-r2-p4-report:
$(PYTHON) $(ROOT_PATH)/scripts/tiny_r2_p4.py --root $(ROOT_PATH) --variant-root $(VARIANT_ROOT) --report
.PHONY: tiny-r2-p4-replay tiny-r2-p4-report
# P5 compares the same P3 binaries to retained P4 observations, with its own
# phase identity and evidence root. The historical P3/P4 commands stay intact.
TINY_P5_REFERENCE ?=
TINY_P5_CASES ?= perf-o2-plain-flat,perf-o2-plain-banked
tiny-r2-p5-replay: | manifest
$(PYTHON) $(ROOT_PATH)/scripts/tiny_r2_p5.py --root $(ROOT_PATH) --variant-root $(VARIANT_ROOT) \
--reference $(TINY_P5_REFERENCE) --cases $(TINY_P5_CASES) --simulator $(SIM_TOOL_NAME) \
--vvp $(VVP) --timeout $(SOC_SIM_TIME) -- \
make --no-print-directory -j1 -B CONFIG=$(CONFIG_PATH) BUILD_TIMESTAMP=$(BUILD_TIMESTAMP) \
$(foreach var,$(CONFIG_KEY_VARS),$(var)=$($(var))) SIMU=$(SIMU) SYNTH=NONE STA=NONE comp
tiny-r2-p5-report:
$(PYTHON) $(ROOT_PATH)/scripts/tiny_r2_p5.py --root $(ROOT_PATH) --variant-root $(VARIANT_ROOT) --report
.PHONY: tiny-r2-p5-replay tiny-r2-p5-report
+18 -5
View File
@@ -7,7 +7,20 @@ Use `make CONFIG=configs/ci/ics55-tiny.mk setup` followed by
`make CONFIG=configs/ci/ics55-tiny.mk firmware sim`. IHP130/no PLL remains
explicit compatibility through `configs/ci/ihp130-tiny.mk`. See the
[platform runbook](../../docs/ip/tiny-ics55-platform.md) for source-bound
validation; backend frequency tests are not SYS switching or timing qualification.
validation of the historical SAFE24 platform. The
[R2-P7 runbook](../../docs/ip/tiny-soc-r2-clock-reset.md) describes the integrated
RCU, runtime domains and separate acceptance evidence. Backend frequency tests
alone are not full-SoC or physical qualification.
The [R2-P4 implementation](../../docs/ip/tiny-soc-r2-local-memory.md) connects the
official dual-port CPU to four independently serviced 32 KiB local SRAM groups.
The 32 physical macros and register ABI are unchanged. Only non-SRAM CPU work
uses the tagged slow path. The [P5 implementation](../../docs/ip/tiny-soc-r2-concurrent-fabric.md)
replaces external serialization with independent target ownership and APB
branches. The [P6 candidate](../../docs/ip/tiny-soc-r2-shared-integration.md)
activates the SDIO ingress, eight-channel DMA, RNG/CRC/WS2812/Crypto and the
existing I2S master path. Its validation and acceptance remain in the ledger.
Source presence alone is not completed phase or physical qualification.
`top` owns product integration, `address_map`, `pin_map` and `integration` own
canonical address, pad, IRQ and clock/reset inputs, `filelist` selects sources,
@@ -43,8 +56,8 @@ final timing closure is mandatory. Software/DMA scheduling is `TINY-R2-P3`, loca
`TINY-R2-P9`, system qualification `TINY-R2-P10` and complete-product physical
qualification `TINY-R2-P11` (historical IHP130 title; now default ICS55).
Legacy `TINY-P0` through `TINY-P12` remain in
the contract history. The committed RTL/profile still implements the initial
24 MHz/no-PLL/four-channel design; no faster CPU/SRAM or PLL timing support
the contract history. The P6 candidate retains SAFE24 with eight DMA channels;
ICS55's present PLL stays off. No faster CPU/SRAM or PLL timing support
follows from this freeze or the `HAVE_PLL` selector alone.
The R2-P2 candidate keeps that executable clock/memory configuration and adds
@@ -88,8 +101,8 @@ FIFOs, APB4 `0x1001D000..0x1001DFFF`, IRQ25 and RCU target16. DMA V2.2 adds
paced fixed-MMIO requests `SPI_TX=16` / `SPI_RX=17`; payload MMIO retains
single-beat transactions. TX borrows channel3 and RX channel2 only after
prior owners fully drain and release them. No extra AXI master, DMA channel,
SRAM capacity or pad is added. None of these paths exists in the current
24 MHz/no-PLL/four-channel RTL/profile.
SRAM capacity or pad is added. SPI and its requests remain absent from the
SAFE24 P6 candidate.
The frozen Gen1 routes add only GPIO27 ALT0 SCK, GPIO28 ALT1 MOSI, GPIO30
ALT1 MISO and GPIO31 ALT1 CS_N. Write-only display mode uses ordinary GPIO30
+54 -18
View File
@@ -300,15 +300,6 @@
"public": true,
"user_access": "none"
},
{
"symbol": "APB4_I2C1",
"base": "0x10011000",
"size": "0x00001000",
"route": "apb4_periph",
"kind": "active",
"public": true,
"user_access": "rw"
},
{
"symbol": "APB4_GPIO_ADMIN",
"base": "0x10014000",
@@ -327,15 +318,6 @@
"public": true,
"user_access": "none"
},
{
"symbol": "APB4_UART1",
"base": "0x10018000",
"size": "0x00001000",
"route": "apb4_periph",
"kind": "active",
"public": true,
"user_access": "rw"
},
{
"symbol": "APB4_ARCHINFO",
"base": "0x20000000",
@@ -399,6 +381,60 @@
"public": true,
"user_access": "rw",
"burst": "incr16"
},
{
"symbol": "APB4_RNG",
"base": "0x20001000",
"size": "0x00001000",
"route": "apb4_system",
"kind": "active",
"public": true,
"user_access": "none"
},
{
"symbol": "APB4_CRC",
"base": "0x20006000",
"size": "0x00001000",
"route": "apb4_system",
"kind": "active",
"public": true,
"user_access": "none"
},
{
"symbol": "APB4_WS2812",
"base": "0x10008000",
"size": "0x00001000",
"route": "apb4_periph",
"kind": "active",
"public": true,
"user_access": "none"
},
{
"symbol": "APB4_CRYPTO",
"base": "0x1000C000",
"size": "0x00001000",
"route": "apb4_periph",
"kind": "active",
"public": true,
"user_access": "none"
},
{
"symbol": "APB4_I2S",
"base": "0x10007000",
"size": "0x00001000",
"route": "apb4_periph",
"kind": "active",
"public": true,
"user_access": "none"
},
{
"symbol": "APB4_SDIO0",
"base": "0x1000F000",
"size": "0x00001000",
"route": "apb4_periph",
"kind": "active",
"public": true,
"user_access": "none"
}
],
"legacy_rib": false,
+188 -60
View File
@@ -1,3 +1,4 @@
`include "tiny_topology.svh"
// Copyright (c) 2026 Yuchi Miao
// SPDX-License-Identifier: MulanPSL-2.0
`timescale 1ns / 1ps
@@ -5,7 +6,10 @@
module retrosoc_tiny_tb;
logic s_clk_driver = 1'b0;
wire s_clk = s_clk_driver;
logic s_reset_driver = 1'b0;
// Assert RESET_N after time zero. With a divided REF24 there are no REF
// edges while reset is held, so a declaration initialized low is not an
// observable asynchronous assertion for every four-state simulator process.
logic s_reset_driver = $test$plusargs("p7_legacy_reset_level") ? 1'b0 : 1'b1;
wire s_rst_n = s_reset_driver;
wire s_uart0_tx, s_uart1_tx;
wire s_uart0_rx = 1'b1;
@@ -18,14 +22,18 @@ module retrosoc_tiny_tb;
wire s_jtag_tdi = s_jtag_tdi_driver;
wire s_jtag_trst_n = s_rst_n;
wire s_jtag_tdo;
tri1 [31:0] s_gpio;
`include "tiny_tb_gpio.svh"
wire s_xpi_sck;
wire [ 3:0] s_xpi_nss;
tri [ 3:0] s_xpi_data;
wire [3:0] s_xpi_nss;
tri [3:0] s_xpi_data;
integer s_cycles;
`ifndef RETROSOC_SOC__TINY_NETLIST
`include "tiny_p6_devices.svh"
`include "tiny_p7_observers.svh"
`endif
integer s_max_cycles = 20000000;
integer s_done_cycles = 0;
logic [ 7:0] s_uart_byte;
logic [7:0] s_uart_byte;
`include "tiny_jtag_smoke.svh"
`ifdef RETROSOC_SOC__TINY_NETLIST
@@ -43,8 +51,12 @@ module retrosoc_tiny_tb;
wire [7:0] s_test_code = u_dut.s_test_code;
`endif
always #20.833333 s_clk_driver = !s_clk_driver;
always #(500000000.0 / `SOC_EXT_CLK_HZ) s_clk_driver = !s_clk_driver;
initial begin
if (!$test$plusargs("p7_legacy_reset_level")) begin
#1;
s_reset_driver = 1'b0;
end
if ($value$plusargs("max_cycles=%d", s_max_cycles)) begin
end
repeat (20) @(posedge s_clk);
@@ -53,7 +65,8 @@ module retrosoc_tiny_tb;
for (s_cycles = 0; s_cycles < s_max_cycles; s_cycles = s_cycles + 1) begin
@(posedge s_clk);
if (u_dut.s_test_done) begin
if (!u_dut.s_test_pass) $fatal(1, "SIM_TEST_FAIL code=%0d", s_test_code);
if (u_dut.s_test_pass !== 1'b1 || (^s_test_code) === 1'bx)
$fatal(1, "SIM_TEST_FAIL code=%0d", s_test_code);
s_done_cycles = s_done_cycles + 1;
if (s_done_cycles >= 128 && s_jtag_checks_done) begin
$display("SIM_TEST_PASS Tiny cycles=%0d", s_cycles);
@@ -64,24 +77,15 @@ module retrosoc_tiny_tb;
if (s_done_cycles != 128) $fatal(1, "SIM_TEST_TIMEOUT Tiny cycles=%0d", s_cycles);
end
`ifdef RETROSOC_SOC__TINY_NETLIST
`ifdef SIMU_IVERILOG
localparam bit ObserveUartAlways = 1'b1;
`else
localparam bit ObserveUartAlways = 1'b0;
`endif
`ifdef SIMU_IVERILOG
localparam bit ObserveUartBaseline = 1'b1;
`else
localparam bit ObserveUartBaseline = 1'b0;
`endif
// The Verilator RTL already prints accepted TX bytes. The Icarus observer is
// opt-in for measurements; SYSCTRL remains the authoritative verdict.
// The Verilator RTL prints accepted TX bytes. Icarus observes serial output for
// all firmware measurements; SYSCTRL remains the authoritative verdict.
initial begin
if (ObserveUartAlways || (ObserveUartBaseline && ($test$plusargs(
"tiny_r2_baseline"
) || $test$plusargs(
"tiny_r2_p3"
)))) begin
if (ObserveUartAlways) begin
forever begin
@(negedge s_uart0_tx);
#1627.604;
@@ -230,57 +234,181 @@ module retrosoc_tiny_tb;
`undef _BASELINE_AXI
end
// The baseline has one AHB port. HPROT[0] distinguishes instruction/data;
// these observations do not assert future independent local-I/D latency.
longint unsigned s_ahb_cycle = 0;
longint unsigned s_ahb_start = 0;
longint unsigned s_ahb_accept[2], s_ahb_done[2], s_ahb_latency[2];
logic s_ahb_pending = 1'b0, s_ahb_measured = 1'b0;
logic s_ahb_data = 1'b0, s_ahb_previous_enable = 1'b0;
integer s_ahb_window = 0;
wire s_ahb_enable = u_dut.u_soc.s_perf_enable && s_baseline_enabled;
`define _BASELINE_AHB u_dut.u_soc.u_cpu.u_ahbl_if
// P5-only observations supplement the unchanged P3/P4 record formats. They
// are testbench measurements, not new software-visible performance counters.
longint unsigned s_p5_overlap = 0, s_p5_beat_overlap = 0;
integer s_p5_window = 0, s_p5_active_max = 0;
logic s_p5_previous_enable = 0;
wire s_p5_enable = s_baseline_enabled && u_dut.u_soc.s_perf_enable;
// Icarus 13 miscounts the temporary expression $countones(~a_23_bit_vector).
// Count declared bits explicitly and reject unknowns rather than correcting
// a report afterward. This function changes only opt-in P5 observations.
function automatic integer p5_active_targets(
input logic [`RETROSOC_TINY__TARGET_COUNT-1:0] idle_i);
integer count;
count = 0;
for (
integer target_index = 0; target_index < `RETROSOC_TINY__TARGET_COUNT; target_index++
) begin
if (idle_i[target_index] === 1'b0) count++;
else if (idle_i[target_index] !== 1'b1) $fatal(1, "SIM_TEST_FAIL P5 unknown target state");
end
return count;
endfunction
initial begin : p5_monitor_check
if ($test$plusargs("tiny_r2_p5_monitor_check")) begin
for (integer active = 0; active <= `RETROSOC_TINY__TARGET_COUNT; active++) begin
if (p5_active_targets(
{`RETROSOC_TINY__TARGET_COUNT{1'b1}} ^ ((`RETROSOC_TINY__TARGET_COUNT'(1) << active) - `RETROSOC_TINY__TARGET_COUNT'(1))
) != active)
$fatal(1, "SIM_TEST_FAIL P5 monitor population count");
end
$display("SIM_TEST_PASS Tiny P5 monitor-check counts=0..27; instrumentation only");
$finish;
end
end
always @(posedge s_clk) begin
s_ahb_cycle = s_ahb_cycle + 1;
if (!u_dut.u_soc.s_rst_n) begin
s_ahb_pending = 1'b0;
s_ahb_measured = 1'b0;
s_ahb_previous_enable = 1'b0;
s_ahb_window = 0;
s_p5_previous_enable = 0;
s_p5_window = 0;
end else if ($test$plusargs("tiny_r2_p5")) begin
if (s_p5_enable && !s_p5_previous_enable) begin
s_p5_window++;
s_p5_overlap = 0;
s_p5_beat_overlap = 0;
s_p5_active_max = 0;
end
if (s_p5_enable) begin
if (p5_active_targets(u_dut.u_soc.u_fabric.target_idle_o) > s_p5_active_max)
s_p5_active_max = p5_active_targets(u_dut.u_soc.u_fabric.target_idle_o);
if (u_dut.u_soc.u_fabric.s_busy_q[2] && u_dut.u_soc.u_fabric.s_busy_q[3]) s_p5_overlap++;
if (u_dut.u_soc.u_masters_axi4_if[1].rvalid && u_dut.u_soc.u_masters_axi4_if[1].rready &&
u_dut.u_soc.u_masters_axi4_if[1].wvalid && u_dut.u_soc.u_masters_axi4_if[1].wready)
s_p5_beat_overlap++;
end
if (!s_p5_enable && s_p5_previous_enable)
$display(
"R2_P5_FABRIC window=%0d active_targets_max=%0d dma_rw_overlap_cycles=%0d dma_rw_beat_overlap_cycles=%0d",
s_p5_window,
s_p5_active_max,
s_p5_overlap,
s_p5_beat_overlap
);
s_p5_previous_enable = s_p5_enable;
end
end
// Verification-only per-group issue/conflict accounting. A bank conflict is
// two or more eligible macro clients in one cycle, not an AXI address stall.
for (genvar group = 0; group < 4; group++) begin : gen_bank_observer
longint unsigned s_issues[3], s_waits[3];
longint unsigned s_conflicts = 0;
logic s_previous_enable = 0;
integer s_window = 0;
wire s_enable = u_dut.u_soc.s_perf_enable && s_baseline_enabled;
wire [2:0] s_req = u_dut.u_soc.u_sram.gen_group[group].u_group.s_req;
wire [2:0] s_grant = u_dut.u_soc.u_sram.gen_group[group].u_group.s_grant;
always @(posedge s_clk) begin
if (!u_dut.u_soc.s_rst_n) begin
s_previous_enable = 0;
s_window = 0;
end else if (s_baseline_enabled) begin
if (s_ahb_enable && !s_ahb_previous_enable) begin
s_ahb_window = s_ahb_window + 1;
s_ahb_measured = 1'b0;
for (int kind = 0; kind < 2; kind++) begin
s_ahb_accept[kind] = 0;
s_ahb_done[kind] = 0;
s_ahb_latency[kind] = 0;
if (s_enable && !s_previous_enable) begin
s_window = s_window + 1;
s_conflicts = 0;
for (int client = 0; client < 3; client++) begin
s_issues[client] = 0;
s_waits[client] = 0;
end
end
if (`_BASELINE_AHB.hready) begin
if (s_ahb_pending && s_ahb_measured && s_ahb_enable) begin
s_ahb_done[s_ahb_data] = s_ahb_done[s_ahb_data] + 1;
if ((s_ahb_cycle - s_ahb_start) > s_ahb_latency[s_ahb_data])
s_ahb_latency[s_ahb_data] = s_ahb_cycle - s_ahb_start;
end
s_ahb_pending = `_BASELINE_AHB.htrans[1];
s_ahb_measured = s_ahb_pending && s_ahb_enable;
if (s_ahb_measured) begin
s_ahb_data = `_BASELINE_AHB.hprot[0];
s_ahb_start = s_ahb_cycle;
s_ahb_accept[s_ahb_data] = s_ahb_accept[s_ahb_data] + 1;
if (s_enable) begin
if ((s_req & (s_req - 3'd1)) != 3'd0) s_conflicts = s_conflicts + 1;
for (int client = 0; client < 3; client++) begin
if (s_grant[client]) s_issues[client] = s_issues[client] + 1;
if (s_req[client] && !s_grant[client]) s_waits[client] = s_waits[client] + 1;
end
end
if (!s_ahb_enable && s_ahb_previous_enable) begin
for (int kind = 0; kind < 2; kind++) begin
if (!s_enable && s_previous_enable)
$display(
"R2_P4_BANK window=%0d group=%0d issues_i=%0d issues_d=%0d issues_external=%0d conflicts=%0d wait_i=%0d wait_d=%0d wait_external=%0d",
s_window,
group,
s_issues[0],
s_issues[1],
s_issues[2],
s_conflicts,
s_waits[0],
s_waits[1],
s_waits[2]
);
s_previous_enable = s_enable;
end
end
end
// P4 retains the original per-kind record format, now observed on two
// independent ports. External AXI counts remain external traffic only.
for (genvar port_index = 0; port_index < 2; port_index++) begin : gen_ahb_observer
longint unsigned s_cycle = 0, s_start = 0;
longint unsigned s_accepted = 0, s_completed = 0, s_latency = 0;
longint unsigned s_local_accepted = 0, s_local_completed = 0, s_local_latency = 0;
longint unsigned s_admission_wait = 0;
logic s_pending = 0, s_measured = 0, s_local = 0, s_previous_enable = 0;
integer s_window = 0;
wire s_enable = u_dut.u_soc.s_perf_enable && s_baseline_enabled;
`define _P4_AHB u_dut.u_soc.u_cpu_ahbl_if[port_index]
always @(posedge s_clk) begin
s_cycle = s_cycle + 1;
if (!u_dut.u_soc.s_core_rst_n) begin
s_pending = 0;
s_measured = 0;
s_previous_enable = 0;
s_window = 0;
end else if (s_baseline_enabled) begin
if (s_enable && !s_previous_enable) begin
s_window = s_window + 1;
s_measured = 0;
s_accepted = 0;
s_completed = 0;
s_latency = 0;
s_local_accepted = 0;
s_local_completed = 0;
s_local_latency = 0;
s_admission_wait = 0;
end
if (s_enable && `_P4_AHB.htrans[1] && !`_P4_AHB.hready)
s_admission_wait = s_admission_wait + 1;
if (`_P4_AHB.hready) begin
if (s_pending && s_measured && s_enable) begin
s_completed = s_completed + 1;
if ((s_cycle - s_start) > s_latency) s_latency = s_cycle - s_start;
if (s_local) begin
s_local_completed = s_local_completed + 1;
if ((s_cycle - s_start) > s_local_latency) s_local_latency = s_cycle - s_start;
end
end
s_pending = `_P4_AHB.htrans[1];
s_measured = s_pending && s_enable;
if (s_measured) begin
s_start = s_cycle;
s_accepted = s_accepted + 1;
s_local = (`_P4_AHB.haddr >= 32'h30000000) && (`_P4_AHB.haddr < 32'h30020000);
if (s_local) s_local_accepted = s_local_accepted + 1;
end
end
if (!s_enable && s_previous_enable) begin
$display("R2_AHB window=%0d kind=%0d accepted=%0d completed=%0d latency_max=%0d",
s_ahb_window, kind, s_ahb_accept[kind], s_ahb_done[kind], s_ahb_latency[kind]);
s_window, port_index, s_accepted, s_completed, s_latency);
$display(
"R2_P4_LOCAL window=%0d port=%0d accepted=%0d completed=%0d latency_max=%0d admission_wait=%0d",
s_window, port_index, s_local_accepted, s_local_completed, s_local_latency,
s_admission_wait);
end
s_previous_enable = s_enable;
end
end
s_ahb_previous_enable = s_ahb_enable;
`undef _P4_AHB
end
end
`undef _BASELINE_AHB
`endif
retrosoc_tiny_asic u_dut (
+332
View File
@@ -0,0 +1,332 @@
// Verification-only external devices on the Gen1 GPIO routes. No DUT state is driven.
logic s_p6_io_trace_started = 0;
always @(negedge u_dut.u_soc.s_i2c0_clk) begin
if ($test$plusargs("p6_debug_io")) begin
if (u_dut.u_soc.u_i2c0.s_cmd_valid && !s_p6_io_trace_started) begin
s_p6_io_trace_started = 1;
$display("P6_IO_DIAGNOSTIC start pads=%b raw=%b%b filtered=%b%b drive=%b%b", s_gpio[11:10],
u_dut.u_soc.u_i2c0_if.sda_i, u_dut.u_soc.u_i2c0_if.scl_i,
u_dut.u_soc.u_i2c0.s_sda_filtered, u_dut.u_soc.u_i2c0.s_scl_filtered,
u_dut.u_soc.u_i2c0.s_sda_pull_low, u_dut.u_soc.u_i2c0.s_scl_pull_low);
$fflush();
end
if (u_dut.u_soc.u_i2c0.s_addr_nack_event || u_dut.u_soc.u_i2c0.s_bus_timeout_event ||
u_dut.u_soc.u_i2c0.s_cmd_timeout_event || u_dut.u_soc.u_i2c0.s_stretch_timeout_event ||
u_dut.u_soc.u_i2c0.s_arb_lost_event) begin
$display(
"P6_IO_DIAGNOSTIC nack=%b bus_timeout=%b command_timeout=%b stretch=%b arbitration=%b pads=%b drive=%b%b",
u_dut.u_soc.u_i2c0.s_addr_nack_event, u_dut.u_soc.u_i2c0.s_bus_timeout_event,
u_dut.u_soc.u_i2c0.s_cmd_timeout_event, u_dut.u_soc.u_i2c0.s_stretch_timeout_event,
u_dut.u_soc.u_i2c0.s_arb_lost_event, s_gpio[11:10], u_dut.u_soc.u_i2c0.s_sda_pull_low,
u_dut.u_soc.u_i2c0.s_scl_pull_low);
$finish; // Diagnostic only: deliberately no SIM_TEST_PASS verdict.
end
end
end
`define _P6_WS u_dut.u_soc.u_ws2812
logic s_p6_uart_rx = 1;
logic [7:0] s_p6_uart_byte;
logic [7:0] s_p6_uart_packet [4];
integer s_p6_uart_index = 0, s_p6_uart_routes = 0;
assign s_gpio_9 = s_p6_uart_rx;
initial begin
forever begin
@(negedge s_gpio_8);
#1627.604;
for (integer bit_index = 0; bit_index < 8; bit_index++) begin
s_p6_uart_byte[bit_index] = s_gpio_8;
#1085.069;
end
if (s_p6_uart_index != 0 || s_p6_uart_byte == 8'ha5) begin
s_p6_uart_packet[s_p6_uart_index] = s_p6_uart_byte;
s_p6_uart_index++;
if (s_p6_uart_index == 4) begin
if (s_p6_uart_packet[0] != 8'ha5 || s_p6_uart_packet[1] != 8'h5a ||
s_p6_uart_packet[2] != 8'h96 || s_p6_uart_packet[3] != 8'h69)
$fatal(1, "SIM_TEST_FAIL P6 UART GPIO8 framing/data");
for (integer byte_index = 0; byte_index < 4; byte_index++) begin
s_p6_uart_rx = 0;
#1085.069;
for (integer bit_index = 0; bit_index < 8; bit_index++) begin
s_p6_uart_rx = s_p6_uart_packet[byte_index][bit_index];
#1085.069;
end
s_p6_uart_rx = 1;
#1085.069;
end
s_p6_uart_index = 0;
s_p6_uart_routes++;
end
end
end
end
integer s_p6_frames = 0, s_p6_completed = 0, s_p6_underflows = 0, s_p6_aborts = 0;
longint unsigned s_p6_sdio_overlap = 0, s_p6_overlap_fetches = 0;
always @(posedge u_dut.u_soc.s_exec_clk) begin
if (u_dut.u_soc.u_dma.s_busy[2] && u_dut.u_soc.u_sdio.s_dma_status[0]) begin
s_p6_sdio_overlap++;
if (u_dut.u_soc.u_cpu_ahbl_if[0].hready && u_dut.u_soc.u_cpu_ahbl_if[0].htrans[1])
s_p6_overlap_fetches++;
end
end
integer s_p6_word = 0, s_p6_bit = 0, s_p6_bit_cycle = 0, s_p6_reset_cycles = 0;
integer s_p6_ws_wait = 0, s_p6_ws_wait_max = 0, s_p6_fifo_max = 0;
integer s_p6_clock_stops = 0;
integer s_p6_wire_cases = 0, s_p6_wire_matched = 0, s_p6_wire_examined = 0;
integer s_p6_wire_edges = 0;
logic s_p6_wire_found = 0, s_p6_wire_boundary = 0, s_p6_wire_lrck = 0;
logic [23:0] s_p6_wire_shift = 0;
logic [31:0] s_p6_wire_word;
wire s_p6_audio_enabled = u_dut.u_soc.u_i2s.u_i2s_core.enable_i;
wire s_p6_audio_wide = u_dut.u_soc.u_i2s.u_i2s_core.s_bitmode;
// Decode actual BCLK/LRCLK/DOUT pads, independently of the receive engine.
always @(posedge s_gpio[12] or negedge s_p6_audio_enabled) begin
if (!s_p6_audio_enabled) begin
if (s_p6_wire_found) begin
if (s_p6_wire_matched != 512) $fatal(1, "SIM_TEST_FAIL P6 truncated I2S wire payload");
s_p6_wire_cases++;
$display("R2_P6_I2S_WIRE bits=%0d samples=%0d", s_p6_audio_wide ? 24 : 16, s_p6_wire_matched);
end
s_p6_wire_matched = 0;
s_p6_wire_examined = 0;
s_p6_wire_edges = 0;
s_p6_wire_found = 0;
s_p6_wire_boundary = 0;
s_p6_wire_lrck = 0;
s_p6_wire_shift = 0;
end else begin
if (s_gpio[14] !== 1'b0 && s_gpio[14] !== 1'b1) $fatal(1, "SIM_TEST_FAIL P6 unknown I2S DOUT");
s_p6_wire_shift = {s_p6_wire_shift[22:0], s_gpio[14]};
s_p6_wire_edges++;
if (s_gpio[13] != s_p6_wire_lrck) begin
if (s_p6_wire_boundary && s_p6_wire_edges != (s_p6_audio_wide ? 24 : 16))
$fatal(1, "SIM_TEST_FAIL P6 I2S slot length");
s_p6_wire_boundary = 1;
s_p6_wire_edges = 0;
s_p6_wire_word = s_p6_audio_wide ? {8'd0, s_p6_wire_shift} : {16'd0, s_p6_wire_shift[15:0]};
if (!s_p6_wire_found && s_p6_wire_word == (s_p6_audio_wide ? 32'h120020 : 32'h2020))
s_p6_wire_found = 1;
if (s_p6_wire_found && s_p6_wire_matched < 512) begin
if (s_p6_wire_word != (s_p6_audio_wide ? 32'h120020 : 32'h2020) + 32'(s_p6_wire_matched))
$fatal(1, "SIM_TEST_FAIL P6 I2S wire ordering/data index=%0d", s_p6_wire_matched);
s_p6_wire_matched++;
end
s_p6_wire_examined++;
if (!s_p6_wire_found && s_p6_wire_examined > 40)
$fatal(1, "SIM_TEST_FAIL P6 missing I2S wire payload");
s_p6_wire_lrck = s_gpio[13];
end
end
end
logic s_p6_busy_previous = 0;
logic [23:0] s_p6_pixel;
logic s_p6_expected_high;
logic s_p6_audio_reference = 1'b0;
logic s_p6_audio_running = 1'b0;
initial begin
repeat (200) @(negedge u_dut.u_soc.s_ref_clk);
if (!u_dut.u_soc.s_cpu_ready_rst_n)
$fatal(1, "SIM_TEST_FAIL CPU boot waited for absent AUDIO clock");
s_p6_audio_running = 1'b1;
end
// GPIO27 is a verification-board control only after the four normal WS frames.
// Stop low and high in separate cases, then restore the source without DUT forcing.
always @(posedge s_gpio[27]) begin
if (s_p6_frames == 4 && s_p6_clock_stops < 2) begin
wait (s_p6_audio_reference == (s_p6_clock_stops != 0));
s_p6_audio_running = 0;
repeat (4800) @(negedge u_dut.u_soc.s_ref_clk);
s_p6_clock_stops++;
s_p6_audio_running = 1;
end
end
always @(negedge u_dut.u_soc.s_ws2812_clk) begin
if (`_P6_WS.s_busy && !s_p6_busy_previous) begin
s_p6_frames++;
s_p6_word = 0;
s_p6_bit = 0;
s_p6_bit_cycle = 0;
s_p6_reset_cycles = 0;
end
if (`_P6_WS.s_underflow) s_p6_underflows++;
if (`_P6_WS.s_aborted) s_p6_aborts++;
if (`_P6_WS.s_busy && !`_P6_WS.s_reset_active && s_p6_frames <= 4) begin
s_p6_pixel = 24'(s_p6_word * 32'h010203);
s_p6_expected_high = s_p6_bit_cycle < (s_p6_pixel[23-s_p6_bit] ? 17 : 8);
if (s_gpio[26] !== s_p6_expected_high)
$fatal(
1,
"SIM_TEST_FAIL Tiny P6 WS pad waveform frame=%0d word=%0d bit=%0d cycle=%0d",
s_p6_frames,
s_p6_word,
s_p6_bit,
s_p6_bit_cycle
);
s_p6_bit_cycle++;
if (s_p6_bit_cycle == 30) begin
s_p6_bit_cycle = 0;
s_p6_bit++;
if (s_p6_bit == 24) begin
s_p6_bit = 0;
s_p6_word++;
end
end
if (`_P6_WS.u_ws2812_reg.s_fifo_level > s_p6_fifo_max)
s_p6_fifo_max = integer'(`_P6_WS.u_ws2812_reg.s_fifo_level);
if (`_P6_WS.u_ws2812_reg.s_fifo_low && !`_P6_WS.u_ws2812_reg.s_tx_push) s_p6_ws_wait++;
else s_p6_ws_wait = 0;
if (s_p6_ws_wait > s_p6_ws_wait_max) s_p6_ws_wait_max = s_p6_ws_wait;
if (s_p6_ws_wait > 4800 || s_p6_fifo_max > 16)
$fatal(1, "SIM_TEST_FAIL Tiny P6 WS service budget/occupancy");
end else s_p6_ws_wait = 0;
if (`_P6_WS.s_reset_active) begin
if (s_gpio[26] !== 1'b0) $fatal(1, "SIM_TEST_FAIL Tiny P6 WS reset-low");
s_p6_reset_cycles++;
end
if (!`_P6_WS.s_busy && s_p6_busy_previous) begin
if (s_p6_reset_cycles < 7200) $fatal(1, "SIM_TEST_FAIL Tiny P6 WS reset interval");
if (s_p6_frames <= 4) begin
if (s_p6_word != ((s_p6_frames % 2) ? 33 : 65) || s_p6_bit != 0 || s_p6_bit_cycle != 0)
$fatal(1, "SIM_TEST_FAIL Tiny P6 WS exact frame length");
s_p6_completed++;
end
end
s_p6_busy_previous = `_P6_WS.s_busy;
end
final begin
if (u_dut.s_test_done && u_dut.s_test_pass && s_p6_frames != 0) begin
if (s_p6_sdio_overlap == 0 || s_p6_overlap_fetches == 0)
$fatal(1, "SIM_TEST_FAIL P6 missing actual SDIO/DMA/CPU contention");
$display("R2_P6_CONTENTION sdio_dma_cycles=%0d cpu_fetches=%0d", s_p6_sdio_overlap,
s_p6_overlap_fetches);
if (s_p6_completed != 4 || s_p6_underflows != 1 || s_p6_aborts != 1 ||
s_p6_clock_stops != 2 || s_p6_wire_cases != 2 || s_p6_uart_routes != 1)
$fatal(1, "SIM_TEST_FAIL Tiny P6 incomplete device coverage");
$display(
"R2_P6_DEVICES ws_frames=%0d ws_wait_max=%0d fifo_max=%0d underflows=%0d aborts=%0d clock_stops=%0d",
s_p6_completed, s_p6_ws_wait_max, s_p6_fifo_max, s_p6_underflows, s_p6_aborts,
s_p6_clock_stops);
end
end
`undef _P6_WS
// Counts describe observed bus handshakes over the complete acceptance run.
// Read bytes require OKAY; write bytes are accepted W strobes, not rollback claims.
for (genvar owner = 0; owner < 3; owner++) begin : gen_p6_axi_observer
longint unsigned read_bytes = 0, write_bytes = 0, descriptor_bytes = 0;
longint unsigned ar_wait = 0, aw_wait = 0, w_wait = 0, r_wait = 0, b_wait = 0;
longint unsigned ar_max = 0, aw_max = 0, w_max = 0, r_max = 0, b_max = 0;
logic pending_read = 0, pending_write = 0, descriptor = 0;
logic [2:0] read_size = 0;
`define _P6_AXI u_dut.u_soc.u_masters_axi4_if[owner]
always @(posedge u_dut.u_soc.s_exec_clk) begin
if (!u_dut.u_soc.s_fabric_rst_n) begin
pending_read = 0;
pending_write = 0;
end else begin
if (`_P6_AXI.arvalid && `_P6_AXI.arready) begin
if (pending_read) $fatal(1, "SIM_TEST_FAIL P6 duplicate read admission");
pending_read = 1;
read_size = `_P6_AXI.arsize;
descriptor = owner == 1 && u_dut.u_soc.u_dma.s_tcd_count[64+:32] != 0 &&
`_P6_AXI.araddr >= u_dut.u_soc.u_dma.s_tcd_head[64+:32] &&
`_P6_AXI.araddr < u_dut.u_soc.u_dma.s_tcd_head[64+:32] + 32'd256;
end
if (`_P6_AXI.rvalid && `_P6_AXI.rready) begin
if (!pending_read) $fatal(1, "SIM_TEST_FAIL P6 unsolicited read response");
if (`_P6_AXI.rresp == 0) begin
read_bytes += 64'd1 << read_size;
if (descriptor) descriptor_bytes += 64'd1 << read_size;
end
if (`_P6_AXI.rlast) pending_read = 0;
end
if (`_P6_AXI.awvalid && `_P6_AXI.awready) begin
if (pending_write) $fatal(1, "SIM_TEST_FAIL P6 duplicate write admission");
pending_write = 1;
end
if (`_P6_AXI.wvalid && `_P6_AXI.wready) begin
if (!pending_write) $fatal(1, "SIM_TEST_FAIL P6 W without saved AW");
for (integer lane = 0; lane < 4; lane++) if (`_P6_AXI.wstrb[lane]) write_bytes++;
end
if (`_P6_AXI.bvalid && `_P6_AXI.bready) begin
if (!pending_write) $fatal(1, "SIM_TEST_FAIL P6 unsolicited write response");
pending_write = 0;
end
ar_wait = `_P6_AXI.arvalid && !`_P6_AXI.arready ? ar_wait + 1 : 0;
aw_wait = `_P6_AXI.awvalid && !`_P6_AXI.awready ? aw_wait + 1 : 0;
w_wait = `_P6_AXI.wvalid && !`_P6_AXI.wready ? w_wait + 1 : 0;
r_wait = `_P6_AXI.rvalid && !`_P6_AXI.rready ? r_wait + 1 : 0;
b_wait = `_P6_AXI.bvalid && !`_P6_AXI.bready ? b_wait + 1 : 0;
if (ar_wait > ar_max) ar_max = ar_wait;
if (aw_wait > aw_max) aw_max = aw_wait;
if (w_wait > w_max) w_max = w_wait;
if (r_wait > r_max) r_max = r_wait;
if (b_wait > b_max) b_max = b_wait;
end
end
final begin
if (u_dut.s_test_done && u_dut.s_test_pass && s_p6_frames != 0) begin
if (pending_read || pending_write) $fatal(1, "SIM_TEST_FAIL P6 unretired accepted work");
$display(
"R2_P6_AXI owner=%0d read_bytes=%0d write_bytes=%0d descriptor_bytes=%0d ar_stall_max=%0d aw_stall_max=%0d w_stall_max=%0d r_stall_max=%0d b_stall_max=%0d",
owner, read_bytes, write_bytes, descriptor_bytes, ar_max, aw_max, w_max, r_max, b_max);
end
end
`undef _P6_AXI
end
always #27.126736111 if (s_p6_audio_running) s_p6_audio_reference = !s_p6_audio_reference;
assign s_gpio_17 = s_p6_audio_reference;
// The first integration case is an external codec loopback, across actual pads.
// Independent wire decoding and error injection supplement firmware readback.
assign s_gpio_15 = s_gpio_14;
wire s_p6_card_cmd;
wire [3:0] s_p6_card_data;
wire s_p6_card_irq;
sdio_native_card_model #(
.MemoryInitialization(1'b1),
.BlockCount (1024)
) u_p6_card (
.rst_n_i (s_rst_n),
.sck_i (s_gpio[0]),
.cmd_oe_i(u_dut.u_soc.u_sdio_if.cmd_oe_o),
.cmd_do_i(s_gpio[1]),
.cmd_do_o(s_p6_card_cmd),
.dat_oe_i(u_dut.u_soc.u_sdio_if.dat_oe_o),
.dat_do_i(s_gpio[5:2]),
.dat_do_o(s_p6_card_data),
.irq_o (s_p6_card_irq)
);
assign s_gpio_1 = u_dut.u_soc.u_sdio_if.cmd_oe_o ? 1'bz : s_p6_card_cmd;
assign s_gpio_2 = u_dut.u_soc.u_sdio_if.dat_oe_o[0] ? 1'bz : s_p6_card_data[0];
assign s_gpio_3 = u_dut.u_soc.u_sdio_if.dat_oe_o[1] ? 1'bz : s_p6_card_data[1];
assign s_gpio_4 = u_dut.u_soc.u_sdio_if.dat_oe_o[2] ? 1'bz : s_p6_card_data[2];
assign s_gpio_5 = u_dut.u_soc.u_sdio_if.dat_oe_o[3] ? 1'bz : s_p6_card_data[3];
always @(negedge u_dut.u_soc.s_sdio_clk) begin
// Reuse the standalone card's explicit data-stimulus API. The wire command
// fixes direction/address/length; host DATA_START only schedules admission.
// This is repository controller integration evidence, not card compliance.
if (u_dut.u_soc.u_sdio.s_data_start) begin
if (u_p6_card.s_last_cmd != 17 && u_p6_card.s_last_cmd != 18 && u_p6_card.s_last_cmd != 25)
$fatal(1, "SIM_TEST_FAIL P6 unexpected SDIO data command");
if (u_dut.u_soc.u_sdio.s_block_size != 512 ||
u_dut.u_soc.u_sdio.s_block_count != (u_p6_card.s_last_cmd == 17 ? 1 : 2) ||
u_dut.u_soc.u_sdio.s_data_direction != (u_p6_card.s_last_cmd == 25) ||
u_dut.u_soc.u_sdio.s_bus_width != (u_p6_card.s_memory_width4 ? 1 : 0))
$fatal(1, "SIM_TEST_FAIL P6 SDIO command/data configuration disagreement");
u_p6_card.set_data_delay(1);
if (u_p6_card.s_last_cmd == 25)
u_p6_card.arm_write(integer'(u_p6_card.s_last_arg * 512), 1024, u_p6_card.s_memory_width4,
1'b0);
else
u_p6_card.arm_read(integer'(u_p6_card.s_last_arg * 512),
u_p6_card.s_last_cmd == 17 ? 512 : 1024, u_p6_card.s_memory_width4, 1'b0);
end
if ($test$plusargs("p6_debug_sdio") && u_dut.u_soc.u_sdio.s_cmd_start) begin
$display("P6_SDIO_DIAGNOSTIC cmd=%0d argument=%h status=%h", u_dut.u_soc.u_sdio.s_cmd_index,
u_dut.u_soc.u_sdio.s_cmd_arg, u_dut.u_soc.u_sdio.s_status);
$fflush();
end
if (u_dut.u_soc.u_sdio.s_cmd_start && u_dut.u_soc.u_sdio.s_cmd_index == 13) begin
if (u_dut.u_soc.u_sdio.s_cmd_arg == 32'hbadc0de0) u_p6_card.inject_next_crc_error();
if (u_dut.u_soc.u_sdio.s_cmd_arg == 32'hbadc0de1) u_p6_card.inject_next_timeout();
end
end
+65
View File
@@ -0,0 +1,65 @@
// P7-only assertions over production paths. No DUT state is written.
logic s_p7_monitor = 0;
longint unsigned s_p7_gated_cycles = 0, s_p7_dma_gated_responses = 0;
integer s_p7_warm_resets = 0;
logic [3:0] s_p7_profiles = 0;
initial s_p7_monitor = $test$plusargs("p7_monitor");
initial begin
if ($test$plusargs("p7_monitor") || $test$plusargs("p7_cold_reset_probe")) begin
wait (s_rst_n === 1'b0);
@(posedge s_rst_n);
wait (u_dut.u_soc.s_cpu_boot_ready === 1'b1);
repeat (2) @(negedge u_dut.u_soc.s_ref_clk);
// Case comparisons require the exact known reset values in both simulators.
if (u_dut.u_soc.u_sysctrl.s_reset_cause_q !== 32'd1 ||
u_dut.u_soc.u_sysctrl.s_rcu_fault_q !== 32'd0 ||
u_dut.u_soc.s_backend_current !== 2'd0 || u_dut.u_soc.s_gate_request !== 14'd0)
$fatal(
1,
"SIM_TEST_FAIL P7 cold reset state cause=%h fault=%h profile=%h gates=%h",
u_dut.u_soc.u_sysctrl.s_reset_cause_q,
u_dut.u_soc.u_sysctrl.s_rcu_fault_q,
u_dut.u_soc.s_backend_current,
u_dut.u_soc.s_gate_request
);
if ($test$plusargs("p7_cold_reset_probe")) begin
$display("P7_COLD_RESET_PASS");
$finish; // Diagnostic only; never emits the full Tiny acceptance marker.
end
end
end
always @(posedge u_dut.u_soc.s_exec_clk) begin
if (s_p7_monitor && u_dut.u_soc.s_actual_gate[0]) begin
s_p7_gated_cycles++;
if (u_dut.u_soc.u_masters_axi4_if[1].bvalid && u_dut.u_soc.u_masters_axi4_if[1].bready)
s_p7_dma_gated_responses++;
end
end
always @(posedge u_dut.u_soc.s_ref_clk) begin
if (s_p7_monitor && u_dut.u_soc.s_clock_done) begin
s_p7_profiles[u_dut.u_soc.s_backend_current] = 1;
$display("P7_CLOCK_COMMIT profile=%0d sys=%0d mem=%0d pclk=%0d", u_dut.u_soc.s_backend_current,
u_dut.u_soc.s_backend_sys_hz, u_dut.u_soc.s_backend_mem_hz,
u_dut.u_soc.s_backend_pclk_hz);
$fflush();
end
if (s_p7_monitor && u_dut.u_soc.s_system_reset) begin
s_p7_warm_resets++;
$display("P7_SOFTWARE_RESET request=%0d", s_p7_warm_resets);
$fflush();
end
end
final begin
if (s_p7_monitor && u_dut.s_test_done && u_dut.s_test_pass) begin
if (s_p7_gated_cycles == 0 || s_p7_dma_gated_responses == 0 || s_p7_warm_resets != 1)
$fatal(1, "SIM_TEST_FAIL P7 WFI/DMA/software-reset coverage");
`ifdef HAVE_PLL
if ((s_p7_profiles & 4'b1101) != 4'b1101) $fatal(1, "SIM_TEST_FAIL P7 PLL profile coverage");
`else
if ((s_p7_profiles & 4'b0011) != 4'b0011)
$fatal(1, "SIM_TEST_FAIL P7 external profile coverage");
`endif
$display("P7_LIFECYCLE_PASS gated_cycles=%0d dma_responses_while_gated=%0d warm_resets=%0d",
s_p7_gated_cycles, s_p7_dma_gated_responses, s_p7_warm_resets);
end
end
+7
View File
@@ -22,3 +22,10 @@
/clusterip/wdg/rtl/wdg_core.sv
/clusterip/wdg/rtl/wdg_reg.sv
/clusterip/wdg/rtl/apb4_wdg.sv
/clusterip/rng/rtl/rng_core.sv
/clusterip/rng/rtl/rng_reg.sv
/clusterip/rng/rtl/rng_deterministic_source.sv
/clusterip/rng/rtl/apb4_rng.sv
/clusterip/crc/rtl/crc_core.sv
/clusterip/crc/rtl/crc_reg.sv
/clusterip/crc/rtl/apb4_crc.sv
+3
View File
@@ -6,6 +6,7 @@
/clusterip/common/rtl/interface/axi4_stream_if.sv
/clusterip/common/rtl/interface/ram_if.sv
/clusterip/common/rtl/utils/register.sv
/clusterip/common/rtl/utils/xchecker.sv
/clusterip/common/rtl/utils/spill_register.sv
/clusterip/common/rtl/stream/round_robin_arbiter.sv
/clusterip/common/rtl/utils/lfsr.sv
@@ -18,5 +19,7 @@
/clusterip/common/rtl/cdc/cdc_2phase.sv
/clusterip/common/rtl/clkrst/rst_sync.sv
/clusterip/common/rtl/clkrst/counter.sv
/clusterip/common/rtl/clock/safe_clock_mux.sv
/clusterip/common/rtl/clock/clock_divider.sv
/ip/util/soc_common_fifo.sv
/ip/util/soc_common_cdc.sv
+3
View File
@@ -17,3 +17,6 @@
+incdir+/ip/serial
+incdir+/ip/peripheral
+incdir+/ip/core
+incdir+/ip/security
+incdir+/clusterip/rng/rtl
+incdir+/clusterip/crc/rtl
+37
View File
@@ -47,3 +47,40 @@
/ip/core/mgmt_debug_wrapper.sv
/ip/core/mgmt_core_wrapper.sv
/ip/peripheral/gpio_pad_bridge.sv
/ip/storage/sdio_if.sv
/ip/storage/sdio_pkg.sv
/ip/storage/sdio_define.svh
/ip/storage/sdio_crc7.sv
/ip/storage/sdio_crc16.sv
/ip/storage/sdio_clock.sv
/ip/storage/sdio_command.sv
/ip/storage/sdio_data.sv
/ip/storage/sdio_dma_descriptor.sv
/ip/serial/i2s_pkg.sv
/ip/serial/i2s_reg.sv
/ip/serial/i2s_clkgen.sv
/ip/serial/i2s_recv.sv
/ip/serial/i2s_send.sv
/ip/serial/i2s_core.sv
/ip/serial/apb4_i2s.sv
/ip/serial/ws2812_if.sv
/ip/serial/ws2812_reg.sv
/ip/serial/ws2812_core.sv
/ip/serial/apb4_ws2812.sv
/ip/security/crypto_pkg.sv
/ip/security/crypto_mem_pkg.sv
/ip/security/crypto_define.svh
/ip/security/crypto_sram_store.sv
/ip/security/crypto_scrubber.sv
/ip/security/crypto_clearable_fifo.sv
/ip/security/crypto_mem_ctrl.sv
/ip/security/crypto_aes_sram_core.sv
/ip/security/crypto_aes_sram_engine.sv
/ip/security/crypto_sha2_sram_engine.sv
/ip/security/crypto_montgomery_sram.sv
/ip/security/crypto_rsa_sram_core.sv
/ip/security/apb4_crypto.sv
/ip/storage/sdio_dma.sv
/ip/storage/sdio_reg.sv
/ip/storage/sdio_core.sv
/ip/storage/apb4_sdio.sv
+1
View File
@@ -1,2 +1,3 @@
/dv/tiny_qspi_nor.sv
/tests/rtl/sdio_native_model.sv
/dv/retrosoc_tiny_tb.sv
+17
View File
@@ -1,7 +1,24 @@
/top/tiny_axi4_target.sv
/top/tiny_axi4_fabric.sv
/top/tiny_fault_collector.sv
/top/tiny_axi42apb4.sv
/top/tiny_sysctrl.sv
/top/tiny_archinfo.sv
/top/tiny_reset_tree.sv
/top/tiny_clock_reset_subsystem.sv
/top/tiny_axi4_async_bridge.sv
/top/tiny_apb_async_bridge.sv
/top/tiny_axi4_admission.sv
/top/tiny_managed_lifecycle.sv
/top/tiny_peripheral_leaf.sv
/top/tiny_peripheral_control.sv
/top/tiny_clock_output.sv
/top/tiny_clock_gate.sv
/top/tiny_sram_port_if.sv
/top/tiny_sram_group.sv
/top/tiny_sram_axi4.sv
/top/tiny_sram.sv
/top/tiny_cpu_mem.sv
/top/tiny_cpu_wrapper.sv
/top/retrosoc_tiny.sv
/top/retrosoc_tiny_asic.sv
+356 -19
View File
@@ -1,31 +1,90 @@
{
"schema_version": 2,
"inactive_macros": {
"ics55_pll": {
"cell": "PLL_TOP",
"scope": "TINY-ICS55-P1: present, EN=0, no SYS connection",
"active_clock_domain": false,
"qualification": "no characterized timing arcs; full RCU remains R2-P7"
}
"external_fabric": {
"phase": "TINY-R2-P7",
"clock_domain": "system",
"target_controllers": "u_soc.u_fabric.gen_target[%d].u_controller",
"target_count": 27,
"apb_bridges": "SYS frontends; acknowledged per-endpoint crossings to AON, PCLK and MEM",
"fabric_reset": "fabric leaf; target reset blocks admission and aborts accepted target work with DECERR",
"hart_reset": "CPU-only accepted-work drain; DMA and all target responses stay active",
"runtime_peripheral_reset": "RCU blocks admissions, drains accepted work, holds reset for five local edges, waits for local release and bus barriers"
},
"local_memory": {
"phase": "TINY-R2-P4",
"clock_domain": "system",
"cpu_instance": "u_soc.u_cpu.u_hazard3_cpu_2port",
"frontend_instance": "u_soc.u_cpu_mem",
"macro_instance": "u_soc.u_sram.gen_group[{group}].u_group.gen_bank[{bank}].u_ram.u_mem",
"groups": 4,
"macros_per_group": 8,
"hart_reset": "halt then drain accepted I/D and slow responses; SRAM/DMA stay active",
"main_sram_payload_reset": "none",
"local_uncontended_max_cycles": 3
},
"reset_ports": [
"ext_rst_n_i_pad",
"jtag_trst_n_i_pad"
],
"domains": [
{
"name": "aon",
"clock": "s_ref_clk",
"reset": "ref_rst_n_o",
"reset_source": "rst_n_i",
"reset_primitive": "rst_sync",
"path": "rtl/tiny/top/tiny_clock_reset_subsystem.sv",
"instance": "u_ref_rst",
"sta": {
"source_port": "extclk_i_pad",
"pin": "u_clock_buffer/clk_o",
"period_ns": 41.666666667,
"async_group": "core"
}
},
{
"name": "system",
"clock": "clk_i",
"clock": "s_backend_sys_clk",
"reset": "system_rst_n_o",
"reset_source": "s_system_req_n",
"reset_primitive": "rst_sync",
"path": "rtl/tiny/top/tiny_reset_tree.sv",
"instance": "u_system_rst_sync",
"sta": {
"source_port": "extclk_i_pad",
"pin": "u_clock_buffer/clk_o",
"source_domain": "aon",
"pin": "u_soc.u_clock_backend.u_sys_mux/clk_o",
"period_ns": 41.666666667,
"async_group": "system"
"async_group": "core"
}
},
{
"name": "memory",
"clock": "s_backend_mem_clk",
"reset": "s_backend_mem_rst_n",
"reset_source": "rst_n_i",
"reset_primitive": "rst_sync",
"path": "rtl/tiny/top/tiny_clock_reset_subsystem.sv",
"instance": "u_mem_rst",
"sta": {
"source_domain": "system",
"pin": "u_soc.u_clock_backend.u_mem_mux/clk_o",
"period_ns": 41.666666667,
"async_group": "core"
}
},
{
"name": "peripheral",
"clock": "s_backend_pclk",
"reset": "pclk_rst_n_o",
"reset_source": "rst_n_i",
"reset_primitive": "rst_sync",
"path": "rtl/tiny/top/tiny_clock_reset_subsystem.sv",
"instance": "u_pclk_rst",
"sta": {
"source_domain": "system",
"pin": "u_soc.u_clock_backend.u_pclk_mux/clk_o",
"period_ns": 41.666666667,
"async_group": "core"
}
},
{
@@ -39,31 +98,152 @@
"sta": {
"source_port": "jtag_tck_i_pad",
"object_type": "port",
"period_ns": 100.0,
"period_ns": 100,
"async_group": "jtag"
}
},
{
"name": "audio",
"clock": "s_audio_clk",
"reset": "s_audio_rst_n",
"reset_source": "s_i2s_rst_n",
"reset_primitive": "rst_sync",
"path": "rtl/tiny/top/retrosoc_tiny.sv",
"instance": "u_audio_rst_sync",
"sta": {
"source_port": "gpio_17_io_pad",
"pin": "u_soc.u_audio_clock_buffer/clk_o",
"period_ns": 54.253472222,
"async_group": "audio"
}
}
],
"reset_distribution": {
"release_edges": 5,
"por": "u_soc.u_reset_tree.u_por_rst_sync",
"system": "u_soc.u_reset_tree.u_system_rst_sync",
"system_source": "por_rst_n_o && !watchdog_reset_req_i",
"system_source": "por_rst_n_o && !watchdog_reset_req_i && backend_reset_n_i",
"leaf_instance_pattern": "u_soc.u_reset_tree.gen_leaf[%d].u_leaf_rst_sync",
"leaf_names": [
"fabric", "gpio", "uart0", "uart1", "timer0", "timer1", "i2c0", "i2c1",
"xpi", "dma", "sysctrl", "clint", "sram", "archinfo", "pwm", "rtc", "wdg"
"fabric",
"gpio",
"uart0",
"timer0",
"timer1",
"i2c0",
"xpi",
"dma",
"sysctrl",
"clint",
"sram",
"archinfo",
"pwm",
"rtc",
"wdg",
"rng",
"crc",
"ws2812",
"crypto",
"i2s",
"sdio"
],
"cpu_prerequisite": "all leaf resets released",
"cpu_prerequisite": "all global leaf resets and actual APB/DMA/XPI/RCU barriers; boot readiness latches until the next system reset",
"hart": "u_soc.u_cpu.u_mgmt_debug_wrapper.u_mgmt_debug_reset.u_core_rst_sync",
"jtag": "u_soc.u_cpu.u_mgmt_debug_wrapper.u_jtag_rst_sync",
"dmi": "u_soc.u_cpu.u_mgmt_debug_wrapper.u_dmi_rst_sync",
"watchdog_functional_reset": "POR only; APB uses wdg leaf",
"watchdog_functional_reset": "REF24 five-edge release after backend retains the watchdog reset event; PCLK APB uses the wdg leaf",
"main_sram_payload_reset": "none",
"crypto": "not integrated; physical erasure contract unchanged",
"clock_domain": "system"
"crypto": "six private macro banks; reset invalidates then scrubs/readbacks; CPU does not wait for READY",
"clock_domain": "domain-specific",
"peripheral": "tiny_peripheral_leaf; five running hold edges and five release edges; I2S additionally requires actual audio-side barrier readiness"
},
"crossings": [
{
"name": "debug_pclk_status",
"source": "system", "destination": "peripheral", "primitive": "cdc_sync",
"path": "rtl/tiny/top/retrosoc_tiny.sv", "instance": "u_debug_pclk_sync"
},
{
"name": "debug_aon_status",
"source": "system", "destination": "aon", "primitive": "cdc_sync",
"path": "rtl/tiny/top/retrosoc_tiny.sv", "instance": "u_debug_ref_sync"
},
{
"name": "rtc_wake",
"source": "aon", "destination": "system", "primitive": "cdc_sync",
"path": "rtl/tiny/top/retrosoc_tiny.sv", "instance": "u_rtc_wake_sync"
},
{
"name": "hart_reset_event",
"source": "system", "destination": "aon", "primitive": "cdc_2phase",
"path": "rtl/tiny/top/retrosoc_tiny.sv", "instance": "u_hart_event_cdc"
},
{
"name": "peripheral_interrupts",
"source": "peripheral", "destination": "system", "primitive": "cdc_sync",
"path": "rtl/tiny/top/retrosoc_tiny.sv", "instance": "u_irq_sync",
"notes": "sticky interrupt levels; this vector also contains AON/MEM and synchronous SYS bits"
},
{
"name": "wfi_permission",
"source": "aon", "destination": "system", "primitive": "cdc_sync",
"path": "rtl/tiny/top/retrosoc_tiny.sv", "instance": "u_wfi_gate_sync"
},
{
"name": "admission_block_system",
"source": "aon", "destination": "system", "primitive": "cdc_sync",
"path": "rtl/tiny/top/retrosoc_tiny.sv", "instance": "u_block_sys"
},
{
"name": "admission_block_dma",
"source": "aon", "destination": "peripheral", "primitive": "cdc_sync",
"path": "rtl/tiny/top/retrosoc_tiny.sv", "instance": "u_block_pclk"
},
{
"name": "gate_reset_disabled",
"source": "aon", "destination": "system", "primitive": "cdc_sync",
"path": "rtl/tiny/top/retrosoc_tiny.sv", "instance": "u_disabled_sync"
},
{
"name": "peripheral_idle",
"source": "peripheral", "destination": "aon", "primitive": "cdc_sync",
"path": "rtl/tiny/top/retrosoc_tiny.sv", "instance": "u_periph_idle_sync"
},
{
"name": "xpi_idle",
"source": "memory", "destination": "aon", "primitive": "cdc_sync",
"path": "rtl/tiny/top/retrosoc_tiny.sv", "instance": "u_xpi_idle_sync"
},
{
"name": "dma_ready",
"source": "peripheral", "destination": "system", "primitive": "cdc_sync",
"path": "rtl/tiny/top/retrosoc_tiny.sv", "instance": "u_dma_ready_sync"
},
{
"name": "dma_retirement",
"source": "peripheral", "destination": "system", "primitive": "cdc_sync",
"path": "rtl/tiny/top/retrosoc_tiny.sv", "instance": "u_dma_idle_sync"
},
{
"name": "fabric_retirement",
"source": "system", "destination": "aon", "primitive": "cdc_sync",
"path": "rtl/tiny/top/retrosoc_tiny.sv", "instance": "u_bus_idle_ref"
},
{
"name": "gate_acknowledgements",
"source": "peripheral", "destination": "aon", "primitive": "cdc_sync",
"path": "rtl/tiny/top/retrosoc_tiny.sv", "instance": "u_gate_feedback"
},
{
"name": "reset_hold_acknowledgements",
"source": "peripheral", "destination": "aon", "primitive": "cdc_sync",
"path": "rtl/tiny/top/retrosoc_tiny.sv", "instance": "u_held_feedback"
},
{
"name": "reset_release_acknowledgements",
"source": "system", "destination": "aon", "primitive": "cdc_sync",
"path": "rtl/tiny/top/retrosoc_tiny.sv", "instance": "u_ready_feedback"
},
{
"name": "debug_dmi",
"source": "jtag",
@@ -72,6 +252,17 @@
"path": "rtl/managed/hazard3/hdl/debug/dtm/hazard3_jtag_dtm_core.v",
"instance": "inst_hazard3_apb_async_bridge"
},
{
"name": "xpi_mem_axi",
"source": "system",
"destination": "memory",
"primitive": "tiny_axi4_async_bridge",
"path": "rtl/tiny/top/retrosoc_tiny.sv",
"instance": "u_xpi_mem_cdc",
"source_reset": "s_fabric_rst_n",
"destination_reset": "s_xpi_rst_n",
"reset_semantics": "five-edge link release; source-owned held responses survive destination reset; drain remaining W before B and synthesize only missing R beats with DECERR; unfinished warm clear requires destination reset before re-admission"
},
{
"name": "debug_hard_reset",
"source": "jtag",
@@ -79,6 +270,152 @@
"primitive": "cdc_sync",
"path": "rtl/ip/core/mgmt_debug_wrapper.sv",
"instance": "u_dmihardreset_cdc_sync"
},
{
"name": "i2s_configuration",
"source": "peripheral",
"destination": "audio",
"primitive": "cdc_2phase",
"path": "rtl/ip/serial/apb4_i2s.sv",
"instance": "u_i2s_cfg_cdc"
},
{
"name": "i2s_commands",
"source": "peripheral",
"destination": "audio",
"primitive": "cdc_2phase",
"path": "rtl/ip/serial/apb4_i2s.sv",
"instance": "u_i2s_cmd_cdc"
},
{
"name": "i2s_tx_samples",
"source": "peripheral",
"destination": "audio",
"primitive": "cdc_fifo_warm_flush",
"path": "rtl/ip/serial/apb4_i2s.sv",
"instance": "u_tx_cdc_fifo"
},
{
"name": "i2s_rx_samples",
"source": "audio",
"destination": "peripheral",
"primitive": "cdc_fifo_warm_flush",
"path": "rtl/ip/serial/apb4_i2s.sv",
"instance": "u_rx_cdc_fifo"
},
{
"name": "i2s_tx_occupancy",
"source": "audio",
"destination": "peripheral",
"primitive": "cdc_2phase",
"path": "rtl/ip/serial/apb4_i2s.sv",
"instance": "u_tx_pop_cnt_cdc"
},
{
"name": "i2s_rx_occupancy",
"source": "audio",
"destination": "peripheral",
"primitive": "cdc_2phase",
"path": "rtl/ip/serial/apb4_i2s.sv",
"instance": "u_rx_push_cnt_cdc"
},
{
"name": "dma_axi",
"source": "peripheral",
"destination": "system",
"primitive": "tiny_axi4_async_bridge",
"path": "rtl/tiny/top/retrosoc_tiny.sv",
"instance": "u_dma_cdc",
"source_reset": "s_dma_rst_n",
"destination_reset": "s_fabric_rst_n",
"reset_semantics": "five-edge link release; source-owned held responses survive destination reset; drain remaining W before B and synthesize only missing R beats with DECERR; unfinished warm clear requires destination reset before re-admission"
},
{
"name": "rcu_commands",
"source": "system",
"destination": "aon",
"primitive": "tiny_apb_async_bridge",
"path": "rtl/tiny/top/tiny_sysctrl.sv",
"instance": "u_rcu_cdc"
},
{
"name": "system_rate",
"source": "aon",
"destination": "system",
"primitive": "cdc_2phase",
"path": "rtl/tiny/top/retrosoc_tiny.sv",
"instance": "u_sys_rate_cdc"
},
{
"name": "peripheral_rate",
"source": "aon",
"destination": "peripheral",
"primitive": "cdc_2phase",
"path": "rtl/tiny/top/retrosoc_tiny.sv",
"instance": "u_pclk_rate_cdc"
},
{
"name": "dma_done",
"source": "peripheral",
"destination": "memory",
"primitive": "cdc_2phase",
"path": "rtl/tiny/top/retrosoc_tiny.sv",
"instance": "u_dma_done_cdc"
},
{
"name": "xpi_requests",
"source": "memory",
"destination": "peripheral",
"primitive": "cdc_sync",
"path": "rtl/tiny/top/retrosoc_tiny.sv",
"instance": "u_xpi_stall_sync"
}
],
"clock_contract": "TINY-R2-P7",
"macros": {
"ics55_pll": {
"cell": "PLL_TOP",
"scope": "single RCU-owned macro; off after cold reset; selectors 5/7",
"active_clock_domain": true,
"qualification": "digital activity/rate qualification only; no analog LOCK or characterized timing arcs"
}
},
"generated_apb_crossings": {
"generator": "scripts/generate_tiny.py",
"template": "tiny_apb_bridges.svh",
"primitive": "tiny_apb_async_bridge",
"source": "system",
"destinations": {
"aon": [
"clint",
"archinfo"
],
"memory": [
"xpi"
],
"peripheral": [
"gpio",
"uart0",
"timer0",
"timer1",
"i2c0",
"dma",
"pwm",
"rtc",
"wdg",
"rng",
"crc",
"ws2812",
"crypto",
"i2s"
]
},
"same_clock_isolation": [
"sdio"
],
"native_system": [
"sysctrl",
"sram"
]
}
}
+370 -267
View File
@@ -1,9 +1,50 @@
{
"schema_version": 1,
"cpu": {
"module": "hazard3_cpu_2port",
"local_ports": 2,
"data_outstanding": 1
},
"main_sram": {
"groups": 4,
"group_bytes": 32768,
"macros_per_group": 8,
"macro_bytes": 4096,
"base": "0x30000000",
"macro_instance": "u_soc.u_sram.gen_group[{group}].u_group.gen_bank[{bank}].u_ram.u_mem",
"external_attachment": "per-target-P5"
},
"fabric": {
"owners": [
"cpu_slow",
"central_dma",
"sdio"
],
"active_owners": [
0,
1,
2
],
"cpu_combined_outstanding": 1,
"dma_direction_outstanding": 1,
"sdio_direction_outstanding": 1,
"target_combined_outstanding": 1,
"sram_targets": [
0,
1,
2,
3
],
"xpi_target": 4,
"apb_target_base": 5,
"decode_error_target": 25,
"protocol_error_target": 26,
"target_count": 27
},
"gpio_pins": 32,
"irq_vector_width": 32,
"external_irq_count": 30,
"dma_channels": 4,
"dma_channels": 8,
"apb_targets": [
{
"name": "gpio",
@@ -18,12 +59,6 @@
"APB4_UART0"
]
},
{
"name": "uart1",
"regions": [
"APB4_UART1"
]
},
{
"name": "timer0",
"regions": [
@@ -42,12 +77,6 @@
"APB4_I2C0"
]
},
{
"name": "i2c1",
"regions": [
"APB4_I2C1"
]
},
{
"name": "xpi",
"regions": [
@@ -101,6 +130,42 @@
"regions": [
"APB4_WDG"
]
},
{
"name": "rng",
"regions": [
"APB4_RNG"
]
},
{
"name": "crc",
"regions": [
"APB4_CRC"
]
},
{
"name": "ws2812",
"regions": [
"APB4_WS2812"
]
},
{
"name": "crypto",
"regions": [
"APB4_CRYPTO"
]
},
{
"name": "i2s",
"regions": [
"APB4_I2S"
]
},
{
"name": "sdio",
"regions": [
"APB4_SDIO0"
]
}
],
"interrupts": [
@@ -134,11 +199,21 @@
"core_bit": 7,
"signal": "u_i2c0_if.irq_o"
},
{
"name": "i2s",
"core_bit": 8,
"signal": "u_i2s_if.irq_o"
},
{
"name": "xpi",
"core_bit": 9,
"signal": "s_xpi_irq"
},
{
"name": "sdio",
"core_bit": 10,
"signal": "u_sdio_if.irq_o"
},
{
"name": "pwm",
"core_bit": 11,
@@ -154,36 +229,44 @@
"core_bit": 14,
"signal": "u_wdg_if.irq_o"
},
{
"name": "rng",
"core_bit": 16,
"signal": "s_rng_irq"
},
{
"name": "ws2812",
"core_bit": 17,
"signal": "u_ws2812_if.irq_o"
},
{
"name": "gpio",
"core_bit": 18,
"signal": "u_gpio_if.irq_o"
},
{
"name": "i2c1",
"core_bit": 19,
"signal": "u_i2c1_if.irq_o"
},
{
"name": "dma",
"core_bit": 20,
"signal": "s_dma_irq"
},
{
"name": "uart1",
"core_bit": 26,
"signal": "u_uart1_if.irq_o"
"name": "crypto",
"core_bit": 23,
"signal": "s_crypto_irq"
},
{
"name": "rcu",
"core_bit": 31,
"signal": "s_rcu_irq"
}
],
"gpio_alt_functions": [
{
"pin": 0,
"alt0": {
"inputs": [
"u_uart0_if.cts_n_i"
],
"do": "1'b0",
"oe": "1'b0"
"inputs": [],
"do": "u_sdio_if.sck_o",
"oe": "1'b1"
},
"alt1": {
"inputs": [],
@@ -194,9 +277,11 @@
{
"pin": 1,
"alt0": {
"inputs": [],
"do": "u_uart0_if.rts_n_o",
"oe": "1'b1"
"inputs": [
"u_sdio_if.cmd_di_i"
],
"do": "u_sdio_if.cmd_do_o",
"oe": "u_sdio_if.cmd_oe_o"
},
"alt1": {
"inputs": [],
@@ -208,10 +293,10 @@
"pin": 2,
"alt0": {
"inputs": [
"u_pwm_if.sync_i"
"u_sdio_if.dat_di_i[0]"
],
"do": "1'b0",
"oe": "1'b0"
"do": "u_sdio_if.dat_do_o[0]",
"oe": "u_sdio_if.dat_oe_o[0]"
},
"alt1": {
"inputs": [],
@@ -221,36 +306,243 @@
},
{
"pin": 3,
"alt0": {
"inputs": [
"u_sdio_if.dat_di_i[1]"
],
"do": "u_sdio_if.dat_do_o[1]",
"oe": "u_sdio_if.dat_oe_o[1]"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 4,
"alt0": {
"inputs": [
"u_sdio_if.dat_di_i[2]"
],
"do": "u_sdio_if.dat_do_o[2]",
"oe": "u_sdio_if.dat_oe_o[2]"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 5,
"alt0": {
"inputs": [
"u_sdio_if.dat_di_i[3]"
],
"do": "u_sdio_if.dat_do_o[3]",
"oe": "u_sdio_if.dat_oe_o[3]"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 6,
"alt0": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 7,
"alt0": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 8,
"alt0": {
"inputs": [],
"do": "u_uart0_if.tx_o",
"oe": "1'b1"
},
"alt1": {
"inputs": [],
"do": "u_pwm_if.pwm_o[0]",
"oe": "u_pwm_if.oe_o[0]"
}
},
{
"pin": 9,
"alt0": {
"inputs": [
"u_uart0_if.rx_i"
],
"do": "1'b0",
"oe": "1'b0"
},
"alt1": {
"inputs": [],
"do": "u_pwm_if.pwm_o[1]",
"oe": "u_pwm_if.oe_o[1]"
}
},
{
"pin": 10,
"alt0": {
"inputs": [
"u_i2c0_if.scl_i"
],
"do": "u_i2c0_if.scl_o",
"oe": "u_i2c0_if.scl_oe_o"
},
"alt1": {
"inputs": [],
"do": "u_pwm_if.pwm_o[2]",
"oe": "u_pwm_if.oe_o[2]"
}
},
{
"pin": 11,
"alt0": {
"inputs": [
"u_i2c0_if.sda_i"
],
"do": "u_i2c0_if.sda_o",
"oe": "u_i2c0_if.sda_oe_o"
},
"alt1": {
"inputs": [],
"do": "u_pwm_if.pwm_o[3]",
"oe": "u_pwm_if.oe_o[3]"
}
},
{
"pin": 12,
"alt0": {
"inputs": [],
"do": "u_i2s_if.sclk_o",
"oe": "1'b1"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 13,
"alt0": {
"inputs": [],
"do": "u_i2s_if.lrck_o",
"oe": "1'b1"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 14,
"alt0": {
"inputs": [],
"do": "u_i2s_if.dacdat_o",
"oe": "1'b1"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 15,
"alt0": {
"inputs": [
"u_i2s_if.adcdat_i"
],
"do": "1'b0",
"oe": "1'b0"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 16,
"alt0": {
"inputs": [],
"do": "u_i2s_if.mclk_o",
"oe": "1'b1"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 17,
"alt0": {
"inputs": [
"s_audio_pad_clk"
],
"do": "1'b0",
"oe": "1'b0"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 18,
"alt0": {
"inputs": [],
"do": "u_pwm_if.pwm_o[0]",
"oe": "u_pwm_if.oe_o[0]"
},
"alt1": {
"inputs": [
"u_i2c1_if.scl_i"
],
"do": "u_i2c1_if.scl_o",
"oe": "u_i2c1_if.scl_oe_o"
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 4,
"pin": 19,
"alt0": {
"inputs": [],
"do": "u_pwm_if.pwm_o[1]",
"oe": "u_pwm_if.oe_o[1]"
},
"alt1": {
"inputs": [
"u_i2c1_if.sda_i"
],
"do": "u_i2c1_if.sda_o",
"oe": "u_i2c1_if.sda_oe_o"
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 5,
"pin": 20,
"alt0": {
"inputs": [],
"do": "u_pwm_if.pwm_o[2]",
@@ -263,7 +555,7 @@
}
},
{
"pin": 6,
"pin": 21,
"alt0": {
"inputs": [],
"do": "u_pwm_if.pwm_o[3]",
@@ -276,37 +568,7 @@
}
},
{
"pin": 7,
"alt0": {
"inputs": [
"u_i2c0_if.scl_i"
],
"do": "u_i2c0_if.scl_o",
"oe": "u_i2c0_if.scl_oe_o"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 8,
"alt0": {
"inputs": [
"u_i2c0_if.sda_i"
],
"do": "u_i2c0_if.sda_o",
"oe": "u_i2c0_if.sda_oe_o"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 9,
"pin": 22,
"alt0": {
"inputs": [
"u_pwm_if.fault_i"
@@ -320,192 +582,27 @@
"oe": "1'b0"
}
},
{
"pin": 10,
"alt0": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 11,
"alt0": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 12,
"alt0": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 13,
"alt0": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 14,
"alt0": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 15,
"alt0": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 16,
"alt0": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 17,
"alt0": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 18,
"alt0": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 19,
"alt0": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 20,
"alt0": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 21,
"alt0": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 22,
"alt0": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
},
{
"pin": 23,
"alt0": {
"inputs": [],
"inputs": [
"u_pwm_if.sync_i"
],
"do": "1'b0",
"oe": "1'b0"
},
"alt1": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
"do": "s_clkout",
"oe": "s_clkout_enable"
}
},
{
"pin": 24,
"alt0": {
"inputs": [],
"inputs": [
"u_pwm_if.capture_i[0]"
],
"do": "1'b0",
"oe": "1'b0"
},
@@ -518,7 +615,9 @@
{
"pin": 25,
"alt0": {
"inputs": [],
"inputs": [
"u_pwm_if.capture_i[1]"
],
"do": "1'b0",
"oe": "1'b0"
},
@@ -532,8 +631,8 @@
"pin": 26,
"alt0": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
"do": "u_ws2812_if.dat_o",
"oe": "1'b1"
},
"alt1": {
"inputs": [],
@@ -558,8 +657,8 @@
"pin": 28,
"alt0": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
"do": "s_clkout",
"oe": "s_clkout_enable"
},
"alt1": {
"inputs": [],
@@ -571,8 +670,8 @@
"pin": 29,
"alt0": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
"do": "xpi.nss_o[1]",
"oe": "1'b1"
},
"alt1": {
"inputs": [],
@@ -584,13 +683,11 @@
"pin": 30,
"alt0": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
"do": "xpi.nss_o[2]",
"oe": "1'b1"
},
"alt1": {
"inputs": [
"u_pwm_if.capture_i[0]"
],
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
@@ -599,16 +696,22 @@
"pin": 31,
"alt0": {
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
"do": "xpi.nss_o[3]",
"oe": "1'b1"
},
"alt1": {
"inputs": [
"u_pwm_if.capture_i[1]"
],
"inputs": [],
"do": "1'b0",
"oe": "1'b0"
}
}
]
],
"dma_request_mask": "0x31ff",
"private_crypto": {
"banks": 6,
"bank_bytes": 4096,
"macro_instance": "u_soc.u_crypto.u_storage.gen_bank[{bank}].gen_macro.u_sram.u_mem",
"clock_domain": "peripheral",
"logical_domain": "PCLK"
}
}
+14 -8
View File
@@ -1,4 +1,5 @@
# Both Tiny integrations remain SAFE24; PLL presence is not SYS selection.
# Tiny always boots from normalized SAFE24. Dynamic selection is RCU-owned.
override TINY_R2_P7 := YES
ifneq ($(MINI_MODE),NONE)
$(error SOC=TINY requires MINI_MODE=NONE)
endif
@@ -10,12 +11,12 @@ $(error SOC=TINY requires HAVE_HP=NO)
endif
ifeq ($(PDK),ICS55)
ifneq ($(HAVE_PLL),YES)
$(error SOC=TINY PDK=ICS55 requires HAVE_PLL=YES with the PLL held off)
$(error SOC=TINY PDK=ICS55 requires HAVE_PLL=YES)
endif
ifeq ($(PDK_BEHAV),YES)
$(error SOC=TINY PDK=ICS55 requires the locked technology models, PDK_BEHAV=NO)
endif
override TINY_SAFE24_PLL_OFF := YES
override TINY_SAFE24_PLL_OFF := NO
else
ifneq ($(HAVE_PLL),NO)
$(error SOC=TINY PDK=IHP130 requires HAVE_PLL=NO)
@@ -31,11 +32,16 @@ endif
ifneq ($(SRAM_SIZE_KIB),128)
$(error SOC=TINY first release requires SRAM_SIZE_KIB=128)
endif
ifneq ($(EXT_CLK_HZ),24000000)
$(error SOC=TINY first release requires EXT_CLK_HZ=24000000)
ifeq ($(filter $(EXT_CLK_HZ),24000000 48000000 96000000),)
$(error SOC=TINY requires external XIN of 24, 48 or 96 MHz)
endif
ifneq ($(AUD_CLK_HZ),$(EXT_CLK_HZ))
$(error SOC=TINY uses the system clock for RTC and watchdog)
ifeq ($(HAVE_PLL),YES)
ifneq ($(EXT_CLK_HZ),24000000)
$(error SOC=TINY with PLL requires EXT_CLK_HZ=24000000)
endif
endif
ifneq ($(AUD_CLK_HZ),24000000)
$(error SOC=TINY RTC and watchdog use normalized REF24)
endif
ifneq ($(CLINT_TIMEBASE_HZ),1000000)
$(error SOC=TINY requires CLINT_TIMEBASE_HZ=1000000)
@@ -56,7 +62,7 @@ ifneq ($(ISA),RV32IM)
$(error SOC=TINY firmware uses ISA=RV32IM)
endif
ifneq ($(WS2812_P3_ACCEPTANCE),NO)
$(error SOC=TINY does not yet integrate WS2812)
$(error SOC=TINY uses its integrated P6 acceptance, not the Mini WS2812 P3 image)
endif
ifneq ($(filter YES,$(APU_ENABLE_P7) $(NPU_P5_ACCEPTANCE) $(NPU_P6_ACCEPTANCE)),)
$(error SOC=TINY does not include multimedia accelerators)
+64 -64
View File
@@ -205,38 +205,38 @@
"jtag_tdi_i_pad": "s_jtag_tdi",
"jtag_trst_n_i_pad": "s_jtag_trst_n",
"jtag_tdo_o_pad": "s_jtag_tdo",
"gpio_0_io_pad": "s_gpio[0]",
"gpio_1_io_pad": "s_gpio[1]",
"gpio_2_io_pad": "s_gpio[2]",
"gpio_3_io_pad": "s_gpio[3]",
"gpio_4_io_pad": "s_gpio[4]",
"gpio_5_io_pad": "s_gpio[5]",
"gpio_6_io_pad": "s_gpio[6]",
"gpio_7_io_pad": "s_gpio[7]",
"gpio_8_io_pad": "s_gpio[8]",
"gpio_9_io_pad": "s_gpio[9]",
"gpio_10_io_pad": "s_gpio[10]",
"gpio_11_io_pad": "s_gpio[11]",
"gpio_12_io_pad": "s_gpio[12]",
"gpio_13_io_pad": "s_gpio[13]",
"gpio_14_io_pad": "s_gpio[14]",
"gpio_15_io_pad": "s_gpio[15]",
"gpio_16_io_pad": "s_gpio[16]",
"gpio_17_io_pad": "s_gpio[17]",
"gpio_18_io_pad": "s_gpio[18]",
"gpio_19_io_pad": "s_gpio[19]",
"gpio_20_io_pad": "s_gpio[20]",
"gpio_21_io_pad": "s_gpio[21]",
"gpio_22_io_pad": "s_gpio[22]",
"gpio_23_io_pad": "s_gpio[23]",
"gpio_24_io_pad": "s_gpio[24]",
"gpio_25_io_pad": "s_gpio[25]",
"gpio_26_io_pad": "s_gpio[26]",
"gpio_27_io_pad": "s_gpio[27]",
"gpio_28_io_pad": "s_gpio[28]",
"gpio_29_io_pad": "s_gpio[29]",
"gpio_30_io_pad": "s_gpio[30]",
"gpio_31_io_pad": "s_gpio[31]",
"gpio_0_io_pad": "s_gpio_0",
"gpio_1_io_pad": "s_gpio_1",
"gpio_2_io_pad": "s_gpio_2",
"gpio_3_io_pad": "s_gpio_3",
"gpio_4_io_pad": "s_gpio_4",
"gpio_5_io_pad": "s_gpio_5",
"gpio_6_io_pad": "s_gpio_6",
"gpio_7_io_pad": "s_gpio_7",
"gpio_8_io_pad": "s_gpio_8",
"gpio_9_io_pad": "s_gpio_9",
"gpio_10_io_pad": "s_gpio_10",
"gpio_11_io_pad": "s_gpio_11",
"gpio_12_io_pad": "s_gpio_12",
"gpio_13_io_pad": "s_gpio_13",
"gpio_14_io_pad": "s_gpio_14",
"gpio_15_io_pad": "s_gpio_15",
"gpio_16_io_pad": "s_gpio_16",
"gpio_17_io_pad": "s_gpio_17",
"gpio_18_io_pad": "s_gpio_18",
"gpio_19_io_pad": "s_gpio_19",
"gpio_20_io_pad": "s_gpio_20",
"gpio_21_io_pad": "s_gpio_21",
"gpio_22_io_pad": "s_gpio_22",
"gpio_23_io_pad": "s_gpio_23",
"gpio_24_io_pad": "s_gpio_24",
"gpio_25_io_pad": "s_gpio_25",
"gpio_26_io_pad": "s_gpio_26",
"gpio_27_io_pad": "s_gpio_27",
"gpio_28_io_pad": "s_gpio_28",
"gpio_29_io_pad": "s_gpio_29",
"gpio_30_io_pad": "s_gpio_30",
"gpio_31_io_pad": "s_gpio_31",
"uart0_tx_o_pad": "s_uart0_tx",
"uart0_rx_i_pad": "s_uart0_rx",
"uart1_tx_o_pad": "s_uart1_tx",
@@ -261,38 +261,38 @@
"jtag_tdi_i_pad": "s_jtag_tdi",
"jtag_trst_n_i_pad": "s_jtag_trst_n",
"jtag_tdo_o_pad": "s_jtag_tdo",
"gpio_0_io_pad": "s_gpio[0]",
"gpio_1_io_pad": "s_gpio[1]",
"gpio_2_io_pad": "s_gpio[2]",
"gpio_3_io_pad": "s_gpio[3]",
"gpio_4_io_pad": "s_gpio[4]",
"gpio_5_io_pad": "s_gpio[5]",
"gpio_6_io_pad": "s_gpio[6]",
"gpio_7_io_pad": "s_gpio[7]",
"gpio_8_io_pad": "s_gpio[8]",
"gpio_9_io_pad": "s_gpio[9]",
"gpio_10_io_pad": "s_gpio[10]",
"gpio_11_io_pad": "s_gpio[11]",
"gpio_12_io_pad": "s_gpio[12]",
"gpio_13_io_pad": "s_gpio[13]",
"gpio_14_io_pad": "s_gpio[14]",
"gpio_15_io_pad": "s_gpio[15]",
"gpio_16_io_pad": "s_gpio[16]",
"gpio_17_io_pad": "s_gpio[17]",
"gpio_18_io_pad": "s_gpio[18]",
"gpio_19_io_pad": "s_gpio[19]",
"gpio_20_io_pad": "s_gpio[20]",
"gpio_21_io_pad": "s_gpio[21]",
"gpio_22_io_pad": "s_gpio[22]",
"gpio_23_io_pad": "s_gpio[23]",
"gpio_24_io_pad": "s_gpio[24]",
"gpio_25_io_pad": "s_gpio[25]",
"gpio_26_io_pad": "s_gpio[26]",
"gpio_27_io_pad": "s_gpio[27]",
"gpio_28_io_pad": "s_gpio[28]",
"gpio_29_io_pad": "s_gpio[29]",
"gpio_30_io_pad": "s_gpio[30]",
"gpio_31_io_pad": "s_gpio[31]",
"gpio_0_io_pad": "s_gpio_0",
"gpio_1_io_pad": "s_gpio_1",
"gpio_2_io_pad": "s_gpio_2",
"gpio_3_io_pad": "s_gpio_3",
"gpio_4_io_pad": "s_gpio_4",
"gpio_5_io_pad": "s_gpio_5",
"gpio_6_io_pad": "s_gpio_6",
"gpio_7_io_pad": "s_gpio_7",
"gpio_8_io_pad": "s_gpio_8",
"gpio_9_io_pad": "s_gpio_9",
"gpio_10_io_pad": "s_gpio_10",
"gpio_11_io_pad": "s_gpio_11",
"gpio_12_io_pad": "s_gpio_12",
"gpio_13_io_pad": "s_gpio_13",
"gpio_14_io_pad": "s_gpio_14",
"gpio_15_io_pad": "s_gpio_15",
"gpio_16_io_pad": "s_gpio_16",
"gpio_17_io_pad": "s_gpio_17",
"gpio_18_io_pad": "s_gpio_18",
"gpio_19_io_pad": "s_gpio_19",
"gpio_20_io_pad": "s_gpio_20",
"gpio_21_io_pad": "s_gpio_21",
"gpio_22_io_pad": "s_gpio_22",
"gpio_23_io_pad": "s_gpio_23",
"gpio_24_io_pad": "s_gpio_24",
"gpio_25_io_pad": "s_gpio_25",
"gpio_26_io_pad": "s_gpio_26",
"gpio_27_io_pad": "s_gpio_27",
"gpio_28_io_pad": "s_gpio_28",
"gpio_29_io_pad": "s_gpio_29",
"gpio_30_io_pad": "s_gpio_30",
"gpio_31_io_pad": "s_gpio_31",
"uart0_tx_o_pad": "s_uart0_tx",
"uart0_rx_i_pad": "s_uart0_rx",
"uart1_tx_o_pad": "s_uart1_tx",
File diff suppressed because it is too large. Load diff
Loaded 100 of 178 files, more files were not shown because too many files have changed in this diff. Show more