diff --git a/AGENTS.md b/AGENTS.md index 7ffd5a89..cf951102 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -22,7 +22,7 @@ truth wins. records approved Required-rule deviations. - [crt/README.md](crt/README.md) defines the freestanding RISC-V runtime and SDK layout; [app/README.md](app/README.md) defines application composition. -- `Makefile`, `rtl/mini/mk/software.mk`, committed `configs/*.mk`, and the +- `Makefile`, `rtl/mk/software.mk`, committed `configs/*.mk`, and the GitHub workflows define the active build and regression behavior. - `quality/embedded_c_policy.json`, `quality/warnings/`, and `quality/metrics/policy.json` define the active quality policy. @@ -77,7 +77,9 @@ README that states its ownership, source of truth, and validation expectations. `bringup`, `debug`, and `shell`; each profile is declared in `app/apps//app.mk`. The `debug` application is an RTL debug-transport acceptance image, not a user-facing firmware profile. -- Do not add new dependencies on `crt/inc`, retired `tiny` names, or legacy +- Tiny is a distinct wired MCU product under `rtl/tiny`; `SOC=TINY` selects its + IHP130 profile and AXI4/APB4 integration. +- Do not add new dependencies on `crt/inc`, retired SDK `tiny` names, or legacy `rs_*.h`/`tiny*.h` include paths. - Treat `app/coremark/coremark-main`, `app/fatfs/ff16`, `app/lvgl/lvgl-main`, and managed ports as third-party or managed code. Update them through their diff --git a/Makefile b/Makefile index 9df0f85b..ae98aab7 100644 --- a/Makefile +++ b/Makefile @@ -29,7 +29,7 @@ $(error HAVE_DEBUG has been removed; the Hazard3 Debug Module is always enabled) endif SOC ?= MINI -MINI_MODE ?= PRODUCT +MINI_MODE ?= $(if $(filter MINI,$(SOC)),PRODUCT,NONE) SIMU ?= VCS SYNTH ?= NONE SYNTH_RECIPE ?= balanced @@ -44,7 +44,7 @@ SRAM_SIZE_KIB ?= $(if $(filter ICS55,$(PDK)),128,32) PDK_BEHAV ?= NO HAVE_SVA ?= NO APU_ENABLE_P7 ?= NO -HAVE_HP ?= YES +HAVE_HP ?= $(if $(filter TINY,$(SOC)),NO,YES) HP_CONFIG ?= rv32imafdc_zicbom_max BUILD_RELEASE ?= NO JTAG_IDCODE ?= DEADBEEF @@ -65,7 +65,7 @@ RTL_SIM_TIMEOUT ?= -1 SIM_FIRMWARE_NAME ?= $(FIRMWARE_NAME) SIM_SUCCESS_MARKER ?= SIM_TEST_PASS -RTL_PATH := $(ROOT_PATH)/rtl/mini +RTL_PATH := $(ROOT_PATH)/rtl/$(shell printf '%s' '$(SOC)' | tr '[:upper:]' '[:lower:]') RTL_TOP ?= retrosoc_tb @@ -212,8 +212,8 @@ else FLOW_FILELIST_DIR := $(SIM_BUILD_ROOT)/filelists endif -VALID_SOC := MINI -VALID_MINI_MODE := PRODUCT MPW +VALID_SOC := MINI TINY +VALID_MINI_MODE := PRODUCT MPW NONE VALID_SIMU := VCS VERILATOR IVERILOG VALID_SYNTH := NONE YOSYS VALID_SYNTH_RECIPE := balanced area speed @@ -276,11 +276,18 @@ $(error NPU_P5_ACCEPTANCE and NPU_P6_ACCEPTANCE are mutually exclusive) endif endif +ifeq ($(SOC),MINI) +ifeq ($(MINI_MODE),NONE) +$(error SOC=MINI requires MINI_MODE=PRODUCT or MPW) +endif ifeq ($(MINI_MODE),PRODUCT) ifneq ($(HAVE_HP),YES) $(error MINI_MODE=PRODUCT requires HAVE_HP=YES) endif endif +else +include $(ROOT_PATH)/rtl/tiny/mk/config.mk +endif MISSING_LOCAL_RTL_FILES := $(filter-out $(wildcard $(LOCAL_RTL_FILES)),$(LOCAL_RTL_FILES)) ifneq ($(strip $(MISSING_LOCAL_RTL_FILES)),) @@ -389,7 +396,9 @@ ifeq ($(APU_ENABLE_P7), YES) DEF_LIST += +define+APU_ENABLE_P7 endif -ifeq ($(MINI_MODE), PRODUCT) +ifeq ($(SOC), TINY) + DEF_LIST += +define+RETROSOC_SOC__TINY +else ifeq ($(MINI_MODE), PRODUCT) DEF_LIST += +define+MINI_PRODUCT else DEF_LIST += +define+MINI_MPW @@ -399,8 +408,10 @@ ifeq ($(SYNTH), YOSYS) DEF_LIST += +define+SYNTHESIS endif -include rtl/mini/Makefile +include $(RTL_PATH)/Makefile +ifeq ($(SOC),MINI) include rtl/mini/mk/formal.mk +endif ifeq ($(SIMU),IVERILOG) PERF_LOG ?= $(IVERILOG_BEHV_DIR)/sim.log @@ -410,9 +421,11 @@ endif ifeq ($(SYNTH), YOSYS) include physical/smoke/syn/yosys/yosys.mk +ifeq ($(SOC),MINI) include physical/smoke/syn/yosys/ga2d_block.mk include physical/smoke/syn/yosys/apu_block.mk endif +endif ifeq ($(STA), OPENSTA) include physical/smoke/sta/opensta/opensta.mk @@ -440,7 +453,7 @@ crypto-p0-report: manifest apu-block-filelist apu-block-synth apu-block-sta apu-block-report apu-block-evidence apu-block-clean \ pin-map check-pin-map soc-topology check-soc-topology user-extensions check-user-extensions \ check-clock-reset-domains tech-cell-test rtl-lint check-rtl-lint \ - formal formal-bus formal-rib-adapter formal-rib2apb formal-gpio formal-ws2812 formal-uart formal-i2c formal-timer formal-dvp formal-i2s formal-onchip-ram formal-opipsram formal-dma formal-apu formal-apu-kws formal-apu-p9 formal-gateway-a formal-sdio formal-clean formal-doctor \ + formal formal-bus formal-rib-adapter formal-rib2apb formal-gpio formal-ws2812 formal-uart formal-i2c formal-timer formal-dvp formal-i2s formal-onchip-ram formal-opipsram formal-dma formal-apu formal-apu-kws formal-apu-p9 formal-gateway-a formal-sdio formal-clean formal-doctor \ rtl-style-check-all rtl-readiness-check rtl-readiness-check-all vexii-generate .NOTPARALLEL: setup @@ -571,7 +584,7 @@ config: doctor: @python3 $(ROOT_PATH)/scripts/doctor.py \ --root $(ROOT_PATH) --simu $(SIMU) --synth $(SYNTH) --sta $(STA) \ - --pdk $(PDK) --have-sram-macro $(HAVE_SRAM_MACRO) \ + --pdk $(PDK) --soc $(SOC) --have-sram-macro $(HAVE_SRAM_MACRO) \ --formal $(FORMAL) --lock $(LOCK_FILE) benchmark-report: firmware @@ -593,7 +606,13 @@ hp-performance-check: --lp $(LP_COREMARK_REPORT) --hp $(HP_COREMARK_REPORT) \ --minimum-ratio $(HP_PERF_MIN_RATIO) --output $(META_DIR)/lp-hp-performance.json +ifeq ($(SOC),TINY) +setup: setup-tiny +setup-tiny: + python3 $(ROOT_PATH)/scripts/setup_tiny.py +else setup: setup-mpw setup-vexiiriscv setup-clusterip setup-ip setup-pdk setup-app +endif setup-regression: $(MAKE) CONFIG=configs/ci/ihp130.mk setup @@ -1091,19 +1110,22 @@ commercial-package: $(MPW_VARIANT_DEP) $(FILELIST_STAMP) manifest --variant-root $(VARIANT_ROOT) --output-dir $(VARIANT_ROOT)/commercial/input regress-smoke: - python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite smoke --pdk IHP130 + python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite smoke --pdk IHP130 $(if $(filter TINY,$(SOC)),--soc TINY) regress-rtl: - python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite rtl --pdk IHP130 + python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite rtl --pdk IHP130 $(if $(filter TINY,$(SOC)),--soc TINY) regress-pr: - python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite pr --pdk IHP130 $(if $(filter YES,$(REGRESS_NETSIM_BOOT_ONLY)),--netsim-boot-only) + python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite pr --pdk IHP130 $(if $(filter TINY,$(SOC)),--soc TINY) $(if $(filter YES,$(REGRESS_NETSIM_BOOT_ONLY)),--netsim-boot-only) +ifneq ($(SOC),TINY) python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite pr --pdk GF180 $(if $(filter YES,$(REGRESS_NETSIM_BOOT_ONLY)),--netsim-boot-only) python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite pr --pdk SKY130 $(if $(filter YES,$(REGRESS_NETSIM_BOOT_ONLY)),--netsim-boot-only) python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite pr --pdk ICS55 $(if $(filter YES,$(REGRESS_NETSIM_BOOT_ONLY)),--netsim-boot-only) +endif + regress-nightly: - python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite nightly + python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite nightly $(if $(filter TINY,$(SOC)),--soc TINY) sim-asm: asm - $(MAKE) SIM_FIRMWARE_NAME=$(ASM_FIRMWARE_NAME) sim + $(MAKE) SIM_FIRMWARE_NAME=$(ASM_FIRMWARE_NAME) sim \ No newline at end of file diff --git a/README.md b/README.md index 3620d74c..2890e665 100644 --- a/README.md +++ b/README.md @@ -20,11 +20,13 @@ An open-source RISC-V SoC platform, from SystemVerilog RTL and firmware to repro ## Platform overview -**Mini is the active implementation.** The product configuration combines a -low-power (LP) management hart and a high-performance (HP) application hart. -The separate Mini MPW profile retains the legacy selectable-core integration. -[Tiny, Std, and Pro](docs/soc-family-positioning.md) are product-roadmap targets, -not additional supported build profiles. +**Mini and Tiny have independent integration flows.** Mini combines a +low-power Hazard3 management hart and a VexiiRiscv application hart; its separate +MPW profile retains selectable-core compatibility. [Tiny](docs/ip/tiny-soc.md) +is a wired MCU with one Hazard3 RV32IMC hart, 128 KiB SRAM, AXI4/APB4 and no +wireless IP. Tiny first targets IHP130; [Std and Pro](docs/soc-family-positioning.md) +remain roadmap products. Qualification is determined by each profile's retained +verification and physical reports. | Area | Mini platform | | --- | --- | @@ -96,6 +98,20 @@ Keep `firmware sim` in the same Make invocation so both use one build variant. Outputs live under `build/--/`. Check the flow logs and result JSON; UART startup text alone is not a pass. +### Build and simulate Tiny + +```sh +make CONFIG=configs/ci/ihp130-tiny.mk setup +make CONFIG=configs/ci/ihp130-tiny.mk doctor +make CONFIG=configs/ci/ihp130-tiny.mk firmware sim +make CONFIG=configs/ci/ihp130-tiny.mk SIMU=IVERILOG firmware sim +``` + +Tiny uses its own source lists and product maps under `rtl/tiny`. Its firmware +loads into on-chip SRAM without PSRAM/SDRAM initialization. The first release +uses a fixed 24 MHz clock and disables atomic instructions. Wireless, external +RAM, HP cores, accelerators and advanced low-power modes remain deferred. + ## Configurations and common flows Start from a [committed profile](configs/README.md). These are selected entry @@ -103,6 +119,7 @@ points, not the complete profile or application inventory. | Profile | Purpose | | --- | --- | +| [IHP130 Tiny](configs/ci/ihp130-tiny.mk) | Single-hart wired MCU, 128 KiB SRAM, pin-level Flash boot and automated acceptance. | | [IHP130 Mini](configs/ci/ihp130.mk) | Manual bring-up with 32 KiB macro-backed SRAM. | | [GF180](configs/ci/gf180.mk) / [SKY130](configs/ci/sky130.mk) | Alternative PDK profiles with 32 KiB macro-backed SRAM. | | [ICS55](configs/ci/ics55.mk) | Regression-compatible profile with SRAM and PLL disabled. | diff --git a/app/README.md b/app/README.md index 6ff77451..ccddd0ac 100644 --- a/app/README.md +++ b/app/README.md @@ -162,3 +162,10 @@ make regress-nightly See the [repository README](../README.md) for prerequisites, supported 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 +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. diff --git a/app/apps/bringup/tiny.mk b/app/apps/bringup/tiny.mk new file mode 100644 index 00000000..fd133f66 --- /dev/null +++ b/app/apps/bringup/tiny.mk @@ -0,0 +1,3 @@ +APP_SRCS += $(ROOT_PATH)/app/apps/ci_smoke/tiny.c +APP_CFLAGS += -Os +APP_SRCS += $(ROOT_PATH)/app/apps/ci_smoke/mcu_acceptance.S \ No newline at end of file diff --git a/app/apps/ci_smoke/mcu_acceptance.S b/app/apps/ci_smoke/mcu_acceptance.S new file mode 100644 index 00000000..2da87716 --- /dev/null +++ b/app/apps/ci_smoke/mcu_acceptance.S @@ -0,0 +1,31 @@ +/* Copyright (c) 2026 Yuchi Miao; SPDX-License-Identifier: MulanPSL-2.0 */ + .section .text + .balign 4 + .global rs_mcu_compressed_probe + .type rs_mcu_compressed_probe,@function +rs_mcu_compressed_probe: + .option push + .option rvc + c.li a0, 21 + c.slli a0, 1 + c.jr ra + .option pop + .size rs_mcu_compressed_probe, .-rs_mcu_compressed_probe + + .balign 4 + .global rs_mcu_atomic_probe + .type rs_mcu_atomic_probe,@function +rs_mcu_atomic_probe: + /* lr.w a0,(a0): must trap because the Tiny A extension is disabled. */ + .word 0x1005252f + ret + .size rs_mcu_atomic_probe, .-rs_mcu_atomic_probe + + .balign 4 + .global rs_mcu_unmapped_probe + .type rs_mcu_unmapped_probe,@function +rs_mcu_unmapped_probe: + li a0, 0x38000000 + lw a0, 0(a0) + ret + .size rs_mcu_unmapped_probe, .-rs_mcu_unmapped_probe diff --git a/app/apps/ci_smoke/mcu_acceptance.h b/app/apps/ci_smoke/mcu_acceptance.h new file mode 100644 index 00000000..3f05d234 --- /dev/null +++ b/app/apps/ci_smoke/mcu_acceptance.h @@ -0,0 +1,10 @@ +#ifndef RETROSOC_MCU_ACCEPTANCE_H +#define RETROSOC_MCU_ACCEPTANCE_H + +#include + +uint32_t rs_mcu_compressed_probe(void); +void rs_mcu_atomic_probe(uint32_t *address); +void rs_mcu_unmapped_probe(void); + +#endif diff --git a/app/apps/ci_smoke/tiny.c b/app/apps/ci_smoke/tiny.c new file mode 100644 index 00000000..a4ebef1e --- /dev/null +++ b/app/apps/ci_smoke/tiny.c @@ -0,0 +1,278 @@ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mcu_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 void rs_mcu_finish(bool pass, uint8_t code) { + printf("Tiny acceptance %s code=%u\n", pass ? "passed" : "failed", code); + rs_sysctrl_write_test_status(pass, code); + /* The terminal status must retain the first accepted full-word write. */ + rs_sysctrl_write_test_status(!pass, UINT8_C(255)); + for (;;) { + } +} + +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; + } + ++rs_mcu_exception_count; + /* system_irq.S saves MEPC in slot 11 of the RV32 trap frame. Both test + * instructions are explicitly 32 bits; normal exceptions do not use this handler. */ + frame[11] += UINT32_C(4); +} + +static void rs_mcu_timer_irq(uintptr_t mcause, uintptr_t stack_pointer) { + (void)mcause; + (void)stack_pointer; + ++rs_mcu_irq_count; + if (rs_timer_interrupt_clear(RS_TIMER_0, RS_TIMER_INTERRUPT_ALL) != RS_OK) { + rs_mcu_handler_failed = true; + } +} + +static bool rs_mcu_dma(void) { + rs_dma_config_t config = { + .kind = RS_DMA_KIND_MM_TO_MM, + .request = RS_DMA_REQUEST_SOFTWARE, + .source = (uintptr_t)rs_mcu_source, + .destination = (uintptr_t)rs_mcu_destination, + .byte_count = UINT32_C(259), + .width = RS_DMA_WIDTH_32, + .source_increment = true, + .destination_increment = true, + .priority = 0U, + .burst_beats = 16U, + }; + + for (uint32_t index = 0U; index < 80U; ++index) { + rs_mcu_source[index] = UINT32_C(0xa519c300) ^ index; + rs_mcu_destination[index] = UINT32_C(0xcccccccc); + } + if ((rs_dma_configure(4U, &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)) { + return false; + } + for (uint32_t index = 0U; index < 64U; ++index) { + if (rs_mcu_source[index] != rs_mcu_destination[index]) { + return false; + } + } + if (rs_mcu_destination[64] != + ((rs_mcu_source[64] & UINT32_C(0x00ffffff)) | UINT32_C(0xcc000000))) { + 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; +} + +static bool rs_mcu_interrupts(void) { + uint32_t timeout = UINT32_C(20000); + + if ((rs_irq_enable_external(RS_SOC_EXT_IRQ_TIMER0, rs_mcu_timer_irq) != RS_OK) || + (rs_timer_interrupt_enable(RS_TIMER_0, RS_TIMER_INTERRUPT_TIMEOUT) != RS_OK) || + (rs_timer_interrupt_test(RS_TIMER_0, RS_TIMER_INTERRUPT_TIMEOUT) != RS_OK)) { + return false; + } + __RV_CSR_SET(CSR_MSTATUS, MSTATUS_MIE); + while ((rs_mcu_irq_count == 0U) && (timeout != 0U)) { + --timeout; + } + __RV_CSR_CLEAR(CSR_MSTATUS, MSTATUS_MIE); + return (rs_mcu_irq_count == 1U) && !rs_mcu_handler_failed && + (rs_irq_disable_external(RS_SOC_EXT_IRQ_TIMER0) == RS_OK); +} + +static bool rs_mcu_io(void) { + const rs_gpio_config_t output = { + .mode = RS_GPIO_MODE_OUTPUT, + .pull = RS_GPIO_PULL_NONE, + .trigger = RS_GPIO_TRIGGER_NONE, + .output_high = false, + }; + const rs_uart_config_t loopback = { + .source_clock_hz = RS_CPU_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, + .loopback_enable = true, + }; + const uint32_t transmit[4] = {UINT32_C(0x54), UINT32_C(0x49), UINT32_C(0x4e), UINT32_C(0x59)}; + uint32_t receive[4] = {0U}; + 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), + .tx_data = &payload, + .tx_length = 1U, + }; + rs_i2c_status_t status; + + if ((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) { + return false; + } + 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; + } + for (uint32_t index = 0U; index < 4U; ++index) { + if (transmit[index] != receive[index]) { + return false; + } + } + 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))) { + 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) { + 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_i2c_get_status(instance, &status) != RS_OK) || + ((status.errors & RS_I2C_ERROR_ADDR_NACK) == 0U)) { + return false; + } + } + return true; +} + +static bool rs_mcu_rtc(void) { + const rs_rtc_config_t config = { + .second_cycles = UINT32_C(4096), + .calibration_ppm = 0, + .interrupt_enable = 0U, + .wake_enable = 0U, + .enable = true, + }; + const rs_rtc_time_t value = {.seconds = UINT64_C(1234), .subsecond = 0U}; + rs_rtc_time_t observed; + + return (rs_rtc_probe() == RS_OK) && (rs_rtc_configure(&config, RS_TIMEOUT_DEFAULT) == RS_OK) && + (rs_rtc_set_time(&value, RS_TIMEOUT_DEFAULT) == RS_OK) && + (rs_rtc_get_time(&observed, RS_TIMEOUT_DEFAULT) == RS_OK) && + (observed.seconds >= value.seconds); +} + +int main(void) { + rs_archinfo_t info; + rs_onchip_sram_info_t memory; + rs_sysctrl_fault_status_t fault; + rs_watchdog_status_t watchdog; + uint64_t before; + uint64_t after; + const rs_watchdog_config_t watchdog_config = { + .prescale_divider = 1U, + .timeout_ticks = UINT32_C(20000), + .window_min_ticks = 0U, + .early_warning_ticks = 0U, + .window_enable = false, + .early_warning_enable = false, + .debug_freeze_enable = true, + }; + + if (rs_uart_init(RS_CPU_CLOCK_HZ, UART_BPS) != RS_OK) { + rs_mcu_finish(false, 1U); + } + printf("Hello retroSoC! Tiny MCU\n"); + if (rs_watchdog_get_status(&watchdog, RS_TIMEOUT_DEFAULT) != RS_OK) { + rs_mcu_finish(false, 2U); + } + if (watchdog.reset_count != 0U) { + rs_mcu_finish(true, 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)) || + (rs_onchip_sram_probe(&memory) != RS_OK) || (memory.memory_bytes != UINT32_C(131072)) || + (memory.data_bytes != 4U) || (memory.bank_count != 32U)) { + rs_mcu_finish(false, 3U); + } + 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_mcu_finish(false, 4U); + } + rs_mcu_atomic_probe(rs_mcu_source); + rs_mcu_unmapped_probe(); + 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)) { + rs_mcu_finish(false, 5U); + } + if (!rs_mcu_dma()) { + rs_mcu_finish(false, 6U); + } + if ((rs_clint_get_time(&before) != RS_OK) || (rs_clint_get_time(&after) != RS_OK) || + (after <= before) || !rs_mcu_interrupts()) { + rs_mcu_finish(false, 7U); + } + if (!rs_mcu_rtc() || (rs_pwm_probe() != RS_OK) || + (rs_sysctrl_set_hp_release(true) != RS_ENOTSUP)) { + rs_mcu_finish(false, 8U); + } + if (!rs_mcu_io()) { + rs_mcu_finish(false, 10U); + } + printf("Tiny CPU/DMA/IRQ/RTC/I/O passed; exercising watchdog reset\n"); + if ((rs_watchdog_configure(&watchdog_config) != RS_OK) || + (rs_watchdog_start(RS_TIMEOUT_DEFAULT) != RS_OK)) { + rs_mcu_finish(false, 9U); + } + for (;;) { + } +} diff --git a/app/apps/ci_smoke/tiny.mk b/app/apps/ci_smoke/tiny.mk new file mode 100644 index 00000000..fd133f66 --- /dev/null +++ b/app/apps/ci_smoke/tiny.mk @@ -0,0 +1,3 @@ +APP_SRCS += $(ROOT_PATH)/app/apps/ci_smoke/tiny.c +APP_CFLAGS += -Os +APP_SRCS += $(ROOT_PATH)/app/apps/ci_smoke/mcu_acceptance.S \ No newline at end of file diff --git a/app/asm/tiny_boot.S b/app/asm/tiny_boot.S new file mode 100644 index 00000000..93accb14 --- /dev/null +++ b/app/asm/tiny_boot.S @@ -0,0 +1,58 @@ +/* Copyright (c) 2026 Yuchi Miao; SPDX-License-Identifier: MulanPSL-2.0 */ +#include "retrosoc/generated/memory_map.h" + +/* Small gate-level acceptance image. Executes through the real XPI pins, + * checks both ends of SRAM and byte strobes, then checks APB/UART/SYSCTRL. + * The full SDK/application acceptance runs separately in both RTL simulators. */ + .section .text + .global _start +_start: + li t0, RS_SOC_SRAM_BASE + li t1, 0x12345678 + sw t1, 0(t0) + lw t2, 0(t0) + bne t1, t2, failed + li t0, RS_SOC_SRAM_END - 3 + sw t1, 0(t0) + li t2, 0xab + sb t2, 1(t0) + lw t2, 0(t0) + li t1, 0x1234ab78 + bne t1, t2, failed + + li s0, RS_SOC_APB4_UART0_BASE + li t0, 26 + sw t0, 0(s0) + li t0, 11 + sw t0, 4(s0) + li t0, 3 + sw t0, 8(s0) + sw t0, 12(s0) + la s1, message +print_loop: + lbu s2, 0(s1) + beqz s2, passed +wait_uart: + lw t0, 24(s0) + andi t0, t0, 32 + bnez t0, wait_uart + sw s2, 16(s0) + addi s1, s1, 1 + j print_loop +passed: + /* Let the serial output drain before setting the terminal verdict. */ + lw t0, 24(s0) + andi t0, t0, 4 + bnez t0, passed + li t0, RS_SOC_APB4_SYSCTRL_BASE + li t1, 0x80000001 + sw t1, RS_SOC_SYSCTRL_TEST_STATUS_OFFSET(t0) +halt: + j halt +failed: + li t0, RS_SOC_APB4_SYSCTRL_BASE + li t1, 0x80000100 + sw t1, RS_SOC_SYSCTRL_TEST_STATUS_OFFSET(t0) + j halt +message: + .asciz "Hello retroSoC! Tiny netlist boot\n" diff --git a/configs/README.md b/configs/README.md index 7691f783..f92cb84e 100644 --- a/configs/README.md +++ b/configs/README.md @@ -52,3 +52,12 @@ macro must select either this mode or a qualified macro timing model. For supported profiles and commands, see the root [README](../README.md) and [agent contract](../AGENTS.md). + +## Tiny MCU + +`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. +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. diff --git a/configs/ci/ihp130-tiny.mk b/configs/ci/ihp130-tiny.mk new file mode 100644 index 00000000..af7ad6d3 --- /dev/null +++ b/configs/ci/ihp130-tiny.mk @@ -0,0 +1,18 @@ +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 := 24000000 +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 \ No newline at end of file diff --git a/crt/README.md b/crt/README.md index d41d7d96..2bf642d6 100644 --- a/crt/README.md +++ b/crt/README.md @@ -160,7 +160,7 @@ on it. ## Build Integration The central software build assembles the runtime sources in -[`../rtl/mini/mk/software.mk`](../rtl/mini/mk/software.mk). It selects the +[`../rtl/mk/software.mk`](../rtl/mk/software.mk). It selects the RISC-V ISA, optional CSR interrupt support, linker layout, and the application profile. Applications add their own sources after the common runtime is selected. @@ -219,3 +219,17 @@ hardware validation. For the complete firmware, simulation, synthesis, and timing flow, see the [repository README](../README.md). + +## Product composition + +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 +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. + +`` 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 +locked watchdog register ABI. diff --git a/crt/arch/riscv/startup.S b/crt/arch/riscv/startup.S index 8f580d18..dc0789c5 100644 --- a/crt/arch/riscv/startup.S +++ b/crt/arch/riscv/startup.S @@ -17,6 +17,7 @@ _start: /* Direct Mode: All exceptions set pc to BASE */ csrc CSR_MTVEC, 0x3 #endif +#ifndef RS_SOC_TINY # wait psram reset done la a0, _flash_wait_start la a1, 2000 @@ -25,6 +26,8 @@ wait_psram_reset: addi a0, a0, 4 blt a0, a1, wait_psram_reset +#endif + # ===== Startup Stage 1 ===== # init the regfile # zero-initialize register file @@ -68,6 +71,7 @@ wait_psram_reset: # when code LMA is different with VMA # test: using psram before loading +#ifndef RS_SOC_TINY # Define the addresses .equ GPIO_ALT_ENABLE, RS_SOC_APB4_GPIO_ADMIN_BASE + 0x34 .equ GPIO_ALT_SELECT, RS_SOC_APB4_GPIO_ADMIN_BASE + 0x38 @@ -98,6 +102,8 @@ PSRAM_READY_WAIT: andi t1, t1, PSRAM_READY beqz t1, PSRAM_READY_WAIT +#endif + app_loader_start: la a0, _ram_lma la a1, _ram_vma diff --git a/crt/include/retrosoc/hal/dma_regs.h b/crt/include/retrosoc/hal/dma_regs.h index 85d7e855..ccf87f06 100644 --- a/crt/include/retrosoc/hal/dma_regs.h +++ b/crt/include/retrosoc/hal/dma_regs.h @@ -77,11 +77,15 @@ #define RS_DMA_ERROR_READ UINT32_C(0x00000040) #define RS_DMA_ERROR_STREAM_LAST UINT32_C(0x00000100) -#define RS_DMA_CHANNEL_COUNT 8U -#define RS_DMA_CHANNEL_HP 6U -#define RS_DMA_CHANNEL_RESERVED 7U -#define RS_DMA_MAX_BURST_BEATS 16U -#define RS_DMA_DATA_WIDTH_BITS 32U +#ifdef RS_SOC_TINY +#define RS_DMA_CHANNEL_COUNT 4U +#else +#define RS_DMA_CHANNEL_COUNT 8U +#endif +#define RS_DMA_CHANNEL_HP 6U +#define RS_DMA_CHANNEL_RESERVED 7U +#define RS_DMA_MAX_BURST_BEATS 16U +#define RS_DMA_DATA_WIDTH_BITS 32U #define RS_DMA_CHANNEL_OFFSET(channel, offset) \ (RS_DMA_CH_BASE + ((uint32_t)(channel)*RS_DMA_CH_STRIDE) + (offset)) diff --git a/crt/include/retrosoc/hal/watchdog.h b/crt/include/retrosoc/hal/watchdog.h index de5b28aa..caa6c7b4 100644 --- a/crt/include/retrosoc/hal/watchdog.h +++ b/crt/include/retrosoc/hal/watchdog.h @@ -1,5 +1,18 @@ #ifndef RETROSOC_WDG_H #define RETROSOC_WDG_H +#include + +#include + +typedef wdg_config_t rs_watchdog_config_t; +typedef wdg_snapshot_t rs_watchdog_status_t; + +rs_status_t rs_watchdog_configure(const rs_watchdog_config_t *config); +rs_status_t rs_watchdog_start(rs_timeout_t timeout); +rs_status_t rs_watchdog_service(rs_timeout_t timeout); +rs_status_t rs_watchdog_get_status(rs_watchdog_status_t *status, rs_timeout_t timeout); +rs_status_t rs_watchdog_clear_reset_cause(rs_timeout_t timeout); + void ip_wdg_test(int argc, char **argv); #endif diff --git a/crt/src/core/archinfo.c b/crt/src/core/archinfo.c index fdf385ff..cab76e0b 100644 --- a/crt/src/core/archinfo.c +++ b/crt/src/core/archinfo.c @@ -39,7 +39,20 @@ rs_status_t rs_archinfo_validate_build(const rs_archinfo_t *info) { .config_id = ARCHINFO_INTEGRATION_CONFIG_ID, }; +#ifdef RS_SOC_TINY + if ((info == NULL) || (info->soc_id != RS_SOC_ID) || + (info->component_id != ARCHINFO_COMPONENT_ID_VALUE) || + (info->ip_version != ARCHINFO_IP_VERSION_VALUE) || + (((info->capability & ARCHINFO_CAPABILITY_ABI_MASK) >> ARCHINFO_CAPABILITY_ABI_SHIFT) != + ARCHINFO_ABI_VERSION) || + (expectation.check_build_id && (info->build_id != expectation.build_id)) || + (info->config_id != expectation.config_id)) { + return RS_EFORMAT; + } + return RS_OK; +#else return rs_archinfo_status(archinfo_validate(info, &expectation)); +#endif } rs_status_t rs_archinfo_read_device_id(uint32_t device_id[4]) { diff --git a/crt/src/hal/dma_math.c b/crt/src/hal/dma_math.c index 6c74e12a..99c35ca6 100644 --- a/crt/src/hal/dma_math.c +++ b/crt/src/hal/dma_math.c @@ -18,6 +18,11 @@ 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) { + return false; + } +#endif switch (config->kind) { case RS_DMA_KIND_MM_TO_MM: return (config->request == RS_DMA_REQUEST_SOFTWARE) || @@ -91,6 +96,11 @@ rs_status_t rs_dma_tcd_validate(uint32_t channel, const rs_dma_tcd_t *tcd) { kind = (tcd->control >> RS_DMA_TCD_KIND_SHIFT) & UINT32_C(0x7); 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) { + return RS_ENOTSUP; + } +#endif if ((kind > (uint32_t)RS_DMA_KIND_STREAM_TO_MM) || (request > (uint32_t)RS_DMA_REQUEST_CRYPTO_OUT) || (burst > RS_DMA_MAX_BURST_BEATS) || ((tcd->source & UINT32_C(0x3)) != UINT32_C(0)) || diff --git a/crt/src/hal/i2c.c b/crt/src/hal/i2c.c index f8acab29..48c59260 100644 --- a/crt/src/hal/i2c.c +++ b/crt/src/hal/i2c.c @@ -454,7 +454,7 @@ rs_status_t rs_i2c_write_dma(rs_i2c_bus_t bus, uint16_t address, bool ten_bit_ad const uint32_t channel = (bus == RS_I2C_BUS_0) ? RS_DMA_CHANNEL_I2C0 : RS_DMA_CHANNEL_I2C1; const rs_dma_request_t request = (bus == RS_I2C_BUS_0) ? RS_DMA_REQUEST_I2C0_TX : RS_DMA_REQUEST_I2C1_TX; - rs_dma_config_t dma_config; + rs_dma_config_t dma_config = {0}; if (!rs_i2c_bus_valid(bus) || (data == NULL) || (workspace == NULL) || (length == 0U) || (length > RS_I2C_DMA_MAX_BYTES)) { @@ -499,7 +499,7 @@ rs_status_t rs_i2c_read_dma(rs_i2c_bus_t bus, uint16_t address, bool ten_bit_add const uint32_t channel = (bus == RS_I2C_BUS_0) ? RS_DMA_CHANNEL_I2C0 : RS_DMA_CHANNEL_I2C1; const rs_dma_request_t request = (bus == RS_I2C_BUS_0) ? RS_DMA_REQUEST_I2C0_RX : RS_DMA_REQUEST_I2C1_RX; - rs_dma_config_t dma_config; + rs_dma_config_t dma_config = {0}; if (!rs_i2c_bus_valid(bus) || (data == NULL) || (workspace == NULL) || (length == 0U) || (length > RS_I2C_DMA_MAX_BYTES)) { diff --git a/crt/src/hal/sysctrl.c b/crt/src/hal/sysctrl.c index 5849ad07..7d150cfd 100644 --- a/crt/src/hal/sysctrl.c +++ b/crt/src/hal/sysctrl.c @@ -86,6 +86,12 @@ static rs_status_t rs_sysctrl_perf_offsets(rs_sysctrl_perf_counter_t counter, ui return RS_EINVAL; } +#ifdef RS_SOC_TINY + if ((counter != RS_SYSCTRL_PERF_MGMT_WAIT) && (counter != RS_SYSCTRL_PERF_DMA_WAIT)) { + return RS_ENOTSUP; + } +#endif + switch (counter) { case RS_SYSCTRL_PERF_MGMT_WAIT: *low_offset = RS_SYSCTRL_PERF_MGMT_WAIT_LO_OFFSET; @@ -215,6 +221,9 @@ rs_status_t rs_sysctrl_clear_user_core_config_error(void) { } rs_status_t rs_sysctrl_get_pll_config(uint8_t *selection) { +#ifdef RS_SOC_TINY + return RS_ENOTSUP; +#endif if (selection == NULL) { return RS_EINVAL; } @@ -224,6 +233,9 @@ rs_status_t rs_sysctrl_get_pll_config(uint8_t *selection) { } rs_status_t rs_sysctrl_set_pll_config(uint8_t selection) { +#ifdef RS_SOC_TINY + return RS_ENOTSUP; +#endif if (((uint32_t)selection & ~RS_SYSCTRL_PLL_SELECTION_MASK) != 0U) { return RS_EINVAL; } @@ -232,16 +244,25 @@ rs_status_t rs_sysctrl_set_pll_config(uint8_t selection) { } rs_status_t rs_sysctrl_apply_pll_config(void) { +#ifdef RS_SOC_TINY + return RS_ENOTSUP; +#endif *rs_sysctrl_register(RS_SYSCTRL_PLL_CMD_OFFSET) = RS_SYSCTRL_PLL_CMD_APPLY; return RS_OK; } rs_status_t rs_sysctrl_clear_pll_error(void) { +#ifdef RS_SOC_TINY + return RS_ENOTSUP; +#endif *rs_sysctrl_register(RS_SYSCTRL_PLL_CMD_OFFSET) = RS_SYSCTRL_PLL_CMD_CLEAR_ERROR; return RS_OK; } rs_status_t rs_sysctrl_get_pll_status(rs_sysctrl_pll_status_t *status) { +#ifdef RS_SOC_TINY + return RS_ENOTSUP; +#endif uint32_t value; if (status == NULL) { @@ -282,6 +303,9 @@ rs_status_t rs_sysctrl_clear_fault(void) { } rs_status_t rs_sysctrl_set_hp_release(bool release) { +#ifdef RS_SOC_TINY + return RS_ENOTSUP; +#endif rs_sysctrl_hp_status_t status; if ((rs_sysctrl_get_hp_status(&status) != RS_OK) || !status.present) { @@ -295,6 +319,9 @@ rs_status_t rs_sysctrl_set_hp_release(bool release) { } rs_status_t rs_sysctrl_get_hp_status(rs_sysctrl_hp_status_t *status) { +#ifdef RS_SOC_TINY + return RS_ENOTSUP; +#endif uint32_t value; if (status == NULL) { @@ -311,6 +338,9 @@ rs_status_t rs_sysctrl_get_hp_status(rs_sysctrl_hp_status_t *status) { } rs_status_t rs_sysctrl_select_hp_debug(bool select_hp) { +#ifdef RS_SOC_TINY + return RS_ENOTSUP; +#endif rs_sysctrl_hp_status_t status; uint32_t value; diff --git a/crt/src/hal/uart.c b/crt/src/hal/uart.c index 909776d6..88f92081 100644 --- a/crt/src/hal/uart.c +++ b/crt/src/hal/uart.c @@ -279,7 +279,7 @@ rs_status_t rs_uart_irq_test(uint32_t mask) { } rs_status_t rs_uart_write_dma(const uint32_t *words, size_t count, rs_timeout_t timeout) { - rs_dma_config_t config; + rs_dma_config_t config = {0}; size_t index; rs_status_t status; @@ -314,7 +314,7 @@ rs_status_t rs_uart_write_dma(const uint32_t *words, size_t count, rs_timeout_t } rs_status_t rs_uart_read_dma(uint32_t *words, size_t count, rs_timeout_t timeout) { - rs_dma_config_t config; + rs_dma_config_t config = {0}; rs_status_t status; if ((words == NULL) || (count == 0U) || (count > (UINT32_MAX / sizeof(*words)))) { diff --git a/crt/src/hal/watchdog.c b/crt/src/hal/watchdog.c index f93e90dc..6ae4ed3f 100644 --- a/crt/src/hal/watchdog.c +++ b/crt/src/hal/watchdog.c @@ -7,6 +7,41 @@ #define RS_WDG_COMMAND_TIMEOUT UINT32_C(1024) +static rs_status_t rs_watchdog_status(wdg_status_t status) { + switch (status) { + case WDG_STATUS_OK: + return RS_OK; + case WDG_STATUS_INVALID_ARGUMENT: + return RS_EINVAL; + case WDG_STATUS_TIMEOUT: + return RS_ETIMEOUT; + case WDG_STATUS_INCOMPATIBLE: + return RS_EFORMAT; + default: + return RS_EIO; + } +} + +rs_status_t rs_watchdog_configure(const rs_watchdog_config_t *config) { + return rs_watchdog_status(wdg_configure((uintptr_t)RS_SOC_APB4_WDG_BASE, config)); +} + +rs_status_t rs_watchdog_start(rs_timeout_t timeout) { + return rs_watchdog_status(wdg_start((uintptr_t)RS_SOC_APB4_WDG_BASE, timeout)); +} + +rs_status_t rs_watchdog_service(rs_timeout_t timeout) { + return rs_watchdog_status(wdg_service((uintptr_t)RS_SOC_APB4_WDG_BASE, timeout)); +} + +rs_status_t rs_watchdog_get_status(rs_watchdog_status_t *status, rs_timeout_t timeout) { + return rs_watchdog_status(wdg_snapshot((uintptr_t)RS_SOC_APB4_WDG_BASE, status, timeout)); +} + +rs_status_t rs_watchdog_clear_reset_cause(rs_timeout_t timeout) { + return rs_watchdog_status(wdg_clear_reset_cause((uintptr_t)RS_SOC_APB4_WDG_BASE, timeout)); +} + void ip_wdg_test(int argc, char **argv) { static const wdg_config_t config = { .prescale_divider = 1U, diff --git a/docs/README.md b/docs/README.md index 47a6a022..a9739779 100644 --- a/docs/README.md +++ b/docs/README.md @@ -3,6 +3,8 @@ This directory contains repository-level engineering policy that supplements the root README and subsystem guides. +- [Tiny MCU](ip/tiny-soc.md) defines the independent wired Tiny product, AXI4/APB4, SRAM-only startup and IHP130 acceptance. + - [Contributing](../CONTRIBUTING.md) describes community discussion, development, review, validation evidence, and maintainer responsibilities. - [git-workflow.md](git-workflow.md) defines branch and commit conventions, diff --git a/docs/engineering.md b/docs/engineering.md index 91d21250..d0db8885 100644 --- a/docs/engineering.md +++ b/docs/engineering.md @@ -4,6 +4,7 @@ | Tier | Profile | Automated coverage | | --- | --- | --- | +| Pull request | `configs/ci/ihp130-tiny.mk` | Tiny firmware, Verilator/Icarus, lint; local Yosys, netlist and OpenSTA | | Smoke | `configs/ci/ihp130.mk` | strict Verilator RTL lint, firmware, Verilator SVA compilation, Icarus assembly self-test | | Pull request | `configs/ci/ihp130.mk` | strict Verilator RTL lint, firmware, Verilator, Icarus | | Pull request | `configs/ci/ihp130-debug.mk` | Verilator remote-bitbang JTAG DTM, Debug Module, OpenOCD, and GDB acceptance | diff --git a/docs/ip/README.md b/docs/ip/README.md index 8af7ebef..b204f619 100644 --- a/docs/ip/README.md +++ b/docs/ip/README.md @@ -4,6 +4,8 @@ This directory owns architecture, register ABI, software contract, and verification documentation for self-owned retroSoC peripheral IP and platform extension interfaces. +- [Tiny MCU](tiny-soc.md) defines the independent wired Tiny product, AXI4/APB4, SRAM-only startup and IHP130 acceptance. + - [timer.md](timer.md) defines the dual APB4 general timer. - [sysctrl.md](sysctrl.md) defines the APB4 SystemCtrl register ABI, control-plane contracts, HAL, and verification. - [resource-controller.md](resource-controller.md) defines root-managed resource ownership, IRQ routing, and cache-maintenance handoff. diff --git a/docs/ip/tiny-soc-verification.md b/docs/ip/tiny-soc-verification.md new file mode 100644 index 00000000..bb5447f9 --- /dev/null +++ b/docs/ip/tiny-soc-verification.md @@ -0,0 +1,146 @@ +# Tiny MCU Verification Record + +This record accompanies the [frozen Tiny contract](tiny-soc.md). It describes +local implementation evidence from 2026-09-25, not a release or silicon signoff. +The working tree is uncommitted and the readiness status remains `prototype`. + +## Configuration and artifact roots + +The selected profile is `configs/ci/ihp130-tiny.mk`: IHP130, 128 KiB SRAM, +24 MHz, RV32IM firmware on an RV32IMC hart with A disabled. Runs below used +`BUILD_TIMESTAMP=2026-09-25-12-00`: + +- Default `bringup`: `build/ihp130-tiny-2026-09-25-12-00-d6009644a134/`. +- `APP=ci_smoke`: `build/ihp130-tiny-2026-09-25-12-00-044d45791ef7/`. +- `APP=ci_smoke HAVE_SVA=YES`: + `build/ihp130-tiny-2026-09-25-12-00-6874f2ffdf11/`. + +Configuration digests identify settings and the dependency lock, not a clean +source revision. A release requires rerunning from its reviewed commit. + +## Functional and quality checks + +| Check | Evidence/boundary | +| --- | --- | +| Tiny maps and source isolation | Generated address, IRQ, pad and clock/reset inputs validate; source closure excludes Mini product RTL, RIB/RIBP and HP generation. | +| AXI fabric, both simulators | Directed tests cover competing CPU/DMA requests, W-before-AW, delayed W, response backpressure, ID/RLAST, 16-beat bursts, 4 KiB crossing, alignment, reserved regions and continued operation after errors. | +| Tiny SYSCTRL, both simulators | APB tests cover unsupported controls, partial terminal writes, sticky first status, counter snapshots, first-fault retention, W1C, RTC wake and reset. | +| Verilator full firmware | `SIM_TEST_PASS`; the CI composition also passes with SVA enabled. Pin-level NOR, compressed instructions, disabled atomics, load faults, SRAM discovery, memory/UART DMA, external timer IRQ, CLINT time, RTC, GPIO, UART1 loopback, I2C NACK and watchdog reboot are exercised. | +| Icarus full firmware | Final expanded CI firmware passes at 3,705,751 cycles, matching the SVA-enabled Verilator run. | +| Icarus synthesized boot | `netsim-boot` passes at 29,362 cycles using the compact assembly image. Covers pin-level NOR, SRAM first/last words, byte writes, UART and terminal status; does not establish full C-firmware gate-level coverage. | +| SDK gates | C formatting, embedded-C policy and host tests pass, including Tiny channel/endpoint rejection compiled from the real DMA validator. No new Required-rule MISRA deviation is recorded; these partial checks do not certify MISRA conformance. | +| Focused Python tests | 112 tests passed across Tiny, address/pad generation and build/regression tooling. Shared debug-reset and SRAM/DMA register-parity checks also pass. After fixing parameterization/publication compatibility and preparing the existing MPW/device-model inputs, all 54 non-reference failures selected for rerun pass. | +| RTL policy | Changed RTL formatting, full owned RTL style audit and readiness metadata pass. Global formatting limitations are listed below. | +| Reproducibility | Dependency lock validation, Ruff, PR/nightly dry-runs and `git diff --check` pass. Tiny source packaging exports the Tiny top, actual filelist and its core timing SDC. | + +The full-firmware verdict is authoritative only when both the tool result and +`result-sim-check.json` pass. UART text alone is not sufficient. Assembly netlist +acceptance uses the same strict terminal marker policy. + +## Synthesis and timing + +For the CI composition, Yosys completed with the IHP130 technology libraries +and 32 `tc_sram_1024x32` banks. The `balanced` report records 207,739 cells and +9,798,326.725198 square micrometers of library area. The area includes the +instantiated memory/pad hierarchy and is not a placed die-area estimate. + +OpenSTA completed with the selected slow library corner and the 41.666666667 ns +system clock. Reported setup WNS is **-455.18 ns**, setup TNS is +**-13,049,600 ns**, and hold WNS/TNS are zero. The worst path starts at the +synchronized system-reset register and reaches peripheral FIFO storage through +the high-fanout reset distribution. This is an open physical/timing issue; +24 MHz is a functional target, not a timing-qualified operating point. + +Reports reside under the CI variant's `syn/yosys/rpt/` and `sta/opensta/`. +The metric collector understands the Tiny top and timestamp-prefixed Yosys JSON +reports. Metrics remain in `observe` mode; no warning or metric baseline has +been edited to accept this feature. + +## Commands and remaining coverage + +The build/verification commands were run with the profile and timestamp above: + +```sh +make CONFIG=configs/ci/ihp130-tiny.mk doctor firmware sim +make CONFIG=configs/ci/ihp130-tiny.mk APP=ci_smoke HAVE_SVA=YES firmware sim +make CONFIG=configs/ci/ihp130-tiny.mk APP=ci_smoke SIMU=IVERILOG firmware sim +make CONFIG=configs/ci/ihp130-tiny.mk APP=ci_smoke SYNTH=YOSYS synth +make CONFIG=configs/ci/ihp130-tiny.mk APP=ci_smoke SIMU=IVERILOG netsim-boot +make CONFIG=configs/ci/ihp130-tiny.mk APP=ci_smoke STA=OPENSTA sta +make CONFIG=configs/ci/ihp130-tiny.mk metrics package +make sw-format-check sw-policy-check sw-host-test rtl-style-check-all rtl-readiness-check +python3 -m pytest -q tests/test_tiny.py tests/test_memory_map.py tests/test_pin_map.py tests/test_script_tools.py +python3 scripts/regress.py --root . --suite pr --pdk IHP130 --netsim-boot-only +``` + +The combined IHP130 regression stops in its Mini phase because the locked +VexiiRiscv checkout is absent. Tiny can be selected independently with +`--soc TINY`, or `make CONFIG=configs/ci/ihp130-tiny.mk regress-pr`. +The initial full Pytest run completed with 1,234 passed, 26 failed, 19 skipped +and 71 errors. Parameterization/format-sensitive expectations and the Mini +publication's constant/cast parsing were corrected; the affected cases were +rerun successfully. MPW/device-model inputs and a writable test ccache resolved +additional environment failures. The remaining 16 cases require the absent +locked FLAC corpus, MLPerf Tiny models or TensorFlow oracle sources. These are +not Tiny setup dependencies. The full suite was not rerun after the focused +corrections; it is not reported as a clean full-suite pass. + +The initial run, focused results, combined-regression failure and remaining +reference-input case list are retained under the CI variant's +`meta/validation/`. +Global RTL/Make formatting checks encounter existing APU/accelerator files +outside this change. Changed-file formatting is checked independently. + +Remaining qualification includes reset-tree/timing closure, placement/routing, +PVT/MMMC, CDC/RDC signoff, silicon and power measurements, full C netlist +acceptance, a Tiny-specific external OpenOCD session, positive external I2C +slave transactions and comprehensive pad-level PWM/RTC coverage. Wireless, +retention, secure boot and other PDKs remain explicit deferred features. + +## Shared-path migration follow-up + +Mini now references the canonical shared RTL, `scripts/rtl` helpers and +`rtl/mk/software.mk` directly. The 21 Mini compatibility links and seven Tiny +helper links have been removed. Filelists, simulator/formal/synthesis rules, +Python imports, tests, publication tooling and ownership documents use the new +paths. No Git file-type changes remain. Historical warning baselines are +unchanged. + +The resolved Mini `commonip.fl`, `ip.fl` and `top.fl` retain the same 299 +unique sources in the same order. `netlist_support.fl` retains its two sources; +`inc.fl` adds `rtl/ip/core`. All relocated shared RTL content hashes match the +pre-migration snapshot. This follow-up changes paths, not RTL behavior or the +public hardware/software interface. + +Validation after removing the links: + +- 245 focused tests passed: 144 build/generator/topology/style/Tiny tests, + 96 publication/LibreLane tests and five debug-reset/SRAM simulations. + Full-suite collection succeeds with 1,356 tests; the full suite was not + rerun because of the reference-input gaps documented above. +- Mini `configs/ci/ihp130.mk` address/pad/topology/clock-reset checks and firmware + build pass. Its Verilator compile reaches HP generation and stops because + the locked VexiiRiscv checkout is missing; full Mini simulation and the + combined regression remain unverified. +- Tiny `configs/ci/ihp130-tiny.mk`, `APP=ci_smoke HAVE_SVA=YES`, firmware and + Verilator simulation pass again at 3,705,751 cycles. Full Icarus, synthesis + and STA were not repeated for this path-only follow-up. +- Eleven Python CLI entrypoints work outside the repository directory. + Mini SRAM formal filelist generation, `sv2v` conversion, filelist combination + and dependency generation pass using the canonical shared paths, including + the relocated SRAM header. Formal proofs were not rerun. +- Ruff, owned RTL style, readiness metadata, 251 local Markdown links and + `git diff --check` pass. The Make formatter still reports existing EOF and + formal-command indentation differences in APU/GA2D/formal fragments; those + unrelated differences were preserved. + +Run logs, exact commands and path snapshots are retained under +`build/ihp130-2026-09-25-12-00-9903b3d112b7/meta/validation/shared-path-migration/`. +No C register/API change or additional MISRA deviation belongs to this +follow-up; the SDK gates from the implementation record above were not rerun. + +## Next review + +```text +Use $retrosoc-mini-feature-review in review mode for feature tiny-soc. Review the current worktree diff against docs/ip/tiny-soc.md and docs/ip/tiny-soc-verification.md. Verify Tiny isolation, default Mini compatibility, AXI/APB/error/reset behavior and the SDK ABI. Treat the observed reset-fanout timing deficit and missing APU/NPU/VexiiRiscv inputs as explicit qualification gaps; do not claim timing closure or alter warning/metric baselines. +``` diff --git a/docs/ip/tiny-soc.md b/docs/ip/tiny-soc.md new file mode 100644 index 00000000..8bf7dd1f --- /dev/null +++ b/docs/ip/tiny-soc.md @@ -0,0 +1,193 @@ +# retroSoC Tiny MCU Contract + +## Purpose and research boundary + +This contract freezes the approved Tiny wired-MCU first release. Tiny owns its +integration in `rtl/tiny`; Mini remains a separate product. The committed +`configs/ci/ihp130-tiny.mk` profile is the reproducible implementation boundary. +The design was approved on 2026-09-25. Source presence does not establish +verification, timing closure, silicon qualification, or low-power performance. + +The [RP2350](https://www.raspberrypi.com/products/rp2350/) is a Hazard3-based MCU +reference for software, SRAM, and deterministic I/O. Its dual-core, security, +USB, and PIO features are not Tiny requirements. The +[CAST SRAM controller](https://www.cast-inc.com/peripherals/memory-controllers/sram-ctrl) +illustrates native AXI synchronous SRAM and documented verification delivery; +no commercial implementation or qualification is reused. + +## Requirements and non-goals + +- TINY-001: one Hazard3 hart, RV32IMC with A disabled, mandatory debug support; + existing RV32IM firmware remains the default compiler target. +- TINY-002: native 32-bit AXI4 data and APB4 control. The CPU's native AHB-Lite + terminates at a direct AXI adapter. No RIB/RIBP source or interface is part of + Tiny's compilation or elaboration closure. +- TINY-003: 128 KiB technology-backed SRAM, XPI NOR boot, no external RAM + dependency. Reset vector is `0x00000000`; SRAM begins at `0x30000000`. +- TINY-004: 32 GPIO, two UARTs, two I2C controllers, two general timers, + CLINT, four DMA channels, PWM, RTC, watchdog, SYSCTRL and architecture info. +- TINY-005: IHP130, 24 MHz system clock, no PLL, 1 MHz CLINT timebase. CPU, + AXI, APB, RTC and watchdog share the system clock. +- TINY-006: Tiny must build independently of MPW, HP CPU generation and + multimedia inputs. Shared SDK APIs retain the `rs_` namespace. +- TINY-007: no wireless IP, radio-specific host integration, or wireless stack. + +Deferred: RV32 A atomics, RTOS ports, authenticated boot, retention/power gating, +independent sleep clock, 256–512 KiB SRAM, USB, SDIO, standalone general SPI, +I2S, CAN, ADC, multimedia accelerators and other PDK qualification. XPI retains +the existing four-chip-select interface; initial pin-level acceptance uses NOR +on NSS0. The unpopulated chip selects are not qualified device configurations. + +## Selected architecture and interfaces + +`retrosoc_tiny` integrates the CPU, two-master fabric, SRAM, XPI and one APB +island. `retrosoc_tiny_asic` owns technology pads; `retrosoc_tiny_tb` owns device +models and terminal verdicts. Product maps, filelists and bindings belong to +Tiny; reusable core/debug, memory and bus utilities belong to shared RTL IP. + +AXI has 32-bit addresses/data and one-bit ID/USER. CPU and DMA share one +globally active read or write transaction. Round-robin ownership changes only +after B or RLAST handshake; accepted W traffic cannot change owners. Backpressure +must preserve VALID payloads. Memory accepts naturally aligned 1/2/4-byte INCR +transactions of 1–16 beats within a target and 4 KiB page. MMIO accepts one-beat +FIXED/INCR accesses. Unsupported size/burst/lock/alignment or crossing returns +SLVERR; absent regions return DECERR. Rejected reads return the advertised +number of response beats; rejected writes drain their accepted data before B. + +APB uses full setup/access phases, PSTRB and PSLVERR. Registers retain their +existing IP offset, access and reset contracts. A missing peripheral has no +successful decode. GPIO alternate functions retain the selected Mini mappings; +unsupported alternate functions drive neither pads nor peripheral requests. +UART TX/RX and XPI have dedicated pins. + +## DMA and interrupt contract + +DMA retains the V2 manual register ABI, direct and TCD modes, 32-bit transfers, +up-to-16-beat memory bursts and existing completion/error/W1C/abort semantics. +Four channels are available: 0 UART0, 1 I2C0, 2 I2C1, 3 bulk/XPI. UART1 uses +PIO/IRQ. Stream endpoints and requests for omitted devices are unsupported and +must fail validation rather than wait indefinitely. Capability discovery and +SDK range checks must agree with hardware. + +CLINT software/timer occupy core bits 0/1. Reused external sources keep Mini +core-bit numbering: UART0 2, timer0 3, timer1 4, I2C0 7, XPI 9, PWM 11, +RTC 13, watchdog 14, GPIO 18, I2C1 19, DMA 20, UART1 26. The vector has 32 +bits (30 Hazard3 external inputs); all omitted sources are zero. + +## Register and software ABI + +Tiny maps reuse the addresses of implemented Mini peripherals, Flash alias and +XPI aperture. Generated outputs contain address/IRQ/capability metadata and +linker regions, not new generated IP register definitions. SVH/C register pairs +remain handwritten and checked for parity. Hardware identity must describe Tiny, +one MCU hart, 128 KiB SRAM and actual capabilities. + +Tiny SYSCTRL preserves fault capture, performance enable/clear and TEST_STATUS +at the existing offsets. The first full-word TEST_STATUS write with bit 31 set +is sticky until system reset; bit 0 is pass and bits 15:8 are the result code. +Unsupported Mini lifecycle/clock controls fail safely; they do not enable HP or +user-core behavior. Unavailable IP drivers are omitted from the Tiny firmware composition. +Unsupported SYSCTRL lifecycle/PLL operations and DMA channel/endpoint requests +return an error before MMIO. GPIO alternate modes follow the Tiny pin map. + +The `bringup` and `ci_smoke` applications select product-specific entrypoints. +Startup skips PSRAM setup on Tiny and uses `ld2_all_sram`: code/data copy from +Flash into SRAM, with SRAM BSS and stack. IRQ/CSR support is enabled. Public +headers remain under ``; retired `tiny*.h` paths are not restored. + +## Clock, reset and lifecycle + +External reset is asynchronously asserted and synchronously released. Watchdog +reset restarts the system. Debug hart reset waits for the CPU AXI adapter to +be idle; it must not abandon an accepted transaction. JTAG and asynchronous +external inputs retain the approved Common synchronizer/handshake boundaries. +There is no cross-domain system bus or dynamic clock switching in this release. +RTC/WDG run at the configured system frequency and provide no independent +deep-sleep guarantee. SRAM contents are not initialized by reset. + +## Errors, observability and security + +Decode/protocol faults complete with AXI errors and feed sticky SYSCTRL fault +information. DMA records errors and supports bounded software waits and abort. +Counters and terminal-test status permit firmware and simulation diagnosis. +Watchdog provides whole-system recovery from a nonresponsive target; this +release makes no unrestricted AXI liveness, secure-boot, production-entropy, +isolation, or safety-certification claim. + +## Development order and acceptance + +### TINY-P0 — Freeze Tiny MCU Contract + +Record the approved requirements here and update document indexes. Acceptance: +reviewable specification, valid links and `git diff --check`. + +### TINY-P1 — Product Selection and Shared Infrastructure + +Add SOC=TINY, the committed profile, dependency selection and shared build/IP +boundaries. Preserve Mini defaults. Acceptance: configuration/generator tests, +dependency lock validation, no implicit Tiny dependency on Mini product RTL. + +### TINY-P2 — AXI4/APB4 Tiny RTL + +Implement the hierarchy and contracts above. Acceptance: lint, standalone AXI, +APB, memory, reset and IRQ checks; no RIB/RIBP in the resolved source closure. + +### TINY-P3 — SDK Boot and System Verification + +Implement generated capabilities, SRAM-only startup, HAL selection and firmware. +Acceptance: both simulators boot through pin-level NOR and emit SIM_TEST_PASS; +test SRAM boundaries, DMA, peripherals, IRQ, disabled A, C instructions and debug. + +### TINY-P4 — IHP130 Qualification and Documentation + +Add Tiny to IHP130 regression without replacing Mini coverage. Run format/style, +lint, software policy/host tests, Ruff, Pytest, firmware and supported regressions. +Run Yosys, Icarus netlist and OpenSTA; archive macro mapping, area, cell count, +24 MHz timing and warnings under the configuration's build variant. Check docs +and example commands. Record unrun/failed gates explicitly; do not promote +warning baselines or metrics policy as part of this feature. + +## Reproducible commands and current evidence + +Use the committed profile, and preserve one `BUILD_TIMESTAMP` when commands +must share artifacts: + +```sh +make CONFIG=configs/ci/ihp130-tiny.mk setup doctor +make CONFIG=configs/ci/ihp130-tiny.mk firmware sim +make CONFIG=configs/ci/ihp130-tiny.mk SIMU=IVERILOG firmware sim +make CONFIG=configs/ci/ihp130-tiny.mk SYNTH=YOSYS synth +make CONFIG=configs/ci/ihp130-tiny.mk SIMU=IVERILOG netsim-boot +make CONFIG=configs/ci/ihp130-tiny.mk STA=OPENSTA sta +make CONFIG=configs/ci/ihp130-tiny.mk metrics package +python3 scripts/regress.py --root . --suite pr --pdk IHP130 --soc TINY --dry-run +``` + +`netsim-boot` runs the dedicated `app/asm/tiny_boot.S` gate-level image. It checks +pin-level NOR boot, both ends of physical SRAM, a byte-lane write, UART and the +SYSCTRL terminal result. It requires `SIM_TEST_PASS`; this is separate from the +complete SDK/IRQ/DMA/watchdog acceptance in both RTL simulators. `netsim` retains +the full firmware option. The shared SRAM IP's capability register describes +the IP's native superset; end-to-end requests must obey Tiny's narrower fabric +contract above. + +Tiny uses two-cycle AHB ERROR completion so Hazard3 can trap AXI failures. The +shared core/bridge parameters retain Mini's existing default behavior. Tiny +ArchInfo reports SOC_ID `0x54494e59`, topology `0x20200001`, and FEATURES0 +`0x00007ffe`: bits 1/2 SRAM interface/macro, 3 AXI, 4 APB, 5 DMA, 6 UART, +7 I2C, 8 PWM, 9 RTC, 10 watchdog, 11 GPIO, 12 CLINT, 13 timers and 14 XPI. +Bit 0 (PLL) is zero. Tiny FAULT_DETAIL contains the raw AXI response (2/3), +FAULT_STATUS reason is 1 for decode and 2 for slave/protocol errors, +and FAULT_MASTER uses 0 for CPU and 1 for DMA; no RIB encoding is exported. +The public watchdog API is `rs_watchdog_*` with `rs_status_t` and bounded waits. + +The [verification record](tiny-soc-verification.md) distinguishes successful +functional checks from missing reference inputs and physical closure. The +product remains `prototype`: negative pre-layout reset-fanout timing must not +be presented as a qualified 24 MHz implementation. + +## Commercial delivery gaps + +Current-revision verification and physical reports govern readiness. Reusable +VIP, coverage closure, full CDC/RDC, DFT/MBIST, PVT/MMMC, post-layout timing, +power characterization, regulatory and silicon qualification remain separate. diff --git a/docs/misra-c-2012.md b/docs/misra-c-2012.md index 31c3604f..79556fec 100644 --- a/docs/misra-c-2012.md +++ b/docs/misra-c-2012.md @@ -32,8 +32,10 @@ and reviewed evidence for the applicable rules. - Use `rs_status_t` for fallible operations. Prefer bounded string/format APIs and timeout-based register waits. Dynamic allocation is not permitted in self-owned embedded C. -- New `tiny` identifiers, legacy include paths, and prohibited library calls +- Retired `tiny`-prefixed SDK identifiers, legacy include paths, and prohibited library calls are not allowed outside the compatibility files explicitly listed by policy. + The Tiny product name in prose/string literals and `RS_SOC_TINY` product + selection do not restore those retired SDK interfaces. The standard is referenced by name and category only. Consult the licensed MISRA publication for the complete rule text and applicability guidance; do diff --git a/docs/rtl-coding-style-compliance.md b/docs/rtl-coding-style-compliance.md index 824a6b44..785d578a 100644 --- a/docs/rtl-coding-style-compliance.md +++ b/docs/rtl-coding-style-compliance.md @@ -10,7 +10,7 @@ validated without changing functional behavior. The owned profile in [`rtl/rtl_style_manifest.json`](../rtl/rtl_style_manifest.json) is the source of truth for the reviewed source set. It includes `rtl/ip/**` and -`rtl/mini/top/**`, but excludes managed, generated, PDK, formal, device-model, +`rtl/mini/top/**` and `rtl/tiny/top/**`, but excludes managed, generated, PDK, formal, device-model, and third-party sources. [`rtl/rtl_style_audit.json`](../rtl/rtl_style_audit.json) records the exact diff --git a/docs/rtl-coding-style.md b/docs/rtl-coding-style.md index 1629983b..d60be5dc 100644 --- a/docs/rtl-coding-style.md +++ b/docs/rtl-coding-style.md @@ -10,7 +10,7 @@ differ: retroSoC uses `clk_i` and `rst_n_i`, not `rst_ni`. ## 1. Scope and rule levels -The owned profile applies to `rtl/ip/**` and `rtl/mini/top/**`. Formal, +The owned profile applies to `rtl/ip/**`, `rtl/mini/top/**`, and `rtl/tiny/top/**`. Formal, device-model, PDK, generated, managed, and third-party sources use the profiles declared in [`rtl/rtl_style_manifest.json`](../rtl/rtl_style_manifest.json). diff --git a/docs/soc-family-positioning.md b/docs/soc-family-positioning.md index 514dc2cd..8491bc31 100644 --- a/docs/soc-family-positioning.md +++ b/docs/soc-family-positioning.md @@ -3,9 +3,9 @@ ## Status and Scope This document defines the intended Tiny, Mini, Std, and Pro product ladder. -It is a product roadmap, not a statement of implemented repository support. -The current build system accepts only `SOC=MINI`, and the committed Mini -profiles remain the executable source of truth. +Tiny and Mini have executable build profiles; Std and Pro remain roadmap +targets. Committed product profiles and retained validation evidence define +implemented support. Tiny first targets IHP130 as a wired MCU. Mini is the family anchor. It establishes the common product model: an open RISC-V SoC in which a small, always-available Hazard3 management core owns @@ -14,8 +14,8 @@ add application processors and accelerators without transferring final lifecycle control to Linux. Tiny reduces this model to an MCU-class device and does not require a separate application processor. -All frequencies, memory sizes, bus widths, accelerator rates, and software -features below are product targets. They require separate RTL integration, +Except for the explicitly identified executable baselines, frequencies, memory +sizes, bus widths, accelerator rates, and software features below are product targets. They require separate RTL integration, driver enablement, verification, and physical qualification before they can be advertised for a device or PDK. @@ -47,45 +47,36 @@ nodes, protocol bridges, and smart-home edge endpoints. It is MCU-first: boot latency, sleep behavior, deterministic I/O, security, package cost, and usable energy per event take precedence over Linux compatibility. -Tiny has two productization profiles: +The first executable product is **Tiny-MCU**, an independent wired MCU +integration in `rtl/tiny`, selected with `configs/ci/ihp130-tiny.mk`. It has no +wireless IP, wireless protocol stack, or radio-specific companion integration. +Tiny-Connect, BLE, IEEE 802.15.4 and Wi-Fi are deferred product research. -- **Tiny-MCU** contains no radio and targets wired control or products that do - not need connectivity on every unit. -- **Tiny-Connect** provides Bluetooth Low Energy and IEEE 802.15.4 capability. - The open digital-RTL baseline should expose a qualified SPI or SDIO host, - interrupt, reset, wake, and power-sequencing interface to a certified radio. - On-die or co-packaged RF becomes a product option only after suitable RF IP, - analog integration, PDK support, and regulatory qualification exist. +### First-release Architecture -Wi-Fi 6 is an optional upper Tiny-Connect SKU rather than a baseline -requirement. This avoids imposing its RF, memory, and active-power costs on -802.15.4 and Bluetooth endpoint products. - -### Recommended Architecture - -| Area | Product target | +| Area | Tiny MCU baseline | | --- | --- | -| CPU | One Hazard3 RV32IMAC core at 64-160 MHz, subject to PDK qualification | -| On-chip memory | 256-512 KiB banked SRAM, boot ROM, and 8-32 KiB retention SRAM | -| Code storage | QSPI flash with execute-in-place and authenticated recovery boot | -| Memory model | No MMU and no external DRAM dependency | -| Interconnect | 32-bit RIB; bounded `INCR4` support for DMA and XIP, without a full AXI4 fabric | -| Software | Freestanding SDK, bare metal, and Zephyr- or FreeRTOS-class RTOS ports | -| Low power | Clock gating, switchable SRAM banks, RTC/event wake, retention, and a separately measured deep-sleep state | -| Security | Immutable boot root, signed boot, OTP key material, PMP, TRNG, and symmetric/hash acceleration | -| Edge I/O | UART, SPI, I2C, PWM, ADC, I2S/PDM, USB full speed, and CAN FD selected by package profile | +| CPU | One Hazard3 RV32IMC hart; A extension disabled until atomic bus semantics are qualified | +| On-chip memory | 128 KiB macro-backed SRAM; 256–512 KiB and retention SRAM deferred | +| Code storage | XPI NOR boot, loaded into SRAM; authenticated recovery boot deferred | +| Memory model | No MMU, HP hart, or external DRAM/PSRAM dependency | +| Interconnect | 32-bit AXI4 data plane and APB4 control; no RIB/RIBP | +| Software | Existing freestanding SDK, RV32IM compiler target, CSR/IRQ-enabled acceptance firmware | +| Clock/reset | 24 MHz system clock, no PLL, 1 MHz CLINT timebase, watchdog and JTAG reset | +| Edge I/O | 32 GPIO, two UARTs, two I2C controllers, two timers, four DMA channels, PWM, RTC, watchdog and XPI | -No power-current number should be published until it is measured on a -qualified physical implementation with the wake sources and retention state -specified. Tiny's acceptance criteria should include sleep-to-active latency, -energy per sensing/reporting cycle, and certified-radio interoperability, not -only CPU benchmarks. +The normative [Tiny MCU contract](ip/tiny-soc.md) defines the AXI subset, +address/interrupt ABI, startup, errors and phased acceptance. Independent +low-power clocks, clock/power gating, retention, secure boot, RTOS ports, USB, +SDIO, standalone general SPI, CAN and ADC remain future work. No measured +frequency ceiling, power-current, wake-latency or security claim follows from +this initial functional profile. ### Commercial Reference Points | Commercial SoC | Relevant axis | Position relative to Tiny | | --- | --- | --- | -| [Espressif ESP32-H2](https://www.espressif.com/en/products/socs/esp32-h2) | Low-power RV32 MCU with Bluetooth LE and IEEE 802.15.4 | Primary Tiny-Connect endpoint and Thread/Zigbee market reference | +| [Espressif ESP32-H2](https://www.espressif.com/en/products/socs/esp32-h2) | Low-power RV32 MCU with Bluetooth LE and IEEE 802.15.4 | Deferred Tiny-Connect research reference | | [Espressif ESP32-C6](https://www.espressif.com/en/products/socs/esp32-c6) | RV32 high- and low-power cores with Wi-Fi 6, Bluetooth LE, and IEEE 802.15.4 | Upper connectivity reference; RF and protocol integration are well beyond a radio-companion Tiny baseline | | [Raspberry Pi RP2350](https://www.raspberrypi.com/products/rp2350/) | Dual Hazard3 option, 520 KiB SRAM, security, USB, and programmable I/O | Closest open-core MCU and deterministic-I/O reference, without integrated radio | | [ST STM32U5](https://www.st.com/en/microcontrollers-microprocessors/stm32u5-series.html) | Ultra-low-power secure MCU family with large embedded memory and graphics options | Energy-efficiency, security, and industrial MCU ecosystem reference rather than an ISA-equivalent peer | @@ -414,7 +405,7 @@ software quality. | Tier | Claim gate | | --- | --- | -| Tiny | Measured active and sleep power, bounded wake latency, secure-boot validation, and qualified edge-connectivity operation | +| Tiny | First release: wired-MCU boot, AXI/APB/DMA/IRQ and IHP130 evidence; future low-power/security claims require separate measurements and qualification | | Mini | Repeatable Linux boot, at least 64 MiB usable main memory, native memory bursts, and management-controlled start/stop recovery | | Std | Full AXI4 ordering tests, coherent accelerator traffic, 1080p60 graphical desktop, audio playback, and NPU inference under concurrent DMA load | | Pro | Four-hart coherent SMP stress, RV64 distribution boot, more-than-4-GiB memory validation, and concurrent GPU/NPU/video operation | diff --git a/physical/commercial/scripts/generate_timing_contract.py b/physical/commercial/scripts/generate_timing_contract.py index 7f1defbd..cd0f9938 100644 --- a/physical/commercial/scripts/generate_timing_contract.py +++ b/physical/commercial/scripts/generate_timing_contract.py @@ -12,7 +12,7 @@ from typing import Any ROOT = Path(__file__).resolve().parents[3] sys.path.insert(0, str(ROOT)) -from rtl.mini.pin_map.generate_pin_map import read_map # noqa: E402 +from scripts.rtl.generate_pin_map import read_map # noqa: E402 from scripts.check_clock_reset_domains import validate # noqa: E402 from scripts.setup_helpers import atomic_write # noqa: E402 diff --git a/physical/librelane/scripts/doctor.py b/physical/librelane/scripts/doctor.py index b38f3980..80cda416 100755 --- a/physical/librelane/scripts/doctor.py +++ b/physical/librelane/scripts/doctor.py @@ -14,7 +14,7 @@ from pathlib import Path ROOT = Path(__file__).resolve().parents[3] sys.path.insert(0, str(ROOT)) from librelane.flows import Flow # noqa: E402 -from rtl.mini.pin_map.generate_pin_map import IHP130_POWER_PAD_COUNTS # noqa: E402 +from scripts.rtl.generate_pin_map import IHP130_POWER_PAD_COUNTS # noqa: E402 from scripts.setup_helpers import atomic_write # noqa: E402 diff --git a/physical/librelane/scripts/generate_chip_config.py b/physical/librelane/scripts/generate_chip_config.py index 6a9a1709..49ca2c5f 100755 --- a/physical/librelane/scripts/generate_chip_config.py +++ b/physical/librelane/scripts/generate_chip_config.py @@ -13,7 +13,7 @@ from pathlib import Path ROOT = Path(__file__).resolve().parents[3] sys.path.insert(0, str(ROOT)) -from rtl.mini.pin_map.generate_pin_map import ( # noqa: E402 +from scripts.rtl.generate_pin_map import ( # noqa: E402 IHP130_POWER_PAD_COUNTS, Pad, ihp130_pad_instance, diff --git a/physical/librelane/scripts/generate_sdc.py b/physical/librelane/scripts/generate_sdc.py index 73f8d583..027648ec 100755 --- a/physical/librelane/scripts/generate_sdc.py +++ b/physical/librelane/scripts/generate_sdc.py @@ -11,7 +11,7 @@ from pathlib import Path ROOT = Path(__file__).resolve().parents[3] sys.path.insert(0, str(ROOT)) -from rtl.mini.pin_map.generate_pin_map import Pad, ihp130_pad_instance, read_map # noqa: E402 +from scripts.rtl.generate_pin_map import Pad, ihp130_pad_instance, read_map # noqa: E402 from scripts.check_clock_reset_domains import validate # noqa: E402 from scripts.setup_helpers import atomic_write # noqa: E402 diff --git a/physical/smoke/sta/opensta/generate_sdc.py b/physical/smoke/sta/opensta/generate_sdc.py index 808b8e8c..b7b1eb42 100644 --- a/physical/smoke/sta/opensta/generate_sdc.py +++ b/physical/smoke/sta/opensta/generate_sdc.py @@ -12,7 +12,7 @@ from typing import Any ROOT = Path(__file__).resolve().parents[4] sys.path.insert(0, str(ROOT)) -from rtl.mini.pin_map.generate_pin_map import read_map # noqa: E402 +from scripts.rtl.generate_pin_map import read_map # noqa: E402 from scripts.check_clock_reset_domains import validate # noqa: E402 from scripts.setup_helpers import atomic_write # noqa: E402 diff --git a/physical/smoke/sta/opensta/opensta.mk b/physical/smoke/sta/opensta/opensta.mk index 75f9363d..2073f969 100644 --- a/physical/smoke/sta/opensta/opensta.mk +++ b/physical/smoke/sta/opensta/opensta.mk @@ -1,5 +1,6 @@ OPENSTA ?= sta +OPENSTA_TOP ?= $(if $(filter TINY,$(SOC)),retrosoc_tiny_asic,retrosoc_asic) OPENSTA_THREADS ?= $(JOBS) OPENSTA_NETLIST ?= $(SYN_BUILD_ROOT)/out/retrosoc_asic_yosys.v OPENSTA_REPORT ?= $(STA_BUILD_ROOT)/retrosoc_sta.log @@ -8,8 +9,8 @@ OPENSTA_METRICS ?= $(STA_BUILD_ROOT)/timing_metrics.rpt OPENSTA_CONFIG ?= $(SYN_BUILD_ROOT)/out/retrosoc_asic_yosys.config OPENSTA_SDC ?= $(STA_BUILD_ROOT)/retrosoc_core.sdc OPENSTA_SDC_GEN := $(ROOT_PATH)/physical/smoke/sta/opensta/generate_sdc.py -OPENSTA_DOMAIN_MAP := $(ROOT_PATH)/rtl/mini/integration/clock_reset_domains.json -OPENSTA_PIN_MAP := $(ROOT_PATH)/rtl/mini/pin_map/pin_map.json +OPENSTA_DOMAIN_MAP := $(RTL_PATH)/integration/clock_reset_domains.json +OPENSTA_PIN_MAP := $(RTL_PATH)/pin_map/pin_map.json include $(ROOT_PATH)/physical/smoke/sta/opensta/pdk_timing.mk @@ -36,7 +37,7 @@ sta: $(OPENSTA_SDC) | manifest } python3 $(ROOT_PATH)/scripts/run_flow.py --tool opensta --log $(OPENSTA_LOG) \ --result $(STA_BUILD_ROOT)/result-sta.json \ - --env OPENSTA_NETLIST=$(OPENSTA_NETLIST) --env OPENSTA_LIBERTY=$(OPENSTA_LIBERTY) \ + --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) -- \ diff --git a/physical/smoke/sta/opensta/opensta.tcl b/physical/smoke/sta/opensta/opensta.tcl index 0a9a1593..d852b3d7 100644 --- a/physical/smoke/sta/opensta/opensta.tcl +++ b/physical/smoke/sta/opensta/opensta.tcl @@ -14,7 +14,11 @@ if {[info exists ::env(OPENSTA_SRAM_LIBS)] && $::env(OPENSTA_SRAM_LIBS) ne ""} { } } read_verilog $::env(OPENSTA_NETLIST) -link_design retrosoc_asic +set top_design retrosoc_asic +if {[info exists ::env(OPENSTA_TOP)] && $::env(OPENSTA_TOP) ne ""} { + set top_design $::env(OPENSTA_TOP) +} +link_design $top_design read_sdc $::env(OPENSTA_SDC) report_checks -path_delay min_max -sort_by_slack -slack_max 0.0 -group_path_count 1000 -endpoint_path_count 1000 > $::env(OPENSTA_REPORT) diff --git a/physical/smoke/syn/tools/export_apu_block_filelist.py b/physical/smoke/syn/tools/export_apu_block_filelist.py index fa18eed5..e0d8e067 100644 --- a/physical/smoke/syn/tools/export_apu_block_filelist.py +++ b/physical/smoke/syn/tools/export_apu_block_filelist.py @@ -18,10 +18,9 @@ from pathlib import Path REPO_ROOT = Path(__file__).resolve().parents[4] -SCRIPT_DIR = REPO_ROOT / "rtl/mini/script" -sys.path.insert(0, str(SCRIPT_DIR)) -from filelist import FileList, parse_filelists, write_filelist # noqa: E402 -from generate_filelist import generate_all # noqa: E402 +sys.path.insert(0, str(REPO_ROOT)) +from scripts.rtl.filelist import FileList, parse_filelists, write_filelist # noqa: E402 +from rtl.mini.script.generate_filelist import generate_all # noqa: E402 APU_SOURCE_MARKERS = ("/ip/multimedia/apu_", "/ip/multimedia/apb4_apu.sv") diff --git a/physical/smoke/syn/tools/export_soc_sources.py b/physical/smoke/syn/tools/export_soc_sources.py index eb547525..9230ea48 100644 --- a/physical/smoke/syn/tools/export_soc_sources.py +++ b/physical/smoke/syn/tools/export_soc_sources.py @@ -13,13 +13,10 @@ from pathlib import Path REPO_ROOT = Path(__file__).resolve().parents[4] -SCRIPT_DIR = REPO_ROOT / "rtl/mini/script" -INTEGRATION_DIR = REPO_ROOT / "rtl/mini/integration" -sys.path.insert(0, str(SCRIPT_DIR)) -sys.path.insert(0, str(INTEGRATION_DIR)) -from filelist import FileList, atomic_write, parse_filelists # noqa: E402 -from generate_filelist import generate_all # noqa: E402 -from generate_user_extensions import generate as generate_user_extensions # noqa: E402 +sys.path.insert(0, str(REPO_ROOT)) +from scripts.rtl.filelist import FileList, atomic_write, parse_filelists # noqa: E402 +from rtl.mini.script.generate_filelist import generate_all # noqa: E402 +from rtl.mini.integration.generate_user_extensions import generate as generate_user_extensions # noqa: E402 INCLUDE_RE = re.compile(r'^\s*`include\s+"([^"]+)"') @@ -69,6 +66,20 @@ def configured_filelist( *, require_files: bool = True, ) -> FileList: + if getattr(args, "soc", "MINI") == "TINY": + directory = getattr(args, "source_filelist_dir", None) + if directory is None: + raise ValueError("Tiny export requires --source-filelist-dir") + names = ("def.fl", "inc.fl", "commonip.fl", "clusterip.fl", "ip.fl", + "tech.fl", "core_hazard3.fl", "top.fl") + filelist = parse_filelists((directory / name for name in names), require_files=require_files) + if "+define+RETROSOC_SOC__TINY" not in filelist.defines: + raise ValueError("Tiny export requires Tiny source lists") + if "+define+SYNTHESIS" not in filelist.defines: + filelist.defines.append("+define+SYNTHESIS") + return filelist + if args.dynamic_core_filelist is None or args.dynamic_ip_filelist is None: + raise ValueError("Mini export requires dynamic core and IP filelists") names: list[str | Path] = [] for name in ( "memory_map_filelist", @@ -271,7 +282,7 @@ def metadata_file(value: str) -> tuple[Path, Path]: def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser(description="Export configured retroSoC RTL sources") parser.add_argument("mode", choices=("tar", "sv")) - parser.add_argument("--soc", default="MINI", choices=("MINI",)) + parser.add_argument("--soc", default="MINI", choices=("MINI", "TINY")) parser.add_argument("--pdk", default="IHP130", choices=("IHP130", "ICS55", "SKY130", "GF180")) parser.add_argument("--simu", default="VCS", choices=("VCS", "VERILATOR", "IVERILOG")) parser.add_argument("--have-pll", action="store_true") @@ -285,18 +296,19 @@ def parse_args() -> argparse.Namespace: parser.add_argument( "--dynamic-core-filelist", type=Path, - required=True, + required=False, ) parser.add_argument( "--dynamic-ip-filelist", type=Path, - required=True, + required=False, ) parser.add_argument("--memory-map-filelist", type=Path) parser.add_argument("--soc-topology-filelist", type=Path) parser.add_argument("--user-extensions-filelist", type=Path) parser.add_argument("--pin-map-filelist", type=Path) parser.add_argument("--archinfo-incdir", type=Path) + parser.add_argument("--source-filelist-dir", type=Path) parser.add_argument("--librelane-safe", action="store_true") parser.add_argument("--metadata-file", type=metadata_file, action="append", default=[]) parser.add_argument("--output-dir", type=Path, default=REPO_ROOT / "export") @@ -312,15 +324,16 @@ def main() -> int: with tempfile.TemporaryDirectory(prefix="retrosoc-filelists-") as temporary: generated_dir = Path(temporary) user_extensions_dir = generated_dir / "user_extensions" - generate_all(generated_dir, build_defines(args)) - if args.user_extensions_filelist is None: + if args.soc == "MINI": + generate_all(generated_dir, build_defines(args)) + if args.soc == "MINI" and args.user_extensions_filelist is None: generate_user_extensions( REPO_ROOT / "rtl/mini/integration/user_extensions.json", user_extensions_dir, ) filelist = configured_filelist(args, generated_dir, user_extensions_dir) if args.mode == "sv": - output = export_dir / "retrosoc_asic_sources.sv" + output = export_dir / ("retrosoc_tiny_asic_sources.sv" if args.soc == "TINY" else "retrosoc_asic_sources.sv") write_single_sv(filelist, output, librelane_safe=args.librelane_safe) else: output = write_tar(filelist, export_dir, args.soc, dict(args.metadata_file)) diff --git a/physical/smoke/syn/yosys/apu_block.mk b/physical/smoke/syn/yosys/apu_block.mk index 0a996df2..41480be2 100644 --- a/physical/smoke/syn/yosys/apu_block.mk +++ b/physical/smoke/syn/yosys/apu_block.mk @@ -98,7 +98,7 @@ APU_BLOCK_STA_RESULT := $(APU_BLOCK_STA)/result-sta.json APU_BLOCK_SYN_RESULT := $(APU_BLOCK_SYN)/result-synth.json $(APU_BLOCK_FL): $(APU_BLOCK_EXPORTER) $(FILELIST_TEMPLATES) \ - $(RTL_PATH)/script/generate_filelist.py $(RTL_PATH)/script/filelist.py + $(RTL_PATH)/script/generate_filelist.py $(ROOT_PATH)/scripts/rtl/filelist.py @mkdir -p $(APU_BLOCK_FL_DIR) python3 $(APU_BLOCK_EXPORTER) --output-dir $(APU_BLOCK_FL_DIR) \ $(foreach define,$(DEF_LIST),--define $(define)) diff --git a/physical/smoke/syn/yosys/ga2d_block.mk b/physical/smoke/syn/yosys/ga2d_block.mk index 7e1d4e58..507f92b7 100644 --- a/physical/smoke/syn/yosys/ga2d_block.mk +++ b/physical/smoke/syn/yosys/ga2d_block.mk @@ -48,7 +48,7 @@ endif # active DEF_LIST but without the SoC generated-include chain, so the block # flow does not depend on the HP/pin-map generation stamps. $(GA2D_BLOCK_FL_DIR)/.stamp: $(RTL_PATH)/script/generate_filelist.py \ - $(RTL_PATH)/script/filelist.py \ + $(ROOT_PATH)/scripts/rtl/filelist.py \ $(sort $(wildcard $(ROOT_PATH)/rtl/filelist/pdk_*.fl) \ $(wildcard $(RTL_PATH)/filelist/*.fl) $(RTL_PATH)/lint.msg) $(FLOW_PYTHON) $(RTL_PATH)/script/generate_filelist.py \ @@ -57,11 +57,11 @@ $(GA2D_BLOCK_FL_DIR)/.stamp: $(RTL_PATH)/script/generate_filelist.py \ @touch $@ $(GA2D_BLOCK_FLIST): $(GA2D_BLOCK_FL_DIR)/.stamp $(MEMORY_MAP_STAMP) - @python3 $(RTL_PATH)/script/comb.py \ + @python3 $(ROOT_PATH)/scripts/rtl/comb.py \ -f $(GA2D_BLOCK_FL_DIR)/def.fl \ -f $(MEMORY_MAP_FILELIST) \ -f $(GA2D_BLOCK_FL_DIR)/ga2d_block.fl --output $@ - @python3 $(RTL_PATH)/script/filelist_deps.py -f $@ \ + @python3 $(ROOT_PATH)/scripts/rtl/filelist_deps.py -f $@ \ --extra $(MEMORY_MAP_FILELIST) \ --target $(GA2D_BLOCK_NETLIST) --output $(GA2D_BLOCK_DEPFILE) diff --git a/physical/smoke/syn/yosys/yosys.mk b/physical/smoke/syn/yosys/yosys.mk index 9971b6ab..38802fce 100644 --- a/physical/smoke/syn/yosys/yosys.mk +++ b/physical/smoke/syn/yosys/yosys.mk @@ -19,18 +19,19 @@ # Directories # directory of the path to the last called Makefile (this one) -YOSYS_DIR := $(realpath $(dir $(realpath $(lastword $(MAKEFILE_LIST))))) -YOSYS_BUILD := $(SYN_BUILD_ROOT) -YOSYS_OUT := $(YOSYS_BUILD)/out -YOSYS_TMP := $(YOSYS_BUILD)/tmp -YOSYS_RPT := $(YOSYS_BUILD)/rpt +YOSYS_DIR := $(realpath $(dir $(realpath $(lastword $(MAKEFILE_LIST))))) +YOSYS_BUILD := $(SYN_BUILD_ROOT) +YOSYS_OUT := $(YOSYS_BUILD)/out +YOSYS_TMP := $(YOSYS_BUILD)/tmp +YOSYS_RPT := $(YOSYS_BUILD)/rpt YOSYS_TIMEOUT ?= 10800 include $(YOSYS_DIR)/synth_config.mk ifeq ($(origin YOSYS_TARGET_PERIOD_PS),undefined) YOSYS_TARGET_PERIOD_PS := $(shell python3 $(ROOT_PATH)/scripts/yosys_period.py \ - --domains $(ROOT_PATH)/rtl/mini/integration/clock_reset_domains.json) + --domains $(RTL_PATH)/integration/clock_reset_domains.json \ + --domain $(if $(filter TINY,$(SOC)),system,hp)) ifeq ($(strip $(YOSYS_TARGET_PERIOD_PS)),) $(error Failed to derive YOSYS_TARGET_PERIOD_PS from the clock/reset inventory) endif @@ -49,8 +50,8 @@ YOSYS_SCRIPTS := $(wildcard $(YOSYS_DIR)/script/*) $(YOSYS_DIR)/synth_config.mk -include $(YOSYS_DEPFILE) $(SV_FLIST): $(MPW_VARIANT_STAMP) $(FILELIST_STAMP) - @python3 $(RTL_PATH)/script/comb.py $(RTL_FLIST) --output $(SV_FLIST) - @python3 $(RTL_PATH)/script/filelist_deps.py $(RTL_FLIST) --target $(NETLIST) \ + @python3 $(ROOT_PATH)/scripts/rtl/comb.py $(RTL_FLIST) --output $(SV_FLIST) + @python3 $(ROOT_PATH)/scripts/rtl/filelist_deps.py $(RTL_FLIST) --target $(NETLIST) \ --output $(YOSYS_DEPFILE) gen_synth_filelist: $(SV_FLIST) @@ -79,4 +80,4 @@ $(NETLIST_DEBUG): $(NETLIST) synth_clean: python3 $(ROOT_PATH)/scripts/clean.py --root $(ROOT_PATH) --path $(YOSYS_BUILD) -.PHONY: gen_synth_filelist synth_clean synth +.PHONY: gen_synth_filelist synth_clean synth \ No newline at end of file diff --git a/publications/build_datasheet.py b/publications/build_datasheet.py index 32e65d85..266417ae 100644 --- a/publications/build_datasheet.py +++ b/publications/build_datasheet.py @@ -146,9 +146,9 @@ def collect_data(config: dict[str, Any], *, check_snapshot: bool = True) -> dict product = profiles["profile"] if product.get("MINI_MODE") != "PRODUCT" or product.get("HAVE_HP") != "YES": raise ValueError("datasheet requires the reviewed dual-hart PRODUCT profile") - address = load_generator("datasheet_memory", "rtl/mini/address_map/generate_memory_map.py") + address = load_generator("datasheet_memory", "scripts/rtl/generate_memory_map.py") topology = load_generator("datasheet_topology", "rtl/mini/integration/generate_soc_topology.py") - pinmap = load_generator("datasheet_pins", "rtl/mini/pin_map/generate_pin_map.py") + pinmap = load_generator("datasheet_pins", "scripts/rtl/generate_pin_map.py") reset, regions, _ = address.read_map(ROOT / MAP, int(product["SRAM_SIZE_KIB"])) _, _, _, gpio, _, _, irqs, policies = topology.read_topology(ROOT / TOPOLOGY, ROOT / MAP) pads, _ = pinmap.read_map(ROOT / PINS) diff --git a/publications/check_examples.py b/publications/check_examples.py index bda39b4f..9b79be92 100644 --- a/publications/check_examples.py +++ b/publications/check_examples.py @@ -20,7 +20,7 @@ def check_examples(compiler: str) -> int: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/address_map/generate_memory_map.py"), + str(ROOT / "scripts/rtl/generate_memory_map.py"), "--map", str(ROOT / "rtl/mini/address_map/memory_map.json"), "--output-dir", diff --git a/publications/datasheets/diagram-circuits.json b/publications/datasheets/diagram-circuits.json index b852700a..34c4b78b 100644 --- a/publications/datasheets/diagram-circuits.json +++ b/publications/datasheets/diagram-circuits.json @@ -8451,13 +8451,13 @@ ], "sources": [ "rtl/mini/top/retrosoc.sv", - "rtl/mini/top/onchip_ram.sv" + "rtl/ip/memory/onchip_ram.sv" ], "instance_source": { "file": "rtl/mini/top/retrosoc.sv", "name": "u_onchip_ram", "module": "onchip_ram", - "declaration_file": "rtl/mini/top/onchip_ram.sv" + "declaration_file": "rtl/ip/memory/onchip_ram.sv" } }, { @@ -8495,7 +8495,7 @@ } ], "sources": [ - "rtl/mini/top/onchip_ram.sv" + "rtl/ip/memory/onchip_ram.sv" ] } ], @@ -8562,11 +8562,11 @@ "style": "direct", "bindings": [ { - "file": "rtl/mini/top/onchip_ram.sv", + "file": "rtl/ip/memory/onchip_ram.sv", "text": ".data_i(s_mem_wdata[31:0])" }, { - "file": "rtl/mini/top/onchip_ram.sv", + "file": "rtl/ip/memory/onchip_ram.sv", "text": ".data_i(s_mem_wdata[63:32])" } ], @@ -10929,13 +10929,13 @@ ], "sources": [ "rtl/mini/top/retrosoc.sv", - "rtl/mini/top/onchip_ram.sv" + "rtl/ip/memory/onchip_ram.sv" ], "instance_source": { "file": "rtl/mini/top/retrosoc.sv", "name": "u_onchip_ram", "module": "onchip_ram", - "declaration_file": "rtl/mini/top/onchip_ram.sv" + "declaration_file": "rtl/ip/memory/onchip_ram.sv" } }, { diff --git a/publications/datasheets/register-profiles.json b/publications/datasheets/register-profiles.json index a42833da..b0519628 100644 --- a/publications/datasheets/register-profiles.json +++ b/publications/datasheets/register-profiles.json @@ -1430,10 +1430,10 @@ "sram": { "document": "docs/ip/onchip-sram.md", "defines": [ - "rtl/mini/top/onchip_ram_define.svh" + "rtl/ip/memory/onchip_ram_define.svh" ], "rtl": [ - "rtl/mini/top/onchip_ram_reg.sv" + "rtl/ip/memory/onchip_ram_reg.sv" ], "parameters": { "Present": 1, diff --git a/publications/datasheets/sections/processor-details.typ b/publications/datasheets/sections/processor-details.typ index 7af21d5d..1b110fd2 100644 --- a/publications/datasheets/sections/processor-details.typ +++ b/publications/datasheets/sections/processor-details.typ @@ -58,7 +58,7 @@ the existing non-coherent buffer protocol. LP and HP configuration differences d two harts into a symmetric cache-coherent SMP platform. #block(above:rhythm.metadata-before,below:rhythm.metadata-after,breakable:false)[ #set text(size:9pt) - #source("rtl/mini/top/mgmt_core_wrapper.sv",title:"LP instance") · + #source("rtl/ip/core/mgmt_core_wrapper.sv",title:"LP instance") · #source("scripts/vexiiriscv/GenerateRetroSocHp.scala",title:"HP configuration/PMA") · #source("scripts/generate_vexiiriscv.py",title:"Artifact manifest") · #source("app/ports/linux/linux/retrosoc_hp.dts",title:"HP platform properties") diff --git a/publications/datasheets/sections/software-runtime.typ b/publications/datasheets/sections/software-runtime.typ index 2a42d404..b852b9c5 100644 --- a/publications/datasheets/sections/software-runtime.typ +++ b/publications/datasheets/sections/software-runtime.typ @@ -41,7 +41,7 @@ The debug replacement sets the stack and calls main, without the generic PSRAM i relocation or BSS loop. The XPI flash-loader replacement sets the stack, clears BSS and calls main; if main returns it executes EBREAK and loops. Those compact entries are tied to their application/linker assumptions and do not establish a universal alternative startup sequence. -#source-note("rtl/mini/mk/software.mk",title:"Generic CRT, conditional IRQ objects and application CRT replacement") +#source-note("rtl/mk/software.mk",title:"Generic CRT, conditional IRQ objects and application CRT replacement") #source-note("crt/arch/riscv/startup.S",title:"Actual generic LP entry and PSRAM-ready loop") #source-note("app/apps/debug/startup.S",title:"Dedicated debug entry") #source-note("app/apps/xpi_flash_loader/startup.S",title:"Dedicated SRAM flash-loader entry") diff --git a/publications/datasheets/soc-architecture.json b/publications/datasheets/soc-architecture.json index 8bdf01ad..3310342f 100644 --- a/publications/datasheets/soc-architecture.json +++ b/publications/datasheets/soc-architecture.json @@ -13,7 +13,7 @@ "rtl/mini/top/apb4_periph.sv", "rtl/mini/top/apb4_system.sv", "rtl/mini/top/memory_pad_mux.sv", - "rtl/mini/top/mgmt_debug_wrapper.sv", + "rtl/ip/core/mgmt_debug_wrapper.sv", "rtl/mini/top/retrosoc.sv", "rtl/mini/top/soc_clock_reset_subsystem.sv", "rtl/mini/top/soc_data_plane.sv" @@ -168,7 +168,7 @@ "ips": [], "rotate": false, "clock": { - "file": "rtl/mini/top/mgmt_debug_wrapper.sv", + "file": "rtl/ip/core/mgmt_debug_wrapper.sv", "name": "u_jtag_rst_sync", "module": "rst_sync", "port": "clk_i", diff --git a/publications/datasheets/system-reference.json b/publications/datasheets/system-reference.json index 016ddd69..7b1f3d40 100644 --- a/publications/datasheets/system-reference.json +++ b/publications/datasheets/system-reference.json @@ -167,7 +167,7 @@ "crt/include/retrosoc/hal/onchip_sram.h", "docs/ip/onchip-sram.md", "publications/examples/sram.c", - "rtl/mini/top/onchip_ram_reg.sv" + "rtl/ip/memory/onchip_ram_reg.sv" ], "tests": [ "tests/test_onchip_sram_register_parity.py" @@ -1159,7 +1159,7 @@ "applies_to": "Reviewed generic LP CRT; dedicated debug and XPI-loader startups have separate initialization contracts.", "sources": [ "crt/arch/riscv/startup.S", - "rtl/mini/mk/software.mk" + "rtl/mk/software.mk" ] }, { @@ -3385,7 +3385,7 @@ "rtl/managed/clusterip/common/rtl/utils/register.sv", "rtl/mini/top/clock_config_controller.sv", "rtl/mini/top/hp_lifecycle_controller.sv", - "rtl/mini/top/mgmt_core_wrapper.sv", + "rtl/ip/core/mgmt_core_wrapper.sv", "rtl/mini/top/resource_controller.sv", "rtl/mini/top/soc_clock_reset_subsystem.sv", "scripts/generate_vexiiriscv.py", @@ -5213,7 +5213,7 @@ "crt/src/service/test.c", "publications/datasheets/mini.json", "rtl/mini/integration/soc_topology.json", - "rtl/mini/mk/software.mk", + "rtl/mk/software.mk", "scripts/build_hp_linux.py", "scripts/regress.py", "crt/src/hal/hp_mailbox.c", @@ -5225,17 +5225,17 @@ "bindings": [ { "id": "csr-selection", - "file": "rtl/mini/mk/software.mk", + "file": "rtl/mk/software.mk", "text": "ifeq ($(HAVE_CSR),YES)\nDEF_VAL += -DCSR_ENABLE\nendif" }, { "id": "irq-objects", - "file": "rtl/mini/mk/software.mk", + "file": "rtl/mk/software.mk", "text": "ifeq ($(HAVE_CSR),YES)\nCRT_SRCS += $(ROOT_PATH)/crt/arch/riscv/system_irq.S\nCRT_SRCS += $(ROOT_PATH)/crt/src/core/system_irq_handler.c\nCRT_SRCS += $(ROOT_PATH)/crt/src/core/irq.c" }, { "id": "app-override", - "file": "rtl/mini/mk/software.mk", + "file": "rtl/mk/software.mk", "text": "ifneq ($(strip $(APP_CRT_SRCS)),)\nCRT_SRCS := $(APP_CRT_SRCS)\nendif" }, { @@ -6151,8 +6151,8 @@ "rtl/mini/top/fabric_monitor.sv", "rtl/mini/top/hp_subsystem.sv", "rtl/mini/top/memory_pad_mux.sv", - "rtl/mini/top/mgmt_debug_wrapper.sv", - "rtl/mini/top/onchip_ram.sv", + "rtl/ip/core/mgmt_debug_wrapper.sv", + "rtl/ip/memory/onchip_ram.sv", "rtl/mini/top/rcu.sv", "rtl/mini/top/resource_controller.sv", "rtl/mini/top/retrosoc.sv", diff --git a/publications/implementation_reference.py b/publications/implementation_reference.py index c1c1c099..0fe8bc43 100644 --- a/publications/implementation_reference.py +++ b/publications/implementation_reference.py @@ -34,7 +34,12 @@ def lp_parameters(text: str) -> dict[str, str]: matches = re.findall(r"\bhazard3_cpu_1port\s*#\s*\((.*?)\)\s+u_hazard3_cpu_1port\s*\(", without_comments(text), re.S) if len(matches) != 1: raise ValueError("LP instance missing or ambiguous") - return unique_pairs(re.findall(r"\.([A-Z][A-Z0-9_]*)\s*\(([^()]*)\)", matches[0]), "LP") + parameters = unique_pairs(re.findall(r"\.([A-Z][A-Z0-9_]*)\s*\(([^()]*)\)", matches[0]), "LP") + # The fixed Mini integration uses the shared wrapper's Boolean feature + # defaults. Keep configurable integer expressions (notably IRQ count) intact. + defaults = dict(re.findall(r"parameter\s+bit\s+(\w+)\s*=\s*1'b([01])", without_comments(text))) + return {name: defaults.get(value, value) if name.startswith("EXTENSION_") else value + for name, value in parameters.items()} def hp_parameters(text: str) -> tuple[dict[str, str], list[str]]: @@ -109,7 +114,7 @@ def validate_details(spec: dict, ids: set[str], root: Path, active_limits: set[s def collect_details(root: Path, spec: dict, ids: set[str], active_limits: set[str]) -> dict: validate_details(spec, ids, root, active_limits) - lp = lp_parameters((root / "rtl/mini/top/mgmt_core_wrapper.sv").read_text(encoding="utf-8")) + lp = lp_parameters((root / "rtl/ip/core/mgmt_core_wrapper.sv").read_text(encoding="utf-8")) hp_source = (root / "scripts/vexiiriscv/GenerateRetroSocHp.scala").read_text(encoding="utf-8") hp, hp_isa = hp_parameters(hp_source) for required, actual, name in ((spec["cpu"]["lp_fields"], lp, "LP"), (spec["cpu"]["hp_fields"], hp, "HP")): diff --git a/publications/programming_reference.py b/publications/programming_reference.py index 00c9e991..ca4de1e9 100644 --- a/publications/programming_reference.py +++ b/publications/programming_reference.py @@ -111,11 +111,11 @@ def buffer_budget(kind: str, *, width=0, height=0, rate=0, bits=0, milliseconds= "total_bytes": stride * count, "dma_supported": supported} -def constants(text: str, prefix: str, rtl=False) -> dict[str, int]: +def constants(text: str, prefix: str, rtl=False, *, exclude: frozenset[str] = frozenset()) -> dict[str, int]: text = re.sub(r"/\*.*?\*/|//[^\n]*", "", text, flags=re.S) pattern = (rf"\b({prefix}\w+)\s*=\s*\d+'d(\d+)\s*;" if rtl else rf"\b({prefix}\w+)\s*(?:=\s*|\s+)(\d+)(?:U)?(?=\s*[,;\n])") - pairs = re.findall(pattern, text) + pairs = [(name, value) for name, value in re.findall(pattern, text) if name not in exclude] if len({name for name, _ in pairs}) != len(pairs): raise ValueError(f"duplicate constant: {prefix}") return {name: int(value) for name, value in pairs} @@ -194,8 +194,10 @@ def collect_programming(root: Path, spec: dict, ids: set[str], revision: str) -> 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.update(constants((root / "crt/include/retrosoc/hal/dma_regs.h").read_text(), "RS_DMA_CHANNEL_")) - channels.pop("RS_DMA_CHANNEL_COUNT") + # 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(), + "RS_DMA_CHANNEL_", exclude=frozenset({"RS_DMA_CHANNEL_COUNT"}))) if {r["symbol"] for r in spec["channels"]} != set(channels): raise ValueError("DMA channel allocation coverage mismatch") for row in result["dma_routes"]: diff --git a/publications/register_reference.py b/publications/register_reference.py index b82ea471..20efdb83 100644 --- a/publications/register_reference.py +++ b/publications/register_reference.py @@ -21,6 +21,22 @@ LITERAL = re.compile(r"(?P\d+)?'[sS]?(?P[hHbBdDoO])(?P[0-9a-fA- def number(expression: str, constants: dict[str, int] | None = None) -> int: constants = constants or {} expression = " ".join(expression.split()).replace("`", "") + # Preserve sized-cast truncation and unwrap a complete parenthesized + # expression before looking for an SV conditional at its top level. + while expression.startswith("(") and expression.endswith(")"): + depth = 0 + enclosed = True + for index, character in enumerate(expression): + depth += (character == "(") - (character == ")") + if depth == 0 and index != len(expression) - 1: + enclosed = False + break + if not enclosed or depth != 0: + break + expression = expression[1:-1].strip() + sized_cast = re.fullmatch(r"(\d+)'\(([^()]*)\)", expression) + if sized_cast: + return number(sized_cast[2], constants) & ((1 << int(sized_cast[1])) - 1) expression = LITERAL.sub( lambda m: str( int(m["digits"].replace("_", ""), {"h": 16, "b": 2, "d": 10, "o": 8}[m["base"].lower()]) @@ -151,7 +167,7 @@ def parse_definitions(paths: list[str], extra: dict[str, int]) -> tuple[dict, di "line": text.count("\n", 0, match.start()) + 1, } for match in re.finditer( - r"\b(?:localparam|parameter)\s+(?:(?:int(?:\s+unsigned)?|logic(?:\s*\[[^]]+\])?|\w+_t)\s+)?" + r"\b(?:localparam|parameter)\s+(?:(?:int(?:\s+unsigned)?|bit|logic(?:\s*\[[^]]+\])?|\w+_t)\s+)?" r"(\w+)\s*=\s*([^;\n]+)", text, ): @@ -165,7 +181,7 @@ def parse_definitions(paths: list[str], extra: dict[str, int]) -> tuple[dict, di }, ) for match in re.finditer( - r"\blocalparam\s+(?:int(?:\s+unsigned)?|logic(?:\s*\[[^]]+\])?|\w+_t)\s+(\w+)\s*=\s*([^;]+);", + r"\blocalparam\s+(?:int(?:\s+unsigned)?|bit|logic(?:\s*\[[^]]+\])?|\w+_t)\s+(\w+)\s*=\s*([^;]+);", text, ): definitions[match[1]] = { diff --git a/rtl/README.md b/rtl/README.md index 276c63a1..c0ccdf45 100644 --- a/rtl/README.md +++ b/rtl/README.md @@ -1,10 +1,11 @@ # RTL Design and Simulation This directory contains retroSoC SystemVerilog RTL, CPU/IP integration, -peripheral and technology wrappers, filelists, testbench support, and the Mini +peripheral and technology wrappers, filelists, testbench support, and the Mini/Tiny SoC build entry points. -`mini/` is the active SoC integration flow. `managed/` contains locked or +`mini/` and `tiny/` own independent product integrations. Tiny is the single-hart +AXI32/APB4 wired MCU described in [its contract](../docs/ip/tiny-soc.md). `managed/` contains locked or vendored integration inputs, `ip/` contains self-owned IP and experiments, and `model/` contains committed device simulation models with preserved upstream notices. `filelist/` selects PDK-specific RTL sources; `tech/` contains diff --git a/rtl/ip/README.md b/rtl/ip/README.md index c6a9a238..d9b4ae76 100644 --- a/rtl/ip/README.md +++ b/rtl/ip/README.md @@ -1,11 +1,14 @@ -# RIB and Experimental IP +# Shared RTL IP This directory owns project RTL IP. Self-owned IP is grouped by function: interconnect, util, memory, storage, -serial, USB, multimedia, and peripheral. The Mini SoC selects active sources -explicitly in rtl/mini/filelist/ip.fl, whose source order is part of -the build contract. +serial, USB, multimedia, and peripheral. Mini and Tiny select active sources +explicitly in their product filelists. +Source order is part of the build contract. Shared Hazard3/debug wrappers are +in `core`, AXI adapters in `interconnect`, and native AXI SRAM in `memory`. +Both products reference these shared files directly in their own filelists. +Tiny does not select any RIB/RIBP module. experimental contains retained inactive RTL. It is not included by any active filelist and must not become a build dependency without an explicit integration diff --git a/rtl/mini/top/mgmt_core_wrapper.sv b/rtl/ip/core/mgmt_core_wrapper.sv similarity index 97% rename from rtl/mini/top/mgmt_core_wrapper.sv rename to rtl/ip/core/mgmt_core_wrapper.sv index 502fcd29..e0c7cfa2 100644 --- a/rtl/mini/top/mgmt_core_wrapper.sv +++ b/rtl/ip/core/mgmt_core_wrapper.sv @@ -11,7 +11,9 @@ `include "mmap_define.svh" module mgmt_core_wrapper #( - parameter int ExternalIrqCount = 30 + parameter int ExternalIrqCount = 30, + parameter bit EnableAtomics = 1'b1, + parameter bit TwoCycleBusErrors = 1'b0 ) ( // verilog_format: off -- preserve reviewed column alignment input logic clk_i, @@ -58,7 +60,7 @@ module mgmt_core_wrapper #( .hclk(clk_i), .hresetn(s_core_rst_n) ); - ahbl2axi4 u_ahbl2axi4 ( + ahbl2axi4 #(.TwoCycleErrors(TwoCycleBusErrors)) u_ahbl2axi4 ( .ahbl (u_ahbl_if), .axi4 (axi4), .idle_o(s_ahbl_idle) @@ -103,7 +105,7 @@ module mgmt_core_wrapper #( hazard3_cpu_1port #( .RESET_VECTOR (`SOC_CPU_RESET_ADDR), .MTVEC_INIT (32'h0000_0000), - .EXTENSION_A (1), + .EXTENSION_A (EnableAtomics), .EXTENSION_C (1), .EXTENSION_E (0), .EXTENSION_M (1), diff --git a/rtl/mini/top/mgmt_debug_reset.sv b/rtl/ip/core/mgmt_debug_reset.sv similarity index 100% rename from rtl/mini/top/mgmt_debug_reset.sv rename to rtl/ip/core/mgmt_debug_reset.sv diff --git a/rtl/mini/top/mgmt_debug_wrapper.sv b/rtl/ip/core/mgmt_debug_wrapper.sv similarity index 100% rename from rtl/mini/top/mgmt_debug_wrapper.sv rename to rtl/ip/core/mgmt_debug_wrapper.sv diff --git a/rtl/mini/top/ahbl2axi4.sv b/rtl/ip/interconnect/ahbl2axi4.sv similarity index 85% rename from rtl/mini/top/ahbl2axi4.sv rename to rtl/ip/interconnect/ahbl2axi4.sv index e8d3391b..330ca1bd 100644 --- a/rtl/mini/top/ahbl2axi4.sv +++ b/rtl/ip/interconnect/ahbl2axi4.sv @@ -11,7 +11,9 @@ `include "axi4_define.svh" -module ahbl2axi4 ( +module ahbl2axi4 #( + parameter bit TwoCycleErrors = 1'b0 +) ( ahbl_if.slave ahbl, axi4_if.master axi4, output logic idle_o @@ -27,6 +29,7 @@ module ahbl2axi4 ( localparam logic [2:0] FSM_WRITE_RESP = 3'd2; localparam logic [2:0] FSM_READ = 3'd3; localparam logic [2:0] FSM_READ_RESP = 3'd4; + localparam logic [2:0] FSM_ERROR_RESP = 3'd5; logic [2:0] s_fsm_d, s_fsm_q; logic [31:0] s_addr_d, s_addr_q; @@ -37,15 +40,22 @@ module ahbl2axi4 ( logic [ 3:0] s_wstrb; logic [31:0] s_wdata; logic s_terminal; + logic s_axi_err; + logic s_resp_valid; assign idle_o = s_fsm_q == FSM_IDLE; - assign s_terminal = ((s_fsm_q == FSM_WRITE_RESP) && axi4.bvalid) || - ((s_fsm_q == FSM_READ_RESP) && axi4.rvalid); + assign s_resp_valid = ((s_fsm_q == FSM_WRITE_RESP) && axi4.bvalid) || + ((s_fsm_q == FSM_READ_RESP) && axi4.rvalid); + assign s_axi_err = s_resp_valid && + (((s_fsm_q == FSM_WRITE_RESP) && (axi4.bresp != `AXI4_RESP_OKAY)) || + ((s_fsm_q == FSM_READ_RESP) && (axi4.rresp != `AXI4_RESP_OKAY))); + // In compliant mode, the AXI terminal error is consumed during the first + // AHB ERROR cycle (HREADY=0). The saved state supplies the final ERROR cycle + // (HREADY=1), allowing Hazard3 to squash a following pipelined access. + assign s_terminal = (s_resp_valid && !(TwoCycleErrors && s_axi_err)) || + (s_fsm_q == FSM_ERROR_RESP); assign ahbl.hready = (s_fsm_q == FSM_IDLE) || s_terminal; - assign ahbl.hresp = s_terminal && - (((s_fsm_q == FSM_WRITE_RESP) && (axi4.bresp != `AXI4_RESP_OKAY)) || - ((s_fsm_q == FSM_READ_RESP) && (axi4.rresp != `AXI4_RESP_OKAY))) ? - AHBL_RESP_ERROR : AHBL_RESP_OKAY; + assign ahbl.hresp = (s_axi_err || (s_fsm_q == FSM_ERROR_RESP)) ? AHBL_RESP_ERROR : AHBL_RESP_OKAY; assign ahbl.hrdata = axi4.rdata; always_comb begin @@ -155,6 +165,9 @@ module ahbl2axi4 ( FSM_READ: begin if (axi4.arvalid && axi4.arready) s_fsm_d = FSM_READ_RESP; end + FSM_WRITE_RESP, FSM_READ_RESP: begin + if (TwoCycleErrors && s_axi_err) s_fsm_d = FSM_ERROR_RESP; + end default: begin end endcase diff --git a/rtl/mini/top/apb4_if_bridge.sv b/rtl/ip/interconnect/apb4_if_bridge.sv similarity index 100% rename from rtl/mini/top/apb4_if_bridge.sv rename to rtl/ip/interconnect/apb4_if_bridge.sv diff --git a/rtl/mini/top/axi4_connector.sv b/rtl/ip/interconnect/axi4_connector.sv similarity index 100% rename from rtl/mini/top/axi4_connector.sv rename to rtl/ip/interconnect/axi4_connector.sv diff --git a/rtl/mini/top/axi4_error_slave.sv b/rtl/ip/interconnect/axi4_error_slave.sv similarity index 100% rename from rtl/mini/top/axi4_error_slave.sv rename to rtl/ip/interconnect/axi4_error_slave.sv diff --git a/rtl/mini/top/onchip_ram.sv b/rtl/ip/memory/onchip_ram.sv similarity index 100% rename from rtl/mini/top/onchip_ram.sv rename to rtl/ip/memory/onchip_ram.sv diff --git a/rtl/mini/top/onchip_ram_define.svh b/rtl/ip/memory/onchip_ram_define.svh similarity index 100% rename from rtl/mini/top/onchip_ram_define.svh rename to rtl/ip/memory/onchip_ram_define.svh diff --git a/rtl/mini/top/onchip_ram_reg.sv b/rtl/ip/memory/onchip_ram_reg.sv similarity index 100% rename from rtl/mini/top/onchip_ram_reg.sv rename to rtl/ip/memory/onchip_ram_reg.sv diff --git a/rtl/ip/peripheral/apb4_dma.sv b/rtl/ip/peripheral/apb4_dma.sv index 053038d0..9cc49bdd 100644 --- a/rtl/ip/peripheral/apb4_dma.sv +++ b/rtl/ip/peripheral/apb4_dma.sv @@ -9,11 +9,13 @@ // See the Mulan PSL v2 for more details. module apb4_dma #( - parameter int AddrWidth = 32, - parameter int DataWidth = 32, - parameter int NumChannels = 4, - parameter int MaxBurstBeats = 16, - parameter int FifoDepth = 16 + parameter int AddrWidth = 32, + parameter int DataWidth = 32, + parameter int NumChannels = 4, + parameter int MaxBurstBeats = 16, + parameter int FifoDepth = 16, + parameter logic [15:0] RequestMask = 16'h3fff, + parameter bit EnableStreams = 1'b1 ) ( // verilog_format: off -- integration ports retain established peripheral endpoint names. input logic clk_i, @@ -72,7 +74,8 @@ module apb4_dma #( dma_reg #( .NumChannels (NumChannels), .DataWidth (DataWidth), - .MaxBurstBeats(MaxBurstBeats) + .MaxBurstBeats(MaxBurstBeats), + .EnableStreams(EnableStreams) ) u_dma_reg ( .clk_i (clk_i), .rst_n_i (rst_n_i), @@ -119,6 +122,8 @@ module apb4_dma #( ); dma_core #( + .RequestMask (RequestMask), + .EnableStreams(EnableStreams), .AddrWidth (AddrWidth), .DataWidth (DataWidth), .NumChannels (NumChannels), diff --git a/rtl/ip/peripheral/dma_core.sv b/rtl/ip/peripheral/dma_core.sv index b7c28ae5..ea373cce 100644 --- a/rtl/ip/peripheral/dma_core.sv +++ b/rtl/ip/peripheral/dma_core.sv @@ -12,13 +12,15 @@ `include "axi4_define.svh" module dma_core #( - parameter int AddrWidth = 32, - parameter int DataWidth = 32, - parameter int NumChannels = 4, - parameter int ChannelIndexWidth = (NumChannels > 1) ? $clog2(NumChannels) : 1, - parameter int MaxBurstBeats = 16, - parameter int FifoDepth = 16, - parameter bit EnableCrc = 1'b1 + parameter int AddrWidth = 32, + parameter int DataWidth = 32, + parameter int NumChannels = 4, + parameter int ChannelIndexWidth = (NumChannels > 1) ? $clog2(NumChannels) : 1, + parameter int MaxBurstBeats = 16, + parameter int FifoDepth = 16, + parameter bit EnableCrc = 1'b1, + parameter logic [15:0] RequestMask = 16'h3fff, + parameter bit EnableStreams = 1'b1 ) ( // verilog_format: off -- channel vectors are kept aligned with the register-bank ABI. input logic clk_i, @@ -447,7 +449,9 @@ module dma_core #( s_start_valid[channel] = 1'b1; s_start_err_code[channel] = DMA_ERROR_CONFIG; - if ((s_cfg_width[channel] != DMA_WIDTH_32) || (s_cfg_byte_count[channel] == 32'd0) || + if (!RequestMask[s_cfg_request[channel]] || + (!EnableStreams && (s_cfg_kind[channel] != DMA_KIND_MM_TO_MM)) || + (s_cfg_width[channel] != DMA_WIDTH_32) || (s_cfg_byte_count[channel] == 32'd0) || (s_cfg_burst[channel] == 5'd0) || (s_cfg_burst[channel] > 5'(MaxBurstBeats))) begin s_start_valid[channel] = 1'b0; @@ -1268,7 +1272,9 @@ module dma_core #( if (s_tcd_parse_q) begin s_tcd_parse_q <= 1'b0; - if (s_err_q[s_tcd_read_owner_q] || + if (!RequestMask[s_tcd_word7_q[s_tcd_read_owner_q][15:12]] || + (!EnableStreams && (s_tcd_word7_q[s_tcd_read_owner_q][10:8] != DMA_KIND_MM_TO_MM)) || + s_err_q[s_tcd_read_owner_q] || (s_tcd_word7_q[s_tcd_read_owner_q][0] == 1'b0) || (s_tcd_word3_q[s_tcd_read_owner_q] == 32'd0) || (s_tcd_word1_q[s_tcd_read_owner_q][1:0] != 2'b00) || diff --git a/rtl/ip/peripheral/dma_reg.sv b/rtl/ip/peripheral/dma_reg.sv index e691d29e..da3b3860 100644 --- a/rtl/ip/peripheral/dma_reg.sv +++ b/rtl/ip/peripheral/dma_reg.sv @@ -12,6 +12,7 @@ module dma_reg #( parameter int NumChannels = 4, + parameter bit EnableStreams = 1'b1, parameter int ChannelIndexWidth = (NumChannels > 1) ? $clog2(NumChannels) : 1, parameter int DataWidth = 32, parameter int MaxBurstBeats = 16 @@ -350,7 +351,15 @@ module dma_reg #( `APB4_DMA__IP_ID: s_read_data = IpId; `APB4_DMA__IP_VERSION: s_read_data = IpVersion; `APB4_DMA__CAPABILITY: - s_read_data = {1'b0, 3'd3, 4'(MaxBurstBeats), 8'(DataWidth), 8'(NumChannels), 7'd0, 1'b1}; + s_read_data = { + 1'b0, + (EnableStreams ? 3'd3 : 3'd0), + 4'(MaxBurstBeats), + 8'(DataWidth), + 8'(NumChannels), + 7'd0, + 1'b1 + }; `APB4_DMA__GLOBAL_CTRL: s_read_data = 32'd0; `APB4_DMA__GLOBAL_STATUS: s_read_data = {31'd0, |busy_i}; `APB4_DMA__IRQ_STATE: s_read_data = {{(32 - NumChannels) {1'b0}}, s_irq_state}; diff --git a/rtl/mini/top/gpio_pad_bridge.sv b/rtl/ip/peripheral/gpio_pad_bridge.sv similarity index 100% rename from rtl/mini/top/gpio_pad_bridge.sv rename to rtl/ip/peripheral/gpio_pad_bridge.sv diff --git a/rtl/ip/storage/apb4_xpi.sv b/rtl/ip/storage/apb4_xpi.sv index 8205879a..b07e4d01 100644 --- a/rtl/ip/storage/apb4_xpi.sv +++ b/rtl/ip/storage/apb4_xpi.sv @@ -10,7 +10,9 @@ `include "xpi_define.svh" -`ifdef SIMU_VERILATOR +`ifdef RETROSOC_SOC__TINY +`define APB4_XPI_CORE_MODULE xpi_core +`elsif SIMU_VERILATOR `define APB4_XPI_CORE_MODULE xpi_core_verilator `else `define APB4_XPI_CORE_MODULE xpi_core diff --git a/rtl/mini/top/soc_common_cdc.sv b/rtl/ip/util/soc_common_cdc.sv similarity index 100% rename from rtl/mini/top/soc_common_cdc.sv rename to rtl/ip/util/soc_common_cdc.sv diff --git a/rtl/mini/top/soc_common_fifo.sv b/rtl/ip/util/soc_common_fifo.sv similarity index 100% rename from rtl/mini/top/soc_common_fifo.sv rename to rtl/ip/util/soc_common_fifo.sv diff --git a/rtl/mini/Makefile b/rtl/mini/Makefile index 15a95b63..017020b6 100644 --- a/rtl/mini/Makefile +++ b/rtl/mini/Makefile @@ -1,6 +1,6 @@ GENERATED_FL_DIR := $(FLOW_FILELIST_DIR) MEMORY_MAP_SOURCE := $(RTL_PATH)/address_map/memory_map.json -MEMORY_MAP_SCRIPT := $(RTL_PATH)/address_map/generate_memory_map.py +MEMORY_MAP_SCRIPT := $(ROOT_PATH)/scripts/rtl/generate_memory_map.py MEMORY_MAP_DIR := $(VARIANT_ROOT)/generated/memory_map MEMORY_MAP_STAMP := $(MEMORY_MAP_DIR)/.stamp MEMORY_MAP_RTL_DIR := $(MEMORY_MAP_DIR)/rtl @@ -8,7 +8,7 @@ MEMORY_MAP_C_DIR := $(MEMORY_MAP_DIR)/include MEMORY_MAP_LINKER_DIR := $(MEMORY_MAP_DIR)/linker MEMORY_MAP_FILELIST := $(MEMORY_MAP_DIR)/memory_map.fl PIN_MAP_SOURCE := $(RTL_PATH)/pin_map/pin_map.json -PIN_MAP_SCRIPT := $(RTL_PATH)/pin_map/generate_pin_map.py +PIN_MAP_SCRIPT := $(ROOT_PATH)/scripts/rtl/generate_pin_map.py PIN_MAP_DIR := $(VARIANT_ROOT)/generated/pin_map PIN_MAP_STAMP := $(PIN_MAP_DIR)/.stamp PIN_MAP_FILELIST := $(PIN_MAP_DIR)/pin_map.fl @@ -132,7 +132,7 @@ check-clock-reset-domains: $(FLOW_PYTHON) $(CLOCK_RESET_DOMAINS_SCRIPT) --map $(CLOCK_RESET_DOMAINS_SOURCE) --root $(ROOT_PATH) $(FILELIST_STAMP): $(FILELIST_TEMPLATES) $(RTL_PATH)/script/generate_filelist.py $(LOCAL_RTL_FILES) \ - $(RTL_PATH)/script/filelist.py $(MEMORY_MAP_STAMP) $(PIN_MAP_STAMP) $(SOC_TOPOLOGY_STAMP) \ + $(ROOT_PATH)/scripts/rtl/filelist.py $(MEMORY_MAP_STAMP) $(PIN_MAP_STAMP) $(SOC_TOPOLOGY_STAMP) \ $(USER_EXTENSIONS_STAMP) $(ARCHINFO_METADATA_STAMP) \ $(if $(filter MPW,$(MINI_MODE)),$(MPW_VARIANT_STAMP)) \ $(if $(filter YES,$(HAVE_HP)),$(HP_GENERATED_STAMP)) @@ -146,7 +146,7 @@ $(FILELIST_STAMP): $(FILELIST_TEMPLATES) $(RTL_PATH)/script/generate_filelist.py generate_filelist: $(FILELIST_STAMP) # software -include $(RTL_PATH)/mk/software.mk +include $(ROOT_PATH)/rtl/mk/software.mk #====================== filelist ====================== ifeq ($(PDK), IHP130) diff --git a/rtl/mini/README.md b/rtl/mini/README.md index a51b4d2d..04689708 100644 --- a/rtl/mini/README.md +++ b/rtl/mini/README.md @@ -1,8 +1,12 @@ # Mini SoC Integration -This directory is the active Mini SoC integration boundary. +This directory is the Mini SoC integration boundary. Tiny owns a separate +[MCU integration](../tiny/README.md). Shared CPU/debug, memory, bus helpers and +software build logic are owned outside Mini. Filelists, build rules and tests +reference their canonical shared paths directly. -- address_map contains the canonical address-map input and generator. +- address_map contains the canonical Mini address-map input. Its generator + lives in `scripts/rtl/generate_memory_map.py`. - core contains self-owned Mini core wrappers. - dv contains behavioural testbench, device models, and Verilator harness sources. @@ -10,8 +14,10 @@ This directory is the active Mini SoC integration boundary. behavioral/synthesis Common source set; `netlist_support.fl` is the explicit allowlist of testbench dependencies not already present in a synthesized netlist. -- mk contains simulator and software make fragments. -- script contains filelist, address-map, conversion, and setup helpers. +- mk contains Mini simulator and formal make fragments; shared firmware rules + live in `rtl/mk/software.mk`. +- script contains Mini filelist generation and simulator-binding helpers. + Shared parsing/conversion helpers live in `scripts/rtl`. - top contains SoC integration RTL, including `apb4_system` for the APB4 platform block and `apb4_periph` for the APB4 peripheral container. diff --git a/rtl/mini/address_map/README.md b/rtl/mini/address_map/README.md index 02178dea..cc7de341 100644 --- a/rtl/mini/address_map/README.md +++ b/rtl/mini/address_map/README.md @@ -1,6 +1,7 @@ # Canonical Address Map -memory_map.json is the sole source for Mini SoC address ranges. Its generator +`memory_map.json` is the sole source for Mini SoC address ranges. The shared +[generator](../../../scripts/rtl/generate_memory_map.py) emits RTL include definitions, SDK headers, linker memory regions, and the filelist include path into the selected build variant. diff --git a/rtl/mini/filelist/commonip.fl b/rtl/mini/filelist/commonip.fl index f5da1514..68a45917 100644 --- a/rtl/mini/filelist/commonip.fl +++ b/rtl/mini/filelist/commonip.fl @@ -9,7 +9,7 @@ /clusterip/common/rtl/interface/ram_if.sv /top/rib_if.sv /clusterip/common/rtl/utils/register.sv -/top/soc_common_fifo.sv +/ip/util/soc_common_fifo.sv /clusterip/common/rtl/utils/spill_register.sv /clusterip/common/rtl/stream/round_robin_arbiter.sv /clusterip/common/rtl/utils/lfsr.sv @@ -20,7 +20,7 @@ /clusterip/common/rtl/cdc/cdc_sync.sv /clusterip/common/rtl/cdc/cdc_rst_ctrlr.sv /clusterip/common/rtl/cdc/cdc_2phase.sv -/top/soc_common_cdc.sv +/ip/util/soc_common_cdc.sv /clusterip/common/rtl/clkrst/clk_int_div.sv /clusterip/common/rtl/clock/safe_clock_mux.sv /clusterip/common/rtl/clkrst/rst_sync.sv diff --git a/rtl/mini/filelist/inc.fl b/rtl/mini/filelist/inc.fl index 2f1af8e9..3f625127 100644 --- a/rtl/mini/filelist/inc.fl +++ b/rtl/mini/filelist/inc.fl @@ -13,6 +13,7 @@ +incdir+/clusterip/rtc/rtl +incdir+/clusterip/wdg/rtl +incdir+/clusterip/crc/rtl ++incdir+/ip/core +incdir+/ip/interconnect +incdir+/ip/util +incdir+/ip/memory diff --git a/rtl/mini/filelist/top.fl b/rtl/mini/filelist/top.fl index 61e1e8af..b481e6d5 100644 --- a/rtl/mini/filelist/top.fl +++ b/rtl/mini/filelist/top.fl @@ -1,11 +1,11 @@ -/top/apb4_if_bridge.sv +/ip/interconnect/apb4_if_bridge.sv /top/apb4_async_bridge.sv -/top/gpio_pad_bridge.sv +/ip/peripheral/gpio_pad_bridge.sv /top/memory_pad_mux.sv /top/ribp2rib.sv /top/rib2ribp.sv /top/rib2apb.sv -/top/axi4_connector.sv +/ip/interconnect/axi4_connector.sv /top/axi4_master_idle.sv /top/axi4_mgmt_router.sv /top/axi4_async_bridge.sv @@ -18,9 +18,9 @@ /top/fabric_monitor.sv /top/soc_data_plane.sv /top/data_plane_fault_cdc.sv -/top/axi4_error_slave.sv +/ip/interconnect/axi4_error_slave.sv /top/rib2axi4.sv -/top/ahbl2axi4.sv +/ip/interconnect/ahbl2axi4.sv /top/axi42apb4_system.sv /top/axi42apb4_periph.sv /top/axi4_word_bridge.sv @@ -33,8 +33,8 @@ /top/retrosoc.sv /top/retrosoc_asic.sv /top/retrosoc_core.sv -/top/onchip_ram_reg.sv -/top/onchip_ram.sv +/ip/memory/onchip_ram_reg.sv +/ip/memory/onchip_ram.sv /top/resource_controller.sv /top/apb4_periph.sv /top/apb4_system.sv @@ -46,10 +46,10 @@ /top/clock_frequency_monitor.sv /top/soc_clock_gate.sv /top/soc_clock_reset_subsystem.sv -/top/mgmt_debug_reset.sv +/ip/core/mgmt_debug_reset.sv /top/hp_lifecycle_controller.sv -/top/mgmt_debug_wrapper.sv -/top/mgmt_core_wrapper.sv +/ip/core/mgmt_debug_wrapper.sv +/ip/core/mgmt_core_wrapper.sv /top/hp_core_wrapper.sv /top/hp_subsystem.sv /top/user_core_design.sv diff --git a/rtl/mini/formal/generate_formal_filelist.py b/rtl/mini/formal/generate_formal_filelist.py index d40c176e..fb7c8a4a 100644 --- a/rtl/mini/formal/generate_formal_filelist.py +++ b/rtl/mini/formal/generate_formal_filelist.py @@ -10,10 +10,9 @@ from pathlib import Path SCRIPT_DIR = Path(__file__).resolve().parent ROOT = SCRIPT_DIR.parents[2] -FILELIST_DIR = ROOT / "rtl/mini/script" -sys.path.insert(0, str(FILELIST_DIR)) +sys.path.insert(0, str(ROOT)) -from filelist import FileList, write_filelist # noqa: E402 +from scripts.rtl.filelist import FileList, write_filelist # noqa: E402 COMMON_RTL = ROOT / "rtl/managed/clusterip/common/rtl" @@ -249,8 +248,8 @@ def source_files(target: str) -> list[Path]: COMMON_RTL / "utils/register.sv", COMMON_RTL / "utils/xchecker.sv", ROOT / "rtl/tech/tc_sram.sv", - TOP / "onchip_ram_reg.sv", - TOP / "onchip_ram.sv", + ROOT / "rtl/ip/memory/onchip_ram_reg.sv", + ROOT / "rtl/ip/memory/onchip_ram.sv", SCRIPT_DIR / "onchip_ram_formal.sv", ] if target == "opipsram": diff --git a/rtl/mini/integration/clock_reset_domains.json b/rtl/mini/integration/clock_reset_domains.json index 43ee342f..ca2b5763 100644 --- a/rtl/mini/integration/clock_reset_domains.json +++ b/rtl/mini/integration/clock_reset_domains.json @@ -68,7 +68,7 @@ "reset": "s_jtag_trst_n_sync", "reset_source": "jtag_trst_n_i", "reset_primitive": "rst_sync", - "path": "rtl/mini/top/mgmt_debug_wrapper.sv", + "path": "rtl/ip/core/mgmt_debug_wrapper.sv", "instance": "u_jtag_rst_sync", "sta": {"source_port": "jtag_tck_i_pad", "object_type": "port", "period_ns": 100.0, "async_group": "jtag"} }, diff --git a/rtl/mini/mk/formal.mk b/rtl/mini/mk/formal.mk index a44689b3..e773d1f6 100644 --- a/rtl/mini/mk/formal.mk +++ b/rtl/mini/mk/formal.mk @@ -178,8 +178,8 @@ FORMAL_SOURCE_FILES := $(RTL_PATH)/formal/bus_formal.sv \ $(ROOT_PATH)/rtl/ip/memory/psram_pkg.sv \ $(ROOT_PATH)/rtl/ip/memory/psram_axi4.sv \ $(ROOT_PATH)/rtl/ip/memory/psram_phy.sv \ - $(RTL_PATH)/top/onchip_ram_reg.sv \ - $(RTL_PATH)/top/onchip_ram.sv \ + $(ROOT_PATH)/rtl/ip/memory/onchip_ram_reg.sv \ + $(ROOT_PATH)/rtl/ip/memory/onchip_ram.sv \ $(ROOT_PATH)/rtl/tech/tc_sram.sv \ $(ROOT_PATH)/rtl/managed/clusterip/common/rtl/utils/register.sv \ $(ROOT_PATH)/rtl/managed/clusterip/common/rtl/utils/xchecker.sv \ @@ -242,11 +242,11 @@ $(FORMAL_DIR)/%/formal.fl: $(FORMAL_FILELIST_GENERATOR) $(FORMAL_SOURCE_FILES) \ --memory-map-dir $(MEMORY_MAP_DIR) --soc-topology-dir $(SOC_TOPOLOGY_DIR) \ --user-extensions-dir $(USER_EXTENSIONS_DIR) -$(FORMAL_DIR)/%/design.v: $(FORMAL_DIR)/%/formal.fl $(RTL_PATH)/script/convt_sv2v.py \ - $(RTL_PATH)/script/filelist.py $(FORMAL_SOURCE_FILES) +$(FORMAL_DIR)/%/design.v: $(FORMAL_DIR)/%/formal.fl $(ROOT_PATH)/scripts/rtl/convt_sv2v.py \ + $(ROOT_PATH)/scripts/rtl/filelist.py $(FORMAL_SOURCE_FILES) python3 $(ROOT_PATH)/scripts/run_flow.py --tool formal-sv2v \ --log $(@D)/sv2v.log --result $(@D)/result-sv2v.json \ - -- python3 $(RTL_PATH)/script/convt_sv2v.py -f $< --output $@ + -- python3 $(ROOT_PATH)/scripts/rtl/convt_sv2v.py -f $< --output $@ $(FORMAL_DIR)/%/prove.sby: $(FORMAL_DIR)/%/design.v \ $(FORMAL_SBY_GENERATOR) diff --git a/rtl/mini/mk/iverilog.mk b/rtl/mini/mk/iverilog.mk index e87c970e..ae1ce74a 100644 --- a/rtl/mini/mk/iverilog.mk +++ b/rtl/mini/mk/iverilog.mk @@ -36,8 +36,8 @@ IVERILOG_SIM_OPTS := +sim_timeout=$(RTL_SIM_TIMEOUT) +wave_$(WAVE) $(CONVERTED_SOC): $(if $(filter MPW,$(MINI_MODE)),$(MPW_VARIANT_STAMP)) $(FILELIST_STAMP) @mkdir -p $(@D) - python3 $(RTL_PATH)/script/convt_sv2v.py $(RTL_FLIST) --output $@ - python3 $(RTL_PATH)/script/filelist_deps.py $(RTL_FLIST) --target $@ \ + python3 $(ROOT_PATH)/scripts/rtl/convt_sv2v.py $(RTL_FLIST) --output $@ + python3 $(ROOT_PATH)/scripts/rtl/filelist_deps.py $(RTL_FLIST) --target $@ \ --output $(CONVERTED_DEPFILE) $(IVERILOG_BEHV_FLIST): $(FILELIST_STAMP) $(CONVERTED_SOC) $(IVERILOG_FILELIST_GEN) @@ -88,7 +88,7 @@ $(IVERILOG_BEHV_SIMV): $(IVERILOG_BEHV_FLIST) --log $(IVERILOG_BEHV_DIR)/compile.log --result $(IVERILOG_BEHV_DIR)/result-compile.json \ --cwd $(IVERILOG_BEHV_DIR) -- $(IVERILOG) $(IVERILOG_COMMON_OPTS) \ $(IVERILOG_TIME_OPTS) -f $< -o simv -s $(RTL_TOP) - python3 $(RTL_PATH)/script/filelist_deps.py -f $(IVERILOG_BEHV_FLIST) \ + python3 $(ROOT_PATH)/scripts/rtl/filelist_deps.py -f $(IVERILOG_BEHV_FLIST) \ --target $@ --output $(IVERILOG_BEHV_DEPFILE) $(IVERILOG_NETL_SIMV): $(IVERILOG_NETL_FLIST) @@ -97,7 +97,7 @@ $(IVERILOG_NETL_SIMV): $(IVERILOG_NETL_FLIST) --log $(IVERILOG_NETL_DIR)/compile.log --result $(IVERILOG_NETL_DIR)/result-compile.json \ --cwd $(IVERILOG_NETL_DIR) -- $(IVERILOG) $(IVERILOG_COMMON_OPTS) \ $(IVERILOG_TIME_OPTS) -f $< -o simv -s $(RTL_TOP) - python3 $(RTL_PATH)/script/filelist_deps.py -f $(IVERILOG_NETL_FLIST) \ + python3 $(ROOT_PATH)/scripts/rtl/filelist_deps.py -f $(IVERILOG_NETL_FLIST) \ --target $@ --output $(IVERILOG_NETL_DEPFILE) $(IVERILOG_POST_SIMV): $(IVERILOG_POST_FLIST) @@ -106,7 +106,7 @@ $(IVERILOG_POST_SIMV): $(IVERILOG_POST_FLIST) --log $(IVERILOG_POST_DIR)/compile.log --result $(IVERILOG_POST_DIR)/result-compile.json \ --cwd $(IVERILOG_POST_DIR) -- $(IVERILOG) $(IVERILOG_COMMON_OPTS) \ $(IVERILOG_POST_OPTS) -f $< -o simv -s $(RTL_TOP) -s retrosoc_sdf_annotator - python3 $(RTL_PATH)/script/filelist_deps.py -f $(IVERILOG_POST_FLIST) \ + python3 $(ROOT_PATH)/scripts/rtl/filelist_deps.py -f $(IVERILOG_POST_FLIST) \ --target $@ --output $(IVERILOG_POST_DEPFILE) comp: $(IVERILOG_BEHV_SIMV) @@ -114,7 +114,7 @@ netcomp: $(IVERILOG_NETL_SIMV) postcomp: $(IVERILOG_POST_SIMV) sim: comp - python3 $(RTL_PATH)/script/prepare_norflash.py --sim-dir $(IVERILOG_BEHV_DIR) \ + python3 $(ROOT_PATH)/scripts/rtl/prepare_norflash.py --sim-dir $(IVERILOG_BEHV_DIR) \ --firmware $(SW_BUILD_DIR)/$(SIM_FIRMWARE_NAME).hex python3 $(ROOT_PATH)/scripts/run_flow.py --tool iverilog-sim \ --log $(IVERILOG_BEHV_DIR)/sim.log --result $(IVERILOG_BEHV_DIR)/result-sim.json \ @@ -123,7 +123,7 @@ sim: comp --result $(IVERILOG_BEHV_DIR)/result-sim-check.json --require '$(SIM_SUCCESS_MARKER)' netsim: netcomp - python3 $(RTL_PATH)/script/prepare_norflash.py --sim-dir $(IVERILOG_NETL_DIR) \ + python3 $(ROOT_PATH)/scripts/rtl/prepare_norflash.py --sim-dir $(IVERILOG_NETL_DIR) \ --firmware $(SW_BUILD_DIR)/$(SIM_FIRMWARE_NAME).hex python3 $(ROOT_PATH)/scripts/run_flow.py --tool iverilog-sim \ --log $(IVERILOG_NETL_DIR)/sim.log --result $(IVERILOG_NETL_DIR)/result-sim.json \ @@ -132,7 +132,7 @@ netsim: netcomp --result $(IVERILOG_NETL_DIR)/result-sim-check.json --require '$(SIM_SUCCESS_MARKER)' netsim-boot: netcomp - python3 $(RTL_PATH)/script/prepare_norflash.py --sim-dir $(IVERILOG_NETL_DIR) \ + python3 $(ROOT_PATH)/scripts/rtl/prepare_norflash.py --sim-dir $(IVERILOG_NETL_DIR) \ --firmware $(SW_BUILD_DIR)/$(SIM_FIRMWARE_NAME).hex python3 $(ROOT_PATH)/scripts/run_flow.py --tool iverilog-sim \ --log $(IVERILOG_NETL_DIR)/sim.log --result $(IVERILOG_NETL_DIR)/result-sim.json \ @@ -142,7 +142,7 @@ netsim-boot: netcomp --result $(IVERILOG_NETL_DIR)/result-sim-check.json --require 'Hello retroSoC!' postsim: postcomp - python3 $(RTL_PATH)/script/prepare_norflash.py --sim-dir $(IVERILOG_POST_DIR) \ + python3 $(ROOT_PATH)/scripts/rtl/prepare_norflash.py --sim-dir $(IVERILOG_POST_DIR) \ --firmware $(SW_BUILD_DIR)/$(SIM_FIRMWARE_NAME).hex python3 $(ROOT_PATH)/scripts/run_flow.py --tool iverilog-sim \ --log $(IVERILOG_POST_DIR)/sim.log --result $(IVERILOG_POST_DIR)/result-sim.json \ diff --git a/rtl/mini/mk/vcs.mk b/rtl/mini/mk/vcs.mk index b39292d8..d08fd0dd 100644 --- a/rtl/mini/mk/vcs.mk +++ b/rtl/mini/mk/vcs.mk @@ -99,13 +99,13 @@ $(VCS_BEHV_SIMV) $(VCS_NETL_SIMV) $(VCS_POST_SIMV): $(MPW_VARIANT_DEP) $(FILELIS $(FLOW_PYTHON) $(ROOT_PATH)/scripts/run_flow.py --tool vcs --log $(DIR)/driver-compile.log \ --result $(DIR)/result-compile.json --cwd $(DIR) -- \ $(SIM_TOOL) $(COMMON_OPTS) $(OPTS) $(FLIST) -top $(RTL_TOP) $(COMP_LOG) - $(FLOW_PYTHON) $(RTL_PATH)/script/filelist_deps.py $(DEPS_ARGS) --target $@ --output $(DEPFILE) + $(FLOW_PYTHON) $(ROOT_PATH)/scripts/rtl/filelist_deps.py $(DEPS_ARGS) --target $@ --output $(DEPFILE) $(VCS_NETL_SIMV): $(SYN_BUILD_ROOT)/out/retrosoc_asic_yosys.v $(VCS_POST_SIMV): $(ROOT_PATH)/physical/sdf/retrosoc_asic.v $(ROOT_PATH)/physical/sdf/retrosoc_asic_CTS_MIN.sdf.gz sim netsim postsim: - $(FLOW_PYTHON) $(RTL_PATH)/script/prepare_norflash.py --sim-dir $(DIR) \ + $(FLOW_PYTHON) $(ROOT_PATH)/scripts/rtl/prepare_norflash.py --sim-dir $(DIR) \ --firmware $(SW_BUILD_DIR)/$(SIM_FIRMWARE_NAME).hex $(FLOW_PYTHON) $(ROOT_PATH)/scripts/run_flow.py --tool vcs-sim --stream-bytes \ --log $(DIR)/driver-sim.log \ diff --git a/rtl/mini/mk/verilator.mk b/rtl/mini/mk/verilator.mk index 01fa65c8..1c654349 100644 --- a/rtl/mini/mk/verilator.mk +++ b/rtl/mini/mk/verilator.mk @@ -74,7 +74,7 @@ $(VERILATOR_STAMP): $(MPW_VARIANT_DEP) $(FILELIST_STAMP) $(VERILATOR_EXTRA_SOURC python3 $(ROOT_PATH)/scripts/run_flow.py --tool verilator \ --log $(BUILD_DIR)/verilating.log --result $(BUILD_DIR)/result-verilate.json \ -- $(VERILATOR) $(VERILATOR_FLAGS) - python3 $(RTL_PATH)/script/filelist_deps.py $(RTL_FLIST) \ + python3 $(ROOT_PATH)/scripts/rtl/filelist_deps.py $(RTL_FLIST) \ $(foreach source,$(VERILATOR_EXTRA_SOURCES),--extra $(source)) \ --target $@ --output $(VERILATOR_DEPFILE) @touch $@ @@ -90,7 +90,7 @@ $(RTL_LINT_STAMP): $(MPW_VARIANT_DEP) $(FILELIST_STAMP) $(VERILATOR_EXTRA_SOURCE python3 $(ROOT_PATH)/scripts/run_flow.py --tool rtl-lint \ --log $(RTL_LINT_DIR)/lint.log --result $(RTL_LINT_DIR)/result-rtl-lint.json \ -- $(VERILATOR) $(RTL_LINT_FLAGS) - python3 $(RTL_PATH)/script/filelist_deps.py $(RTL_FLIST) \ + python3 $(ROOT_PATH)/scripts/rtl/filelist_deps.py $(RTL_FLIST) \ $(foreach source,$(VERILATOR_EXTRA_SOURCES),--extra $(source)) \ --target $@ --output $(RTL_LINT_DEPFILE) @touch $@ diff --git a/rtl/mini/pin_map/README.md b/rtl/mini/pin_map/README.md index b11e1b97..b307a18a 100644 --- a/rtl/mini/pin_map/README.md +++ b/rtl/mini/pin_map/README.md @@ -1,7 +1,8 @@ # Canonical SoC Pin Map `pin_map.json` is the single source for the Mini SoC logical pad ABI and its -ASIC, FPGA, Verilator, and RTL-testbench bindings. The generator emits +ASIC, FPGA, Verilator, and RTL-testbench bindings. The shared +[generator](../../../scripts/rtl/generate_pin_map.py) emits temporary SystemVerilog include files below the selected build variant; do not edit generated files. diff --git a/rtl/mini/script/gen_iverilog_filelist.py b/rtl/mini/script/gen_iverilog_filelist.py index 973bc120..cad21a1c 100644 --- a/rtl/mini/script/gen_iverilog_filelist.py +++ b/rtl/mini/script/gen_iverilog_filelist.py @@ -5,9 +5,12 @@ from __future__ import annotations import argparse import gzip import re +import sys from pathlib import Path -from filelist import atomic_write, parse_filelists, write_filelist +REPO_ROOT = Path(__file__).resolve().parents[3] +sys.path.insert(0, str(REPO_ROOT)) +from scripts.rtl.filelist import atomic_write, parse_filelists, write_filelist # noqa: E402 SCRIPT_DIR = Path(__file__).resolve().parent diff --git a/rtl/mini/script/generate_filelist.py b/rtl/mini/script/generate_filelist.py index 54234e77..bddf461c 100644 --- a/rtl/mini/script/generate_filelist.py +++ b/rtl/mini/script/generate_filelist.py @@ -4,9 +4,12 @@ from __future__ import annotations import argparse import re +import sys from pathlib import Path -from filelist import atomic_write +REPO_ROOT = Path(__file__).resolve().parents[3] +sys.path.insert(0, str(REPO_ROOT)) +from scripts.rtl.filelist import atomic_write # noqa: E402 SCRIPT_DIR = Path(__file__).resolve().parent diff --git a/rtl/mk/README.md b/rtl/mk/README.md new file mode 100644 index 00000000..f7eb1952 --- /dev/null +++ b/rtl/mk/README.md @@ -0,0 +1,6 @@ +# Shared RTL/SDK Build Rules + +`software.mk` owns common compiler, linker and firmware artifact rules for Mini +and Tiny. Product configuration and application source selection remain explicit. +Both products include this canonical file directly. Changes require both +product configuration tests, affected firmware builds, and SDK gates. diff --git a/rtl/mini/mk/software.mk b/rtl/mk/software.mk similarity index 92% rename from rtl/mini/mk/software.mk rename to rtl/mk/software.mk index 23197ec8..c57e7802 100644 --- a/rtl/mini/mk/software.mk +++ b/rtl/mk/software.mk @@ -142,6 +142,17 @@ CRT_SRCS := $(ROOT_PATH)/crt/arch/riscv/startup.S \ $(ROOT_PATH)/crt/src/hal/user_ip.c \ $(ROOT_PATH)/crt/src/hal/user_core.c +ifeq ($(SOC),TINY) +DEF_VAL += -DRS_SOC_TINY +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 +CRT_SRCS := $(foreach source,$(CRT_SRCS),$(if $(filter $(notdir $(source)),$(TINY_CRT_NAMES)),$(source))) +CRT_SRCS += $(ROOT_PATH)/crt/src/hal/sysctrl.c +endif + ifeq ($(HAVE_CSR),YES) CRT_SRCS += $(ROOT_PATH)/crt/arch/riscv/system_irq.S CRT_SRCS += $(ROOT_PATH)/crt/src/core/system_irq_handler.c @@ -154,7 +165,7 @@ APP_SRCS := APP_INC_DIRS := APP_CFLAGS := APP_CRT_SRCS := -APP_MK := $(ROOT_PATH)/app/apps/$(APP)/app.mk +APP_MK := $(ROOT_PATH)/app/apps/$(APP)/$(if $(filter TINY,$(SOC)),tiny.mk,app.mk) ifeq ($(wildcard $(APP_MK)),) $(error Application profile not found: $(APP_MK)) @@ -219,6 +230,11 @@ FORCE_VERSION: upd_ver_info: $(VERSION_HEADER) +ifeq ($(SOC),TINY) +asm: tiny-boot-image + cp $(SW_BUILD_DIR)/tiny_boot.hex $(SW_BUILD_DIR)/$(ASM_FIRMWARE_NAME).hex + cp $(SW_BUILD_DIR)/tiny_boot.bin $(SW_BUILD_DIR)/$(ASM_FIRMWARE_NAME).bin +else asm: $(MPW_VARIANT_DEP) $(MEMORY_MAP_STAMP) @mkdir -p $(SW_BUILD_DIR)/asm $(MAKE) -C $(ROOT_PATH)/app/asm OUT_DIR=$(SW_BUILD_DIR)/asm \ @@ -227,8 +243,10 @@ 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 + $(FIRMWARE_ELF): $(MPW_VARIANT_DEP) $(MEMORY_MAP_STAMP) $(SOC_TOPOLOGY_STAMP) $(SOC_IRQ_METADATA) $(USER_EXTENSIONS_STAMP) $(ARCHINFO_METADATA_STAMP) $(VERSION_HEADER) $(SRC_PATH) $(SW_HEADERS) $(LDS_PATH) \ - $(ROOT_PATH)/rtl/mini/mk/software.mk + $(ROOT_PATH)/rtl/mk/software.mk $(APP_MK) @mkdir -p $(SW_BUILD_DIR) cd $(SW_BUILD_DIR) && $(CP) -P -o $(LINK_TYPE).lds $(LDS_PATH) cp $(MEMORY_REGIONS_LD) $(SW_BUILD_DIR)/memory_regions.ld diff --git a/rtl/rtl_readiness.json b/rtl/rtl_readiness.json index d025efec..a1756008 100644 --- a/rtl/rtl_readiness.json +++ b/rtl/rtl_readiness.json @@ -5,7 +5,8 @@ "name": "owned-rtl", "paths": [ "rtl/ip", - "rtl/mini/top" + "rtl/mini/top", + "rtl/tiny/top" ], "status": "prototype", "baseline_revision": null, diff --git a/rtl/rtl_style_audit.json b/rtl/rtl_style_audit.json index 7619667a..4505846e 100644 --- a/rtl/rtl_style_audit.json +++ b/rtl/rtl_style_audit.json @@ -96,12 +96,21 @@ } ], "inventory": { + "rtl/ip/core": [ + "mgmt_core_wrapper.sv", + "mgmt_debug_reset.sv", + "mgmt_debug_wrapper.sv" + ], "rtl/ip/experimental": [ "apb4_gpu.sv", "axi4f2ribp.sv", "wb2ribp.sv" ], "rtl/ip/interconnect": [ + "ahbl2axi4.sv", + "apb4_if_bridge.sv", + "axi4_connector.sv", + "axi4_error_slave.sv", "axi4l2ribp.sv", "ribp2ribp.sv", "ribp_regslice.sv" @@ -110,6 +119,9 @@ "apb4_opipsram.sv", "apb4_psram.sv", "axi4_sdram.sv", + "onchip_ram.sv", + "onchip_ram_define.svh", + "onchip_ram_reg.sv", "opipsram_axi4.sv", "opipsram_core.sv", "opipsram_define.svh", @@ -125,11 +137,11 @@ "psram_phy.sv", "psram_pkg.sv", "psram_reg.sv", + "sdram_axi4.sv", "sdram_clkgen.sv", "sdram_core.sv", "sdram_define.svh", "sdram_pkg.sv", - "sdram_axi4.sv", "sdram_reg.sv" ], "rtl/ip/multimedia": [ @@ -142,8 +154,8 @@ "apu_codec_sequencer.sv", "apu_codec_transport.sv", "apu_control_store.sv", - "apu_dma.sv", "apu_define.svh", + "apu_dma.sv", "apu_entropy_engine.sv", "apu_kernel_engine.sv", "apu_kws_apum_profile.svh", @@ -159,9 +171,9 @@ "apu_primitive_dispatcher.sv", "apu_proof_memo.sv", "apu_reconstruction_engine.sv", + "apu_reg.sv", "apu_resampler.sv", "apu_ring_scheduler.sv", - "apu_reg.sv", "apu_stream_router.sv", "apu_transform_engine.sv", "axi4s_dvp.sv", @@ -171,8 +183,8 @@ "ga2d_addr_gen.sv", "ga2d_axi4_master.sv", "ga2d_core.sv", - "ga2d_dma.sv", "ga2d_define.svh", + "ga2d_dma.sv", "ga2d_pixel.sv", "ga2d_pkg.sv", "ga2d_reg.sv", @@ -217,7 +229,6 @@ ], "rtl/ip/peripheral": [ "apb4_clint.sv", - "clock_ctrl_if.sv", "apb4_dma.sv", "apb4_gpio.sv", "apb4_hp_mailbox.sv", @@ -229,6 +240,7 @@ "clint_if.sv", "clint_reg.sv", "clint_timebase.sv", + "clock_ctrl_if.sv", "dma_axi4_master.sv", "dma_core.sv", "dma_define.svh", @@ -238,6 +250,7 @@ "gpio_core.sv", "gpio_define.svh", "gpio_if.sv", + "gpio_pad_bridge.sv", "gpio_reg.sv", "hp_mailbox_define.svh", "pll_ctrl_if.sv", @@ -250,6 +263,17 @@ "timer_reg.sv", "user_gpio_if.sv" ], + "rtl/ip/security": [ + "apb4_crypto.sv", + "crypto_aes_core.sv", + "crypto_aes_engine.sv", + "crypto_define.svh", + "crypto_montgomery.sv", + "crypto_pkg.sv", + "crypto_rsa_core.sv", + "crypto_sha2_core.sv", + "crypto_sha2_engine.sv" + ], "rtl/ip/serial": [ "apb4_i2c.sv", "apb4_i2c_define.svh", @@ -280,17 +304,6 @@ "ws2812_if.sv", "ws2812_reg.sv" ], - "rtl/ip/security": [ - "apb4_crypto.sv", - "crypto_aes_core.sv", - "crypto_aes_engine.sv", - "crypto_define.svh", - "crypto_montgomery.sv", - "crypto_pkg.sv", - "crypto_rsa_core.sv", - "crypto_sha2_core.sv", - "crypto_sha2_engine.sv" - ], "rtl/ip/storage": [ "apb4_sdio.sv", "apb4_spisd.sv", @@ -324,9 +337,6 @@ "xpi_pkg.sv", "xpi_reg.sv" ], - "rtl/ip/util": [ - "async_fifo.sv" - ], "rtl/ip/usb": [ "apb4_usb2.sv", "usb2_define.svh", @@ -345,53 +355,49 @@ "usb2_ulpi_if.sv", "usb2_ulpi_link.sv" ], + "rtl/ip/util": [ + "async_fifo.sv", + "soc_common_cdc.sv", + "soc_common_fifo.sv" + ], "rtl/mini/top": [ - "ahbl2axi4.sv", "ahbl2ribp.sv", - "apb4_if_bridge.sv", "apb4_async_bridge.sv", "apb4_periph.sv", "apb4_system.sv", - "axi42apb4_system.sv", + "axi42apb4.svh", "axi42apb4_periph.sv", + "axi42apb4_state.svh", + "axi42apb4_system.sv", "axi42ram.sv", "axi42ribp.sv", "axi42ribp_burst.sv", - "axi4_bus.sv", "axi4_address_gate.sv", "axi4_async_bridge.sv", - "axi4_connector.sv", + "axi4_bus.sv", "axi4_data_crossbar.sv", - "axi4_target_guard.sv", "axi4_downsizer_64to32.sv", - "axi4_error_slave.sv", "axi4_id_prefix.sv", "axi4_interconnect.sv", "axi4_master_idle.sv", "axi4_mgmt_router.sv", + "axi4_target_guard.sv", "axi4_upsizer_32to64.sv", "axi4_word_bridge.sv", - "core_wrapper.sv", "clock_config_controller.sv", "clock_frequency_monitor.sv", + "core_wrapper.sv", "data_plane_fault_cdc.sv", "extension_compat.sv", "extension_dma_master.sv", "extension_slot.sv", "extension_subsystem.sv", "fabric_monitor.sv", - "gpio_pad_bridge.sv", "hp_axi4_mux3.sv", "hp_core_wrapper.sv", - "hp_subsystem.sv", "hp_lifecycle_controller.sv", - "mgmt_core_wrapper.sv", - "mgmt_debug_reset.sv", - "mgmt_debug_wrapper.sv", + "hp_subsystem.sv", "memory_pad_mux.sv", - "onchip_ram_define.svh", - "onchip_ram_reg.sv", - "onchip_ram.sv", "pll_rcu_controller.sv", "rcu.sv", "resource_controller.sv", @@ -412,24 +418,28 @@ "soc_clock_config.svh", "soc_clock_gate.sv", "soc_clock_reset_subsystem.sv", - "soc_common_cdc.sv", - "soc_common_fifo.sv", "soc_data_plane.sv", "user_core_design.sv", "user_core_top.sv", "user_extensions.svh", "user_ip_design_template.sv", "user_ip_top.sv", - "user_ip_wrapper.sv", - "axi42apb4.svh", - "axi42apb4_state.svh" + "user_ip_wrapper.sv" + ], + "rtl/tiny/top": [ + "retrosoc_tiny.sv", + "retrosoc_tiny_asic.sv", + "tiny_archinfo.sv", + "tiny_axi42apb4.sv", + "tiny_axi4_fabric.sv", + "tiny_sysctrl.sv" ] }, "reviewed_boundaries": [ { "paths": [ - "rtl/ip/experimental/axi4f2ribp.sv", "rtl/ip/experimental/apb4_gpu.sv", + "rtl/ip/experimental/axi4f2ribp.sv", "rtl/ip/experimental/wb2ribp.sv" ], "result": "inactive experimental source", @@ -471,16 +481,16 @@ }, { "paths": [ - "rtl/mini/top/onchip_ram.sv", - "rtl/mini/top/onchip_ram_reg.sv" + "rtl/ip/memory/onchip_ram.sv", + "rtl/ip/memory/onchip_ram_reg.sv" ], "result": "reviewed AXI and technology-memory boundary", "rationale": "AXI and APB state uses Common registers; the synchronous bank array retains the stable technology wrapper and PDK SRAM pin boundary." }, { "paths": [ - "rtl/mini/top/soc_common_cdc.sv", - "rtl/mini/top/soc_common_fifo.sv" + "rtl/ip/util/soc_common_cdc.sv", + "rtl/ip/util/soc_common_fifo.sv" ], "result": "managed compatibility wrapper retained", "rationale": "The narrow self-owned wrappers preserve required Verilator filename behavior while including managed Common source." diff --git a/rtl/rtl_style_manifest.json b/rtl/rtl_style_manifest.json index ffaf3c01..6a5f3141 100644 --- a/rtl/rtl_style_manifest.json +++ b/rtl/rtl_style_manifest.json @@ -4,7 +4,8 @@ "owned": { "roots": [ "rtl/ip", - "rtl/mini/top" + "rtl/mini/top", + "rtl/tiny/top" ], "suffixes": [ ".sv", @@ -18,7 +19,8 @@ "verification": { "roots": [ "rtl/mini/formal", - "rtl/mini/dv" + "rtl/mini/dv", + "rtl/tiny/dv" ], "suffixes": [ ".sv", diff --git a/rtl/tiny/Makefile b/rtl/tiny/Makefile new file mode 100644 index 00000000..faaaa48b --- /dev/null +++ b/rtl/tiny/Makefile @@ -0,0 +1,151 @@ +export CCACHE_DIR := $(CACHE_ROOT)/ccache/verilator +export CCACHE_TEMPDIR := $(CACHE_ROOT)/ccache/verilator/tmp + +TINY_GENERATED_DIR := $(VARIANT_ROOT)/generated/tiny +GENERATED_FL_DIR := $(FLOW_FILELIST_DIR) +FILELIST_STAMP := $(GENERATED_FL_DIR)/.stamp +MEMORY_MAP_DIR := $(TINY_GENERATED_DIR)/memory_map +MEMORY_MAP_C_DIR := $(MEMORY_MAP_DIR)/include +MEMORY_MAP_LINKER_DIR := $(MEMORY_MAP_DIR)/linker +MEMORY_MAP_STAMP := $(FILELIST_STAMP) +PIN_MAP_DIR := $(TINY_GENERATED_DIR)/pin_map +SOC_TOPOLOGY_INCLUDE_DIR := $(TINY_GENERATED_DIR)/include +SOC_TOPOLOGY_STAMP := $(FILELIST_STAMP) +SOC_IRQ_METADATA := $(SOC_TOPOLOGY_INCLUDE_DIR)/retrosoc/generated/irq_metadata.h +USER_EXTENSIONS_DIR := $(TINY_GENERATED_DIR) +USER_EXTENSIONS_STAMP := $(FILELIST_STAMP) +ARCHINFO_METADATA_DIR := $(TINY_GENERATED_DIR)/archinfo +ARCHINFO_METADATA_SCRIPT := $(ROOT_PATH)/rtl/managed/clusterip/archinfo/scripts/generate_metadata.py +ARCHINFO_METADATA_STAMP := $(ARCHINFO_METADATA_DIR)/.stamp +TINY_MAPS := $(wildcard $(RTL_PATH)/address_map/*.json $(RTL_PATH)/pin_map/*.json $(RTL_PATH)/integration/*.json) +TINY_TEMPLATES := $(wildcard $(RTL_PATH)/filelist/*.fl) +TINY_TOOLS := $(ROOT_PATH)/scripts/generate_tiny.py $(wildcard $(ROOT_PATH)/scripts/rtl/*.py) + +$(ARCHINFO_METADATA_STAMP): $(LOCK_FILE) $(wildcard $(ARCHINFO_METADATA_SCRIPT)) + @test -f $(ARCHINFO_METADATA_SCRIPT) || { echo "Run Tiny setup first" >&2; exit 1; } + $(PYTHON) $(ARCHINFO_METADATA_SCRIPT) --repo-root $(ROOT_PATH) --lock $(LOCK_FILE) \ + --config-digest $(CONFIG_DIGEST) $(if $(filter YES,$(BUILD_RELEASE)),--release) \ + --output-sv $(ARCHINFO_METADATA_DIR)/archinfo_integration_metadata.svh \ + --output-c $(ARCHINFO_METADATA_DIR)/archinfo_integration_metadata.h + @touch $@ + +$(FILELIST_STAMP): $(ROOT_PATH)/Makefile $(RTL_PATH)/Makefile $(TINY_MAPS) $(TINY_TEMPLATES) $(TINY_TOOLS) $(ARCHINFO_METADATA_STAMP) + $(PYTHON) $(ROOT_PATH)/scripts/generate_tiny.py --output $(TINY_GENERATED_DIR) \ + --filelists $(GENERATED_FL_DIR) $(foreach define,$(DEF_LIST),--define $(define)) + @touch $@ + +$(SOC_IRQ_METADATA): $(FILELIST_STAMP) + @test -f $@ + +generate_filelist memory-map pin-map soc-topology user-extensions: $(FILELIST_STAMP) +archinfo-metadata: $(ARCHINFO_METADATA_STAMP) +check-memory-map: + $(PYTHON) $(ROOT_PATH)/scripts/rtl/generate_memory_map.py --map $(RTL_PATH)/address_map/memory_map.json --check +check-pin-map: + $(PYTHON) $(ROOT_PATH)/scripts/rtl/generate_pin_map.py --map $(RTL_PATH)/pin_map/pin_map.json --check +check-soc-topology: + $(PYTHON) $(ROOT_PATH)/scripts/generate_tiny.py --check +check-clock-reset-domains: + $(PYTHON) $(ROOT_PATH)/scripts/check_clock_reset_domains.py --map $(RTL_PATH)/integration/clock_reset_domains.json --root $(ROOT_PATH) + +RTL_FILELISTS := $(addprefix $(GENERATED_FL_DIR)/,def.fl inc.fl commonip.fl clusterip.fl ip.fl tech.fl core_hazard3.fl top.fl) +RTL_FLIST := $(foreach filelist,$(RTL_FILELISTS),-f $(filelist)) +PDK_FILELIST := $(GENERATED_FL_DIR)/pdk_ihp130.fl +TINY_TB_FLIST := -f $(GENERATED_FL_DIR)/tb.fl +TOP_DESIGN := retrosoc_tiny_asic +RTL_NAME := retrosoc_tiny_asic +OPENSTA_NETLIST := $(SYN_BUILD_ROOT)/out/$(RTL_NAME)_yosys.v +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_LINT_LOG := $(VARIANT_ROOT)/lint/verilator/lint.log +SOC_SIM_TIME ?= 1800 +TINY_SIM_TIMEOUT ?= 20000000 +VERILATOR ?= verilator +IVERILOG ?= iverilog +VVP ?= vvp + +include $(ROOT_PATH)/rtl/mk/software.mk +-include $(TINY_DEPFILE) + +$(TINY_CONVERTED): $(FILELIST_STAMP) + @mkdir -p $(@D) + $(PYTHON) $(ROOT_PATH)/scripts/rtl/convt_sv2v.py $(RTL_FLIST) --output $@ + $(PYTHON) $(ROOT_PATH)/scripts/rtl/filelist_deps.py $(RTL_FLIST) \ + --target $@ --output $(TINY_DEPFILE) + +ifeq ($(SIMU),VERILATOR) +$(SIM_BUILD_ROOT)/emu: $(FILELIST_STAMP) $(wildcard $(RTL_PATH)/dv/*.sv) + @mkdir -p $(SIM_BUILD_ROOT) $(CCACHE_TEMPDIR) + $(PYTHON) $(ROOT_PATH)/scripts/run_flow.py --tool verilator \ + --log $(SIM_BUILD_ROOT)/compile.log --result $(SIM_BUILD_ROOT)/result-compile.json -- \ + $(VERILATOR) --binary --timing --assert -j $(JOBS) --top-module $(RTL_TOP) \ + -Wno-fatal --timescale 1ns/1ps --Mdir $(SIM_BUILD_ROOT)/obj_dir -o $(SIM_BUILD_ROOT)/emu \ + $(RTL_FLIST) -f $(PDK_FILELIST) $(TINY_TB_FLIST) + $(PYTHON) $(ROOT_PATH)/scripts/rtl/filelist_deps.py $(RTL_FLIST) $(TINY_TB_FLIST) \ + --target $@ --output $(TINY_DEPFILE) +comp: $(SIM_BUILD_ROOT)/emu +TINY_RUN := timeout --foreground $(SOC_SIM_TIME)s $(SIM_BUILD_ROOT)/emu +else +$(SIM_BUILD_ROOT)/simv: $(TINY_CONVERTED) $(wildcard $(RTL_PATH)/dv/*.sv) + $(PYTHON) $(ROOT_PATH)/scripts/run_flow.py --tool iverilog \ + --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) +comp: $(SIM_BUILD_ROOT)/simv +TINY_RUN := $(VVP) $(SIM_BUILD_ROOT)/simv +endif + +rtl-lint: $(FILELIST_STAMP) | manifest + $(PYTHON) $(ROOT_PATH)/scripts/run_flow.py --tool rtl-lint \ + --log $(TINY_LINT_LOG) --result $(VARIANT_ROOT)/lint/verilator/result-rtl-lint.json -- \ + $(VERILATOR) --lint-only --timing --assert --timescale 1ns/1ps --Wall -Wno-fatal \ + --top-module retrosoc_tiny_asic $(RTL_FLIST) -f $(PDK_FILELIST) +check-rtl-lint: rtl-lint + $(PYTHON) $(ROOT_PATH)/scripts/analyze_warnings.py check --root $(ROOT_PATH) \ + --profile $(PROFILE_NAME) --variant-root $(VARIANT_ROOT) --tool rtl-lint \ + --output $(META_DIR)/rtl-lint-warnings.json + +sim: comp firmware | manifest + $(PYTHON) $(ROOT_PATH)/scripts/run_flow.py --tool $(SIM_TOOL_NAME)-sim \ + --log $(SIM_BUILD_ROOT)/sim.log --result $(SIM_BUILD_ROOT)/result-sim.json -- \ + $(TINY_RUN) +firmware=$(SW_BUILD_DIR)/$(SIM_FIRMWARE_NAME).hex +max_cycles=$(TINY_SIM_TIMEOUT) + $(PYTHON) $(ROOT_PATH)/scripts/check_simulation.py --log $(SIM_BUILD_ROOT)/sim.log \ + --result $(SIM_BUILD_ROOT)/result-sim-check.json --require '$(SIM_SUCCESS_MARKER)' + +netcomp: $(FILELIST_STAMP) + @mkdir -p $(NETLIST_SIM_ROOT) + $(PYTHON) $(ROOT_PATH)/scripts/run_flow.py --tool iverilog \ + --log $(NETLIST_SIM_ROOT)/compile.log --result $(NETLIST_SIM_ROOT)/result-compile.json -- \ + $(IVERILOG) -g2012 -gno-specify -DRETROSOC_SOC__TINY_NETLIST -s $(RTL_TOP) -o $(NETLIST_SIM_ROOT)/simv \ + -f $(GENERATED_FL_DIR)/def.fl -f $(GENERATED_FL_DIR)/inc.fl -f $(PDK_FILELIST) \ + $(TINY_TB_FLIST) $(NETLIST_PATH) +netsim: netcomp firmware | manifest + $(PYTHON) $(ROOT_PATH)/scripts/run_flow.py --tool iverilog-sim \ + --log $(NETLIST_SIM_ROOT)/sim.log --result $(NETLIST_SIM_ROOT)/result-sim.json -- \ + $(VVP) $(NETLIST_SIM_ROOT)/simv +firmware=$(SW_BUILD_DIR)/$(SIM_FIRMWARE_NAME).hex +max_cycles=$(TINY_SIM_TIMEOUT) + $(PYTHON) $(ROOT_PATH)/scripts/check_simulation.py --log $(NETLIST_SIM_ROOT)/sim.log \ + --result $(NETLIST_SIM_ROOT)/result-sim-check.json --require '$(SIM_SUCCESS_MARKER)' + +.PHONY: generate_filelist memory-map pin-map soc-topology user-extensions archinfo-metadata \ + check-memory-map check-pin-map check-soc-topology check-clock-reset-domains comp sim \ + rtl-lint check-rtl-lint netcomp netsim netsim-boot + +TINY_BOOT_ELF := $(SW_BUILD_DIR)/tiny_boot.elf +$(TINY_BOOT_ELF): $(ROOT_PATH)/app/asm/tiny_boot.S $(FILELIST_STAMP) + @mkdir -p $(SW_BUILD_DIR) + $(CC) -march=rv32im_zicsr -mabi=ilp32 -nostdlib -I$(MEMORY_MAP_C_DIR) -I$(SOC_TOPOLOGY_INCLUDE_DIR) \ + -Wl,-Ttext=0x0,--entry=_start,--build-id=none -o $@ $< + $(OBJC) -O verilog $@ $(SW_BUILD_DIR)/tiny_boot.hex + $(OBJC) -O binary $@ $(SW_BUILD_DIR)/tiny_boot.bin + +tiny-boot-image: $(TINY_BOOT_ELF) +netsim-boot: netcomp tiny-boot-image | manifest + $(PYTHON) $(ROOT_PATH)/scripts/run_flow.py --tool iverilog-sim \ + --log $(NETLIST_SIM_ROOT)/sim.log --result $(NETLIST_SIM_ROOT)/result-sim.json -- \ + $(VVP) $(NETLIST_SIM_ROOT)/simv +firmware=$(SW_BUILD_DIR)/tiny_boot.hex +max_cycles=100000 + $(PYTHON) $(ROOT_PATH)/scripts/check_simulation.py --log $(NETLIST_SIM_ROOT)/sim.log \ + --result $(NETLIST_SIM_ROOT)/result-sim-check.json --require 'SIM_TEST_PASS' + +.PHONY: tiny-boot-image \ No newline at end of file diff --git a/rtl/tiny/README.md b/rtl/tiny/README.md new file mode 100644 index 00000000..59872d53 --- /dev/null +++ b/rtl/tiny/README.md @@ -0,0 +1,27 @@ +# Tiny MCU Integration + +Tiny is the independent single-Hazard3 wired MCU product. Its frozen contract +is [Tiny MCU](../../docs/ip/tiny-soc.md). Use +`make CONFIG=configs/ci/ihp130-tiny.mk setup` followed by +`make CONFIG=configs/ci/ihp130-tiny.mk firmware sim`. + +`top` owns product integration, `address_map`, `pin_map` and `integration` own +canonical address, pad, IRQ and clock/reset inputs, `filelist` selects sources, +`dv` owns product verification, and `mk` owns product configuration. Reusable +RTL lives in `rtl/ip`; Common and Hazard3 remain locked managed inputs. +No Tiny source list includes RIB/RIBP or Mini product RTL. Build rules invoke +shared helpers in `scripts/rtl` directly; generated files belong only under +the selected build variant. + +The initial IHP130 profile has 128 KiB SRAM, 24 MHz AXI32/APB4, four DMA channels, +and RV32IMC without atomics. Wireless, external RAM, HP cores and accelerators +are outside this release. Run both Icarus and Verilator, the Tiny directed +tests, and the IHP130 regression before claiming qualification. Synthesis and +STA use the Tiny top and clock inventory, not Mini constraints. + +`make CONFIG=configs/ci/ihp130-tiny.mk regress-pr` runs only Tiny's IHP130 +matrix. The regression runner also accepts `--soc TINY`; leaving it unset +retains the combined Mini/Tiny IHP130 matrix. `netsim-boot` uses the compact +assembly SRAM/pad test, while `netsim` uses the full SDK acceptance image. +See the [verification record](../../docs/ip/tiny-soc-verification.md) for +qualification boundaries and the observed reset-distribution timing deficit. diff --git a/rtl/tiny/address_map/memory_map.json b/rtl/tiny/address_map/memory_map.json new file mode 100644 index 00000000..0036f910 --- /dev/null +++ b/rtl/tiny/address_map/memory_map.json @@ -0,0 +1,406 @@ +{ + "schema_version": 2, + "cpu_reset": "0x00000000", + "sysctrl_registers": [ + { + "symbol": "CORESEL", + "offset": "0x00" + }, + { + "symbol": "IPSEL", + "offset": "0x04" + }, + { + "symbol": "PLL_CFG", + "offset": "0x08" + }, + { + "symbol": "PLL_CMD", + "offset": "0x0C" + }, + { + "symbol": "FAULT_STATUS", + "offset": "0x10" + }, + { + "symbol": "FAULT_ADDR", + "offset": "0x14" + }, + { + "symbol": "FAULT_COUNT", + "offset": "0x18" + }, + { + "symbol": "PLL_STATUS", + "offset": "0x1C" + }, + { + "symbol": "USER_CORE_RESET", + "offset": "0x20" + }, + { + "symbol": "USER_CORE_STATUS", + "offset": "0x24" + }, + { + "symbol": "FAULT_MASTER", + "offset": "0x28" + }, + { + "symbol": "FAULT_DETAIL", + "offset": "0x2C" + }, + { + "symbol": "PERF_CTRL", + "offset": "0x40" + }, + { + "symbol": "PERF_MGMT_WAIT_LO", + "offset": "0x44" + }, + { + "symbol": "PERF_MGMT_WAIT_HI", + "offset": "0x48" + }, + { + "symbol": "PERF_USER_WAIT_LO", + "offset": "0x4C" + }, + { + "symbol": "PERF_USER_WAIT_HI", + "offset": "0x50" + }, + { + "symbol": "PERF_DMA_WAIT_LO", + "offset": "0x54" + }, + { + "symbol": "PERF_DMA_WAIT_HI", + "offset": "0x58" + }, + { + "symbol": "PERF_APB4_PERIPH_WAIT_LO", + "offset": "0x5C" + }, + { + "symbol": "PERF_APB4_PERIPH_WAIT_HI", + "offset": "0x60" + }, + { + "symbol": "PERF_APB4_SYSTEM_WAIT_LO", + "offset": "0x64" + }, + { + "symbol": "PERF_APB4_SYSTEM_WAIT_HI", + "offset": "0x68" + }, + { + "symbol": "PERF_SDRAM_WAIT_LO", + "offset": "0x6C" + }, + { + "symbol": "PERF_SDRAM_WAIT_HI", + "offset": "0x70" + }, + { + "symbol": "PERF_PSRAM_WAIT_LO", + "offset": "0x74" + }, + { + "symbol": "PERF_PSRAM_WAIT_HI", + "offset": "0x78" + }, + { + "symbol": "PERF_FLASH_WAIT_LO", + "offset": "0x7C" + }, + { + "symbol": "PERF_FLASH_WAIT_HI", + "offset": "0x80" + }, + { + "symbol": "TEST_STATUS", + "offset": "0x84" + }, + { + "symbol": "RTC_WAKE_STATUS", + "offset": "0x88" + }, + { + "symbol": "PERF_SDIO0_WAIT_LO", + "offset": "0x8C" + }, + { + "symbol": "PERF_SDIO0_WAIT_HI", + "offset": "0x90" + }, + { + "symbol": "PERF_SDIO1_WAIT_LO", + "offset": "0x94" + }, + { + "symbol": "PERF_SDIO1_WAIT_HI", + "offset": "0x98" + }, + { + "symbol": "PERF_USB2_WAIT_LO", + "offset": "0x9C" + }, + { + "symbol": "PERF_USB2_WAIT_HI", + "offset": "0xA0" + }, + { + "symbol": "HP_CTRL", + "offset": "0xA4" + }, + { + "symbol": "HP_STATUS", + "offset": "0xA8" + }, + { + "symbol": "DEBUG_SELECT", + "offset": "0xAC" + }, + { + "symbol": "CLK_HP_REQ", + "offset": "0xB0" + }, + { + "symbol": "CLK_HP_CURRENT", + "offset": "0xB4" + }, + { + "symbol": "CLK_LP_CFG", + "offset": "0xB8" + }, + { + "symbol": "CLK_PCLK_CFG", + "offset": "0xBC" + }, + { + "symbol": "CLK_STATUS", + "offset": "0xC0" + }, + { + "symbol": "CLK_FAULT", + "offset": "0xC4" + }, + { + "symbol": "CLK_TIMEOUT", + "offset": "0xC8" + }, + { + "symbol": "CLK_GATE", + "offset": "0xCC" + }, + { + "symbol": "MEM_PAD_CTRL", + "offset": "0xD0" + }, + { + "symbol": "MEM_PAD_STATUS", + "offset": "0xD4" + }, + { + "symbol": "MEM_PAD_FAULT", + "offset": "0xD8" + } + ], + "regions": [ + { + "symbol": "FLASH", + "base": "0x00000000", + "size": "0x01000000", + "route": "axi4", + "kind": "active", + "public": true, + "linker": "FLASH", + "user_access": "ro", + "burst": "incr16" + }, + { + "symbol": "APB4_GPIO", + "base": "0x10000000", + "size": "0x00001000", + "route": "apb4_periph", + "kind": "active", + "public": true, + "user_access": "rw" + }, + { + "symbol": "APB4_UART0", + "base": "0x10001000", + "size": "0x00001000", + "route": "apb4_periph", + "kind": "active", + "public": true, + "user_access": "rw" + }, + { + "symbol": "APB4_TIM0", + "base": "0x10002000", + "size": "0x00001000", + "route": "apb4_periph", + "kind": "active", + "public": true, + "user_access": "rw" + }, + { + "symbol": "APB4_TIM1", + "base": "0x10003000", + "size": "0x00001000", + "route": "apb4_periph", + "kind": "active", + "public": true, + "user_access": "rw" + }, + { + "symbol": "APB4_I2C0", + "base": "0x10006000", + "size": "0x00001000", + "route": "apb4_periph", + "kind": "active", + "public": true, + "user_access": "rw" + }, + { + "symbol": "APB4_XPI", + "base": "0x10009000", + "size": "0x00001000", + "route": "apb4_periph", + "kind": "active", + "public": true, + "user_access": "none" + }, + { + "symbol": "APB4_DMA", + "base": "0x1000A000", + "size": "0x00001000", + "route": "apb4_periph", + "kind": "active", + "public": true, + "user_access": "none" + }, + { + "symbol": "APB4_SYSCTRL", + "base": "0x1000B000", + "size": "0x00001000", + "route": "apb4_periph", + "kind": "active", + "public": true, + "user_access": "none" + }, + { + "symbol": "APB4_CLINT", + "base": "0x10020000", + "size": "0x00010000", + "route": "apb4_periph", + "kind": "active", + "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", + "size": "0x00001000", + "route": "apb4_periph", + "kind": "active", + "public": true, + "user_access": "none" + }, + { + "symbol": "APB4_SRAM", + "base": "0x10017000", + "size": "0x00001000", + "route": "apb4_periph", + "kind": "active", + "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", + "size": "0x00001000", + "route": "apb4_system", + "kind": "active", + "public": true, + "user_access": "ro" + }, + { + "symbol": "APB4_PWM", + "base": "0x20002000", + "size": "0x00001000", + "route": "apb4_system", + "kind": "active", + "public": true, + "user_access": "rw" + }, + { + "symbol": "APB4_RTC", + "base": "0x20004000", + "size": "0x00001000", + "route": "apb4_system", + "kind": "active", + "public": true, + "user_access": "rw" + }, + { + "symbol": "APB4_WDG", + "base": "0x20005000", + "size": "0x00001000", + "route": "apb4_system", + "kind": "active", + "public": true, + "user_access": "none" + }, + { + "symbol": "SRAM", + "base": "0x30000000", + "size": "0x00020000", + "size_options": [ + "0x00001000", + "0x00004000", + "0x00008000", + "0x00010000", + "0x00020000" + ], + "route": "ram", + "kind": "active", + "public": true, + "linker": "SRAM", + "user_access": "rw", + "burst": "incr16" + }, + { + "symbol": "XPI", + "base": "0x50000000", + "size": "0x10000000", + "route": "axi4", + "kind": "active", + "public": true, + "user_access": "rw", + "burst": "incr16" + } + ], + "legacy_rib": false, + "capability_header": "retrosoc/generated/soc_features.h" +} diff --git a/rtl/tiny/dv/retrosoc_tiny_tb.sv b/rtl/tiny/dv/retrosoc_tiny_tb.sv new file mode 100644 index 00000000..d29f15c6 --- /dev/null +++ b/rtl/tiny/dv/retrosoc_tiny_tb.sv @@ -0,0 +1,86 @@ +// Copyright (c) 2026 Yuchi Miao +// SPDX-License-Identifier: MulanPSL-2.0 +`timescale 1ns / 1ps + +module retrosoc_tiny_tb; + logic s_clk_driver = 1'b0; + wire s_clk = s_clk_driver; + logic s_reset_driver = 1'b0; + wire s_rst_n = s_reset_driver; + wire s_uart0_tx, s_uart1_tx; + wire s_uart0_rx = 1'b1; + wire s_uart1_rx = 1'b1; + wire s_jtag_tck = 1'b0; + wire s_jtag_tms = 1'b1; + wire s_jtag_tdi = 1'b0; + wire s_jtag_trst_n = s_rst_n; + wire s_jtag_tdo; + tri1 [31:0] s_gpio; + wire s_xpi_sck; + wire [ 3:0] s_xpi_nss; + tri [ 3:0] s_xpi_data; + integer s_cycles; + integer s_max_cycles = 20000000; + integer s_done_cycles = 0; + logic [ 7:0] s_uart_byte; + +`ifdef RETROSOC_SOC__TINY_NETLIST + wire [7:0] s_test_code = { + u_dut.s_test_code_7_, + u_dut.s_test_code_6_, + u_dut.s_test_code_5_, + u_dut.s_test_code_4_, + u_dut.s_test_code_3_, + u_dut.s_test_code_2_, + u_dut.s_test_code_1_, + u_dut.s_test_code_0_ + }; +`else + wire [7:0] s_test_code = u_dut.s_test_code; +`endif + + always #20.833333 s_clk_driver = !s_clk_driver; + initial begin + if ($value$plusargs("max_cycles=%d", s_max_cycles)) begin + end + repeat (20) @(posedge s_clk); + @(negedge s_clk); + s_reset_driver = 1'b1; + 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); + s_done_cycles = s_done_cycles + 1; + if (s_done_cycles == 128) begin + $display("SIM_TEST_PASS Tiny cycles=%0d", s_cycles); + $finish; + end + end + end + if (s_done_cycles != 128) $fatal(1, "SIM_TEST_TIMEOUT Tiny cycles=%0d", s_cycles); + end + +`ifdef RETROSOC_SOC__TINY_NETLIST + // UART0 diagnostic output; SYSCTRL remains the authoritative test verdict. + initial + forever begin + @(negedge s_uart0_tx); + #1627.604; + for (int bit_index = 0; bit_index < 8; bit_index++) begin + s_uart_byte[bit_index] = s_uart0_tx; + #1085.069; + end + $write("%c", s_uart_byte); + end + +`endif + + retrosoc_tiny_asic u_dut ( + `include "retrosoc_asic_tb_bindings.svh" + ); + tiny_qspi_nor u_flash ( + .sck_i (s_xpi_sck), + .cs_n_i (s_xpi_nss[0]), + .data_io(s_xpi_data) + ); +endmodule diff --git a/rtl/tiny/dv/tiny_qspi_nor.sv b/rtl/tiny/dv/tiny_qspi_nor.sv new file mode 100644 index 00000000..9f2afc1b --- /dev/null +++ b/rtl/tiny/dv/tiny_qspi_nor.sv @@ -0,0 +1,91 @@ +// Copyright (c) 2026 Yuchi Miao +// SPDX-License-Identifier: MulanPSL-2.0 +`timescale 1ns / 1ps + +// Pin-level 0xEB NOR model: 8 command bits, 24 quad address bits, 8 mode +// bits and four dummy clocks. Unsupported commands fail closed. This model +// verifies reset-read transport; program/erase and analog NOR timing are not +// represented. Both simulators use exactly this same serial model. +module tiny_qspi_nor ( + input logic sck_i, + input logic cs_n_i, + inout wire [3:0] data_io +); + typedef enum logic [2:0] { + Command, + Address, + Mode, + Dummy, + Data + } state_e; + state_e s_state; + logic [ 7:0] s_memory [0:16777215]; + logic [ 7:0] s_command; + logic [23:0] s_address; + logic [ 3:0] s_output; + integer s_count; + integer s_nibble; + string s_firmware; + + assign data_io = (!cs_n_i && (s_state == Data)) ? s_output : 4'bzzzz; + initial begin + s_state = Command; + s_count = 0; + s_command = '0; + s_address = '0; + if (!$value$plusargs("firmware=%s", s_firmware)) $fatal(1, "Missing +firmware hex image"); + $readmemh(s_firmware, s_memory); + end + always @(posedge sck_i or posedge cs_n_i) begin + if (cs_n_i) begin + s_state = Command; + s_count = 0; + s_command = '0; + s_address = '0; + end else begin + case (s_state) + Command: begin + s_command = {s_command[6:0], data_io[0]}; + if (s_count == 7) begin + if (s_command != 8'heb) $fatal(1, "Unsupported NOR command %02x", s_command); + s_state = Address; + s_count = 0; + end else s_count = s_count + 1; + end + Address: begin + s_address = {s_address[19:0], data_io}; + if (s_count == 5) begin + s_state = Mode; + s_count = 0; + end else s_count = s_count + 1; + end + Mode: begin + if (s_count == 1) begin + s_state = Dummy; + s_count = 0; + end else s_count = s_count + 1; + end + Dummy: begin + if (s_count == 3) s_state = Data; + else s_count = s_count + 1; + end + Data: begin + end + default: $fatal(1, "Invalid NOR state"); + endcase + end + end + always @(negedge sck_i or posedge cs_n_i) begin + if (cs_n_i) begin + s_nibble = 0; + s_output = '0; + end else if (s_state == Data) begin + if ((s_nibble & 1) == 0) s_output = s_memory[s_address+(s_nibble/2)][7:4]; + else s_output = s_memory[s_address+(s_nibble/2)][3:0]; + s_nibble = s_nibble + 1; + end else begin + s_nibble = 0; + s_output = '0; + end + end +endmodule diff --git a/rtl/tiny/filelist/clusterip.fl b/rtl/tiny/filelist/clusterip.fl new file mode 100644 index 00000000..33e91aeb --- /dev/null +++ b/rtl/tiny/filelist/clusterip.fl @@ -0,0 +1,24 @@ +/clusterip/archinfo/rtl/archinfo_reg.sv +/clusterip/archinfo/rtl/apb4_archinfo.sv +/clusterip/pwm/rtl/pwm_pkg.sv +/clusterip/pwm/rtl/pwm_tickgen.sv +/clusterip/pwm/rtl/pwm_timer.sv +/clusterip/pwm/rtl/pwm_channel.sv +/clusterip/pwm/rtl/pwm_deadtime.sv +/clusterip/pwm/rtl/pwm_carrier.sv +/clusterip/pwm/rtl/pwm_capture.sv +/clusterip/pwm/rtl/pwm_core.sv +/clusterip/pwm/rtl/pwm_reg.sv +/clusterip/pwm/rtl/pwm_if.sv +/clusterip/pwm/rtl/apb4_pwm.sv +/clusterip/rtc/rtl/rtc_pkg.sv +/clusterip/rtc/rtl/rtc_if.sv +/clusterip/rtc/rtl/rtc_tickgen.sv +/clusterip/rtc/rtl/rtc_core.sv +/clusterip/rtc/rtl/rtc_reg.sv +/clusterip/rtc/rtl/apb4_rtc.sv +/clusterip/wdg/rtl/wdg_pkg.sv +/clusterip/wdg/rtl/wdg_if.sv +/clusterip/wdg/rtl/wdg_core.sv +/clusterip/wdg/rtl/wdg_reg.sv +/clusterip/wdg/rtl/apb4_wdg.sv diff --git a/rtl/tiny/filelist/commonip.fl b/rtl/tiny/filelist/commonip.fl new file mode 100644 index 00000000..d603e2c4 --- /dev/null +++ b/rtl/tiny/filelist/commonip.fl @@ -0,0 +1,22 @@ +/clusterip/common/rtl/interface/apb4_if.sv +/clusterip/common/rtl/interface/apb4_pure_if.sv +/clusterip/common/rtl/interface/ahbl_if.sv +/clusterip/common/rtl/interface/axi4_if.sv +/clusterip/common/rtl/interface/axi4_addr_gen.sv +/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/spill_register.sv +/clusterip/common/rtl/stream/round_robin_arbiter.sv +/clusterip/common/rtl/utils/lfsr.sv +/clusterip/common/rtl/utils/bin2gray.sv +/clusterip/common/rtl/utils/gray2bin.sv +/clusterip/common/rtl/base/ecc_secded.sv +/clusterip/common/rtl/utils/edge_det.sv +/clusterip/common/rtl/cdc/cdc_sync.sv +/clusterip/common/rtl/cdc/cdc_rst_ctrlr.sv +/clusterip/common/rtl/cdc/cdc_2phase.sv +/clusterip/common/rtl/clkrst/rst_sync.sv +/clusterip/common/rtl/clkrst/counter.sv +/ip/util/soc_common_fifo.sv +/ip/util/soc_common_cdc.sv diff --git a/rtl/tiny/filelist/core_hazard3.fl b/rtl/tiny/filelist/core_hazard3.fl new file mode 100644 index 00000000..19f4138a --- /dev/null +++ b/rtl/tiny/filelist/core_hazard3.fl @@ -0,0 +1,28 @@ +/hazard3/hazard3_core.v +/hazard3/hazard3_cpu_1port.v +/hazard3/hazard3_cpu_2port.v +/hazard3/arith/hazard3_alu.v +/hazard3/arith/hazard3_branchcmp.v +/hazard3/arith/hazard3_mul_fast.v +/hazard3/arith/hazard3_muldiv_seq.v +/hazard3/arith/hazard3_onehot_encode.v +/hazard3/arith/hazard3_onehot_priority.v +/hazard3/arith/hazard3_onehot_priority_dynamic.v +/hazard3/arith/hazard3_priority_encode.v +/hazard3/arith/hazard3_shift_barrel.v +/hazard3/hazard3_csr.v +/hazard3/hazard3_decode.v +/hazard3/hazard3_frontend.v +/hazard3/hazard3_instr_decompress.v +/hazard3/hazard3_irq_ctrl.v +/hazard3/hazard3_pmp.v +/hazard3/hazard3_power_ctrl.v +/hazard3/hazard3_regfile_1w2r.v +/hazard3/hazard3_triggers.v +/hazard3/debug/cdc/hazard3_apb_async_bridge.v +/hazard3/debug/cdc/hazard3_reset_sync.v +/hazard3/debug/cdc/hazard3_sync_1bit.v +/hazard3/debug/dtm/hazard3_jtag_dtm_core.v +/hazard3/debug/dtm/hazard3_jtag_dtm.v +/hazard3/debug/dm/hazard3_dm.v ++incdir+/hazard3/ diff --git a/rtl/tiny/filelist/inc.fl b/rtl/tiny/filelist/inc.fl new file mode 100644 index 00000000..6bfcb184 --- /dev/null +++ b/rtl/tiny/filelist/inc.fl @@ -0,0 +1,19 @@ ++incdir+/top ++incdir+/clusterip/common/rtl ++incdir+/clusterip/common/rtl/cdc ++incdir+/clusterip/common/rtl/clkrst ++incdir+/clusterip/common/rtl/clock ++incdir+/clusterip/common/rtl/interface ++incdir+/clusterip/common/rtl/tech ++incdir+/clusterip/common/rtl/utils ++incdir+/clusterip/archinfo/rtl ++incdir+/clusterip/pwm/rtl ++incdir+/clusterip/rtc/rtl ++incdir+/clusterip/wdg/rtl ++incdir+/ip/interconnect ++incdir+/ip/util ++incdir+/ip/memory ++incdir+/ip/storage ++incdir+/ip/serial ++incdir+/ip/peripheral ++incdir+/ip/core diff --git a/rtl/tiny/filelist/ip.fl b/rtl/tiny/filelist/ip.fl new file mode 100644 index 00000000..097d03f2 --- /dev/null +++ b/rtl/tiny/filelist/ip.fl @@ -0,0 +1,48 @@ +/ip/serial/i2c_if.sv +/ip/serial/i2c_filter.sv +/ip/serial/i2c_core.sv +/ip/serial/i2c_reg.sv +/ip/serial/apb4_i2c.sv +/ip/serial/uart_if.sv +/ip/serial/uart_baudgen.sv +/ip/serial/apb4_uart_tx.sv +/ip/serial/apb4_uart_rx.sv +/ip/serial/uart_flow_ctrl.sv +/ip/serial/uart_core.sv +/ip/serial/uart_reg.sv +/ip/serial/apb4_uart.sv +/ip/storage/xpi_pkg.sv +/ip/storage/xpi_if.sv +/ip/storage/xpi_reg.sv +/ip/storage/xpi_mm.sv +/ip/storage/xpi_indirect.sv +/ip/storage/xpi_clkgen.sv +/ip/storage/xpi_core.sv +/ip/storage/apb4_xpi.sv +/ip/peripheral/dma_pkg.sv +/ip/peripheral/dma_req_if.sv +/ip/peripheral/dma_axi4_master.sv +/ip/peripheral/dma_reg.sv +/ip/peripheral/dma_core.sv +/ip/peripheral/apb4_dma.sv +/ip/peripheral/clint_if.sv +/ip/peripheral/clint_reg.sv +/ip/peripheral/clint_core.sv +/ip/peripheral/apb4_clint.sv +/ip/peripheral/timer_core.sv +/ip/peripheral/timer_reg.sv +/ip/peripheral/apb4_timer.sv +/ip/peripheral/gpio_if.sv +/ip/peripheral/user_gpio_if.sv +/ip/peripheral/gpio_core.sv +/ip/peripheral/gpio_reg.sv +/ip/peripheral/apb4_gpio.sv +/ip/memory/onchip_ram_reg.sv +/ip/memory/onchip_ram.sv +/ip/interconnect/ahbl2axi4.sv +/ip/interconnect/axi4_connector.sv +/ip/interconnect/axi4_error_slave.sv +/ip/core/mgmt_debug_reset.sv +/ip/core/mgmt_debug_wrapper.sv +/ip/core/mgmt_core_wrapper.sv +/ip/peripheral/gpio_pad_bridge.sv diff --git a/rtl/tiny/filelist/netlist_support.fl b/rtl/tiny/filelist/netlist_support.fl new file mode 100644 index 00000000..71a99538 --- /dev/null +++ b/rtl/tiny/filelist/netlist_support.fl @@ -0,0 +1 @@ +// The Tiny testbench uses only scalar pad ports. diff --git a/rtl/tiny/filelist/sys_def.fl b/rtl/tiny/filelist/sys_def.fl new file mode 100644 index 00000000..58b038bd --- /dev/null +++ b/rtl/tiny/filelist/sys_def.fl @@ -0,0 +1 @@ +// Tiny uses no external DRAM model definitions. diff --git a/rtl/tiny/filelist/tb.fl b/rtl/tiny/filelist/tb.fl new file mode 100644 index 00000000..106d6b51 --- /dev/null +++ b/rtl/tiny/filelist/tb.fl @@ -0,0 +1,2 @@ +/dv/tiny_qspi_nor.sv +/dv/retrosoc_tiny_tb.sv diff --git a/rtl/tiny/filelist/tech.fl b/rtl/tiny/filelist/tech.fl new file mode 100644 index 00000000..e82b3a91 --- /dev/null +++ b/rtl/tiny/filelist/tech.fl @@ -0,0 +1,3 @@ +/tech/tc_io.sv +/tech/tc_clk.sv +/tech/tc_sram.sv diff --git a/rtl/tiny/filelist/top.fl b/rtl/tiny/filelist/top.fl new file mode 100644 index 00000000..fbea0eac --- /dev/null +++ b/rtl/tiny/filelist/top.fl @@ -0,0 +1,6 @@ +/top/tiny_axi4_fabric.sv +/top/tiny_axi42apb4.sv +/top/tiny_sysctrl.sv +/top/tiny_archinfo.sv +/top/retrosoc_tiny.sv +/top/retrosoc_tiny_asic.sv diff --git a/rtl/tiny/integration/clock_reset_domains.json b/rtl/tiny/integration/clock_reset_domains.json new file mode 100644 index 00000000..718581e9 --- /dev/null +++ b/rtl/tiny/integration/clock_reset_domains.json @@ -0,0 +1,57 @@ +{ + "schema_version": 2, + "reset_ports": [ + "ext_rst_n_i_pad", + "jtag_trst_n_i_pad" + ], + "domains": [ + { + "name": "system", + "clock": "clk_i", + "reset": "s_rst_n", + "reset_source": "rst_n_i", + "reset_primitive": "rst_sync", + "path": "rtl/tiny/top/retrosoc_tiny.sv", + "instance": "u_system_rst_sync", + "sta": { + "source_port": "extclk_i_pad", + "pin": "u_clock_buffer/clk_o", + "period_ns": 41.666666667, + "async_group": "system" + } + }, + { + "name": "jtag", + "clock": "s_jtag_tck_buf", + "reset": "s_jtag_trst_n_sync", + "reset_source": "jtag_trst_n_i", + "reset_primitive": "rst_sync", + "path": "rtl/ip/core/mgmt_debug_wrapper.sv", + "instance": "u_jtag_rst_sync", + "sta": { + "source_port": "jtag_tck_i_pad", + "object_type": "port", + "period_ns": 100.0, + "async_group": "jtag" + } + } + ], + "crossings": [ + { + "name": "debug_dmi", + "source": "jtag", + "destination": "system", + "primitive": "hazard3_apb_async_bridge", + "path": "rtl/managed/hazard3/hdl/debug/dtm/hazard3_jtag_dtm_core.v", + "instance": "inst_hazard3_apb_async_bridge" + }, + { + "name": "debug_hard_reset", + "source": "jtag", + "destination": "system", + "primitive": "cdc_sync", + "path": "rtl/ip/core/mgmt_debug_wrapper.sv", + "instance": "u_dmihardreset_cdc_sync" + } + ] +} diff --git a/rtl/tiny/integration/soc_topology.json b/rtl/tiny/integration/soc_topology.json new file mode 100644 index 00000000..004795d2 --- /dev/null +++ b/rtl/tiny/integration/soc_topology.json @@ -0,0 +1,614 @@ +{ + "schema_version": 1, + "gpio_pins": 32, + "irq_vector_width": 32, + "external_irq_count": 30, + "dma_channels": 4, + "apb_targets": [ + { + "name": "gpio", + "regions": [ + "APB4_GPIO", + "APB4_GPIO_ADMIN" + ] + }, + { + "name": "uart0", + "regions": [ + "APB4_UART0" + ] + }, + { + "name": "uart1", + "regions": [ + "APB4_UART1" + ] + }, + { + "name": "timer0", + "regions": [ + "APB4_TIM0" + ] + }, + { + "name": "timer1", + "regions": [ + "APB4_TIM1" + ] + }, + { + "name": "i2c0", + "regions": [ + "APB4_I2C0" + ] + }, + { + "name": "i2c1", + "regions": [ + "APB4_I2C1" + ] + }, + { + "name": "xpi", + "regions": [ + "APB4_XPI" + ] + }, + { + "name": "dma", + "regions": [ + "APB4_DMA" + ] + }, + { + "name": "sysctrl", + "regions": [ + "APB4_SYSCTRL" + ] + }, + { + "name": "clint", + "regions": [ + "APB4_CLINT" + ] + }, + { + "name": "sram", + "regions": [ + "APB4_SRAM" + ] + }, + { + "name": "archinfo", + "regions": [ + "APB4_ARCHINFO" + ] + }, + { + "name": "pwm", + "regions": [ + "APB4_PWM" + ] + }, + { + "name": "rtc", + "regions": [ + "APB4_RTC" + ] + }, + { + "name": "wdg", + "regions": [ + "APB4_WDG" + ] + } + ], + "interrupts": [ + { + "name": "clint_software", + "core_bit": 0, + "signal": "u_clint_if.software_irq_o[0]" + }, + { + "name": "clint_timer", + "core_bit": 1, + "signal": "u_clint_if.timer_irq_o[0]" + }, + { + "name": "uart0", + "core_bit": 2, + "signal": "u_uart0_if.irq_o" + }, + { + "name": "timer0", + "core_bit": 3, + "signal": "s_timer0_irq" + }, + { + "name": "timer1", + "core_bit": 4, + "signal": "s_timer1_irq" + }, + { + "name": "i2c0", + "core_bit": 7, + "signal": "u_i2c0_if.irq_o" + }, + { + "name": "xpi", + "core_bit": 9, + "signal": "s_xpi_irq" + }, + { + "name": "pwm", + "core_bit": 11, + "signal": "u_pwm_if.irq_o" + }, + { + "name": "rtc", + "core_bit": 13, + "signal": "u_rtc_if.irq_o" + }, + { + "name": "watchdog_early_warning", + "core_bit": 14, + "signal": "u_wdg_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" + } + ], + "gpio_alt_functions": [ + { + "pin": 0, + "alt0": { + "inputs": [ + "u_uart0_if.cts_n_i" + ], + "do": "1'b0", + "oe": "1'b0" + }, + "alt1": { + "inputs": [], + "do": "1'b0", + "oe": "1'b0" + } + }, + { + "pin": 1, + "alt0": { + "inputs": [], + "do": "u_uart0_if.rts_n_o", + "oe": "1'b1" + }, + "alt1": { + "inputs": [], + "do": "1'b0", + "oe": "1'b0" + } + }, + { + "pin": 2, + "alt0": { + "inputs": [ + "u_pwm_if.sync_i" + ], + "do": "1'b0", + "oe": "1'b0" + }, + "alt1": { + "inputs": [], + "do": "1'b0", + "oe": "1'b0" + } + }, + { + "pin": 3, + "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" + } + }, + { + "pin": 4, + "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" + } + }, + { + "pin": 5, + "alt0": { + "inputs": [], + "do": "u_pwm_if.pwm_o[2]", + "oe": "u_pwm_if.oe_o[2]" + }, + "alt1": { + "inputs": [], + "do": "1'b0", + "oe": "1'b0" + } + }, + { + "pin": 6, + "alt0": { + "inputs": [], + "do": "u_pwm_if.pwm_o[3]", + "oe": "u_pwm_if.oe_o[3]" + }, + "alt1": { + "inputs": [], + "do": "1'b0", + "oe": "1'b0" + } + }, + { + "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, + "alt0": { + "inputs": [ + "u_pwm_if.fault_i" + ], + "do": "1'b0", + "oe": "1'b0" + }, + "alt1": { + "inputs": [], + "do": "1'b0", + "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": [], + "do": "1'b0", + "oe": "1'b0" + }, + "alt1": { + "inputs": [], + "do": "1'b0", + "oe": "1'b0" + } + }, + { + "pin": 24, + "alt0": { + "inputs": [], + "do": "1'b0", + "oe": "1'b0" + }, + "alt1": { + "inputs": [], + "do": "1'b0", + "oe": "1'b0" + } + }, + { + "pin": 25, + "alt0": { + "inputs": [], + "do": "1'b0", + "oe": "1'b0" + }, + "alt1": { + "inputs": [], + "do": "1'b0", + "oe": "1'b0" + } + }, + { + "pin": 26, + "alt0": { + "inputs": [], + "do": "1'b0", + "oe": "1'b0" + }, + "alt1": { + "inputs": [], + "do": "1'b0", + "oe": "1'b0" + } + }, + { + "pin": 27, + "alt0": { + "inputs": [], + "do": "1'b0", + "oe": "1'b0" + }, + "alt1": { + "inputs": [], + "do": "1'b0", + "oe": "1'b0" + } + }, + { + "pin": 28, + "alt0": { + "inputs": [], + "do": "1'b0", + "oe": "1'b0" + }, + "alt1": { + "inputs": [], + "do": "1'b0", + "oe": "1'b0" + } + }, + { + "pin": 29, + "alt0": { + "inputs": [], + "do": "1'b0", + "oe": "1'b0" + }, + "alt1": { + "inputs": [], + "do": "1'b0", + "oe": "1'b0" + } + }, + { + "pin": 30, + "alt0": { + "inputs": [], + "do": "1'b0", + "oe": "1'b0" + }, + "alt1": { + "inputs": [ + "u_pwm_if.capture_i[0]" + ], + "do": "1'b0", + "oe": "1'b0" + } + }, + { + "pin": 31, + "alt0": { + "inputs": [], + "do": "1'b0", + "oe": "1'b0" + }, + "alt1": { + "inputs": [ + "u_pwm_if.capture_i[1]" + ], + "do": "1'b0", + "oe": "1'b0" + } + } + ] +} diff --git a/rtl/tiny/mk/config.mk b/rtl/tiny/mk/config.mk new file mode 100644 index 00000000..59e65aef --- /dev/null +++ b/rtl/tiny/mk/config.mk @@ -0,0 +1,49 @@ +# Tiny's first qualified integration is deliberately a fixed MCU profile. +ifneq ($(MINI_MODE),NONE) +$(error SOC=TINY requires MINI_MODE=NONE) +endif +ifneq ($(PDK),IHP130) +$(error SOC=TINY currently supports only PDK=IHP130) +endif +ifneq ($(HAVE_HP),NO) +$(error SOC=TINY requires HAVE_HP=NO) +endif +ifneq ($(HAVE_PLL),NO) +$(error SOC=TINY requires HAVE_PLL=NO) +endif +ifneq ($(HAVE_SRAM_IF),YES) +$(error SOC=TINY requires HAVE_SRAM_IF=YES) +endif +ifneq ($(HAVE_SRAM_MACRO),YES) +$(error SOC=TINY requires HAVE_SRAM_MACRO=YES) +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) +endif +ifneq ($(AUD_CLK_HZ),$(EXT_CLK_HZ)) +$(error SOC=TINY uses the system clock for RTC and watchdog) +endif +ifneq ($(CLINT_TIMEBASE_HZ),1000000) +$(error SOC=TINY requires CLINT_TIMEBASE_HZ=1000000) +endif +ifeq ($(filter $(SIMU),IVERILOG VERILATOR),) +$(error SOC=TINY supports SIMU=IVERILOG or VERILATOR) +endif +ifeq ($(filter $(APP),bringup ci_smoke),) +$(error SOC=TINY supports APP=bringup or ci_smoke) +endif +ifneq ($(LINK_TYPE),ld2_all_sram) +$(error SOC=TINY requires LINK_TYPE=ld2_all_sram) +endif +ifneq ($(HAVE_CSR),YES) +$(error SOC=TINY requires HAVE_CSR=YES) +endif +ifneq ($(ISA),RV32IM) +$(error SOC=TINY firmware uses ISA=RV32IM) +endif +ifneq ($(filter YES,$(APU_ENABLE_P7) $(NPU_P5_ACCEPTANCE) $(NPU_P6_ACCEPTANCE)),) +$(error SOC=TINY does not include multimedia accelerators) +endif \ No newline at end of file diff --git a/rtl/tiny/pin_map/pin_map.json b/rtl/tiny/pin_map/pin_map.json new file mode 100644 index 00000000..b02c05d8 --- /dev/null +++ b/rtl/tiny/pin_map/pin_map.json @@ -0,0 +1,306 @@ +{ + "schema_version": 1, + "pads": [ + { + "kind": "tri_input", + "direction": "inout", + "ports": [ + { + "name": "extclk_i_pad", + "signal": "s_ext_clk" + } + ] + }, + { + "kind": "schmitt_input", + "direction": "inout", + "ports": [ + { + "name": "ext_rst_n_i_pad", + "signal": "s_ext_rst_n" + } + ] + }, + { + "kind": "tri_input", + "direction": "inout", + "ports": [ + { + "name": "jtag_tck_i_pad", + "signal": "s_jtag_tck" + }, + { + "name": "jtag_tms_i_pad", + "signal": "s_jtag_tms" + }, + { + "name": "jtag_tdi_i_pad", + "signal": "s_jtag_tdi" + } + ] + }, + { + "kind": "schmitt_input", + "direction": "inout", + "ports": [ + { + "name": "jtag_trst_n_i_pad", + "signal": "s_jtag_trst_n" + } + ] + }, + { + "kind": "tri_output", + "direction": "output", + "ports": [ + { + "name": "jtag_tdo_o_pad", + "signal": "s_jtag_tdo" + } + ] + }, + { + "kind": "full", + "direction": "inout", + "prefix": "gpio_", + "suffix": "_io_pad", + "count": 32, + "interface": "u_gpio_if" + }, + { + "kind": "tri_output", + "direction": "output", + "ports": [ + { + "name": "uart0_tx_o_pad", + "signal": "s_uart0_tx" + } + ] + }, + { + "kind": "tri_input", + "direction": "inout", + "ports": [ + { + "name": "uart0_rx_i_pad", + "signal": "s_uart0_rx" + } + ] + }, + { + "kind": "tri_output", + "direction": "output", + "ports": [ + { + "name": "uart1_tx_o_pad", + "signal": "s_uart1_tx" + } + ] + }, + { + "kind": "tri_input", + "direction": "inout", + "ports": [ + { + "name": "uart1_rx_i_pad", + "signal": "s_uart1_rx" + } + ] + }, + { + "kind": "tri_output", + "direction": "output", + "ports": [ + { + "name": "xpi_sck_o_pad", + "signal": "u_xpi_if.sck_o" + } + ] + }, + { + "kind": "tri_output", + "direction": "output", + "prefix": "xpi_nss", + "suffix": "_o_pad", + "count": 4, + "signal": "u_xpi_if.nss_o[{index}]" + }, + { + "kind": "xpi_data", + "direction": "inout", + "prefix": "xpi_dat", + "suffix": "_io_pad", + "count": 4 + } + ], + "profiles": { + "fpga_mini": { + "bindings": { + "extclk_i_pad": "s_clk", + "ext_rst_n_i_pad": "s_rst_n", + "jtag_tck_i_pad": "s_jtag_tck", + "jtag_tms_i_pad": "s_jtag_tms", + "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]", + "uart0_tx_o_pad": "s_uart0_tx", + "uart0_rx_i_pad": "s_uart0_rx", + "uart1_tx_o_pad": "s_uart1_tx", + "uart1_rx_i_pad": "s_uart1_rx", + "xpi_sck_o_pad": "s_xpi_sck", + "xpi_nss0_o_pad": "s_xpi_nss[0]", + "xpi_nss1_o_pad": "s_xpi_nss[1]", + "xpi_nss2_o_pad": "s_xpi_nss[2]", + "xpi_nss3_o_pad": "s_xpi_nss[3]", + "xpi_dat0_io_pad": "s_xpi_data[0]", + "xpi_dat1_io_pad": "s_xpi_data[1]", + "xpi_dat2_io_pad": "s_xpi_data[2]", + "xpi_dat3_io_pad": "s_xpi_data[3]" + } + }, + "verilator": { + "bindings": { + "extclk_i_pad": "s_clk", + "ext_rst_n_i_pad": "s_rst_n", + "jtag_tck_i_pad": "s_jtag_tck", + "jtag_tms_i_pad": "s_jtag_tms", + "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]", + "uart0_tx_o_pad": "s_uart0_tx", + "uart0_rx_i_pad": "s_uart0_rx", + "uart1_tx_o_pad": "s_uart1_tx", + "uart1_rx_i_pad": "s_uart1_rx", + "xpi_sck_o_pad": "s_xpi_sck", + "xpi_nss0_o_pad": "s_xpi_nss[0]", + "xpi_nss1_o_pad": "s_xpi_nss[1]", + "xpi_nss2_o_pad": "s_xpi_nss[2]", + "xpi_nss3_o_pad": "s_xpi_nss[3]", + "xpi_dat0_io_pad": "s_xpi_data[0]", + "xpi_dat1_io_pad": "s_xpi_data[1]", + "xpi_dat2_io_pad": "s_xpi_data[2]", + "xpi_dat3_io_pad": "s_xpi_data[3]" + } + }, + "tb": { + "bindings": { + "extclk_i_pad": "s_clk", + "ext_rst_n_i_pad": "s_rst_n", + "jtag_tck_i_pad": "s_jtag_tck", + "jtag_tms_i_pad": "s_jtag_tms", + "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]", + "uart0_tx_o_pad": "s_uart0_tx", + "uart0_rx_i_pad": "s_uart0_rx", + "uart1_tx_o_pad": "s_uart1_tx", + "uart1_rx_i_pad": "s_uart1_rx", + "xpi_sck_o_pad": "s_xpi_sck", + "xpi_nss0_o_pad": "s_xpi_nss[0]", + "xpi_nss1_o_pad": "s_xpi_nss[1]", + "xpi_nss2_o_pad": "s_xpi_nss[2]", + "xpi_nss3_o_pad": "s_xpi_nss[3]", + "xpi_dat0_io_pad": "s_xpi_data[0]", + "xpi_dat1_io_pad": "s_xpi_data[1]", + "xpi_dat2_io_pad": "s_xpi_data[2]", + "xpi_dat3_io_pad": "s_xpi_data[3]" + } + } + } +} diff --git a/rtl/tiny/top/retrosoc_tiny.sv b/rtl/tiny/top/retrosoc_tiny.sv new file mode 100644 index 00000000..f8938bd4 --- /dev/null +++ b/rtl/tiny/top/retrosoc_tiny.sv @@ -0,0 +1,375 @@ +// Copyright (c) 2026 Yuchi Miao +// SPDX-License-Identifier: MulanPSL-2.0 +`include "mmap_define.svh" + +module retrosoc_tiny ( + input logic clk_i, + input logic rst_n_i, + input logic jtag_tck_i, + input logic jtag_tms_i, + input logic jtag_tdi_i, + input logic jtag_trst_n_i, + output logic jtag_tdo_o, + input logic uart0_rx_i, + output logic uart0_tx_o, + input logic uart1_rx_i, + output logic uart1_tx_o, + gpio_if.soc_pad gpio, + xpi_if.dut xpi, + output logic test_done_o, + output logic test_pass_o, + output logic [7:0] test_code_o +); + logic s_por_rst_n, s_rst_n; + logic s_debug_halted; + logic [31:0] s_irq; + logic s_fault_valid, s_fault_master, s_fault_write; + logic [31:0] s_fault_addr; + logic [ 1:0] s_fault_resp; + logic s_perf_enable, s_perf_clear; + logic [63:0] s_cpu_wait, s_dma_wait; + logic s_timer0_irq, s_timer1_irq, s_xpi_irq, s_dma_irq; + logic s_dma_done; + logic [7:0] s_dma_stall; + logic [4:0] s_tick_count_d, s_tick_count_q; + logic s_tick; + logic s_unused_uart1_tx_stall, s_unused_uart1_rx_stall; + uart_if u_uart0_if (); + uart_if u_uart1_if (); + i2c_if u_i2c0_if (); + i2c_if u_i2c1_if (); + pwm_if u_pwm_if (); + clint_if u_clint_if (); + gpio_if u_gpio_if (); + user_gpio_if u_user_gpio_if (); + dma_req_if u_dma_req_if (); + rtc_if u_rtc_if ( + .rtc_clk_i (clk_i), + .rtc_rst_n_i(s_rst_n) + ); + // The watchdog reset pulse must survive resetting the bus/CPU. Its timer + // uses POR reset while its APB register domain uses the system reset. + wdg_if u_wdg_if ( + .wdg_clk_i (clk_i), + .wdg_rst_n_i(s_por_rst_n) + ); + axi4_if #( + .ADDR_WIDTH(32), + .DATA_WIDTH(32), + .ID_WIDTH (1), + .USER_WIDTH(1) + ) u_masters_axi4_if[2] ( + .aclk (clk_i), + .aresetn(s_rst_n) + ); + axi4_if #( + .ADDR_WIDTH(32), + .DATA_WIDTH(32), + .ID_WIDTH (1), + .USER_WIDTH(1) + ) u_targets_axi4_if[5] ( + .aclk (clk_i), + .aresetn(s_rst_n) + ); + axi4_stream_if #( + .DATA_WIDTH(32) + ) u_i2s_tx_axis_if ( + .aclk (clk_i), + .aresetn(s_rst_n) + ); + axi4_stream_if #( + .DATA_WIDTH(32) + ) u_i2s_rx_axis_if ( + .aclk (clk_i), + .aresetn(s_rst_n) + ); + axi4_stream_if #( + .DATA_WIDTH(32) + ) u_dvp_rx_axis_if ( + .aclk (clk_i), + .aresetn(s_rst_n) + ); + axi4_stream_if #( + .DATA_WIDTH(32) + ) u_crypto_tx_axis_if ( + .aclk (clk_i), + .aresetn(s_rst_n) + ); + axi4_stream_if #( + .DATA_WIDTH(32) + ) u_crypto_rx_axis_if ( + .aclk (clk_i), + .aresetn(s_rst_n) + ); + `include "tiny_apb_interfaces.svh" + +rst_sync u_por_rst_sync ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .rst_n_o(s_por_rst_n) + ); + rst_sync u_system_rst_sync ( + .clk_i (clk_i), + .rst_n_i(s_por_rst_n && !u_wdg_if.reset_req_o), + .rst_n_o(s_rst_n) + ); + assign s_tick = s_tick_count_q == 5'd23; + assign s_tick_count_d = s_tick ? 5'd0 : (s_tick_count_q + 5'd1); + dffr #( + .DATA_WIDTH(5) + ) u_tick_reg ( + .clk_i (clk_i), + .rst_n_i(s_rst_n), + .dat_i (s_tick_count_d), + .dat_o (s_tick_count_q) + ); + mgmt_core_wrapper #( + .ExternalIrqCount (30), + .EnableAtomics (1'b0), + .TwoCycleBusErrors(1'b1) + ) u_cpu ( + .clk_i (clk_i), + .rst_n_i (s_rst_n), + .irq_i (s_irq), + .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), + .debug_halted_o(s_debug_halted), + .axi4 (u_masters_axi4_if[0]) + ); + tiny_axi4_fabric u_fabric ( + .clk_i (clk_i), + .rst_n_i (s_rst_n), + .masters (u_masters_axi4_if), + .targets (u_targets_axi4_if), + .perf_enable_i (s_perf_enable), + .perf_clear_i (s_perf_clear), + .fault_valid_o (s_fault_valid), + .fault_addr_o (s_fault_addr), + .fault_master_o(s_fault_master), + .fault_write_o (s_fault_write), + .fault_resp_o (s_fault_resp), + .cpu_wait_o (s_cpu_wait), + .dma_wait_o (s_dma_wait) + ); + onchip_ram #( + .CapacityKiB(128), + .DataWidth (32), + .IdWidth (1) + ) u_sram ( + .clk_i (clk_i), + .rst_n_i (s_rst_n), + .mem_axi4 (u_targets_axi4_if[0]), + .cfg_apb4 (u_sram_apb4_if), + .perf_enable_i(s_perf_enable), + .perf_clear_i (s_perf_clear) + ); + apb4_xpi u_xpi ( + .clk_i (clk_i), + .rst_n_i (s_rst_n), + .mem_axi4 (u_targets_axi4_if[1]), + .apb4 (u_xpi_apb4_if), + .xpi (xpi), + .dma_xfer_done_i(s_dma_done), + .dma_tx_stall_o (s_dma_stall[0]), + .dma_rx_stall_o (s_dma_stall[1]), + .irq_o (s_xpi_irq) + ); + tiny_axi42apb4 u_apb_bridge ( + .clk_i (clk_i), + .rst_n_i(s_rst_n), + .axi4 (u_targets_axi4_if[2]), + `include "tiny_apb_connections.svh" + ); + axi4_error_slave #( + .Response(2'b11) + ) u_decode_error ( + .clk_i (clk_i), + .rst_n_i(s_rst_n), + .axi4 (u_targets_axi4_if[3]) + ); + axi4_error_slave #( + .Response(2'b10) + ) u_protocol_error ( + .clk_i (clk_i), + .rst_n_i(s_rst_n), + .axi4 (u_targets_axi4_if[4]) + ); + tiny_sysctrl u_sysctrl ( + .clk_i (clk_i), + .rst_n_i (s_rst_n), + .apb4 (u_sysctrl_apb4_if), + .fault_valid_i (s_fault_valid), + .fault_addr_i (s_fault_addr), + .fault_write_i (s_fault_write), + .fault_master_i(s_fault_master), + .fault_resp_i (s_fault_resp), + .cpu_wait_i (s_cpu_wait), + .dma_wait_i (s_dma_wait), + .rtc_wake_i (u_rtc_if.wake_o), + .perf_enable_o (s_perf_enable), + .perf_clear_o (s_perf_clear), + .test_done_o (test_done_o), + .test_pass_o (test_pass_o), + .test_code_o (test_code_o) + ); + tiny_archinfo u_archinfo ( + .clk_i (clk_i), + .rst_n_i(s_rst_n), + .apb4 (u_archinfo_apb4_if) + ); + apb4_gpio u_gpio ( + .clk_i (clk_i), + .rst_n_i (s_rst_n), + .apb4 (u_gpio_apb4_if), + .gpio (u_gpio_if), + .user_gpio(u_user_gpio_if) + ); + gpio_pad_bridge u_gpio_pads ( + .inner(u_gpio_if), + .outer(gpio) + ); + assign u_user_gpio_if.do_o = '0; + assign u_user_gpio_if.oe_o = '0; + assign u_uart0_if.rx_i = uart0_rx_i; + assign uart0_tx_o = u_uart0_if.tx_o; + assign u_uart1_if.rx_i = uart1_rx_i; + assign u_uart1_if.cts_n_i = 1'b0; + assign uart1_tx_o = u_uart1_if.tx_o; + assign u_wdg_if.debug_halted_i = s_debug_halted; + `include "tiny_gpio.svh" + `include "tiny_irq.svh" +apb4_uart u_uart0 ( + .clk_i (clk_i), + .rst_n_i (s_rst_n), + .apb4 (u_uart0_apb4_if), + .uart (u_uart0_if), + .dma_tx_stall_o(s_dma_stall[2]), + .dma_rx_stall_o(s_dma_stall[3]) + ); + apb4_i2c u_i2c0 ( + .clk_i (clk_i), + .rst_n_i (s_rst_n), + .apb4 (u_i2c0_apb4_if), + .i2c (u_i2c0_if), + .dma_tx_stall_o(s_dma_stall[4]), + .dma_rx_stall_o(s_dma_stall[5]) + ); + apb4_timer u_timer0 ( + .clk_i (clk_i), + .rst_n_i (s_rst_n), + .debug_halted_i(s_debug_halted), + .apb4 (u_timer0_apb4_if), + .irq_o (s_timer0_irq) + ); + apb4_uart u_uart1 ( + .clk_i (clk_i), + .rst_n_i (s_rst_n), + .apb4 (u_uart1_apb4_if), + .uart (u_uart1_if), + .dma_tx_stall_o(s_unused_uart1_tx_stall), + .dma_rx_stall_o(s_unused_uart1_rx_stall) + ); + apb4_i2c u_i2c1 ( + .clk_i (clk_i), + .rst_n_i (s_rst_n), + .apb4 (u_i2c1_apb4_if), + .i2c (u_i2c1_if), + .dma_tx_stall_o(s_dma_stall[6]), + .dma_rx_stall_o(s_dma_stall[7]) + ); + apb4_timer u_timer1 ( + .clk_i (clk_i), + .rst_n_i (s_rst_n), + .debug_halted_i(s_debug_halted), + .apb4 (u_timer1_apb4_if), + .irq_o (s_timer1_irq) + ); + apb4_clint u_clint ( + .clk_i (clk_i), + .rst_n_i (s_rst_n), + .timebase_tick_i(s_tick), + .apb4 (u_clint_apb4_if), + .clint (u_clint_if) + ); + apb4_pwm #( + .PCLK_HZ(24_000_000) + ) u_pwm ( + .debug_halted_i(s_debug_halted), + .apb4 (u_pwm_apb4_if), + .pwm (u_pwm_if) + ); + apb4_rtc #( + .RTC_CLOCK_HZ(24_000_000) + ) u_rtc ( + .apb4(u_rtc_apb4_if), + .rtc (u_rtc_if) + ); + apb4_wdg #( + .WDG_CLOCK_HZ (24_000_000), + .RESET_PULSE_CYCLES(8) + ) u_watchdog ( + .apb4(u_wdg_apb4_if), + .wdg (u_wdg_if) + ); + apb4_dma #( + .NumChannels (4), + .MaxBurstBeats(16), + .FifoDepth (32), + .RequestMask (16'h07f9), + .EnableStreams(1'b0) + ) u_dma ( + .clk_i (clk_i), + .rst_n_i (s_rst_n), + .dma_xfer_done_o(s_dma_done), + .irq_o (s_dma_irq), + .hw_trg (u_dma_req_if), + .apb4 (u_dma_apb4_if), + .axi4 (u_masters_axi4_if[1]), + .i2s_tx_axis (u_i2s_tx_axis_if), + .i2s_rx_axis (u_i2s_rx_axis_if), + .dvp_rx_axis (u_dvp_rx_axis_if), + .crypto_in_axis (u_crypto_tx_axis_if), + .crypto_out_axis(u_crypto_rx_axis_if) + ); + assign u_dma_req_if.i2s_tx_proc = 1'b0; + assign u_dma_req_if.i2s_rx_proc = 1'b0; + assign u_dma_req_if.crypto_in_proc = 1'b0; + assign u_dma_req_if.crypto_out_proc = 1'b0; + assign u_dma_req_if.qspi_tx_proc = !s_dma_stall[0]; + assign u_dma_req_if.qspi_rx_proc = !s_dma_stall[1]; + assign u_dma_req_if.uart_tx_proc = !s_dma_stall[2]; + assign u_dma_req_if.uart_rx_proc = !s_dma_stall[3]; + assign u_dma_req_if.i2c0_tx_proc = !s_dma_stall[4]; + assign u_dma_req_if.i2c0_rx_proc = !s_dma_stall[5]; + assign u_dma_req_if.i2c1_tx_proc = !s_dma_stall[6]; + assign u_dma_req_if.i2c1_rx_proc = !s_dma_stall[7]; + assign u_i2s_rx_axis_if.tvalid = '0; + assign u_i2s_rx_axis_if.tdata = '0; + assign u_i2s_rx_axis_if.tstrb = '0; + assign u_i2s_rx_axis_if.tkeep = '0; + assign u_i2s_rx_axis_if.tlast = '0; + assign u_i2s_rx_axis_if.tid = '0; + assign u_i2s_rx_axis_if.tdest = '0; + assign u_i2s_rx_axis_if.tuser = '0; + assign u_dvp_rx_axis_if.tvalid = '0; + assign u_dvp_rx_axis_if.tdata = '0; + assign u_dvp_rx_axis_if.tstrb = '0; + assign u_dvp_rx_axis_if.tkeep = '0; + assign u_dvp_rx_axis_if.tlast = '0; + assign u_dvp_rx_axis_if.tid = '0; + assign u_dvp_rx_axis_if.tdest = '0; + assign u_dvp_rx_axis_if.tuser = '0; + assign u_crypto_rx_axis_if.tvalid = '0; + assign u_crypto_rx_axis_if.tdata = '0; + assign u_crypto_rx_axis_if.tstrb = '0; + assign u_crypto_rx_axis_if.tkeep = '0; + assign u_crypto_rx_axis_if.tlast = '0; + assign u_crypto_rx_axis_if.tid = '0; + assign u_crypto_rx_axis_if.tdest = '0; + assign u_crypto_rx_axis_if.tuser = '0; + assign u_i2s_tx_axis_if.tready = 1'b0; + assign u_crypto_tx_axis_if.tready = 1'b0; +endmodule diff --git a/rtl/tiny/top/retrosoc_tiny_asic.sv b/rtl/tiny/top/retrosoc_tiny_asic.sv new file mode 100644 index 00000000..0c89657b --- /dev/null +++ b/rtl/tiny/top/retrosoc_tiny_asic.sv @@ -0,0 +1,40 @@ +// Copyright (c) 2026 Yuchi Miao +// SPDX-License-Identifier: MulanPSL-2.0 +module retrosoc_tiny_asic ( + `include "retrosoc_asic_ports.svh" +); + logic s_ext_clk, s_clk, s_ext_rst_n; + logic s_jtag_tck, s_jtag_tms, s_jtag_tdi, s_jtag_trst_n, s_jtag_tdo; + logic s_uart0_rx, s_uart0_tx, s_uart1_rx, s_uart1_tx; + // Stable simulator/netlist observation points; no package test pins. + /* verilator lint_off UNUSEDSIGNAL */ + (* keep = "true" *)logic s_test_done; + (* keep = "true" *)logic s_test_pass; + (* keep = "true" *)logic [7:0] s_test_code; + /* verilator lint_on UNUSEDSIGNAL */ + gpio_if u_gpio_if (); + xpi_if u_xpi_if (); + `include "retrosoc_asic_pad_bindings.svh" +tc_clk_buf u_clock_buffer ( + .clk_i(s_ext_clk), + .clk_o(s_clk) + ); + retrosoc_tiny u_soc ( + .clk_i (s_clk), + .rst_n_i (s_ext_rst_n), + .jtag_tck_i (s_jtag_tck), + .jtag_tms_i (s_jtag_tms), + .jtag_tdi_i (s_jtag_tdi), + .jtag_trst_n_i(s_jtag_trst_n), + .jtag_tdo_o (s_jtag_tdo), + .uart0_rx_i (s_uart0_rx), + .uart0_tx_o (s_uart0_tx), + .uart1_rx_i (s_uart1_rx), + .uart1_tx_o (s_uart1_tx), + .gpio (u_gpio_if), + .xpi (u_xpi_if), + .test_done_o (s_test_done), + .test_pass_o (s_test_pass), + .test_code_o (s_test_code) + ); +endmodule diff --git a/rtl/tiny/top/tiny_archinfo.sv b/rtl/tiny/top/tiny_archinfo.sv new file mode 100644 index 00000000..40f0ad62 --- /dev/null +++ b/rtl/tiny/top/tiny_archinfo.sv @@ -0,0 +1,43 @@ +// Copyright (c) 2026 Yuchi Miao +// SPDX-License-Identifier: MulanPSL-2.0 +`include "archinfo_integration_metadata.svh" +`include "archinfo_define.svh" + +// The locked ArchInfo V2 has a fixed Mini SOC_ID. This owned adapter changes +// only that read-only identity, retaining the managed ABI and build metadata. +module tiny_archinfo ( + input logic clk_i, + input logic rst_n_i, + apb4_if.slave apb4 +); + apb4_if u_info_apb4_if ( + .pclk (clk_i), + .presetn(rst_n_i) + ); + assign u_info_apb4_if.paddr = apb4.paddr; + assign u_info_apb4_if.psel = apb4.psel; + assign u_info_apb4_if.penable = apb4.penable; + assign u_info_apb4_if.pwrite = apb4.pwrite; + assign u_info_apb4_if.pwdata = apb4.pwdata; + assign u_info_apb4_if.pstrb = apb4.pstrb; + assign u_info_apb4_if.pprot = apb4.pprot; + assign apb4.pready = u_info_apb4_if.pready; + assign apb4.pslverr = u_info_apb4_if.pslverr; + assign apb4.prdata = (apb4.paddr[11:0] == `ARCHINFO_SOC_ID_OFFSET) ? + 32'h5449_4e59 : u_info_apb4_if.prdata; + apb4_archinfo #( + .REFERENCE_CLOCK_HZ(24_000_000), + .SRAM_BYTES (131072), + .TOPOLOGY (32'h2020_0001), + .FEATURES0 (32'h0000_7ffe), + .TECHNOLOGY (32'h0201_0082), + .BUILD_ID (`ARCHINFO_INTEGRATION_BUILD_ID), + .CONFIG_ID (`ARCHINFO_INTEGRATION_CONFIG_ID), + .BUILD_STATUS (`ARCHINFO_INTEGRATION_BUILD_STATUS) + ) u_archinfo ( + .device_id_i (128'd0), + .device_id_valid_i (1'b0), + .device_id_read_enable_i(1'b0), + .apb4 (u_info_apb4_if) + ); +endmodule diff --git a/rtl/tiny/top/tiny_axi42apb4.sv b/rtl/tiny/top/tiny_axi42apb4.sv new file mode 100644 index 00000000..4cd6fdc3 --- /dev/null +++ b/rtl/tiny/top/tiny_axi42apb4.sv @@ -0,0 +1,220 @@ +// Copyright (c) 2026 Yuchi Miao +// SPDX-License-Identifier: MulanPSL-2.0 +`include "mmap_define.svh" + +// APB setup/access are separate cycles; rejected bursts never assert PSEL. +module tiny_axi42apb4 ( + input logic clk_i, + input logic rst_n_i, + axi4_if.slave axi4, + `include "tiny_apb_ports.svh" +); + typedef enum logic [2:0] { + Idle, + WriteData, + Setup, + Access, + ReadResponse, + WriteResponse, + ReadError, + WriteError + } state_e; + state_e s_state_d, s_state_q; + logic [2:0] s_state_bits_q; + logic [31:0] s_addr_d, s_addr_q; + logic [31:0] s_wdata_d, s_wdata_q; + logic [31:0] s_data_d, s_data_q; + logic [3:0] s_wstrb_d, s_wstrb_q; + logic s_id_d, s_id_q; + logic s_write_d, s_write_q; + logic [1:0] s_resp_d, s_resp_q; + logic [7:0] s_len_d, s_len_q; + logic [7:0] s_beat_d, s_beat_q; + logic [15:0] s_select, s_ready, s_error; + logic [15:0][31:0] s_rdata; + logic s_access, s_enable, s_done, s_err; + logic [31:0] s_read_data; + + assign s_state_q = state_e'(s_state_bits_q); + assign axi4.arready = s_state_q == Idle; + assign axi4.awready = (s_state_q == Idle) && !axi4.arvalid; + assign axi4.wready = (s_state_q == WriteData) || (s_state_q == WriteError); + assign axi4.bid = s_id_q; + assign axi4.bresp = s_resp_q; + assign axi4.buser = '0; + assign axi4.bvalid = s_state_q == WriteResponse; + assign axi4.rid = s_id_q; + assign axi4.rdata = s_data_q; + assign axi4.rresp = s_resp_q; + assign axi4.ruser = '0; + assign axi4.rlast = s_beat_q == s_len_q; + assign axi4.rvalid = (s_state_q == ReadResponse) || (s_state_q == ReadError); + assign s_access = (s_state_q == Setup) || (s_state_q == Access); + assign s_enable = s_state_q == Access; + assign s_done = |(s_select & s_ready); + assign s_err = |(s_select & s_error); + + always_comb begin + s_select = '0; + `include "tiny_apb_decode.svh" +s_read_data = '0; + for (int unsigned target = 0; target < 16; target++) begin + if (s_select[target]) s_read_data = s_rdata[target]; + end + end + `include "tiny_apb_wiring.svh" + + always_comb begin + s_state_d = s_state_q; + s_addr_d = s_addr_q; + s_wdata_d = s_wdata_q; + s_wstrb_d = s_wstrb_q; + s_id_d = s_id_q; + s_write_d = s_write_q; + s_resp_d = s_resp_q; + s_data_d = s_data_q; + s_len_d = s_len_q; + s_beat_d = s_beat_q; + unique case (s_state_q) + Idle: begin + s_beat_d = '0; + s_data_d = '0; + s_resp_d = 2'b00; + if (axi4.arvalid) begin + s_addr_d = axi4.araddr; + s_id_d = axi4.arid; + s_write_d = 1'b0; + s_len_d = axi4.arlen; + if ((axi4.arlen != 8'd0) || (axi4.arburst[1]) || axi4.arlock || + (axi4.arsize > 3'd2) || (axi4.araddr[1:0] != 2'd0)) begin + s_resp_d = 2'b10; + s_state_d = ReadError; + end else s_state_d = Setup; + end else if (axi4.awvalid) begin + s_addr_d = axi4.awaddr; + s_id_d = axi4.awid; + s_write_d = 1'b1; + s_len_d = axi4.awlen; + if ((axi4.awlen != 8'd0) || (axi4.awburst[1]) || axi4.awlock || + (axi4.awsize > 3'd2) || (axi4.awaddr[1:0] != 2'd0)) begin + s_resp_d = 2'b10; + s_state_d = WriteError; + end else s_state_d = WriteData; + end + end + WriteData: + if (axi4.wvalid) begin + s_wdata_d = axi4.wdata; + s_wstrb_d = axi4.wstrb; + if (!axi4.wlast) begin + s_resp_d = 2'b10; + s_state_d = WriteResponse; + end else s_state_d = Setup; + end + Setup: begin + if (s_select == 16'd0) begin + s_resp_d = 2'b11; + s_state_d = s_write_q ? WriteResponse : ReadResponse; + end else s_state_d = Access; + end + Access: + if (s_done) begin + s_resp_d = s_err ? 2'b10 : 2'b00; + s_data_d = s_read_data; + s_state_d = s_write_q ? WriteResponse : ReadResponse; + end + ReadResponse, ReadError: + if (axi4.rready) begin + if (s_beat_q == s_len_q) s_state_d = Idle; + else s_beat_d = s_beat_q + 8'd1; + end + WriteResponse: if (axi4.bready) s_state_d = Idle; + WriteError: + if (axi4.wvalid) begin + if (s_beat_q == s_len_q) s_state_d = WriteResponse; + else s_beat_d = s_beat_q + 8'd1; + end + default: s_state_d = Idle; + endcase + end + dffr #( + .DATA_WIDTH(3) + ) u_state_reg ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .dat_i (s_state_d), + .dat_o (s_state_bits_q) + ); + dffr #( + .DATA_WIDTH(32) + ) u_addr_reg ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .dat_i (s_addr_d), + .dat_o (s_addr_q) + ); + dffr #( + .DATA_WIDTH(32) + ) u_wdata_reg ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .dat_i (s_wdata_d), + .dat_o (s_wdata_q) + ); + dffr #( + .DATA_WIDTH(32) + ) u_data_reg ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .dat_i (s_data_d), + .dat_o (s_data_q) + ); + dffr #( + .DATA_WIDTH(4) + ) u_wstrb_reg ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .dat_i (s_wstrb_d), + .dat_o (s_wstrb_q) + ); + dffr #( + .DATA_WIDTH(1) + ) u_id_reg ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .dat_i (s_id_d), + .dat_o (s_id_q) + ); + dffr #( + .DATA_WIDTH(1) + ) u_write_reg ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .dat_i (s_write_d), + .dat_o (s_write_q) + ); + dffr #( + .DATA_WIDTH(2) + ) u_resp_reg ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .dat_i (s_resp_d), + .dat_o (s_resp_q) + ); + dffr #( + .DATA_WIDTH(8) + ) u_len_reg ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .dat_i (s_len_d), + .dat_o (s_len_q) + ); + dffr #( + .DATA_WIDTH(8) + ) u_beat_reg ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .dat_i (s_beat_d), + .dat_o (s_beat_q) + ); +endmodule diff --git a/rtl/tiny/top/tiny_axi4_fabric.sv b/rtl/tiny/top/tiny_axi4_fabric.sv new file mode 100644 index 00000000..a4473d48 --- /dev/null +++ b/rtl/tiny/top/tiny_axi4_fabric.sv @@ -0,0 +1,297 @@ +// Copyright (c) 2026 Yuchi Miao +// SPDX-License-Identifier: MulanPSL-2.0 + +`include "mmap_define.svh" + +// One accepted transaction globally. AW and W remain independent: W is +// backpressured until the saved AW has reached its target. The owner and all +// address attributes remain registered until the terminal response handshake. +module tiny_axi4_fabric ( + input logic clk_i, + input logic rst_n_i, + input logic perf_enable_i, + input logic perf_clear_i, + axi4_if.slave masters [2], + axi4_if.master targets [5], + output logic fault_valid_o, + output logic [31:0] fault_addr_o, + output logic fault_master_o, + output logic fault_write_o, + output logic [ 1:0] fault_resp_o, + output logic [63:0] cpu_wait_o, + output logic [63:0] dma_wait_o +); + typedef enum logic [1:0] { + Idle, + Addr, + Response + } state_e; + typedef struct packed { + logic [31:0] addr; + logic [7:0] len; + logic [2:0] size; + logic [1:0] burst; + logic lock; + logic [3:0] cache; + logic [2:0] prot; + logic [3:0] qos; + logic [3:0] region; + logic id; + logic user; + } addr_t; + state_e s_state_d, s_state_q; + logic [1:0] s_state_bits_q; + addr_t s_addr_d, s_addr_q; + addr_t s_read_addr [2]; + addr_t s_write_addr [2]; + addr_t s_selected_addr; + logic s_owner_d, s_owner_q; + logic s_write_d, s_write_q; + logic [2:0] s_tgt_d, s_tgt_q; + logic [3:0] s_request, s_unused_grant; + logic [ 1:0] s_selected; + logic s_selected_valid; + logic s_accept; + logic s_terminal; + logic [32:0] s_last_addr; + logic [ 2:0] s_decoded_target; + logic s_protocol_legal; + logic [1:0] s_awvalid, s_arvalid, s_wvalid, s_wlast, s_bready, s_rready; + logic [1:0][31:0] s_wdata; + logic [1:0][ 3:0] s_wstrb; + logic [1:0] s_wuser; + logic [4:0] s_target_awready, s_target_arready, s_target_wready; + logic [4:0] s_target_bvalid, s_target_rvalid, s_target_rlast; + logic [4:0] s_target_bid, s_target_rid, s_target_buser, s_target_ruser; + logic [4:0][1:0] s_target_bresp, s_target_rresp; + logic [4:0][31:0] s_target_rdata; + logic [1:0][63:0] s_wait_d, s_wait_q; + + function automatic logic [2:0] decode_target(input logic [31:0] addr_i); + if (`SOC_ADDR_IS_SRAM(addr_i)) return 3'd0; + if (`SOC_ADDR_IS_FLASH(addr_i) || `SOC_ADDR_IS_XPI(addr_i)) return 3'd1; + if (`SOC_ADDR_IS_APB4_PERIPH(addr_i) || `SOC_ADDR_IS_APB4_SYSTEM(addr_i)) return 3'd2; + return 3'd3; + endfunction + + assign s_state_q = state_e'(s_state_bits_q); + assign s_request = {s_awvalid[1], s_arvalid[1], s_awvalid[0], s_arvalid[0]}; + assign s_selected_addr = s_selected[0] ? s_write_addr[s_selected[1]] : s_read_addr[s_selected[1]]; + assign s_decoded_target = decode_target(s_selected_addr.addr); + assign s_last_addr = {1'b0, s_selected_addr.addr} + + (({25'd0, s_selected_addr.len} + 33'd1) << s_selected_addr.size) - 33'd1; + assign s_protocol_legal = !s_selected_addr.lock && (s_selected_addr.size <= 3'd2) && + ((s_selected_addr.addr & ((32'd1 << s_selected_addr.size) - 32'd1)) == 32'd0) && + ((s_selected_addr.burst == 2'b01) || + ((s_selected_addr.burst == 2'b00) && (s_selected_addr.len == 8'd0))) && + (s_selected_addr.len <= 8'd15) && !s_last_addr[32] && + (s_last_addr[31:12] == s_selected_addr.addr[31:12]) && + (decode_target( + s_last_addr[31:0] + ) == s_decoded_target) && ((s_decoded_target != 3'd2) || (s_selected_addr.len == 8'd0)); + assign s_accept = (s_state_q == Idle) && s_selected_valid; + assign s_terminal = (s_state_q == Response) && + (s_write_q ? (s_target_bvalid[s_tgt_q] && s_bready[s_owner_q]) : + (s_target_rvalid[s_tgt_q] && s_rready[s_owner_q] && s_target_rlast[s_tgt_q])); + assign fault_resp_o = s_write_q ? s_target_bresp[s_tgt_q] : s_target_rresp[s_tgt_q]; + assign fault_valid_o = s_terminal && (fault_resp_o != 2'b00); + assign fault_addr_o = s_addr_q.addr; + assign fault_master_o = s_owner_q; + assign fault_write_o = s_write_q; + assign cpu_wait_o = s_wait_q[0]; + assign dma_wait_o = s_wait_q[1]; + + // Advance on acceptance: the chosen owner is saved until completion, so the + // next grant after completion starts after this source/direction. + round_robin_arbiter #( + .CLIENTS(4) + ) u_arbiter ( + .clk_i (clk_i), + .rst_n_i (rst_n_i), + .advance_i (s_accept), + .request_i (s_request), + .grant_o (s_unused_grant), + .selected_o(s_selected), + .valid_o (s_selected_valid) + ); + + always_comb begin + s_state_d = s_state_q; + s_owner_d = s_owner_q; + s_write_d = s_write_q; + s_addr_d = s_addr_q; + s_tgt_d = s_tgt_q; + unique case (s_state_q) + Idle: + if (s_accept) begin + s_owner_d = s_selected[1]; + s_write_d = s_selected[0]; + s_addr_d = s_selected_addr; + s_tgt_d = s_protocol_legal ? s_decoded_target : 3'd4; + s_state_d = Addr; + end + Addr: + if (s_write_q ? s_target_awready[s_tgt_q] : s_target_arready[s_tgt_q]) s_state_d = Response; + Response: if (s_terminal) s_state_d = Idle; + default: s_state_d = Idle; + endcase + s_wait_d = s_wait_q; + for (int unsigned master = 0; master < 2; master++) begin + if (perf_clear_i) s_wait_d[master] = '0; + else if (perf_enable_i && (s_awvalid[master] || s_arvalid[master]) && + !(s_accept && (s_selected[1] == 1'(master))) && + (s_wait_q[master] != 64'hffff_ffff_ffff_ffff)) + s_wait_d[master] = s_wait_q[master] + 64'd1; + end + end + for (genvar master = 0; master < 2; master++) begin : gen_master + assign s_write_addr[master] = { + masters[master].awaddr, + masters[master].awlen, + masters[master].awsize, + masters[master].awburst, + masters[master].awlock, + masters[master].awcache, + masters[master].awprot, + masters[master].awqos, + masters[master].awregion, + masters[master].awid, + masters[master].awuser + }; + assign s_read_addr[master] = { + masters[master].araddr, + masters[master].arlen, + masters[master].arsize, + masters[master].arburst, + masters[master].arlock, + masters[master].arcache, + masters[master].arprot, + masters[master].arqos, + masters[master].arregion, + masters[master].arid, + masters[master].aruser + }; + assign s_awvalid[master] = masters[master].awvalid; + assign s_arvalid[master] = masters[master].arvalid; + assign s_wdata[master] = masters[master].wdata; + assign s_wstrb[master] = masters[master].wstrb; + assign s_wlast[master] = masters[master].wlast; + assign s_wuser[master] = masters[master].wuser; + assign s_wvalid[master] = masters[master].wvalid; + assign s_bready[master] = masters[master].bready; + assign s_rready[master] = masters[master].rready; + assign masters[master].awready = s_accept && (s_selected == {1'(master), 1'b1}); + assign masters[master].arready = s_accept && (s_selected == {1'(master), 1'b0}); + assign masters[master].wready = (s_state_q == Response) && s_write_q && + (s_owner_q == 1'(master)) && s_target_wready[s_tgt_q]; + assign masters[master].bvalid = (s_state_q == Response) && s_write_q && + (s_owner_q == 1'(master)) && s_target_bvalid[s_tgt_q]; + assign masters[master].rvalid = (s_state_q == Response) && !s_write_q && + (s_owner_q == 1'(master)) && s_target_rvalid[s_tgt_q]; + assign masters[master].bid = s_target_bid[s_tgt_q]; + assign masters[master].bresp = s_target_bresp[s_tgt_q]; + assign masters[master].buser = s_target_buser[s_tgt_q]; + assign masters[master].rid = s_target_rid[s_tgt_q]; + assign masters[master].rdata = s_target_rdata[s_tgt_q]; + assign masters[master].rresp = s_target_rresp[s_tgt_q]; + assign masters[master].rlast = s_target_rlast[s_tgt_q]; + assign masters[master].ruser = s_target_ruser[s_tgt_q]; + end + for (genvar target = 0; target < 5; target++) begin : gen_target + assign targets[target].awaddr = s_addr_q.addr; + assign targets[target].awlen = s_addr_q.len; + assign targets[target].awsize = s_addr_q.size; + assign targets[target].awburst = s_addr_q.burst; + assign targets[target].awlock = s_addr_q.lock; + assign targets[target].awcache = s_addr_q.cache; + assign targets[target].awprot = s_addr_q.prot; + assign targets[target].awqos = s_addr_q.qos; + assign targets[target].awregion = s_addr_q.region; + assign targets[target].awid = s_addr_q.id; + assign targets[target].awuser = s_addr_q.user; + assign targets[target].awvalid = (s_state_q == Addr) && s_write_q && (s_tgt_q == 3'(target)); + assign targets[target].araddr = s_addr_q.addr; + assign targets[target].arlen = s_addr_q.len; + assign targets[target].arsize = s_addr_q.size; + assign targets[target].arburst = s_addr_q.burst; + assign targets[target].arlock = s_addr_q.lock; + assign targets[target].arcache = s_addr_q.cache; + assign targets[target].arprot = s_addr_q.prot; + assign targets[target].arqos = s_addr_q.qos; + assign targets[target].arregion = s_addr_q.region; + assign targets[target].arid = s_addr_q.id; + assign targets[target].aruser = s_addr_q.user; + assign targets[target].arvalid = (s_state_q == Addr) && !s_write_q && (s_tgt_q == 3'(target)); + assign targets[target].wdata = s_wdata[s_owner_q]; + assign targets[target].wstrb = s_wstrb[s_owner_q]; + assign targets[target].wlast = s_wlast[s_owner_q]; + assign targets[target].wuser = s_wuser[s_owner_q]; + assign targets[target].wvalid = (s_state_q == Response) && s_write_q && + (s_tgt_q == 3'(target)) && s_wvalid[s_owner_q]; + assign targets[target].bready = (s_state_q == Response) && s_write_q && + (s_tgt_q == 3'(target)) && s_bready[s_owner_q]; + assign targets[target].rready = (s_state_q == Response) && !s_write_q && + (s_tgt_q == 3'(target)) && s_rready[s_owner_q]; + assign s_target_awready[target] = targets[target].awready; + assign s_target_arready[target] = targets[target].arready; + assign s_target_wready[target] = targets[target].wready; + assign s_target_bvalid[target] = targets[target].bvalid; + assign s_target_bid[target] = targets[target].bid; + assign s_target_bresp[target] = targets[target].bresp; + assign s_target_buser[target] = targets[target].buser; + assign s_target_rvalid[target] = targets[target].rvalid; + assign s_target_rid[target] = targets[target].rid; + assign s_target_rresp[target] = targets[target].rresp; + assign s_target_rdata[target] = targets[target].rdata; + assign s_target_rlast[target] = targets[target].rlast; + assign s_target_ruser[target] = targets[target].ruser; + end + dffr #( + .DATA_WIDTH(2) + ) u_state_reg ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .dat_i (s_state_d), + .dat_o (s_state_bits_q) + ); + dffr #( + .DATA_WIDTH(1) + ) u_owner_reg ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .dat_i (s_owner_d), + .dat_o (s_owner_q) + ); + dffr #( + .DATA_WIDTH(1) + ) u_write_reg ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .dat_i (s_write_d), + .dat_o (s_write_q) + ); + dffr #( + .DATA_WIDTH(63) + ) u_addr_reg ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .dat_i (s_addr_d), + .dat_o (s_addr_q) + ); + dffr #( + .DATA_WIDTH(3) + ) u_target_reg ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .dat_i (s_tgt_d), + .dat_o (s_tgt_q) + ); + dffr #( + .DATA_WIDTH(128) + ) u_wait_reg ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .dat_i (s_wait_d), + .dat_o (s_wait_q) + ); +endmodule diff --git a/rtl/tiny/top/tiny_sysctrl.sv b/rtl/tiny/top/tiny_sysctrl.sv new file mode 100644 index 00000000..7a0ac57a --- /dev/null +++ b/rtl/tiny/top/tiny_sysctrl.sv @@ -0,0 +1,240 @@ +// Copyright (c) 2026 Yuchi Miao +// SPDX-License-Identifier: MulanPSL-2.0 +`include "sysctrl_define.svh" + +module tiny_sysctrl ( + input logic clk_i, + input logic rst_n_i, + apb4_if.slave apb4, + input logic fault_valid_i, + input logic [31:0] fault_addr_i, + input logic fault_write_i, + input logic fault_master_i, + input logic [ 1:0] fault_resp_i, + input logic [63:0] cpu_wait_i, + input logic [63:0] dma_wait_i, + input logic rtc_wake_i, + output logic perf_enable_o, + output logic perf_clear_o, + output logic test_done_o, + output logic test_pass_o, + output logic [ 7:0] test_code_o +); + logic [31:0] s_test_d, s_test_q; + logic [31:0] s_fault_addr_d, s_fault_addr_q; + logic [31:0] s_fault_count_d, s_fault_count_q; + logic [4:0] s_fault_stat_d, s_fault_stat_q; + logic s_fault_master_d, s_fault_master_q; + logic [1:0] s_fault_resp_d, s_fault_resp_q; + logic s_perf_en_d, s_perf_en_q; + logic s_wake_d, s_wake_q; + logic [63:0] s_cpu_snapshot_d, s_cpu_snapshot_q; + logic [63:0] s_dma_snapshot_d, s_dma_snapshot_q; + logic s_req, s_write, s_valid, s_writable, s_access_err; + logic s_ready_d, s_ready_q; + logic s_err_d, s_err_q; + logic [31:0] s_read_data; + logic [31:0] s_offset; + logic [31:0] s_rdata_d, s_rdata_q; + + assign s_offset = {20'd0, apb4.paddr[11:0]}; + assign s_req = apb4.psel && apb4.penable && !s_ready_q; + assign s_write = s_req && apb4.pwrite && !s_access_err; + assign s_access_err = (apb4.paddr[1:0] != 2'd0) || !s_valid || + (apb4.pwrite && (!s_writable || (apb4.pstrb != 4'hf))); + assign s_ready_d = s_req; + assign s_err_d = s_req && s_access_err; + assign s_rdata_d = s_req ? s_read_data : s_rdata_q; + assign apb4.pready = s_ready_q; + assign apb4.pslverr = s_err_q; + assign apb4.prdata = s_rdata_q; + assign perf_enable_o = s_perf_en_q; + assign perf_clear_o = s_write && (s_offset == `APB4_SYSCTRL__PERF_CTRL) && apb4.pwdata[1]; + assign test_done_o = s_test_q[31]; + assign test_pass_o = s_test_q[0]; + assign test_code_o = s_test_q[15:8]; + + always_comb begin + s_valid = 1'b1; + s_writable = 1'b0; + s_read_data = '0; + unique case (s_offset) + `APB4_SYSCTRL__CORESEL, `APB4_SYSCTRL__IPSEL: s_read_data = '0; + `APB4_SYSCTRL__USER_CORE_RESET: s_read_data = 32'hffff_ffff; + `APB4_SYSCTRL__USER_CORE_STATUS: s_read_data = 32'h0000_0200; + `APB4_SYSCTRL__FAULT_STATUS: begin + s_read_data = {27'd0, s_fault_stat_q}; + s_writable = 1'b1; + end + `APB4_SYSCTRL__FAULT_ADDR: s_read_data = s_fault_addr_q; + `APB4_SYSCTRL__FAULT_COUNT: s_read_data = s_fault_count_q; + `APB4_SYSCTRL__FAULT_MASTER: s_read_data = {31'd0, s_fault_master_q}; + `APB4_SYSCTRL__FAULT_DETAIL: s_read_data = {30'd0, s_fault_resp_q}; + `APB4_SYSCTRL__PERF_CTRL: begin + s_read_data = {31'd0, s_perf_en_q}; + s_writable = 1'b1; + end + `APB4_SYSCTRL__PERF_MGMT_WAIT_LO: s_read_data = s_cpu_snapshot_q[31:0]; + `APB4_SYSCTRL__PERF_MGMT_WAIT_HI: s_read_data = s_cpu_snapshot_q[63:32]; + `APB4_SYSCTRL__PERF_DMA_WAIT_LO: s_read_data = s_dma_snapshot_q[31:0]; + `APB4_SYSCTRL__PERF_DMA_WAIT_HI: s_read_data = s_dma_snapshot_q[63:32]; + `APB4_SYSCTRL__TEST_STATUS: begin + s_read_data = s_test_q; + s_writable = 1'b1; + end + `APB4_SYSCTRL__RTC_WAKE_STATUS: begin + s_read_data = {30'd0, s_wake_q, rtc_wake_i}; + s_writable = 1'b1; + end + default: s_valid = 1'b0; + endcase + end + always_comb begin + s_test_d = s_test_q; + s_fault_addr_d = s_fault_addr_q; + s_fault_count_d = s_fault_count_q; + s_fault_stat_d = s_fault_stat_q; + s_fault_master_d = s_fault_master_q; + s_fault_resp_d = s_fault_resp_q; + s_perf_en_d = s_perf_en_q; + s_wake_d = s_wake_q; + s_cpu_snapshot_d = s_cpu_snapshot_q; + s_dma_snapshot_d = s_dma_snapshot_q; + if (s_write) begin + unique case ({ + 20'd0, apb4.paddr[11:0] + }) + `APB4_SYSCTRL__FAULT_STATUS: if (apb4.pwdata[0]) s_fault_stat_d[0] = 1'b0; + `APB4_SYSCTRL__PERF_CTRL: begin + s_perf_en_d = apb4.pwdata[0]; + if (apb4.pwdata[1]) begin + s_cpu_snapshot_d = '0; + s_dma_snapshot_d = '0; + end else if (apb4.pwdata[2]) begin + s_cpu_snapshot_d = cpu_wait_i; + s_dma_snapshot_d = dma_wait_i; + end + end + `APB4_SYSCTRL__TEST_STATUS: + if (!s_test_q[31] && apb4.pwdata[31]) s_test_d = apb4.pwdata & 32'h8000_ff01; + `APB4_SYSCTRL__RTC_WAKE_STATUS: if (apb4.pwdata[1]) s_wake_d = 1'b0; + default: begin + end + endcase + end + if (rtc_wake_i) s_wake_d = 1'b1; + if (fault_valid_i) begin + if (s_fault_count_q != 32'hffff_ffff) s_fault_count_d = s_fault_count_q + 32'd1; + if (!s_fault_stat_d[0]) begin + s_fault_stat_d = {(fault_resp_i == 2'b11) ? 3'd1 : 3'd2, fault_write_i, 1'b1}; + s_fault_addr_d = fault_addr_i; + s_fault_master_d = fault_master_i; + s_fault_resp_d = fault_resp_i; + end + end + end + dffr #( + .DATA_WIDTH(32) + ) u_test_reg ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .dat_i (s_test_d), + .dat_o (s_test_q) + ); + dffr #( + .DATA_WIDTH(32) + ) u_fault_addr_reg ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .dat_i (s_fault_addr_d), + .dat_o (s_fault_addr_q) + ); + dffr #( + .DATA_WIDTH(32) + ) u_fault_count_reg ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .dat_i (s_fault_count_d), + .dat_o (s_fault_count_q) + ); + dffr #( + .DATA_WIDTH(5) + ) u_fault_status_reg ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .dat_i (s_fault_stat_d), + .dat_o (s_fault_stat_q) + ); + dffr #( + .DATA_WIDTH(1) + ) u_fault_master_reg ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .dat_i (s_fault_master_d), + .dat_o (s_fault_master_q) + ); + dffr #( + .DATA_WIDTH(2) + ) u_fault_resp_reg ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .dat_i (s_fault_resp_d), + .dat_o (s_fault_resp_q) + ); + dffr #( + .DATA_WIDTH(1) + ) u_perf_enable_reg ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .dat_i (s_perf_en_d), + .dat_o (s_perf_en_q) + ); + dffr #( + .DATA_WIDTH(1) + ) u_wake_reg ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .dat_i (s_wake_d), + .dat_o (s_wake_q) + ); + dffr #( + .DATA_WIDTH(64) + ) u_cpu_snapshot_reg ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .dat_i (s_cpu_snapshot_d), + .dat_o (s_cpu_snapshot_q) + ); + dffr #( + .DATA_WIDTH(64) + ) u_dma_snapshot_reg ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .dat_i (s_dma_snapshot_d), + .dat_o (s_dma_snapshot_q) + ); + dffr #( + .DATA_WIDTH(1) + ) u_ready_reg ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .dat_i (s_ready_d), + .dat_o (s_ready_q) + ); + dffr #( + .DATA_WIDTH(1) + ) u_error_reg ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .dat_i (s_err_d), + .dat_o (s_err_q) + ); + dffr #( + .DATA_WIDTH(32) + ) u_rdata_reg ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .dat_i (s_rdata_d), + .dat_o (s_rdata_q) + ); +endmodule diff --git a/scripts/check_embedded_c.py b/scripts/check_embedded_c.py index ab37fb25..2aa53276 100644 --- a/scripts/check_embedded_c.py +++ b/scripts/check_embedded_c.py @@ -66,7 +66,11 @@ def policy_issues(root: Path, path: Path, policy: dict[str, object]) -> list[str text = path.read_text(encoding="utf-8") issues: list[str] = [] - if TINY_TOKEN.search(text): + # Product names in comments/string literals are prose, not retired SDK + # identifiers. Legacy include paths are still checked against the raw text. + code = re.sub(r'/\*.*?\*/|//[^\n]*|"(?:\\.|[^"\\])*"|\'(?:\\.|[^\'\\])*\'', + " ", text, flags=re.DOTALL) + if TINY_TOKEN.search(code): issues.append("contains a retired tiny-prefixed identifier or label") if LEGACY_INCLUDE.search(text): issues.append("contains a retired legacy include path") diff --git a/scripts/check_format.py b/scripts/check_format.py index 647d22ca..bd85ce7a 100644 --- a/scripts/check_format.py +++ b/scripts/check_format.py @@ -17,6 +17,7 @@ SELF_OWNED_RTL_ROOTS = { ("rtl", "demo"), ("rtl", "ip"), ("rtl", "mini"), + ("rtl", "tiny"), ("rtl", "tech"), ("tests", "rtl"), } diff --git a/scripts/crypto_p0.py b/scripts/crypto_p0.py index f5d89fec..5a4cd829 100644 --- a/scripts/crypto_p0.py +++ b/scripts/crypto_p0.py @@ -193,7 +193,7 @@ def rtl(variant: Path, output: Path, jobs: int, selected: str | None = None) -> includes = [CRYPTO, COMMON, COMMON / "interface"] if top == "crypto_dma_p0_tb": generated = case / "memory_map" - execute(case, "memory-map", [sys.executable, str(ROOT / "rtl/mini/address_map/generate_memory_map.py"), + execute(case, "memory-map", [sys.executable, str(ROOT / "scripts/rtl/generate_memory_map.py"), "--map", str(ROOT / "rtl/mini/address_map/memory_map.json"), "--output-dir", str(generated), "--have-sram-if", "NO"]) includes += [generated / "rtl", COMMON / "stream", COMMON / "utils"] @@ -214,7 +214,7 @@ def rtl(variant: Path, output: Path, jobs: int, selected: str | None = None) -> fl.write_text("+define+SV_ASSRT_DISABLE\n" + "\n".join([*[f"+incdir+{p}" for p in includes], *map(str, sources)]) + "\n") converted = case / "test.v" - execute(case, "convert", [sys.executable, str(ROOT / "rtl/mini/script/convt_sv2v.py"), + execute(case, "convert", [sys.executable, str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(fl), "--output", str(converted)]) binary = case / "test.vvp" execute(case, "compile", ["iverilog", "-g2012", "-s", top, diff --git a/scripts/doctor.py b/scripts/doctor.py index 973bcff9..8e0c6961 100644 --- a/scripts/doctor.py +++ b/scripts/doctor.py @@ -23,6 +23,7 @@ def parse_args() -> argparse.Namespace: parser.add_argument("--synth", required=True) parser.add_argument("--sta", required=True) parser.add_argument("--pdk", required=True) + parser.add_argument("--soc", choices=("MINI", "TINY"), default="MINI") parser.add_argument("--have-sram-macro", choices=("YES", "NO"), default="NO") parser.add_argument("--formal", choices=("YES", "NO"), default="NO") parser.add_argument("--require-debug-tools", action="store_true") @@ -73,6 +74,9 @@ def main() -> int: tools.extend(("openocd", "riscv32-unknown-elf-gdb")) source_names = ["mpw", "hazard3", "cluster_common", "third_party_ip"] + if args.soc == "TINY": + from scripts.setup_tiny import TINY_SOURCES + source_names = [name for name in TINY_SOURCES if not name.startswith("pdk_")] pdk_names = { "IHP130": "pdk_ihp130", "ICS55": "pdk_ics55", diff --git a/scripts/generate_tiny.py b/scripts/generate_tiny.py new file mode 100644 index 00000000..6c908315 --- /dev/null +++ b/scripts/generate_tiny.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python3 +"""Generate Tiny integration bindings and absolute source lists from owned maps.""" + +from __future__ import annotations + +import argparse +import json +import re +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(ROOT)) +from scripts.rtl.generate_memory_map import generate as generate_memory_map # noqa: E402 +from scripts.rtl.generate_pin_map import generate as generate_pin_map # noqa: E402 +from scripts.setup_helpers import atomic_write # noqa: E402 + +TINY = ROOT / "rtl/tiny" + + +def read_topology(path: Path, memory_map: Path) -> dict: + document = json.loads(path.read_text()) + regions = {r["symbol"] for r in json.loads(memory_map.read_text())["regions"]} + if document.get("schema_version") != 1 or document.get("dma_channels") != 4: + raise ValueError("Tiny requires topology schema 1 and four DMA channels") + if document.get("irq_vector_width") != 32 or document.get("external_irq_count") != 30: + raise ValueError("Tiny requires a 32-bit IRQ vector and 30 external inputs") + names: set[str] = set() + bits: set[int] = set() + for irq in document["interrupts"]: + if irq["name"] in names or irq["core_bit"] in bits: + raise ValueError("duplicate Tiny interrupt") + if not 0 <= irq["core_bit"] < 32: + raise ValueError("Tiny IRQ outside vector") + names.add(irq["name"]) + bits.add(irq["core_bit"]) + assigned: set[str] = set() + for target in document["apb_targets"]: + for region in target["regions"]: + if region not in regions or region in assigned: + raise ValueError(f"invalid or duplicate APB region {region}") + assigned.add(region) + if assigned != {r for r in regions if r.startswith("APB4_")}: + raise ValueError("Tiny topology must route every APB region") + if len(document["apb_targets"]) != 16: + raise ValueError("Tiny's fixed APB bridge requires sixteen targets") + target_names = [target["name"] for target in document["apb_targets"]] + if len(set(target_names)) != 16 or any(re.fullmatch(r"[a-z][a-z0-9_]*", name) is None + for name in target_names): + raise ValueError("Tiny APB target names must be unique identifiers") + if sorted(item["pin"] for item in document["gpio_alt_functions"]) != list(range(32)): + raise ValueError("Tiny GPIO map must contain all 32 pins exactly once") + return document + + +def generate_bindings(document: dict, output: Path) -> None: + comment = "// Generated by scripts/generate_tiny.py; do not edit.\n" + irq = [comment, "always_comb begin", " s_irq = '0;"] + header = ["#ifndef RETROSOC_GENERATED_IRQ_METADATA_H", + "#define RETROSOC_GENERATED_IRQ_METADATA_H", "#include ", + "#define RS_SOC_IRQ_VECTOR_WIDTH UINT32_C(32)", + "#define RS_SOC_EXTERNAL_IRQ_COUNT UINT32_C(30)", + f"#define RS_SOC_ALLOCATED_IRQ_COUNT UINT32_C({len(document['interrupts']) - 2})"] + for item in document["interrupts"]: + bit, name = item["core_bit"], item["name"].upper() + irq.append(f" s_irq[{bit}] = {item['signal']};") + header.append(f"#define RS_SOC_IRQ_{name} UINT32_C({bit})") + if bit >= 2: + header.append(f"#define RS_SOC_EXT_IRQ_{name} UINT32_C({bit - 2})") + irq.append("end\n") + header.append("#endif\n") + atomic_write(output / "rtl/tiny_irq.svh", "\n".join(irq)) + atomic_write(output / "include/retrosoc/generated/irq_metadata.h", "\n".join(header)) + atomic_write(output / "include/retrosoc/generated/user_extensions.h", + "#ifndef RETROSOC_GENERATED_USER_EXTENSIONS_H\n" + "#define RETROSOC_GENERATED_USER_EXTENSIONS_H\n" + "#define RS_SOC_USER_CORE_COUNT 0U\n#define RS_SOC_USER_IP_COUNT 0U\n" + "#define RS_SOC_HAS_USER_CORES 0U\n#define RS_SOC_HAS_USER_IP_MUX 0U\n#endif\n") + atomic_write(output / "include/retrosoc/generated/soc_features.h", + "#ifndef RETROSOC_GENERATED_SOC_FEATURES_H\n" + "#define RETROSOC_GENERATED_SOC_FEATURES_H\n" + "#define RS_SOC_TINY 1\n#define RS_SOC_HAS_PSRAM 0U\n" + "#define RS_SOC_DMA_CHANNELS 4U\n#define RS_SOC_ID 0x54494E59U\n#endif\n") + gpio = [comment] + for function in document["gpio_alt_functions"]: + pin = function["pin"] + for mode in ("alt0", "alt1"): + value = function[mode] + gpio.extend(f"assign {signal} = u_gpio_if.di_i[{pin}];" for signal in value["inputs"]) + gpio.extend((f"assign u_gpio_if.{mode}_do_i[{pin}] = {value['do']};", + f"assign u_gpio_if.{mode}_oe_i[{pin}] = {value['oe']};")) + atomic_write(output / "rtl/tiny_gpio.svh", "\n".join(gpio) + "\n") + ports, declarations, routes, decode = [], [comment], [comment], [comment] + for index, target in enumerate(document["apb_targets"]): + name = target["name"] + ports.append(f"apb4_if.master {name}") + declarations.append(f"apb4_if u_{name}_apb4_if (.pclk(clk_i), .presetn(s_rst_n));") + routes.append(f".{name}(u_{name}_apb4_if)") + decode.append(f"if ({' || '.join(f'`SOC_ADDR_IS_{r}(s_addr_q)' for r in target['regions'])}) " + f"s_select = {len(document['apb_targets'])}'b{1 << index:016b};") + atomic_write(output / "rtl/tiny_apb_ports.svh", ",\n".join(ports) + "\n") + atomic_write(output / "rtl/tiny_apb_interfaces.svh", "\n".join(declarations) + "\n") + atomic_write(output / "rtl/tiny_apb_connections.svh", ",\n".join(routes[1:]) + "\n") + atomic_write(output / "rtl/tiny_apb_decode.svh", "\n".join(decode) + "\n") + wiring = [comment] + for index, target in enumerate(document["apb_targets"]): + name = target["name"] + for field, value in {"paddr": "s_addr_q", "pwrite": "s_write_q", + "pwdata": "s_wdata_q", "pstrb": "s_wstrb_q", "pprot": "3'd0", + "psel": f"s_select[{index}] && s_access", + "penable": "s_enable"}.items(): + wiring.append(f"assign {name}.{field} = {value};") + wiring.extend((f"assign s_ready[{index}] = {name}.pready;", + f"assign s_error[{index}] = {name}.pslverr;", + f"assign s_rdata[{index}] = {name}.prdata;")) + atomic_write(output / "rtl/tiny_apb_wiring.svh", "\n".join(wiring) + "\n") + + +def generate_filelists(output: Path, generated: Path, defines: list[str]) -> None: + prefixes = {"top": TINY / "top", "dv": TINY / "dv", "ip": ROOT / "rtl/ip", + "tech": ROOT / "rtl/tech", "clusterip": ROOT / "rtl/managed/clusterip", + "hazard3": ROOT / "rtl/managed/hazard3/hdl", "physical": ROOT / "physical"} + templates = [*sorted((TINY / "filelist").glob("*.fl")), + ROOT / "rtl/filelist/pdk_ihp130.fl"] + for template in templates: + content = template.read_text() + for prefix, directory in prefixes.items(): + content = re.sub(rf"(? int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--output", type=Path) + parser.add_argument("--filelists", type=Path) + parser.add_argument("--define", action="append", default=[]) + parser.add_argument("--check", action="store_true") + args = parser.parse_args() + document = read_topology(TINY / "integration/soc_topology.json", + TINY / "address_map/memory_map.json") + if args.check: + return 0 + if args.output is None or args.filelists is None: + parser.error("--output and --filelists are required unless --check is used") + if any(not value.startswith("+define+") for value in args.define): + parser.error("--define requires a +define+ token") + output = args.output.resolve() + generate_memory_map(TINY / "address_map/memory_map.json", output / "memory_map", "YES", 128) + generate_pin_map(TINY / "pin_map/pin_map.json", output / "pin_map") + generate_bindings(document, output) + generate_filelists(args.filelists.resolve(), output, args.define) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/metrics.py b/scripts/metrics.py index cf9bf916..a17b61a4 100644 --- a/scripts/metrics.py +++ b/scripts/metrics.py @@ -17,7 +17,7 @@ from scripts.setup_helpers import atomic_write # noqa: E402 AREA_RE = re.compile(r"Chip area for top module[^:]*:\s*([0-9.eE+-]+)") -HIERARCHY_RE = re.compile(r"^\s*(\d+)\s+[0-9.eE+-]+\s+retrosoc_asic\s*$", re.MULTILINE) +HIERARCHY_RE = re.compile(r"^\s*(\d+)\s+[0-9.eE+-]+\s+retrosoc_(?:tiny_)?asic\s*$", re.MULTILINE) def collect(args: argparse.Namespace) -> int: @@ -35,10 +35,18 @@ def collect(args: argparse.Namespace) -> int: for binary in sorted((root / "sw").glob("*.bin")): metrics["firmware"][binary.name] = {"bytes": binary.stat().st_size} - area_json = synth_root / "rpt/retrosoc_asic_area.json" - area_report = synth_root / "rpt/retrosoc_asic_area.rpt" + manifest_path = root / "meta/manifest.json" + soc = "MINI" + if manifest_path.is_file(): + soc = json.loads(manifest_path.read_text())["configuration"].get("SOC", "MINI") + top = "retrosoc_tiny_asic" if soc == "TINY" else "retrosoc_asic" + area_json = synth_root / "rpt" / f"{top}_area.json" + area_report = synth_root / "rpt" / f"{top}_area.rpt" if area_json.is_file(): - design = json.loads(area_json.read_text(encoding="utf-8"))["design"] + # Yosys -t also prefixes tee report lines in some locked tool builds. + contents = re.sub(r"^\[\d+\.\d+\]\s?", "", area_json.read_text(encoding="utf-8"), + flags=re.MULTILINE) + design = json.loads(contents)["design"] metrics["synthesis"]["top_area"] = float(design["area"]) metrics["synthesis"]["top_cells"] = int(design["num_cells"]) elif area_report.is_file(): diff --git a/scripts/package.py b/scripts/package.py index 28a8b807..82f0b6c9 100644 --- a/scripts/package.py +++ b/scripts/package.py @@ -93,6 +93,8 @@ def main() -> int: manifest = json.loads(manifest_path.read_text(encoding="utf-8")) config = manifest["configuration"] lock = load_lock(args.lock) + soc = config.get("SOC", "MINI") + product_root = root / "rtl" / soc.lower() args.output_dir.parent.mkdir(parents=True, exist_ok=True) with tempfile.TemporaryDirectory( @@ -100,7 +102,9 @@ def main() -> int: ) as temp: staging = Path(temp) / "package" staging.mkdir() - timing_contract = Path(temp) / "commercial_timing_contract.tcl" + timing_contract = Path(temp) / ("tiny_core.sdc" if soc == "TINY" else "commercial_timing_contract.tcl") + timing_generator = (root / "physical/smoke/sta/opensta/generate_sdc.py" if soc == "TINY" + else root / "physical/commercial/scripts/generate_timing_contract.py") if config.get("MINI_MODE", "PRODUCT") == "MPW": dynamic_core_filelist = ( args.variant_root @@ -124,14 +128,11 @@ def main() -> int: subprocess.run( [ "python3", - str( - root - / "physical/commercial/scripts/generate_timing_contract.py" - ), + str(timing_generator), "--domains", - str(root / "rtl/mini/integration/clock_reset_domains.json"), + str(product_root / "integration/clock_reset_domains.json"), "--pin-map", - str(root / "rtl/mini/pin_map/pin_map.json"), + str(product_root / "pin_map/pin_map.json"), "--output", str(timing_contract), ], @@ -171,8 +172,16 @@ def main() -> int: "--archinfo-incdir", str(args.variant_root / "generated/archinfo"), "--metadata-file", - "contracts/commercial_timing_contract.tcl={0}".format(timing_contract), + f"contracts/{timing_contract.name}={timing_contract}", ] + if soc == "TINY": + if config.get("SYNTH") == "YOSYS": + recipe = config.get("SYNTH_RECIPE", "balanced") + folder = "yosys" if recipe == "balanced" else f"yosys-{recipe}" + source_dir = args.variant_root / "syn" / folder / "filelists" + else: + source_dir = args.variant_root / "sim" / config["SIMU"].lower() / "filelists" + command.extend(("--soc", "TINY", "--source-filelist-dir", str(source_dir))) for key, flag in ( ("HAVE_PLL", "--have-pll"), ("HAVE_SRAM_IF", "--have-sram-if"), diff --git a/scripts/regress.py b/scripts/regress.py index df161afe..92c8b4fc 100644 --- a/scripts/regress.py +++ b/scripts/regress.py @@ -70,7 +70,16 @@ PR_COMMANDS = ( ), ("configs/ci/ihp130.mk", ("STA=OPENSTA", "sta")), ) -RTL_COMMANDS = PR_COMMANDS[:6] +TINY_PR_COMMANDS = ( + ("configs/ci/ihp130-tiny.mk", RTL_LINT_VALUES), + ("configs/ci/ihp130-tiny.mk", ("APP=ci_smoke", "LINK_TYPE=ld2_all_sram", "SIMU=VERILATOR", "SOC_SIM_TIME=1800", "HAVE_CSR=YES", "HAVE_SVA=YES", "firmware", "sim")), + ("configs/ci/ihp130-tiny.mk", ("APP=ci_smoke", "SIMU=IVERILOG", "firmware", "sim")), + ("configs/ci/ihp130-tiny.mk", ("APP=ci_smoke", "SYNTH=YOSYS", "synth")), + ("configs/ci/ihp130-tiny.mk", ("APP=ci_smoke", "SIMU=IVERILOG", "netsim-boot")), + ("configs/ci/ihp130-tiny.mk", ("APP=ci_smoke", "STA=OPENSTA", "sta")), +) +RTL_COMMANDS = (*PR_COMMANDS[:6], *TINY_PR_COMMANDS[:3]) +PR_COMMANDS = (*PR_COMMANDS, *TINY_PR_COMMANDS) SMOKE_COMMANDS = ( ("configs/ci/ihp130.mk", RTL_LINT_VALUES), ("configs/ci/ihp130.mk", (CI_SMOKE_APP_VALUE, "firmware")), @@ -106,7 +115,7 @@ NIGHTLY_COMMANDS = ( *PR_COMMANDS, *NIGHTLY_EXTRA_COMMANDS, ) -PR_PROFILES = ("configs/ci/ihp130.mk",) +PR_PROFILES = ("configs/ci/ihp130.mk", "configs/ci/ihp130-tiny.mk") SMOKE_PROFILES: tuple[str, ...] = () NIGHTLY_PROFILES = PR_PROFILES @@ -161,7 +170,7 @@ def select_regression( if suite == "nightly-extra": if pdk is not None and pdk != "IHP130": raise ValueError("nightly-extra regression supports only --pdk IHP130") - return NIGHTLY_EXTRA_COMMANDS, NIGHTLY_PROFILES + return NIGHTLY_EXTRA_COMMANDS, ("configs/ci/ihp130.mk",) if pdk: if suite == "nightly": if pdk != "IHP130": @@ -259,6 +268,7 @@ def main() -> int: "--suite", choices=("smoke", "rtl", "pr", "nightly", "nightly-extra"), required=True ) parser.add_argument("--pdk", choices=tuple(PDK_PR_PROFILES), help="run one PDK matrix") + parser.add_argument("--soc", choices=("MINI", "TINY"), help="run one product only") parser.add_argument("--dry-run", action="store_true") parser.add_argument( "--netsim-boot-only", @@ -275,6 +285,17 @@ def main() -> int: commands, profiles = select_regression(args.suite, args.pdk) except ValueError as error: parser.error(str(error)) + if args.soc is not None: + tiny_profile = "configs/ci/ihp130-tiny.mk" + if args.soc == "TINY" and args.suite == "smoke": + commands, profiles = TINY_PR_COMMANDS[:2], (tiny_profile,) + else: + commands = tuple((profile, values) for profile, values in commands + if (profile == tiny_profile) == (args.soc == "TINY")) + profiles = tuple(profile for profile in profiles + if (profile == tiny_profile) == (args.soc == "TINY")) + if not commands: + parser.error(f"suite {args.suite} has no {args.soc} configuration for this PDK") if args.netsim_boot_only: commands = with_netsim_boot_only(commands) if args.behavioral_only: diff --git a/scripts/rtl/README.md b/scripts/rtl/README.md new file mode 100644 index 00000000..9325a82e --- /dev/null +++ b/scripts/rtl/README.md @@ -0,0 +1,10 @@ +# Shared RTL Flow Helpers + +These product-independent helpers parse filelists, convert SystemVerilog, +generate address/pad artifacts, and prepare simulation inputs. Both products +invoke them here directly, with Python imports through `scripts.rtl`. Product +inputs stay under their owning `rtl/` directory; outputs stay below +`build/`. + +Changes require focused generator tests, Ruff and Pytest, plus compilation of +each affected product. `generate_tiny.py` owns Tiny-only integration bindings. diff --git a/rtl/mini/script/comb.py b/scripts/rtl/comb.py similarity index 85% rename from rtl/mini/script/comb.py rename to scripts/rtl/comb.py index c4ec62ed..8de52f62 100644 --- a/rtl/mini/script/comb.py +++ b/scripts/rtl/comb.py @@ -3,9 +3,12 @@ from __future__ import annotations import argparse +import sys from pathlib import Path -from filelist import parse_filelists, write_filelist +ROOT = Path(__file__).resolve().parents[2] +sys.path.insert(0, str(ROOT)) +from scripts.rtl.filelist import parse_filelists, write_filelist # noqa: E402 DEFAULT_OUTPUT = Path(__file__).resolve().parent.parent / ".generated_fl" / "yosys.fl" diff --git a/rtl/mini/script/convt_sv2v.py b/scripts/rtl/convt_sv2v.py similarity index 89% rename from rtl/mini/script/convt_sv2v.py rename to scripts/rtl/convt_sv2v.py index 4d7160e6..df183201 100644 --- a/rtl/mini/script/convt_sv2v.py +++ b/scripts/rtl/convt_sv2v.py @@ -4,9 +4,12 @@ from __future__ import annotations import argparse import subprocess +import sys from pathlib import Path -from filelist import parse_filelists +ROOT = Path(__file__).resolve().parents[2] +sys.path.insert(0, str(ROOT)) +from scripts.rtl.filelist import parse_filelists # noqa: E402 def parse_args() -> argparse.Namespace: diff --git a/rtl/mini/script/filelist.py b/scripts/rtl/filelist.py similarity index 100% rename from rtl/mini/script/filelist.py rename to scripts/rtl/filelist.py diff --git a/rtl/mini/script/filelist_deps.py b/scripts/rtl/filelist_deps.py similarity index 91% rename from rtl/mini/script/filelist_deps.py rename to scripts/rtl/filelist_deps.py index dfb78d9a..f25a066c 100644 --- a/rtl/mini/script/filelist_deps.py +++ b/scripts/rtl/filelist_deps.py @@ -3,9 +3,12 @@ from __future__ import annotations import argparse +import sys from pathlib import Path -from filelist import atomic_write, parse_filelists +ROOT = Path(__file__).resolve().parents[2] +sys.path.insert(0, str(ROOT)) +from scripts.rtl.filelist import atomic_write, parse_filelists # noqa: E402 HEADER_SUFFIXES = {".h", ".vh", ".svh"} diff --git a/rtl/mini/address_map/generate_memory_map.py b/scripts/rtl/generate_memory_map.py similarity index 94% rename from rtl/mini/address_map/generate_memory_map.py rename to scripts/rtl/generate_memory_map.py index 5095a4f1..aef31204 100644 --- a/rtl/mini/address_map/generate_memory_map.py +++ b/scripts/rtl/generate_memory_map.py @@ -153,10 +153,10 @@ def join_or(expressions: list[str]) -> str: def render_rtl( - cpu_reset: int, regions: list[dict[str, Any]], sysctrl_registers: list[dict[str, Any]] + cpu_reset: int, regions: list[dict[str, Any]], sysctrl_registers: list[dict[str, Any]], legacy_rib: bool = True ) -> str: lines = [ - "// Generated by rtl/mini/address_map/generate_memory_map.py; do not edit.", + "// Generated by scripts/rtl/generate_memory_map.py; do not edit.", "`ifndef RETROSOC_GENERATED_MMAP_DEFINE_SVH", "`define RETROSOC_GENERATED_MMAP_DEFINE_SVH", "", @@ -189,7 +189,8 @@ def render_rtl( for region in regions if region["route"] in {"apb4_periph", "axi4"} ] - lines.append(f"`define SOC_ADDR_IS_RIB_LEGACY_TARGET(addr) ({join_or(legacy_rib_targets)})") + if legacy_rib: + lines.append(f"`define SOC_ADDR_IS_RIB_LEGACY_TARGET(addr) ({join_or(legacy_rib_targets)})") user_readable = [ f"`SOC_ADDR_IS_{region['symbol']}(addr)" for region in regions @@ -230,9 +231,10 @@ def render_c( regions: list[dict[str, Any]], sysctrl_registers: list[dict[str, Any]], have_sram_if: str, + capability_header: str | None = None, ) -> str: lines = [ - "/* Generated by rtl/mini/address_map/generate_memory_map.py; do not edit. */", + "/* Generated by scripts/rtl/generate_memory_map.py; do not edit. */", "#ifndef RETROSOC_GENERATED_MEMORY_MAP_H", "#define RETROSOC_GENERATED_MEMORY_MAP_H", "", @@ -260,13 +262,15 @@ def render_c( f"#define RS_SOC_SYSCTRL_{register['symbol']}_OFFSET {c_hex32(register['offset'])}" for register in sysctrl_registers ) + if capability_header is not None: + lines.append(f"#include <{capability_header}>") lines += ["", "#endif", ""] return "\n".join(lines) def render_linker(regions: list[dict[str, Any]]) -> str: lines = [ - "/* Generated by rtl/mini/address_map/generate_memory_map.py; do not edit. */", + "/* Generated by scripts/rtl/generate_memory_map.py; do not edit. */", "MEMORY", "{", ] @@ -301,12 +305,14 @@ def generate(map_path: Path, output_dir: Path, have_sram_if: str, sram_size_kib: cpu_reset, regions, sysctrl_registers = read_map(map_path, sram_size_kib) atomic_write( output_dir / "rtl" / "mmap_define.svh", - render_rtl(cpu_reset, regions, sysctrl_registers), + render_rtl(cpu_reset, regions, sysctrl_registers, + json.loads(map_path.read_text()).get("legacy_rib", True)), ) atomic_write(output_dir / "memory_map.fl", f"+incdir+{output_dir / 'rtl'}\n") atomic_write( output_dir / "include" / "retrosoc" / "generated" / "memory_map.h", - render_c(cpu_reset, regions, sysctrl_registers, have_sram_if), + render_c(cpu_reset, regions, sysctrl_registers, have_sram_if, + json.loads(map_path.read_text()).get("capability_header")), ) atomic_write(output_dir / "linker" / "memory_regions.ld", render_linker(regions)) diff --git a/rtl/mini/pin_map/generate_pin_map.py b/scripts/rtl/generate_pin_map.py similarity index 97% rename from rtl/mini/pin_map/generate_pin_map.py rename to scripts/rtl/generate_pin_map.py index 99156b06..6975b065 100644 --- a/rtl/mini/pin_map/generate_pin_map.py +++ b/scripts/rtl/generate_pin_map.py @@ -221,7 +221,7 @@ def wrap_feature(pad: Pad, line: str) -> list[str]: def render_ports(pads: list[Pad]) -> str: lines = [ - "// Generated by rtl/mini/pin_map/generate_pin_map.py; do not edit.", + "// Generated by scripts/rtl/generate_pin_map.py; do not edit.", "`ifdef PDK_IHP130", "`ifdef USE_POWER_PINS", " inout wire IOVDD,", @@ -239,7 +239,7 @@ def render_ports(pads: list[Pad]) -> str: def render_core_ports(pads: list[Pad]) -> str: """Render the logical-chip port list without technology power pins.""" - lines = ["// Generated by rtl/mini/pin_map/generate_pin_map.py; do not edit."] + lines = ["// Generated by scripts/rtl/generate_pin_map.py; do not edit."] for index, pad in enumerate(pads): suffix = "," if index != len(pads) - 1 else "" lines.extend(wrap_feature(pad, f" {pad.direction} {pad.name}{suffix}")) @@ -301,7 +301,7 @@ def render_pad_instance(pad: Pad) -> str | None: def render_pad_bindings(pads: list[Pad]) -> str: lines = [ - "// Generated by rtl/mini/pin_map/generate_pin_map.py; do not edit.", + "// Generated by scripts/rtl/generate_pin_map.py; do not edit.", "`ifdef PDK_IHP130", "`ifdef USE_POWER_PINS", "`define RETROSOC_PAD_POWER_CONNECTIONS .iovdd(IOVDD), .iovss(IOVSS), .vdd(VDD), .vss(VSS),", @@ -395,7 +395,7 @@ def render_core_binding(pad: Pad) -> str | None: def render_core_bindings(pads: list[Pad]) -> str: - lines = ["// Generated by rtl/mini/pin_map/generate_pin_map.py; do not edit."] + lines = ["// Generated by scripts/rtl/generate_pin_map.py; do not edit."] for pad in pads: binding = render_core_binding(pad) if binding is not None: @@ -424,7 +424,7 @@ def ihp130_pad_instance(pad: Pad) -> str | None: def render_profile_bindings(pads: list[Pad], bindings: dict[str, str | None]) -> str: - lines = ["// Generated by rtl/mini/pin_map/generate_pin_map.py; do not edit."] + lines = ["// Generated by scripts/rtl/generate_pin_map.py; do not edit."] for index, pad in enumerate(pads): connection = bindings.get(pad.name) suffix = "," if index != len(pads) - 1 else "" diff --git a/rtl/mini/script/prepare_norflash.py b/scripts/rtl/prepare_norflash.py similarity index 98% rename from rtl/mini/script/prepare_norflash.py rename to scripts/rtl/prepare_norflash.py index 5f42126e..536bbd4a 100644 --- a/rtl/mini/script/prepare_norflash.py +++ b/scripts/rtl/prepare_norflash.py @@ -9,7 +9,7 @@ from pathlib import Path SCRIPT_DIR = Path(__file__).resolve().parent MINI_DIR = SCRIPT_DIR.parent -ROOT_DIR = MINI_DIR.parent.parent +ROOT_DIR = SCRIPT_DIR.parents[1] DEFAULT_MODELS = ROOT_DIR / "rtl" / "managed" / "third_party" / "norflash" DEFAULT_FIRMWARE = ROOT_DIR / ".sw_build" / "retrosoc_fw.hex" diff --git a/scripts/run_apu_p5_corpus_rtl.py b/scripts/run_apu_p5_corpus_rtl.py index 03889e83..40720cbf 100644 --- a/scripts/run_apu_p5_corpus_rtl.py +++ b/scripts/run_apu_p5_corpus_rtl.py @@ -194,7 +194,7 @@ def _compile(build_dir: Path, bundle: Path) -> tuple[dict[str, Path], dict[str, conversion = _run_command( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(filelist), "--output", diff --git a/scripts/run_apu_p7_concurrent_rtl.py b/scripts/run_apu_p7_concurrent_rtl.py index f93bc999..e743f9ca 100644 --- a/scripts/run_apu_p7_concurrent_rtl.py +++ b/scripts/run_apu_p7_concurrent_rtl.py @@ -217,7 +217,7 @@ def _verification_sources() -> dict[str, str]: ROOT / "scripts/apu_codecs.py", ROOT / "scripts/apu_kws.py", ROOT / "scripts/apu_kws_convert.py", - ROOT / "rtl/mini/script/convt_sv2v.py", + ROOT / "scripts/rtl/convt_sv2v.py", Path(__file__).resolve(), ] return { @@ -346,7 +346,7 @@ def _compile(build_dir: Path) -> dict[str, Any]: conversion = _run_command( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(filelist), "--output", diff --git a/scripts/run_apu_p7_kws_rtl.py b/scripts/run_apu_p7_kws_rtl.py index e5e5d601..01004a4a 100644 --- a/scripts/run_apu_p7_kws_rtl.py +++ b/scripts/run_apu_p7_kws_rtl.py @@ -303,7 +303,7 @@ def _compile(build_dir: Path, simulator: str) -> dict[str, Any]: conversion = _run_command( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(filelist), "--output", diff --git a/scripts/run_c_tests.py b/scripts/run_c_tests.py index 2354329e..bb2e3129 100644 --- a/scripts/run_c_tests.py +++ b/scripts/run_c_tests.py @@ -72,7 +72,7 @@ def main() -> int: subprocess.run( [ sys.executable, - str(root / "rtl/mini/address_map/generate_memory_map.py"), + str(root / "scripts/rtl/generate_memory_map.py"), "--map", str(root / "rtl/mini/address_map/memory_map.json"), "--output-dir", @@ -122,6 +122,17 @@ def main() -> int: str(executable), *(str(root / source) for source in TEST_SOURCES), ] + tiny_object = temporary_root / "tiny_dma_math.o" + subprocess.run([ + compiler, "-std=c11", "-Wall", "-Wextra", "-Werror", "-DRS_SOC_TINY", + "-Drs_dma_config_validate=rs_tiny_dma_config_validate", + "-Drs_dma_tcd_validate=rs_tiny_dma_tcd_validate", + "-I", str(memory_map_root / "include"), + "-I", str(user_extensions_root / "include"), + "-I", str(root / "crt/include"), "-c", str(root / "crt/src/hal/dma_math.c"), + "-o", str(tiny_object), + ], check=True) + command.append(str(tiny_object)) subprocess.run(command, check=True) subprocess.run([str(executable)], check=True) return 0 diff --git a/scripts/setup_tiny.py b/scripts/setup_tiny.py new file mode 100644 index 00000000..57235911 --- /dev/null +++ b/scripts/setup_tiny.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +"""Prepare only the locked inputs used by the Tiny MCU.""" + +from __future__ import annotations + +import argparse +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(ROOT)) +from scripts.dependency_lock import source # noqa: E402 +from scripts.setup_helpers import ensure_git_repo # noqa: E402 + +TINY_SOURCES = ( + "hazard3", "cluster_common", "cluster_archinfo", "cluster_pwm", + "cluster_rtc", "cluster_wdg", "pdk_ihp130", +) + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--update", action="store_true") + args = parser.parse_args() + for name in TINY_SOURCES: + dependency = source(name) + ensure_git_repo( + dependency["url"], ROOT / dependency["destination"], dependency["revision"], + recursive=dependency.get("recursive", False), update=args.update, + ) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/c/test_runtime.c b/tests/c/test_runtime.c index 07f0b01d..922ca9bf 100644 --- a/tests/c/test_runtime.c +++ b/tests/c/test_runtime.c @@ -730,6 +730,41 @@ static int test_gpio_helpers(void) { return 0; } +/* The runner compiles the same validator with Tiny's capability selection. */ +rs_status_t rs_tiny_dma_config_validate(uint32_t channel, const rs_dma_config_t *config); +rs_status_t rs_tiny_dma_tcd_validate(uint32_t channel, const rs_dma_tcd_t *tcd); + +static int test_tiny_dma_config_validation(void) { + rs_dma_config_t config = { + .kind = RS_DMA_KIND_MM_TO_MM, .request = RS_DMA_REQUEST_SOFTWARE, + .source = (uintptr_t)UINT32_C(0x30000000), + .destination = (uintptr_t)UINT32_C(0x30001000), .byte_count = 259U, + .width = RS_DMA_WIDTH_32, .source_increment = true, .destination_increment = true, + .burst_beats = 16U, + }; + static _Alignas(64) rs_dma_tcd_t descriptor; + if ((rs_tiny_dma_config_validate(3U, &config) != RS_OK) || + (rs_tiny_dma_config_validate(4U, &config) != RS_EINVAL)) { + return 1; + } + config.kind = RS_DMA_KIND_MM_TO_STREAM; + config.request = RS_DMA_REQUEST_I2S_TX; + config.byte_count = 256U; + if (rs_tiny_dma_config_validate(0U, &config) != RS_EINVAL) { + return 2; + } + descriptor.source = UINT32_C(0x30000000); + descriptor.byte_count = 256U; + descriptor.y_count = 1U; + descriptor.control = RS_DMA_TCD_VALID | RS_DMA_TCD_SRC_INC | + ((uint32_t)RS_DMA_KIND_MM_TO_STREAM << RS_DMA_TCD_KIND_SHIFT) | + ((uint32_t)RS_DMA_REQUEST_I2S_TX << RS_DMA_TCD_REQUEST_SHIFT); + if (rs_tiny_dma_tcd_validate(0U, &descriptor) != RS_ENOTSUP) { + return 3; + } + return 0; +} + static int test_dma_config_validation(void) { static rs_dma_tcd_t tcd __attribute__((aligned(64))); rs_dma_config_t config = { @@ -2631,6 +2666,7 @@ int main(void) { test_spisd_helpers(), test_gpio_helpers(), test_dma_config_validation(), + test_tiny_dma_config_validation(), test_opipsram_helpers(), test_user_ip_validation(), test_extension_validation(), diff --git a/tests/rtl/tiny_axi4_fabric_tb.sv b/tests/rtl/tiny_axi4_fabric_tb.sv new file mode 100644 index 00000000..58f0cff4 --- /dev/null +++ b/tests/rtl/tiny_axi4_fabric_tb.sv @@ -0,0 +1,277 @@ +`timescale 1ns / 1ps +module tiny_axi4_fabric_tb; + logic clk_i = 0; + logic rst_n_i = 0; + always #5 clk_i = !clk_i; + axi4_if #( + .ADDR_WIDTH(32), + .DATA_WIDTH(32), + .ID_WIDTH (1), + .USER_WIDTH(1) + ) masters[2] ( + .aclk (clk_i), + .aresetn(rst_n_i) + ); + axi4_if #( + .ADDR_WIDTH(32), + .DATA_WIDTH(32), + .ID_WIDTH (1), + .USER_WIDTH(1) + ) targets[5] ( + .aclk (clk_i), + .aresetn(rst_n_i) + ); + logic fault_valid, fault_master, fault_write; + logic [31:0] fault_addr; + logic [ 1:0] fault_resp; + logic [63:0] cpu_wait, dma_wait; + integer faults = 0; + integer completions [2]; + integer target_requests[5]; + logic [1:0][31:0] awaddr; + logic [1:0][ 7:0] awlen; + logic [1:0][ 2:0] awsize; + logic [1:0][ 1:0] awburst; + logic [1:0][ 0:0] awlock; + logic [1:0][ 0:0] awid; + logic [1:0][ 0:0] awvalid; + logic [1:0][31:0] wdata; + logic [1:0][ 3:0] wstrb; + logic [1:0][ 0:0] wlast; + logic [1:0][ 0:0] wvalid; + logic [1:0][ 0:0] bready; + logic [1:0][31:0] araddr; + logic [1:0][ 7:0] arlen; + logic [1:0][ 2:0] arsize; + logic [1:0][ 1:0] arburst; + logic [1:0][ 0:0] arlock; + logic [1:0][ 0:0] arid; + logic [1:0][ 0:0] arvalid; + logic [1:0][ 0:0] rready; + logic [1:0][ 0:0] awready; + logic [1:0][ 0:0] arready; + logic [1:0][ 0:0] wready; + logic [1:0][ 0:0] bvalid; + logic [1:0][ 0:0] bid; + logic [1:0][ 1:0] bresp; + logic [1:0][ 0:0] rvalid; + logic [1:0][ 0:0] rid; + logic [1:0][ 0:0] rlast; + logic [1:0][31:0] rdata; + logic [1:0][ 1:0] rresp; + tiny_axi4_fabric u_dut ( + .clk_i (clk_i), + .rst_n_i (rst_n_i), + .perf_enable_i (1'b1), + .perf_clear_i (1'b0), + .masters (masters), + .targets (targets), + .fault_valid_o (fault_valid), + .fault_addr_o (fault_addr), + .fault_master_o(fault_master), + .fault_write_o (fault_write), + .fault_resp_o (fault_resp), + .cpu_wait_o (cpu_wait), + .dma_wait_o (dma_wait) + ); + always @(posedge clk_i) if (fault_valid) faults <= faults + 1; + for (genvar m = 0; m < 2; m++) begin : gen_master + assign masters[m].awaddr = awaddr[m]; + assign masters[m].awlen = awlen[m]; + assign masters[m].awsize = awsize[m]; + assign masters[m].awburst = awburst[m]; + assign masters[m].awlock = awlock[m]; + assign masters[m].awid = awid[m]; + assign masters[m].awvalid = awvalid[m]; + assign masters[m].wdata = wdata[m]; + assign masters[m].wstrb = wstrb[m]; + assign masters[m].wlast = wlast[m]; + assign masters[m].wvalid = wvalid[m]; + assign masters[m].bready = bready[m]; + assign masters[m].araddr = araddr[m]; + assign masters[m].arlen = arlen[m]; + assign masters[m].arsize = arsize[m]; + assign masters[m].arburst = arburst[m]; + assign masters[m].arlock = arlock[m]; + assign masters[m].arid = arid[m]; + assign masters[m].arvalid = arvalid[m]; + assign masters[m].rready = rready[m]; + assign awready[m] = masters[m].awready; + assign arready[m] = masters[m].arready; + assign wready[m] = masters[m].wready; + assign bvalid[m] = masters[m].bvalid; + assign bid[m] = masters[m].bid; + assign bresp[m] = masters[m].bresp; + assign rvalid[m] = masters[m].rvalid; + assign rid[m] = masters[m].rid; + assign rlast[m] = masters[m].rlast; + assign rdata[m] = masters[m].rdata; + assign rresp[m] = masters[m].rresp; + assign masters[m].awcache = '0; + assign masters[m].awprot = '0; + assign masters[m].awqos = '0; + assign masters[m].awregion = '0; + assign masters[m].awuser = '0; + assign masters[m].arcache = '0; + assign masters[m].arprot = '0; + assign masters[m].arqos = '0; + assign masters[m].arregion = '0; + assign masters[m].aruser = '0; + assign masters[m].wuser = '0; + end + for (genvar t = 0; t < 5; t++) begin : gen_target + // Deterministic terminal responder allows independent checks of routing, + // burst length and ID preservation without sharing the fabric decoder. + axi4_error_slave #( + .Response(t == 3 ? 2'b11 : (t == 4 ? 2'b10 : 2'b00)) + ) u_response ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .axi4 (targets[t]) + ); + initial target_requests[t] = 0; + always @(posedge clk_i) begin + if ((targets[t].arvalid && targets[t].arready) || (targets[t].awvalid && targets[t].awready)) + target_requests[t] <= target_requests[t] + 1; + end + end + task automatic read_request(input int m, input logic [31:0] addr, input int beats, + input logic [2:0] size, input logic [1:0] burst, + input logic [1:0] expected); + logic [35:0] held; + @(negedge clk_i); + araddr[m] = addr; + arlen[m] = 8'(beats - 1); + arsize[m] = size; + arburst[m] = burst; + arid[m] = 1'(m); + arvalid[m] = 1; + do @(posedge clk_i); while (!arready[m]); + @(negedge clk_i); + arvalid[m] = 0; + for (int beat = 0; beat < beats; beat++) begin + do @(posedge clk_i); while (!rvalid[m]); + held = {rid[m], rlast[m], rresp[m], rdata[m]}; + repeat (3) begin + @(negedge clk_i); + if (!rvalid[m] || held != {rid[m], rlast[m], rresp[m], rdata[m]}) + $fatal(1, "read changed under backpressure"); + end + if (rresp[m] != expected || rid[m] != 1'(m) || rlast[m] != (beat == beats - 1)) + $fatal( + 1, + "read response/ID/length mismatch m=%0d addr=%x beat=%0d resp=%0d", + m, + addr, + beat, + rresp[m] + ); + rready[m] = 1; + @(posedge clk_i); + @(negedge clk_i); + rready[m] = 0; + end + completions[m]++; + endtask + task automatic write_request(input int m, input logic [31:0] addr, input int beats, + input logic [1:0] expected, input bit early_w); + @(negedge clk_i); + awaddr[m] = addr; + awlen[m] = 8'(beats - 1); + awsize[m] = 2; + awburst[m] = 1; + awid[m] = 1'(m); + wdata[m] = 32'h12345678; + wstrb[m] = 4'b0101; + wlast[m] = (beats == 1); + if (early_w) begin + wvalid[m] = 1; + repeat (4) begin + @(posedge clk_i); + if (wready[m]) $fatal(1, "W accepted before AW"); + end + @(negedge clk_i); + end + awvalid[m] = 1; + do @(posedge clk_i); while (!awready[m]); + @(negedge clk_i); + awvalid[m] = 0; + if (!early_w) repeat (5) @(negedge clk_i); + for (int beat = 0; beat < beats; beat++) begin + wvalid[m] = 1; + wlast[m] = (beat == beats - 1); + wdata[m] = 32'(beat); + do @(posedge clk_i); while (!wready[m]); + @(negedge clk_i); + wvalid[m] = 0; + repeat (2) @(negedge clk_i); + end + do @(posedge clk_i); while (!bvalid[m]); + repeat (3) begin + @(negedge clk_i); + if (!bvalid[m] || bresp[m] != expected || bid[m] != 1'(m)) + $fatal(1, "write response mismatch"); + end + bready[m] = 1; + @(posedge clk_i); + @(negedge clk_i); + bready[m] = 0; + completions[m]++; + endtask + initial begin + awaddr = '0; + awlen = '0; + awsize = '0; + awburst = '0; + awlock = '0; + awid = '0; + awvalid = '0; + wdata = '0; + wstrb = '0; + wlast = '0; + wvalid = '0; + bready = '0; + araddr = '0; + arlen = '0; + arsize = '0; + arburst = '0; + arlock = '0; + arid = '0; + arvalid = '0; + rready = '0; + completions[0] = 0; + completions[1] = 0; + repeat (5) @(negedge clk_i); + rst_n_i = 1; + fork + begin + write_request(0, 32'h30000000, 16, 0, 1); + read_request(0, 32'h30000000, 16, 2, 1, 0); + read_request(0, 32'h38000000, 4, 2, 1, 3); + read_request(0, 32'h30000ffc, 2, 2, 1, 2); + read_request(0, 32'h30000001, 1, 2, 1, 2); + read_request(0, 32'h10001000, 2, 2, 1, 2); + read_request(0, 32'h30000000, 4, 2, 2, 2); + end + begin + read_request(1, 32'h50000000, 4, 2, 1, 0); + write_request(1, 32'h10001000, 1, 0, 0); + write_request(1, 32'h38000000, 3, 3, 1); + read_request(1, 32'h10001000, 1, 2, 0, 0); + read_request(1, 32'h3001ffff, 1, 0, 1, 0); + read_request(1, 32'h30020000, 1, 2, 1, 3); + end + join + if (completions[0]!=7 || completions[1]!=6 || faults!=7 || + target_requests[0]!=3 || target_requests[1]!=1 || target_requests[2]!=2 || + target_requests[3]!=3 || target_requests[4]!=4 || cpu_wait==0 || dma_wait==0) + $fatal(1, "accounting/fairness mismatch faults=%0d", faults); + read_request(0, 32'h30000000, 1, 2, 1, 0); + $display("SIM_TEST_PASS Tiny fabric"); + $finish; + end + initial begin + repeat (20000) @(posedge clk_i); + $fatal(1, "SIM_TEST_TIMEOUT Tiny fabric"); + end +endmodule diff --git a/tests/rtl/tiny_sysctrl_tb.sv b/tests/rtl/tiny_sysctrl_tb.sv new file mode 100644 index 00000000..39433bdd --- /dev/null +++ b/tests/rtl/tiny_sysctrl_tb.sv @@ -0,0 +1,96 @@ +`timescale 1ns / 1ps +`include "sysctrl_define.svh" + +module tiny_sysctrl_tb; + logic clk_i = 0; + logic rst_n_i = 0; + always #5 clk_i = !clk_i; + apb4_if apb4 ( + .pclk (clk_i), + .presetn(rst_n_i) + ); + logic fault_valid = 0; + logic [31:0] fault_addr = 32'h38000000; + logic [63:0] cpu_wait = 64'h123456789abcdef0; + logic [63:0] dma_wait = 64'h1122334455667788; + logic wake = 0; + logic perf_enable, perf_clear, done, pass; + logic [ 7:0] code; + logic [31:0] value; + tiny_sysctrl u_dut ( + .clk_i (clk_i), + .rst_n_i (rst_n_i), + .apb4 (apb4), + .fault_valid_i (fault_valid), + .fault_addr_i (fault_addr), + .fault_write_i (1'b1), + .fault_master_i(1'b1), + .fault_resp_i (2'b11), + .cpu_wait_i (cpu_wait), + .dma_wait_i (dma_wait), + .rtc_wake_i (wake), + .perf_enable_o (perf_enable), + .perf_clear_o (perf_clear), + .test_done_o (done), + .test_pass_o (pass), + .test_code_o (code) + ); + `include "apb4_bfm.svh" + initial begin + apb4_idle(); + repeat (5) @(negedge clk_i); + rst_n_i = 1; + apb4_write(`APB4_SYSCTRL__HP_CTRL, 1, 4'hf, 1); + apb4_write(`APB4_SYSCTRL__PLL_CMD, 1, 4'hf, 1); + apb4_read(32'hfff, 1, value); + apb4_write(`APB4_SYSCTRL__TEST_STATUS, 32'h80001201, 4'h1, 1); + if (done) $fatal(1, "partial terminal write changed state"); + apb4_write(`APB4_SYSCTRL__TEST_STATUS, 32'h00001201, 4'hf, 0); + if (done) $fatal(1, "invalid terminal write changed state"); + apb4_write(`APB4_SYSCTRL__TEST_STATUS, 32'h80001201, 4'hf, 0); + apb4_write(`APB4_SYSCTRL__TEST_STATUS, 32'h80009900, 4'hf, 0); + if (!done || !pass || code != 8'h12) $fatal(1, "terminal status is not sticky"); + apb4_write(`APB4_SYSCTRL__PERF_CTRL, 5, 4'hf, 0); + cpu_wait = 0; + dma_wait = 0; + apb4_read(`APB4_SYSCTRL__PERF_MGMT_WAIT_LO, 0, value); + if (!perf_enable || value != 32'h9abcdef0) $fatal(1, "CPU snapshot changed"); + apb4_read(`APB4_SYSCTRL__PERF_DMA_WAIT_HI, 0, value); + if (value != 32'h11223344) $fatal(1, "DMA snapshot changed"); + @(negedge clk_i); + fault_valid = 1; + @(negedge clk_i); + fault_valid = 0; + fault_addr = 32'hffffffff; + @(negedge clk_i); + fault_valid = 1; + @(negedge clk_i); + fault_valid = 0; + apb4_read(`APB4_SYSCTRL__FAULT_ADDR, 0, value); + if (value != 32'h38000000) $fatal(1, "first fault overwritten"); + apb4_read(`APB4_SYSCTRL__FAULT_COUNT, 0, value); + if (value != 2) $fatal(1, "fault count mismatch"); + apb4_write(`APB4_SYSCTRL__FAULT_STATUS, 1, 4'hf, 0); + apb4_read(`APB4_SYSCTRL__FAULT_STATUS, 0, value); + if (value[0]) $fatal(1, "fault W1C failed"); + @(negedge clk_i); + wake = 1; + @(negedge clk_i); + wake = 0; + apb4_read(`APB4_SYSCTRL__RTC_WAKE_STATUS, 0, value); + if (value != 2) $fatal(1, "RTC sticky wake missing"); + apb4_write(`APB4_SYSCTRL__RTC_WAKE_STATUS, 2, 4'hf, 0); + apb4_read(`APB4_SYSCTRL__RTC_WAKE_STATUS, 0, value); + if (value != 0) $fatal(1, "RTC wake W1C failed"); + @(negedge clk_i); + rst_n_i = 0; + repeat (3) @(negedge clk_i); + if (done || perf_enable) $fatal(1, "reset failed"); + $display("SIM_TEST_PASS Tiny SYSCTRL"); + $finish; + end + initial begin + #100000; + $fatal(1, "SIM_TEST_TIMEOUT Tiny SYSCTRL"); + end +endmodule diff --git a/tests/test_apu.py b/tests/test_apu.py index 59e66f09..3706a8f8 100644 --- a/tests/test_apu.py +++ b/tests/test_apu.py @@ -121,7 +121,7 @@ def test_apu_p1_apb_register_shell(tmp_path: Path) -> None: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", @@ -238,7 +238,7 @@ def test_apu_p1_integrated_irq_ownership_topology(tmp_path: Path) -> None: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", @@ -530,7 +530,7 @@ last: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", @@ -658,7 +658,7 @@ done: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", @@ -747,7 +747,7 @@ def test_apu_p2_stream_router(tmp_path: Path) -> None: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", @@ -801,7 +801,7 @@ def test_apu_p2_private_dma(tmp_path: Path) -> None: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", @@ -846,7 +846,7 @@ def test_apu_p2_ring_scheduler_backend(tmp_path: Path) -> None: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", @@ -904,7 +904,7 @@ def test_apu_p2_integrated_dma_ring_backend(tmp_path: Path) -> None: str(ROOT / "rtl/ip/peripheral/dma_axi4_master.sv"), str(multimedia / "apu_dma.sv"), str(multimedia / "apu_ring_scheduler.sv"), - str(ROOT / "rtl/mini/top/soc_common_cdc.sv"), + str(ROOT / "rtl/ip/util/soc_common_cdc.sv"), str(ROOT / "rtl/mini/top/axi4_async_bridge.sv"), str(ROOT / "rtl/mini/top/axi4_target_guard.sv"), str(ROOT / "rtl/mini/top/hp_axi4_mux3.sv"), @@ -919,7 +919,7 @@ def test_apu_p2_integrated_dma_ring_backend(tmp_path: Path) -> None: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", @@ -972,7 +972,7 @@ def test_apu_p2_gateway_a_round_robin_fairness(tmp_path: Path) -> None: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", @@ -1039,7 +1039,7 @@ def test_apu_p7_kws_engine_directed(tmp_path: Path) -> None: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", @@ -1151,7 +1151,7 @@ def test_apu_p7_kws_sram_layout_and_isolation(tmp_path: Path) -> None: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", @@ -1202,7 +1202,7 @@ def test_apu_p7_kws_job_lifecycle(tmp_path: Path) -> None: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", @@ -1242,7 +1242,7 @@ def test_apu_p7_kws_loader_admission_and_abort(tmp_path: Path) -> None: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", diff --git a/tests/test_apu_codec_transport.py b/tests/test_apu_codec_transport.py index df30cf02..66332fe5 100644 --- a/tests/test_apu_codec_transport.py +++ b/tests/test_apu_codec_transport.py @@ -350,7 +350,7 @@ def test_p5_production_transport_matches_icarus_and_verilator(tmp_path: Path) -> subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(filelist), "--output", @@ -508,7 +508,7 @@ def test_p5_v2_4096_word_loader_store_and_sequencer(tmp_path: Path) -> None: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(filelist), "--output", @@ -705,7 +705,7 @@ def test_p5_direct_wav_uses_product_loader_dma_sequencer_and_tx(tmp_path: Path) subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(filelist), "--output", diff --git a/tests/test_apu_kws_frontend.py b/tests/test_apu_kws_frontend.py index 0e09476e..371384c5 100644 --- a/tests/test_apu_kws_frontend.py +++ b/tests/test_apu_kws_frontend.py @@ -148,7 +148,7 @@ def _compile_dump_tb(build_dir: Path) -> dict[str, Path | None]: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(filelist), "--output", diff --git a/tests/test_apu_p8_contention.py b/tests/test_apu_p8_contention.py index 475a4136..39ba8039 100644 --- a/tests/test_apu_p8_contention.py +++ b/tests/test_apu_p8_contention.py @@ -41,7 +41,7 @@ def test_apu_p8_gateway_a_contention(tmp_path: Path) -> None: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", diff --git a/tests/test_apu_p8_quiesce_load.py b/tests/test_apu_p8_quiesce_load.py index 523572d7..f9c1a0f3 100644 --- a/tests/test_apu_p8_quiesce_load.py +++ b/tests/test_apu_p8_quiesce_load.py @@ -114,7 +114,7 @@ def test_apu_p8_quiesced_loader_admission(tmp_path: Path) -> None: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", diff --git a/tests/test_apu_p9_storage.py b/tests/test_apu_p9_storage.py index 87e642a1..531d25ca 100644 --- a/tests/test_apu_p9_storage.py +++ b/tests/test_apu_p9_storage.py @@ -67,7 +67,7 @@ def test_apu_p9_coefficient_store_and_proof_memo(tmp_path: Path) -> None: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", diff --git a/tests/test_apu_primitives.py b/tests/test_apu_primitives.py index 793f2f5a..7ef31499 100644 --- a/tests/test_apu_primitives.py +++ b/tests/test_apu_primitives.py @@ -1279,7 +1279,7 @@ def test_p4_production_primitives_icarus_and_verilator(tmp_path: Path) -> None: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", @@ -1404,7 +1404,7 @@ def test_p4_loader_publishes_control_and_table_atomically(tmp_path: Path) -> Non subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", @@ -1457,7 +1457,7 @@ def test_p4_sequencer_kernel_pending_and_wait(tmp_path: Path) -> None: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", diff --git a/tests/test_axi4.py b/tests/test_axi4.py index 1a61c8ff..8a8f85f3 100644 --- a/tests/test_axi4.py +++ b/tests/test_axi4.py @@ -76,7 +76,7 @@ def test_axi4_async_bridge_transfers_and_warm_flushes(tmp_path: Path) -> None: str(ROOT / "rtl/managed/clusterip/common/rtl/cdc/cdc_rst_ctrlr.sv"), str(ROOT / "rtl/managed/clusterip/common/rtl/cdc/cdc_2phase.sv"), str(ROOT / "rtl/managed/clusterip/common/rtl/clkrst/rst_sync.sv"), - str(ROOT / "rtl/mini/top/soc_common_cdc.sv"), + str(ROOT / "rtl/ip/util/soc_common_cdc.sv"), str(ROOT / "rtl/mini/top/axi4_async_bridge.sv"), str(ROOT / "tests/rtl/axi4_async_bridge_tb.sv"), "-Mdir", str(tmp_path / "obj"), "-o", str(output), @@ -187,7 +187,7 @@ def test_axi4_interconnect_classifies_decode_protocol_and_access_errors( subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/address_map/generate_memory_map.py"), + str(ROOT / "scripts/rtl/generate_memory_map.py"), "--map", str(ROOT / "rtl/mini/address_map/memory_map.json"), "--output-dir", @@ -217,7 +217,7 @@ def test_axi4_interconnect_classifies_decode_protocol_and_access_errors( str(ROOT / "rtl/managed/clusterip/common/rtl/utils/register.sv"), str(ROOT / "rtl/managed/clusterip/common/rtl/stream/round_robin_arbiter.sv"), str(ROOT / "rtl/mini/top/axi4_interconnect.sv"), - str(ROOT / "rtl/mini/top/axi4_error_slave.sv"), + str(ROOT / "rtl/ip/interconnect/axi4_error_slave.sv"), str(ROOT / "tests/rtl/axi4_interconnect_tb.sv"), "-Mdir", str(tmp_path / "obj"), @@ -246,7 +246,7 @@ def test_axi4_data_crossbar_supports_cross_target_outstanding_and_acl(tmp_path: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/address_map/generate_memory_map.py"), + str(ROOT / "scripts/rtl/generate_memory_map.py"), "--map", str(ROOT / "rtl/mini/address_map/memory_map.json"), "--output-dir", diff --git a/tests/test_axi4_hp_postfault.py b/tests/test_axi4_hp_postfault.py index cda280d3..97349bff 100644 --- a/tests/test_axi4_hp_postfault.py +++ b/tests/test_axi4_hp_postfault.py @@ -21,7 +21,7 @@ def test_axi4_interconnect_hp_postfault_visibility(tmp_path: Path) -> None: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/address_map/generate_memory_map.py"), + str(ROOT / "scripts/rtl/generate_memory_map.py"), "--map", str(ROOT / "rtl/mini/address_map/memory_map.json"), "--output-dir", @@ -51,7 +51,7 @@ def test_axi4_interconnect_hp_postfault_visibility(tmp_path: Path) -> None: str(ROOT / "rtl/managed/clusterip/common/rtl/utils/register.sv"), str(ROOT / "rtl/managed/clusterip/common/rtl/stream/round_robin_arbiter.sv"), str(ROOT / "rtl/mini/top/axi4_interconnect.sv"), - str(ROOT / "rtl/mini/top/axi4_error_slave.sv"), + str(ROOT / "rtl/ip/interconnect/axi4_error_slave.sv"), str(ROOT / "tests/rtl/axi4_interconnect_hp_postfault_tb.sv"), "-Mdir", str(tmp_path / "obj"), diff --git a/tests/test_clint.py b/tests/test_clint.py index 5cd22e33..f7721a07 100644 --- a/tests/test_clint.py +++ b/tests/test_clint.py @@ -49,7 +49,7 @@ def test_clint_standard_map_multi_hart_and_timebase(tmp_path: Path) -> None: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", diff --git a/tests/test_crypto.py b/tests/test_crypto.py index 4e2990f4..b2efb80d 100644 --- a/tests/test_crypto.py +++ b/tests/test_crypto.py @@ -124,7 +124,7 @@ def test_crypto_apb_register_path(tmp_path: Path) -> None: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", diff --git a/tests/test_dma.py b/tests/test_dma.py index 0c19cd0b..49db77df 100644 --- a/tests/test_dma.py +++ b/tests/test_dma.py @@ -21,7 +21,7 @@ def test_dma_reports_native_axi4_errors_bursts_and_streams(tmp_path: Path) -> No subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/address_map/generate_memory_map.py"), + str(ROOT / "scripts/rtl/generate_memory_map.py"), "--map", str(ROOT / "rtl/mini/address_map/memory_map.json"), "--output-dir", @@ -61,7 +61,7 @@ def test_dma_reports_native_axi4_errors_bursts_and_streams(tmp_path: Path) -> No subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", @@ -88,7 +88,7 @@ def test_dma_apb_registers_and_irq(tmp_path: Path) -> None: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/address_map/generate_memory_map.py"), + str(ROOT / "scripts/rtl/generate_memory_map.py"), "--map", str(ROOT / "rtl/mini/address_map/memory_map.json"), "--output-dir", @@ -130,7 +130,7 @@ def test_dma_apb_registers_and_irq(tmp_path: Path) -> None: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", @@ -157,7 +157,7 @@ def test_dma_crypto_stream_endpoints(tmp_path: Path) -> None: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/address_map/generate_memory_map.py"), + str(ROOT / "scripts/rtl/generate_memory_map.py"), "--map", str(ROOT / "rtl/mini/address_map/memory_map.json"), "--output-dir", @@ -195,7 +195,7 @@ def test_dma_crypto_stream_endpoints(tmp_path: Path) -> None: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", diff --git a/tests/test_dvp.py b/tests/test_dvp.py index f910b0c9..9123a7df 100644 --- a/tests/test_dvp.py +++ b/tests/test_dvp.py @@ -54,7 +54,7 @@ def test_dvp_capture_framing(tmp_path: Path) -> None: source_list.write_text("\n".join([*defines, *source, ""]), encoding="utf-8") converted = tmp_path / "dvp.v" subprocess.run( - [sys.executable, str(ROOT / "rtl/mini/script/convt_sv2v.py"), "-f", str(source_list), "--output", str(converted)], + [sys.executable, str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", str(converted)], check=True, ) simulation = tmp_path / "dvp_tb" diff --git a/tests/test_ga2d.py b/tests/test_ga2d.py index 427d1c94..ce261b16 100644 --- a/tests/test_ga2d.py +++ b/tests/test_ga2d.py @@ -26,7 +26,7 @@ def _generate_memory_map(tmp_path: Path) -> Path: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/address_map/generate_memory_map.py"), + str(ROOT / "scripts/rtl/generate_memory_map.py"), "--map", str(ROOT / "rtl/mini/address_map/memory_map.json"), "--output-dir", @@ -122,7 +122,7 @@ def _run_iverilog_test( subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", diff --git a/tests/test_ga2d_platform.py b/tests/test_ga2d_platform.py index 56b84257..7854314c 100644 --- a/tests/test_ga2d_platform.py +++ b/tests/test_ga2d_platform.py @@ -111,7 +111,7 @@ def test_ga2d_p5_preserves_p2_bridge_prefix_lifecycle_and_recovery(tmp_path: Pat subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/address_map/generate_memory_map.py"), + str(ROOT / "scripts/rtl/generate_memory_map.py"), "--map", str(ROOT / "rtl/mini/address_map/memory_map.json"), "--output-dir", @@ -156,8 +156,8 @@ def test_ga2d_p5_preserves_p2_bridge_prefix_lifecycle_and_recovery(tmp_path: Pat str(common / "cdc/cdc_rst_ctrlr.sv"), str(common / "cdc/cdc_2phase.sv"), str(common / "clkrst/rst_sync.sv"), - str(top / "soc_common_cdc.sv"), - str(top / "axi4_connector.sv"), + str(ROOT / "rtl/ip/util/soc_common_cdc.sv"), + str(ROOT / "rtl/ip/interconnect/axi4_connector.sv"), str(top / "axi4_master_idle.sv"), str(top / "axi4_async_bridge.sv"), str(top / "axi4_address_gate.sv"), @@ -166,7 +166,7 @@ def test_ga2d_p5_preserves_p2_bridge_prefix_lifecycle_and_recovery(tmp_path: Pat str(top / "axi4_data_crossbar.sv"), str(top / "axi4_target_guard.sv"), str(top / "fabric_monitor.sv"), - str(top / "axi4_error_slave.sv"), + str(ROOT / "rtl/ip/interconnect/axi4_error_slave.sv"), str(top / "axi4_downsizer_64to32.sv"), str(top / "hp_axi4_mux3.sv"), str(top / "soc_data_plane.sv"), diff --git a/tests/test_gpio.py b/tests/test_gpio.py index 8f9e1b91..65a5fa6d 100644 --- a/tests/test_gpio.py +++ b/tests/test_gpio.py @@ -47,7 +47,7 @@ def test_gpio_register_pad_interrupt_and_filter_contract(tmp_path: Path) -> None subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", diff --git a/tests/test_hp_mailbox.py b/tests/test_hp_mailbox.py index 89304742..953757a9 100644 --- a/tests/test_hp_mailbox.py +++ b/tests/test_hp_mailbox.py @@ -39,7 +39,7 @@ def test_hp_mailbox_registers_and_doorbells(tmp_path: Path) -> None: ) converted = tmp_path / "hp_mailbox_tb.v" subprocess.run( - [sys.executable, str(ROOT / "rtl/mini/script/convt_sv2v.py"), "-f", str(filelist), + [sys.executable, str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(filelist), "--output", str(converted)], check=True, ) diff --git a/tests/test_i2c.py b/tests/test_i2c.py index f69b6ae9..96df868c 100644 --- a/tests/test_i2c.py +++ b/tests/test_i2c.py @@ -48,7 +48,7 @@ def test_i2c_controller_and_recovery_contract(tmp_path: Path) -> None: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", diff --git a/tests/test_i2s.py b/tests/test_i2s.py index 129c0174..854ca05b 100644 --- a/tests/test_i2s.py +++ b/tests/test_i2s.py @@ -58,7 +58,7 @@ def test_i2s_controller_abi_and_clocking(tmp_path: Path) -> None: source_list.write_text("\n".join([*defines, *source, ""]), encoding="utf-8") converted = tmp_path / "i2s.v" subprocess.run( - [sys.executable, str(ROOT / "rtl/mini/script/convt_sv2v.py"), "-f", str(source_list), "--output", str(converted)], + [sys.executable, str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", str(converted)], check=True, ) simulation = tmp_path / "i2s_tb" diff --git a/tests/test_jpeg.py b/tests/test_jpeg.py index 148991c0..f58c9ea6 100644 --- a/tests/test_jpeg.py +++ b/tests/test_jpeg.py @@ -182,7 +182,7 @@ def test_jpeg_apb_register_path(tmp_path: Path) -> None: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", @@ -226,7 +226,7 @@ def test_jpeg_2d_axi_dma(tmp_path: Path) -> None: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", @@ -302,7 +302,7 @@ def test_jpeg_apb_ring_descriptor_path(tmp_path: Path) -> None: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", @@ -410,7 +410,7 @@ def test_jpeg_byte_unpacker_and_bit_reader(tmp_path: Path) -> None: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", @@ -590,7 +590,7 @@ def test_jpeg_mcu_reconstructor(tmp_path: Path) -> None: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", @@ -640,7 +640,7 @@ def test_jpeg_mcu_builder(tmp_path: Path) -> None: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", @@ -754,7 +754,7 @@ def test_jpeg_byte_joiner(tmp_path: Path) -> None: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", @@ -847,7 +847,7 @@ def test_jpeg_encode_core_complete_file(tmp_path: Path) -> None: ) converted = tmp_path / "jpeg_encode_core_tb.v" subprocess.run( - [sys.executable, str(ROOT / "rtl/mini/script/convt_sv2v.py"), "-f", str(source_list), + [sys.executable, str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", str(converted)], check=True, ) @@ -943,7 +943,7 @@ def test_jpeg_decode_core_complete_file(tmp_path: Path) -> None: ) converted = tmp_path / "jpeg_decode_core_tb.v" subprocess.run( - [sys.executable, str(ROOT / "rtl/mini/script/convt_sv2v.py"), "-f", str(source_list), + [sys.executable, str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", str(converted)], check=True, ) diff --git a/tests/test_lp_irq.py b/tests/test_lp_irq.py index cc376e73..e3a1c7bf 100644 --- a/tests/test_lp_irq.py +++ b/tests/test_lp_irq.py @@ -115,14 +115,14 @@ int main(void) { def test_phase1_wrapper_and_backend_contracts() -> None: wrapper = (ROOT / "rtl/mini/top/core_wrapper.sv").read_text(encoding="utf-8") - management = (ROOT / "rtl/mini/top/mgmt_core_wrapper.sv").read_text(encoding="utf-8") + management = (ROOT / "rtl/ip/core/mgmt_core_wrapper.sv").read_text(encoding="utf-8") top = (ROOT / "rtl/mini/top/retrosoc.sv").read_text(encoding="utf-8") handler = (ROOT / "crt/src/core/system_irq_handler.c").read_text(encoding="utf-8") csr = (ROOT / "crt/include/retrosoc/arch/riscv/system_csr.h").read_text(encoding="utf-8") firmware = (ROOT / "app/apps/ci_smoke/main.c").read_text(encoding="utf-8") assert "parameter int ExternalIrqCount = 30" in wrapper - assert "parameter int ExternalIrqCount = 30" in management + assert re.search(r"parameter int ExternalIrqCount\s*= 30", management) assert ".NUM_IRQS (ExternalIrqCount)" in management assert ".IRQ_INPUT_BYPASS ({ExternalIrqCount{1'b0}})" in management assert "localparam int ManagementExternalIrqCount = `SOC_IRQ_VECTOR_WIDTH - 2" in top diff --git a/tests/test_memory_map.py b/tests/test_memory_map.py index 83213e5b..1688eaf7 100644 --- a/tests/test_memory_map.py +++ b/tests/test_memory_map.py @@ -11,7 +11,7 @@ from pathlib import Path ROOT = Path(__file__).resolve().parents[1] -GENERATOR = ROOT / "rtl/mini/address_map/generate_memory_map.py" +GENERATOR = ROOT / "scripts/rtl/generate_memory_map.py" MEMORY_MAP = ROOT / "rtl/mini/address_map/memory_map.json" USER_EXTENSIONS = ROOT / "rtl/mini/integration/user_extensions.json" USER_GENERATOR = ROOT / "rtl/mini/integration/generate_user_extensions.py" @@ -242,7 +242,7 @@ def test_bus_fault_responder_handles_reserved_and_unmapped_addresses(tmp_path: P subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", @@ -320,7 +320,7 @@ def test_pll_controller_reconfigures_and_falls_back_to_the_safe_clock(tmp_path: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", @@ -349,7 +349,7 @@ def test_pll_controller_reconfigures_and_falls_back_to_the_safe_clock(tmp_path: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(unsupported_list), "--output", diff --git a/tests/test_mgmt_core_spec.py b/tests/test_mgmt_core_spec.py index 71fa4793..6a72f072 100644 --- a/tests/test_mgmt_core_spec.py +++ b/tests/test_mgmt_core_spec.py @@ -1,14 +1,15 @@ """Keep the boot display aligned with the fixed Hazard3 management-core integration.""" +import re from pathlib import Path ROOT = Path(__file__).resolve().parents[1] BOOTER = ROOT / "crt/src/service/booter.c" -SOFTWARE_MAKE = ROOT / "rtl/mini/mk/software.mk" -MGMT_CORE = ROOT / "rtl/mini/top/mgmt_core_wrapper.sv" -MGMT_DEBUG = ROOT / "rtl/mini/top/mgmt_debug_wrapper.sv" -AHBL_TO_AXI4 = ROOT / "rtl/mini/top/ahbl2axi4.sv" +SOFTWARE_MAKE = ROOT / "rtl/mk/software.mk" +MGMT_CORE = ROOT / "rtl/ip/core/mgmt_core_wrapper.sv" +MGMT_DEBUG = ROOT / "rtl/ip/core/mgmt_debug_wrapper.sv" +AHBL_TO_AXI4 = ROOT / "rtl/ip/interconnect/ahbl2axi4.sv" VEXII_CONFIG = ROOT / "scripts/vexiiriscv/GenerateRetroSocHp.scala" @@ -41,7 +42,7 @@ def test_booter_prints_the_fixed_hazard3_specification() -> None: assert "exact execute-address hardware breakpoints" not in booter for parameter in ( - ".EXTENSION_A (1)", + ".EXTENSION_A (EnableAtomics)", ".EXTENSION_C (1)", ".EXTENSION_M (1)", ".EXTENSION_ZBA (1)", @@ -57,12 +58,13 @@ def test_booter_prints_the_fixed_hazard3_specification() -> None: ".U_MODE (0)", ".PMP_REGIONS (0)", ".BREAKPOINT_TRIGGERS(2)", - "parameter int ExternalIrqCount = 30", ".NUM_IRQS (ExternalIrqCount)", ".IRQ_PRIORITY_BITS (2)", ): assert parameter in management_core + assert re.search(r"parameter int ExternalIrqCount\s*= 30", management_core) + assert re.search(r"parameter bit EnableAtomics\s*= 1'b1", management_core) assert ".HAVE_SBA(0)" in management_debug assert "ahbl.htrans == AHBL_TRANS_NSEQ" in ahbl_to_axi4 assert "axi4.awlen = 8'd0;" in ahbl_to_axi4 diff --git a/tests/test_mgmt_debug.py b/tests/test_mgmt_debug.py index 8c122f47..fbe9539d 100644 --- a/tests/test_mgmt_debug.py +++ b/tests/test_mgmt_debug.py @@ -20,7 +20,7 @@ def test_management_debug_reset_waits_for_the_ahbl_bridge(tmp_path: Path) -> Non sources = ( ROOT / "rtl/managed/clusterip/common/rtl/utils/register.sv", ROOT / "rtl/managed/clusterip/common/rtl/clkrst/rst_sync.sv", - ROOT / "rtl/mini/top/mgmt_debug_reset.sv", + ROOT / "rtl/ip/core/mgmt_debug_reset.sv", ROOT / "tests/rtl/mgmt_debug_reset_tb.sv", ) subprocess.run( diff --git a/tests/test_npu_rtl_compute.py b/tests/test_npu_rtl_compute.py index 929d2892..4b28a102 100644 --- a/tests/test_npu_rtl_compute.py +++ b/tests/test_npu_rtl_compute.py @@ -76,7 +76,7 @@ def _run_icarus( subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", diff --git a/tests/test_npu_rtl_memory.py b/tests/test_npu_rtl_memory.py index 276abcb7..65da0a23 100644 --- a/tests/test_npu_rtl_memory.py +++ b/tests/test_npu_rtl_memory.py @@ -62,7 +62,7 @@ def _build_iverilog( subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(filelist), "--output", diff --git a/tests/test_npu_rtl_p3.py b/tests/test_npu_rtl_p3.py index c6fcd714..fbb2c1e6 100644 --- a/tests/test_npu_rtl_p3.py +++ b/tests/test_npu_rtl_p3.py @@ -137,7 +137,7 @@ def _build_iverilog(tools: dict[str, str], tmp_path: Path, name: str, top: str, subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(filelist), "--output", diff --git a/tests/test_npu_rtl_shell.py b/tests/test_npu_rtl_shell.py index 51bea20d..212916cf 100644 --- a/tests/test_npu_rtl_shell.py +++ b/tests/test_npu_rtl_shell.py @@ -77,7 +77,7 @@ def _build_iverilog(tools: dict[str, str], tmp_path: Path, name: str, top: str, subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(filelist), "--output", diff --git a/tests/test_onchip_ram.py b/tests/test_onchip_ram.py index 34924e41..d57cef75 100644 --- a/tests/test_onchip_ram.py +++ b/tests/test_onchip_ram.py @@ -27,7 +27,7 @@ def test_onchip_ram_axi4_capacity_protocol_and_performance( subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/address_map/generate_memory_map.py"), + str(ROOT / "scripts/rtl/generate_memory_map.py"), "--map", str(ROOT / "rtl/mini/address_map/memory_map.json"), "--output-dir", @@ -55,6 +55,7 @@ def test_onchip_ram_axi4_capacity_protocol_and_performance( "+define+SV_ASSRT_DISABLE", "-I" + str(generated / "rtl"), "-I" + str(ROOT / "rtl/mini/top"), + "-I" + str(ROOT / "rtl/ip/memory"), "-I" + str(ROOT / "rtl/managed/clusterip/common/rtl"), "-I" + str(ROOT / "rtl/managed/clusterip/common/rtl/interface"), str(ROOT / "rtl/managed/clusterip/common/rtl/interface/axi4_if.sv"), @@ -63,8 +64,8 @@ def test_onchip_ram_axi4_capacity_protocol_and_performance( str(ROOT / "rtl/managed/clusterip/common/rtl/utils/register.sv"), str(ROOT / "rtl/managed/clusterip/common/rtl/tech/ram.sv"), str(ROOT / "rtl/tech/tc_sram.sv"), - str(ROOT / "rtl/mini/top/onchip_ram_reg.sv"), - str(ROOT / "rtl/mini/top/onchip_ram.sv"), + str(ROOT / "rtl/ip/memory/onchip_ram_reg.sv"), + str(ROOT / "rtl/ip/memory/onchip_ram.sv"), str(ROOT / "tests/rtl/onchip_ram_tb.sv"), "-Mdir", str(tmp_path / "obj"), @@ -96,7 +97,7 @@ def test_onchip_ram_native_axi64_width_ids_and_lanes( subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/address_map/generate_memory_map.py"), + str(ROOT / "scripts/rtl/generate_memory_map.py"), "--map", str(ROOT / "rtl/mini/address_map/memory_map.json"), "--output-dir", @@ -124,6 +125,7 @@ def test_onchip_ram_native_axi64_width_ids_and_lanes( "+define+SV_ASSRT_DISABLE", "-I" + str(generated / "rtl"), "-I" + str(ROOT / "rtl/mini/top"), + "-I" + str(ROOT / "rtl/ip/memory"), "-I" + str(ROOT / "rtl/managed/clusterip/common/rtl"), "-I" + str(ROOT / "rtl/managed/clusterip/common/rtl/interface"), str(ROOT / "rtl/managed/clusterip/common/rtl/interface/axi4_if.sv"), @@ -132,8 +134,8 @@ def test_onchip_ram_native_axi64_width_ids_and_lanes( str(ROOT / "rtl/managed/clusterip/common/rtl/utils/register.sv"), str(ROOT / "rtl/managed/clusterip/common/rtl/tech/ram.sv"), str(ROOT / "rtl/tech/tc_sram.sv"), - str(ROOT / "rtl/mini/top/onchip_ram_reg.sv"), - str(ROOT / "rtl/mini/top/onchip_ram.sv"), + str(ROOT / "rtl/ip/memory/onchip_ram_reg.sv"), + str(ROOT / "rtl/ip/memory/onchip_ram.sv"), str(ROOT / "tests/rtl/onchip_ram64_tb.sv"), "-Mdir", str(tmp_path / "obj64"), @@ -162,7 +164,7 @@ def test_absent_onchip_ram_reports_capability_and_decode_errors(tmp_path: Path) subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/address_map/generate_memory_map.py"), + str(ROOT / "scripts/rtl/generate_memory_map.py"), "--map", str(ROOT / "rtl/mini/address_map/memory_map.json"), "--output-dir", @@ -186,6 +188,7 @@ def test_absent_onchip_ram_reports_capability_and_decode_errors(tmp_path: Path) "+define+SV_ASSRT_DISABLE", "-I" + str(generated / "rtl"), "-I" + str(ROOT / "rtl/mini/top"), + "-I" + str(ROOT / "rtl/ip/memory"), "-I" + str(ROOT / "rtl/managed/clusterip/common/rtl"), "-I" + str(ROOT / "rtl/managed/clusterip/common/rtl/interface"), str(ROOT / "rtl/managed/clusterip/common/rtl/interface/axi4_if.sv"), @@ -194,8 +197,8 @@ def test_absent_onchip_ram_reports_capability_and_decode_errors(tmp_path: Path) str(ROOT / "rtl/managed/clusterip/common/rtl/utils/register.sv"), str(ROOT / "rtl/managed/clusterip/common/rtl/tech/ram.sv"), str(ROOT / "rtl/tech/tc_sram.sv"), - str(ROOT / "rtl/mini/top/onchip_ram_reg.sv"), - str(ROOT / "rtl/mini/top/onchip_ram.sv"), + str(ROOT / "rtl/ip/memory/onchip_ram_reg.sv"), + str(ROOT / "rtl/ip/memory/onchip_ram.sv"), str(ROOT / "tests/rtl/onchip_ram_absent_tb.sv"), "-Mdir", str(tmp_path / "obj-absent"), @@ -425,7 +428,7 @@ def test_native_axi4_burst_reaches_peak_and_removes_bridge_overhead(tmp_path: Pa subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/address_map/generate_memory_map.py"), + str(ROOT / "scripts/rtl/generate_memory_map.py"), "--map", str(ROOT / "rtl/mini/address_map/memory_map.json"), "--output-dir", @@ -452,6 +455,7 @@ def test_native_axi4_burst_reaches_peak_and_removes_bridge_overhead(tmp_path: Pa "+define+SV_ASSRT_DISABLE", "-I" + str(generated / "rtl"), "-I" + str(ROOT / "rtl/mini/top"), + "-I" + str(ROOT / "rtl/ip/memory"), "-I" + str(ROOT / "rtl/managed/clusterip/common/rtl"), "-I" + str(ROOT / "rtl/managed/clusterip/common/rtl/interface"), str(ROOT / "rtl/managed/clusterip/common/rtl/interface/axi4_if.sv"), @@ -464,8 +468,8 @@ def test_native_axi4_burst_reaches_peak_and_removes_bridge_overhead(tmp_path: Pa str(ROOT / "rtl/managed/clusterip/common/rtl/tech/ram.sv"), str(ROOT / "rtl/tech/tc_sram.sv"), str(ROOT / "rtl/mini/top/axi42ram.sv"), - str(ROOT / "rtl/mini/top/onchip_ram_reg.sv"), - str(ROOT / "rtl/mini/top/onchip_ram.sv"), + str(ROOT / "rtl/ip/memory/onchip_ram_reg.sv"), + str(ROOT / "rtl/ip/memory/onchip_ram.sv"), str(ROOT / "tests/rtl/onchip_ram_perf_tb.sv"), "-Mdir", str(tmp_path / "obj-perf"), diff --git a/tests/test_onchip_sram_register_parity.py b/tests/test_onchip_sram_register_parity.py index 63d59d52..561c4f49 100644 --- a/tests/test_onchip_sram_register_parity.py +++ b/tests/test_onchip_sram_register_parity.py @@ -7,7 +7,7 @@ from pathlib import Path ROOT = Path(__file__).resolve().parents[1] -RTL_DEFINE = ROOT / "rtl/mini/top/onchip_ram_define.svh" +RTL_DEFINE = ROOT / "rtl/ip/memory/onchip_ram_define.svh" C_HEADER = ROOT / "crt/include/retrosoc/hal/onchip_sram_regs.h" diff --git a/tests/test_pin_map.py b/tests/test_pin_map.py index a3833d4b..0c0d7102 100644 --- a/tests/test_pin_map.py +++ b/tests/test_pin_map.py @@ -11,7 +11,7 @@ from pathlib import Path ROOT = Path(__file__).resolve().parents[1] -GENERATOR = ROOT / "rtl/mini/pin_map/generate_pin_map.py" +GENERATOR = ROOT / "scripts/rtl/generate_pin_map.py" PIN_MAP = ROOT / "rtl/mini/pin_map/pin_map.json" FPGA_TOP = ROOT / "fpga/mini/retrosoc_top.sv" FPGA_XDC = ROOT / "fpga/mini/starrysky_v2.xdc" @@ -213,7 +213,7 @@ def test_apb4_interface_bridge(tmp_path: Path) -> None: [ str(ROOT / "rtl/managed/clusterip/common/rtl/interface/apb4_if.sv"), str(ROOT / "rtl/managed/clusterip/common/rtl/interface/apb4_pure_if.sv"), - str(ROOT / "rtl/mini/top/apb4_if_bridge.sv"), + str(ROOT / "rtl/ip/interconnect/apb4_if_bridge.sv"), str(ROOT / "tests/rtl/apb4_if_bridge_tb.sv"), "", ] @@ -224,7 +224,7 @@ def test_apb4_interface_bridge(tmp_path: Path) -> None: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", diff --git a/tests/test_plic.py b/tests/test_plic.py index 9ff20fdf..a923e9cd 100644 --- a/tests/test_plic.py +++ b/tests/test_plic.py @@ -38,7 +38,7 @@ def test_plic_priority_claim_and_completion(tmp_path: Path) -> None: ) converted = tmp_path / "plic_tb.v" subprocess.run( - [sys.executable, str(ROOT / "rtl/mini/script/convt_sv2v.py"), "-f", str(filelist), + [sys.executable, str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(filelist), "--output", str(converted)], check=True, ) diff --git a/tests/test_psram.py b/tests/test_psram.py index f7d7c872..4f2d8aad 100644 --- a/tests/test_psram.py +++ b/tests/test_psram.py @@ -22,7 +22,7 @@ def test_psram_controller_data_integrity_and_fault_isolation(tmp_path: Path) -> subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/address_map/generate_memory_map.py"), + str(ROOT / "scripts/rtl/generate_memory_map.py"), "--map", str(ROOT / "rtl/mini/address_map/memory_map.json"), "--output-dir", diff --git a/tests/test_publication_implementation.py b/tests/test_publication_implementation.py index a0db6561..82b285bb 100644 --- a/tests/test_publication_implementation.py +++ b/tests/test_publication_implementation.py @@ -143,3 +143,13 @@ def test_real_tool_dry_run_never_invokes_gdb_and_is_not_device_pass(tmp_path, mo assert result["passed"] is True and result["executed"] is False assert ir.programming_result(result) == "Script generated; device not programmed" assert "XPI_FLASH_PROGRAM_PASS" in args.gdb_script.read_text() + + +def test_lp_boolean_feature_defaults_preserve_mini_atomics(): + text = """ + module mgmt_core_wrapper #(parameter bit EnableAtomics = 1'b1) (); + hazard3_cpu_1port #(.EXTENSION_A(EnableAtomics), .NUM_IRQS(ExternalIrqCount)) + u_hazard3_cpu_1port (); + endmodule + """ + assert ir.lp_parameters(text) == {"EXTENSION_A": "1", "NUM_IRQS": "ExternalIrqCount"} diff --git a/tests/test_publication_registers.py b/tests/test_publication_registers.py index 7f857e34..6fd0a27e 100644 --- a/tests/test_publication_registers.py +++ b/tests/test_publication_registers.py @@ -95,6 +95,9 @@ def test_reset_disagreement_and_field_overflow_are_rejected(reference): def test_constant_reader_does_not_execute_source_text(): assert number("8'(DEPTH / 2)", {"DEPTH": 16}) == 8 + assert number("4'(MaxBurstBeats)", {"MaxBurstBeats": 16}) == 0 + assert number("(EnableStreams ? 3'd3 : 3'd0)", {"EnableStreams": 1}) == 3 + assert number("(EnableStreams ? 3'd3 : 3'd0)", {"EnableStreams": 0}) == 0 assert number("(DEPTH > 4) ? 32'd4 : DEPTH - 1", {"DEPTH": 16}) == 4 assert number("(32'd1 << 0) |\n (32'd1 << 8)") == 0x101 with pytest.raises(ValueError): diff --git a/tests/test_publication_software.py b/tests/test_publication_software.py index 424e2381..c130811b 100644 --- a/tests/test_publication_software.py +++ b/tests/test_publication_software.py @@ -61,7 +61,7 @@ def test_function_extraction_rejects_unsupported_definitions(source): @pytest.mark.parametrize("relative,old,new", [ - ("rtl/mini/mk/software.mk", "CRT_SRCS := $(APP_CRT_SRCS)", "CRT_SRCS += $(APP_CRT_SRCS)"), + ("rtl/mk/software.mk", "CRT_SRCS := $(APP_CRT_SRCS)", "CRT_SRCS += $(APP_CRT_SRCS)"), ("crt/arch/riscv/startup.S", "beqz t1, PSRAM_READY_WAIT", "beqz t1, main_start"), ("crt/arch/riscv/startup.S", "call _premain_init", "call other_premain"), ("crt/arch/riscv/system_irq.S", "SAVE_CSR_CONTEXT\n", "SAVE_CHANGED_CONTEXT\n"), diff --git a/tests/test_publications.py b/tests/test_publications.py index 642862f6..7daccef4 100644 --- a/tests/test_publications.py +++ b/tests/test_publications.py @@ -57,7 +57,7 @@ def test_profile_rejects_make_evaluation_and_duplicate_assignment(tmp_path): def test_reviewed_profile_resolves_sram_instead_of_using_decoder_maximum(): config = ds.read_json(ds.CONFIG) profile = ds.profile_values(ROOT / config["profile"]) - generator = ds.load_generator("test_datasheet_memory", "rtl/mini/address_map/generate_memory_map.py") + generator = ds.load_generator("test_datasheet_memory", "scripts/rtl/generate_memory_map.py") _, regions, _ = generator.read_map(ROOT / ds.MAP, int(profile["SRAM_SIZE_KIB"])) sram = next(r for r in regions if r["symbol"] == "SRAM") assert sram["size"] == 32768 diff --git a/tests/test_rib.py b/tests/test_rib.py index 815dbeaf..d6d01c56 100644 --- a/tests/test_rib.py +++ b/tests/test_rib.py @@ -21,7 +21,7 @@ def test_bus_preserves_incr4_and_rejects_illegal_bursts(tmp_path: Path) -> None: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/address_map/generate_memory_map.py"), + str(ROOT / "scripts/rtl/generate_memory_map.py"), "--map", str(ROOT / "rtl/mini/address_map/memory_map.json"), "--output-dir", @@ -58,7 +58,7 @@ def test_bus_preserves_incr4_and_rejects_illegal_bursts(tmp_path: Path) -> None: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", @@ -103,7 +103,7 @@ def test_burst_sram_preserves_data_and_backpressure(tmp_path: Path) -> None: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", diff --git a/tests/test_rib2apb.py b/tests/test_rib2apb.py index eb1ca1a6..3821c3e1 100644 --- a/tests/test_rib2apb.py +++ b/tests/test_rib2apb.py @@ -23,7 +23,7 @@ def test_rib2apb_preserves_apb_and_rib_handshakes(tmp_path: Path) -> None: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/address_map/generate_memory_map.py"), + str(ROOT / "scripts/rtl/generate_memory_map.py"), "--map", str(ROOT / "rtl/mini/address_map/memory_map.json"), "--output-dir", @@ -71,7 +71,7 @@ def test_rib2apb_preserves_apb_and_rib_handshakes(tmp_path: Path) -> None: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", diff --git a/tests/test_rtl_style.py b/tests/test_rtl_style.py index e8fd0e4c..d0a3ce51 100644 --- a/tests/test_rtl_style.py +++ b/tests/test_rtl_style.py @@ -259,7 +259,7 @@ def test_owned_style_audit_matches_current_inventory() -> None: } actual_paths = { str(path.relative_to(ROOT)) - for root in (ROOT / "rtl/ip", ROOT / "rtl/mini/top") + for root in (ROOT / "rtl/ip", ROOT / "rtl/mini/top", ROOT / "rtl/tiny/top") for path in root.rglob("*") if path.is_file() and path.suffix in {".sv", ".svh"} } diff --git a/tests/test_script_tools.py b/tests/test_script_tools.py index 7026a0f1..616ff116 100644 --- a/tests/test_script_tools.py +++ b/tests/test_script_tools.py @@ -18,16 +18,12 @@ import pytest ROOT = Path(__file__).resolve().parents[1] -FILELIST_SCRIPT_DIR = ROOT / "rtl/mini/script" -FORMAL_SCRIPT_DIR = ROOT / "rtl/mini/formal" sys.path.insert(0, str(ROOT)) -sys.path.insert(0, str(FILELIST_SCRIPT_DIR)) -sys.path.insert(0, str(FORMAL_SCRIPT_DIR)) -from filelist import atomic_write, parse_filelists, write_filelist # noqa: E402 -from generate_filelist import generate_all # noqa: E402 -from generate_formal_filelist import generate as generate_formal_filelist # noqa: E402 -from generate_sby_config import render as render_sby_config # noqa: E402 +from scripts.rtl.filelist import atomic_write, parse_filelists, write_filelist # noqa: E402 +from rtl.mini.script.generate_filelist import generate_all # noqa: E402 +from rtl.mini.formal.generate_formal_filelist import generate as generate_formal_filelist # noqa: E402 +from rtl.mini.formal.generate_sby_config import render as render_sby_config # noqa: E402 from scripts.bitwuzla_smt2 import translate_arguments # noqa: E402 from scripts.analyze_warnings import normalize # noqa: E402 from scripts.check_c_warnings import self_owned_warnings # noqa: E402 @@ -870,7 +866,7 @@ def test_prepare_norflash_and_missing_firmware(tmp_path: Path) -> None: firmware = tmp_path / "firmware.hex" firmware.write_text("00\n", encoding="utf-8") sim_dir = tmp_path / "sim" - script = ROOT / "rtl/mini/script/prepare_norflash.py" + script = ROOT / "scripts/rtl/prepare_norflash.py" run( sys.executable, @@ -1043,7 +1039,7 @@ def test_make_dry_run_and_validation_do_not_write_filelists(tmp_path: Path) -> N line.startswith("MGMT_CPU_CLK_HZ") and line.rstrip().endswith("72000000") for line in mpw_config.splitlines() ) - software_makefile = (ROOT / "rtl/mini/mk/software.mk").read_text(encoding="utf-8") + software_makefile = (ROOT / "rtl/mk/software.mk").read_text(encoding="utf-8") assert "-DRS_CPU_CLOCK_HZ=$(MGMT_CPU_CLK_HZ)U" in software_makefile removed_debug = subprocess.run( @@ -1278,13 +1274,16 @@ def test_verilator_simulations_use_uniform_timeout() -> None: assert "--assert --Wall" in verilator_source regression_commands = (*SMOKE_COMMANDS, *PR_COMMANDS, *NIGHTLY_COMMANDS) - for _, values in regression_commands: + for profile, values in regression_commands: if "SIMU=VERILATOR" in values: simulation_timeout = [value for value in values if value.startswith("SOC_SIM_TIME=")] if "APP=ci_smoke" in values and "sim" in values: assert simulation_timeout == ["SOC_SIM_TIME=1800"] assert "LINK_TYPE=ld2_all_sram" in values - assert "VERILATOR_SIM_ARGS=--fast-flash" in values + if profile == "configs/ci/ihp130-tiny.mk": + assert not any(value.startswith("VERILATOR_SIM_ARGS=") for value in values) + else: + assert "VERILATOR_SIM_ARGS=--fast-flash" in values assert "HAVE_CSR=YES" in values elif "coremark-report" in values: assert simulation_timeout == ["SOC_SIM_TIME=7200"] @@ -1320,7 +1319,7 @@ def test_systemverilog_testbench_starts_in_reset_with_known_clocks() -> None: def test_management_core_is_fixed_to_hazard3_with_debug() -> None: - wrapper = (ROOT / "rtl/mini/top/mgmt_core_wrapper.sv").read_text(encoding="utf-8") + wrapper = (ROOT / "rtl/ip/core/mgmt_core_wrapper.sv").read_text(encoding="utf-8") makefile = (ROOT / "Makefile").read_text(encoding="utf-8") assert "CORE has been removed" in makefile @@ -1328,7 +1327,8 @@ def test_management_core_is_fixed_to_hazard3_with_debug() -> None: assert "CORE_$(CORE)" not in makefile assert "`ifdef CORE_" not in wrapper assert "`ifdef HAVE_DEBUG" not in wrapper - assert "ahbl2axi4 u_ahbl2axi4" in wrapper + assert re.search(r"ahbl2axi4\s*(?:#\([\s\S]*?\)\s*)?u_ahbl2axi4", wrapper) + assert re.search(r"TwoCycleBusErrors\s*=\s*1'b0", wrapper) assert ".RESET_VECTOR (`SOC_CPU_RESET_ADDR)" in wrapper assert ".DEBUG_SUPPORT (1)" in wrapper @@ -1336,8 +1336,8 @@ def test_management_core_is_fixed_to_hazard3_with_debug() -> None: def test_hazard3_debug_flow_is_locked_and_uses_remote_bitbang() -> None: lock = load_lock(ROOT / "dependencies/dependencies.lock.json") makefile = (ROOT / "Makefile").read_text(encoding="utf-8") - wrapper = (ROOT / "rtl/mini/top/mgmt_core_wrapper.sv").read_text(encoding="utf-8") - debug_wrapper = (ROOT / "rtl/mini/top/mgmt_debug_wrapper.sv").read_text(encoding="utf-8") + wrapper = (ROOT / "rtl/ip/core/mgmt_core_wrapper.sv").read_text(encoding="utf-8") + debug_wrapper = (ROOT / "rtl/ip/core/mgmt_debug_wrapper.sv").read_text(encoding="utf-8") verilator_makefile = (ROOT / "rtl/mini/mk/verilator.mk").read_text(encoding="utf-8") driver = (ROOT / "scripts/run_debug_session.py").read_text(encoding="utf-8") openocd = (ROOT / "rtl/mini/dv/verilator/openocd/retrosoc_hazard3.cfg").read_text( @@ -1466,7 +1466,7 @@ def test_rtl_regression_excludes_synthesis_and_timing() -> None: commands, profiles = select_regression("rtl", "IHP130") assert commands == RTL_COMMANDS - assert profiles == ("configs/ci/ihp130.mk",) + assert profiles == ("configs/ci/ihp130.mk", "configs/ci/ihp130-tiny.mk") values = [value for _, command_values in commands for value in command_values] assert "sim" in values assert "sim-asm" in values @@ -1535,7 +1535,7 @@ def test_hosted_regression_keeps_tools_but_skips_synthesis_execution() -> None: def test_hosted_smoke_formal_check_is_doctor_only() -> None: workflow = (ROOT / ".github/workflows/_regression.yml").read_text(encoding="utf-8") smoke = (ROOT / ".github/workflows/regression-smoke.yml").read_text(encoding="utf-8") - software_makefile = (ROOT / "rtl/mini/mk/software.mk").read_text(encoding="utf-8") + software_makefile = (ROOT / "rtl/mk/software.mk").read_text(encoding="utf-8") assert "timeout_minutes: 60" in smoke assert "formal_checks: true" in smoke @@ -1586,7 +1586,7 @@ def test_nightly_regression_runs_optional_yosys_recipes() -> None: commands, profiles = select_regression("nightly", "IHP130") assert commands == NIGHTLY_COMMANDS - assert profiles == ("configs/ci/ihp130.mk",) + assert profiles == ("configs/ci/ihp130.mk", "configs/ci/ihp130-tiny.mk") assert ( "configs/ci/ihp130.mk", ("SYNTH=YOSYS", "SYNTH_RECIPE=area", "synth"), diff --git a/tests/test_sdio.py b/tests/test_sdio.py index a8a6cac6..ccc1843f 100644 --- a/tests/test_sdio.py +++ b/tests/test_sdio.py @@ -8,7 +8,7 @@ from pathlib import Path ROOT = Path(__file__).resolve().parents[1] -CONVERTER = ROOT / "rtl/mini/script/convt_sv2v.py" +CONVERTER = ROOT / "scripts/rtl/convt_sv2v.py" BUILD = ROOT / "build/test-sdio" SDIO_FULL_SOURCES = [ diff --git a/tests/test_sdram.py b/tests/test_sdram.py index 0e2e47ff..3168bcdf 100644 --- a/tests/test_sdram.py +++ b/tests/test_sdram.py @@ -15,7 +15,7 @@ def _write_memory_map(generated: Path) -> None: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/address_map/generate_memory_map.py"), + str(ROOT / "scripts/rtl/generate_memory_map.py"), "--map", str(ROOT / "rtl/mini/address_map/memory_map.json"), "--output-dir", @@ -72,7 +72,7 @@ def test_sdram_controller_preserves_full_and_masked_writes(tmp_path: Path) -> No subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", @@ -103,7 +103,7 @@ def test_sdram_controller_preserves_data_with_micron_timing_model(tmp_path: Path subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", diff --git a/tests/test_soc_topology.py b/tests/test_soc_topology.py index edeac8b1..b82c86d7 100644 --- a/tests/test_soc_topology.py +++ b/tests/test_soc_topology.py @@ -13,7 +13,7 @@ ROOT = Path(__file__).resolve().parents[1] TOPOLOGY = ROOT / "rtl/mini/integration/soc_topology.json" GENERATOR = ROOT / "rtl/mini/integration/generate_soc_topology.py" MEMORY_MAP = ROOT / "rtl/mini/address_map/memory_map.json" -MEMORY_MAP_GENERATOR = ROOT / "rtl/mini/address_map/generate_memory_map.py" +MEMORY_MAP_GENERATOR = ROOT / "scripts/rtl/generate_memory_map.py" def generate(output_dir: Path) -> None: @@ -574,7 +574,7 @@ def test_generated_rib_routes_select_and_return_the_expected_target(tmp_path: Pa subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", diff --git a/tests/test_spisd.py b/tests/test_spisd.py index 6d0ae275..f9cd7092 100644 --- a/tests/test_spisd.py +++ b/tests/test_spisd.py @@ -8,7 +8,7 @@ from pathlib import Path ROOT = Path(__file__).resolve().parents[1] -CONVERTER = ROOT / "rtl/mini/script/convt_sv2v.py" +CONVERTER = ROOT / "scripts/rtl/convt_sv2v.py" BUILD = ROOT / "build/test-spisd" COMMON_REGISTER = "rtl/managed/clusterip/common/rtl/utils/register.sv" INCDIRS = [ diff --git a/tests/test_sysctrl.py b/tests/test_sysctrl.py index a9e34c84..f5b41b83 100644 --- a/tests/test_sysctrl.py +++ b/tests/test_sysctrl.py @@ -40,7 +40,7 @@ def test_sysctrl_registers_lifecycle_faults_and_wake(tmp_path: Path) -> None: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/address_map/generate_memory_map.py"), + str(ROOT / "scripts/rtl/generate_memory_map.py"), "--map", str(ROOT / "rtl/mini/address_map/memory_map.json"), "--output-dir", @@ -78,7 +78,7 @@ def test_sysctrl_registers_lifecycle_faults_and_wake(tmp_path: Path) -> None: str(common / "cdc/cdc_sync.sv"), str(common / "clkrst/rst_sync.sv"), str(common / "cdc/cdc_rst_ctrlr.sv"), - str(TOP / "soc_common_cdc.sv"), + str(ROOT / "rtl/ip/util/soc_common_cdc.sv"), str(TOP / "data_plane_fault_cdc.sv"), str(PERIPHERAL / "pll_ctrl_if.sv"), str(PERIPHERAL / "clock_ctrl_if.sv"), @@ -97,7 +97,7 @@ def test_sysctrl_registers_lifecycle_faults_and_wake(tmp_path: Path) -> None: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", diff --git a/tests/test_timer.py b/tests/test_timer.py index 7b7f366b..39c31848 100644 --- a/tests/test_timer.py +++ b/tests/test_timer.py @@ -43,7 +43,7 @@ def test_timer_modes_interrupts_and_errors(tmp_path: Path) -> None: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", diff --git a/tests/test_tiny.py b/tests/test_tiny.py new file mode 100644 index 00000000..7c633fb7 --- /dev/null +++ b/tests/test_tiny.py @@ -0,0 +1,170 @@ +"""Tiny product isolation, generated ABI and native bus acceptance.""" + +from __future__ import annotations + +import json +import os +import shutil +import subprocess +from pathlib import Path + +import pytest + +from scripts.generate_tiny import generate_bindings, generate_filelists, read_topology +from scripts.rtl.filelist import parse_filelists +from scripts.rtl.generate_memory_map import generate as generate_memory_map + +ROOT = Path(__file__).resolve().parents[1] +TINY = ROOT / "rtl/tiny" +COMMON = ROOT / "rtl/managed/clusterip/common/rtl" + + +def run(command: list[str], **kwargs) -> subprocess.CompletedProcess: + result = subprocess.run(command, text=True, capture_output=True, **kwargs) + assert result.returncode == 0, result.stdout + result.stderr + return result + + +def test_tiny_sources_and_abi_are_independent(tmp_path: Path) -> None: + output = tmp_path / "generated" + filelists = tmp_path / "filelists" + document = read_topology(TINY / "integration/soc_topology.json", + TINY / "address_map/memory_map.json") + generate_bindings(document, output) + generate_filelists(filelists, output, ["+define+RETROSOC_SOC__TINY"]) + generate_memory_map(TINY / "address_map/memory_map.json", output / "memory_map", "YES", 128) + resolved = parse_filelists(filelists / name for name in + ("commonip.fl", "clusterip.fl", "core_hazard3.fl", "ip.fl", "top.fl")) + for source in resolved.files: + assert "/rtl/mini/" not in str(source) + assert not any(token in source.name.lower() for token in ("rib", "vexii", "radio")) + rtl = (output / "memory_map/rtl/mmap_define.svh").read_text() + sdk = (output / "memory_map/include/retrosoc/generated/memory_map.h").read_text() + assert "RIB" not in rtl + assert "SOC_ADDR_SRAM_END 32'h3001FFFF" in rtl + assert "RS_SOC_SRAM_SIZE UINT32_C(0x00020000)" in sdk + assert "RS_SOC_PSRAM_BASE" not in sdk + irq = (output / "include/retrosoc/generated/irq_metadata.h").read_text() + assert "RS_SOC_EXTERNAL_IRQ_COUNT UINT32_C(30)" in irq + assert "RS_SOC_EXT_IRQ_UART1 UINT32_C(24)" in irq + assert "RS_SOC_EXT_IRQ_DMA UINT32_C(18)" in irq + assert "RS_SOC_EXT_IRQ_NPU" not in irq + # A consumer of public SDK headers must see Tiny capabilities without + # reconstructing the firmware build's private preprocessor command line. + if shutil.which("cc"): + probe = tmp_path / "consumer.c" + probe.write_text('#include \n' + '_Static_assert(RS_DMA_CHANNEL_COUNT == 4, "wrong product");\n') + run(["cc", "-std=c11", "-Werror", "-I" + str(ROOT / "crt/include"), + "-I" + str(output / "memory_map/include"), "-I" + str(output / "include"), + "-c", str(probe), "-o", str(tmp_path / "consumer.o")]) + + +def test_tiny_topology_rejects_duplicate_irqs_and_missing_routes(tmp_path: Path) -> None: + source = json.loads((TINY / "integration/soc_topology.json").read_text()) + path = tmp_path / "invalid.json" + source["interrupts"][1]["core_bit"] = source["interrupts"][0]["core_bit"] + path.write_text(json.dumps(source)) + with pytest.raises(ValueError, match="duplicate"): + read_topology(path, TINY / "address_map/memory_map.json") + source = json.loads((TINY / "integration/soc_topology.json").read_text()) + source["apb_targets"].pop() + path.write_text(json.dumps(source)) + with pytest.raises(ValueError, match="every APB"): + read_topology(path, TINY / "address_map/memory_map.json") + + +@pytest.mark.parametrize("override", ["HAVE_HP=YES", "PDK=GF180", "MINI_MODE=PRODUCT", + "LINK_TYPE=ld2_psram", "APP=hp_boot", "HAVE_PLL=YES"]) +def test_tiny_rejects_incompatible_profiles(override: str) -> None: + result = subprocess.run( + ["make", "CONFIG=configs/ci/ihp130-tiny.mk", override, "config"], + cwd=ROOT, text=True, capture_output=True, + ) + assert result.returncode != 0 + assert "TINY" in result.stderr + + +@pytest.mark.parametrize("simulator", ["verilator", "iverilog"]) +@pytest.mark.parametrize("block", ["fabric", "sysctrl"]) +def test_tiny_protocol_contracts(tmp_path: Path, simulator: str, block: str) -> None: + if not shutil.which(simulator) or not (COMMON / "interface/axi4_if.sv").exists(): + pytest.skip("locked RTL/tool dependency unavailable") + generated = tmp_path / "map" + generate_memory_map(TINY / "address_map/memory_map.json", generated, "YES", 128) + top = "tiny_axi4_fabric_tb" if block == "fabric" else "tiny_sysctrl_tb" + sources = [COMMON / "utils/register.sv"] + if block == "fabric": + sources += [COMMON / "interface/axi4_if.sv", COMMON / "stream/round_robin_arbiter.sv", + ROOT / "rtl/ip/interconnect/axi4_error_slave.sv", TINY / "top/tiny_axi4_fabric.sv"] + else: + sources += [COMMON / "interface/apb4_if.sv", TINY / "top/tiny_sysctrl.sv"] + sources.append(ROOT / "tests/rtl" / f"{top}.sv") + includes = [f"-I{p}" for p in (COMMON, COMMON / "interface", generated / "rtl", + ROOT / "rtl/ip/peripheral", ROOT / "tests/rtl")] + output = tmp_path / "sim" + if simulator == "verilator": + cache = tmp_path / "cache" + cache.mkdir() + run([simulator, "--binary", "--timing", "--assert", "-Wno-fatal", + "-DSV_ASSRT_DISABLE", "--top-module", top, + *includes, *(str(p) for p in sources), "--Mdir", str(tmp_path / "obj"), + "-o", str(output)], + env={**os.environ, "CCACHE_DIR": str(cache), "CCACHE_TEMPDIR": str(cache)}) + command = [str(output)] + else: + if not shutil.which("sv2v"): + pytest.skip("sv2v unavailable") + converted = tmp_path / "converted.v" + conversion = run(["sv2v", "-DSV_ASSRT_DISABLE", *includes, *(str(p) for p in sources)]) + converted.write_text(conversion.stdout) + run([simulator, "-g2012", "-s", top, "-o", str(output), str(converted)]) + command = ["vvp", str(output)] + result = run(command, timeout=60) + assert ("SIM_TEST_PASS Tiny fabric" if block == "fabric" else "SIM_TEST_PASS Tiny SYSCTRL") in result.stdout + assert not any(marker in result.stdout for marker in ("SIM_TEST_FAIL", "FATAL", "%Error")) + + +def test_tiny_product_prose_does_not_restore_legacy_sdk_names(tmp_path: Path) -> None: + from scripts.check_embedded_c import load_policy, policy_issues + + path = tmp_path / "crt/probe.c" + path.parent.mkdir() + policy = load_policy(ROOT) + path.write_text('/* Tiny product */\nconst char *rs_name = "Tiny MCU";\n') + assert policy_issues(tmp_path, path, policy) == [] + path.write_text('void tiny_uart_init(void);\n') + assert any("retired tiny" in issue for issue in policy_issues(tmp_path, path, policy)) + path.write_text('#include \n') + assert any("legacy include" in issue for issue in policy_issues(tmp_path, path, policy)) + + +def test_tiny_metrics_use_product_top_and_timestamped_yosys_report(tmp_path: Path) -> None: + import argparse + from scripts.metrics import collect + + (tmp_path / "meta").mkdir() + (tmp_path / "meta/manifest.json").write_text('{"configuration": {"SOC": "TINY"}}') + report = tmp_path / "syn/yosys/rpt" + report.mkdir(parents=True) + (report / "retrosoc_tiny_asic_area.json").write_text( + '[00001.250000] {"design": {"area": 123.5, "num_cells": 42}}\n') + output = tmp_path / "meta/metrics.json" + assert collect(argparse.Namespace(variant_root=tmp_path, synth_root=None, sta_root=None, + recipe="balanced", output=output)) == 0 + data = json.loads(output.read_text()) + assert data["synthesis"]["top_area"] == 123.5 + assert data["synthesis"]["top_cells"] == 42 + + +def test_tiny_regression_does_not_require_mini_sources() -> None: + import sys + + result = run([sys.executable, str(ROOT / "scripts/regress.py"), "--root", str(ROOT), + "--suite", "pr", "--pdk", "IHP130", "--soc", "TINY", "--dry-run"]) + commands = result.stdout.splitlines() + assert commands + assert all("CONFIG=configs/ci/ihp130-tiny.mk" in line for line in commands) + assert any("SYNTH=YOSYS synth" in line for line in commands) + assert any("netsim-boot" in line for line in commands) + assert any("STA=OPENSTA sta" in line for line in commands) diff --git a/tests/test_uart.py b/tests/test_uart.py index ed9e132a..22c1be61 100644 --- a/tests/test_uart.py +++ b/tests/test_uart.py @@ -52,7 +52,7 @@ def test_uart_register_fifo_loopback_flow_control_and_errors(tmp_path: Path) -> subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", diff --git a/tests/test_usb2.py b/tests/test_usb2.py index f9b94306..df264677 100644 --- a/tests/test_usb2.py +++ b/tests/test_usb2.py @@ -9,7 +9,7 @@ from pathlib import Path ROOT = Path(__file__).resolve().parents[1] BUILD = ROOT / "build/test-usb2" -CONVERTER = ROOT / "rtl/mini/script/convt_sv2v.py" +CONVERTER = ROOT / "scripts/rtl/convt_sv2v.py" def test_usb2_drain_stream_has_registered_ready_cut() -> None: diff --git a/tests/test_ws2812.py b/tests/test_ws2812.py index a60bc57a..fa95162d 100644 --- a/tests/test_ws2812.py +++ b/tests/test_ws2812.py @@ -45,7 +45,7 @@ def test_ws2812_register_waveform_streaming_and_errors(tmp_path: Path) -> None: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", @@ -72,7 +72,7 @@ def test_ws2812_accepts_dma_fixed_destination_backpressure(tmp_path: Path) -> No subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/address_map/generate_memory_map.py"), + str(ROOT / "scripts/rtl/generate_memory_map.py"), "--map", str(ROOT / "rtl/mini/address_map/memory_map.json"), "--output-dir", @@ -121,7 +121,7 @@ def test_ws2812_accepts_dma_fixed_destination_backpressure(tmp_path: Path) -> No subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output", diff --git a/tests/test_xpi_io.py b/tests/test_xpi_io.py index 1a8a2144..9d182a2f 100644 --- a/tests/test_xpi_io.py +++ b/tests/test_xpi_io.py @@ -39,7 +39,7 @@ def test_xpi_releases_data_pads_while_receiving(tmp_path: Path) -> None: subprocess.run( [ sys.executable, - str(ROOT / "rtl/mini/script/convt_sv2v.py"), + str(ROOT / "scripts/rtl/convt_sv2v.py"), "-f", str(source_list), "--output",