feat: enhance Tiny R2 Baseline and ICS55 Support

- Updated  to include support for the ICS55 PDK, allowing for conditional source inputs and configuration checks.
- Added new test cases for the ICS55 platform, including PLL backend and GPIO readback functionality.
- Modified existing tests to accommodate changes in configuration and source input handling.
- Introduced new parameters in the  testbench for better configurability.
- Ensured compatibility checks for the IHP130 and ICS55 PDKs in the feasibility script.
- Enhanced the publication tests to validate hardware changes more robustly.
This commit is contained in:
maksyuki committed 2026-10-08 09:44:39 +08:00
1 parent ecac3559b0
commit e3ddeacea3
56 files changed
+1675 -172

No files matched your search

+5 -4
View File
@@ -78,10 +78,11 @@ README that states its ownership, source of truth, and validation expectations.
`app/apps/<name>/app.mk`. The `debug` application is an RTL debug-transport
acceptance image, not a user-facing firmware profile.
- Tiny is a distinct wired MCU product under `rtl/tiny` with AXI4/APB4 integration.
Its 2026-10-07 specification default is ICS55 with `HAVE_PLL=YES` and SAFE24
boot; TINY-ICS55-P1 executable integration is pending. The current committed
Tiny profile remains IHP130/no PLL. Do not confuse target defaults with
implemented configuration or change Mini defaults.
Its default profile is `configs/ci/ics55-tiny.mk`: ICS55, `HAVE_PLL=YES`,
SAFE24 boot with the PLL macro parked off. IHP130/no PLL remains explicit
compatibility. PLL presence does not enable SYS switching; Tiny RCU remains
R2-P7. Keep implementation, observed timing and physical qualification distinct,
and do not change Mini defaults.
- 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`,
+14 -6
View File
@@ -6,6 +6,12 @@ ROOT_PATH ?= $(abspath $(dir $(firstword $(MAKEFILE_LIST))))
CONFIG ?=
LOCK_FILE ?= $(ROOT_PATH)/dependencies/dependencies.lock.json
ifeq ($(strip $(CONFIG)),)
ifeq ($(SOC),TINY)
CONFIG := configs/ci/$(if $(filter IHP130,$(PDK)),ihp130,ics55)-tiny.mk
endif
endif
ifneq ($(strip $(CONFIG)),)
CONFIG_PATH := $(if $(filter /%,$(CONFIG)),$(CONFIG),$(ROOT_PATH)/$(CONFIG))
ifeq ($(wildcard $(CONFIG_PATH)),)
@@ -357,9 +363,11 @@ endif
ifeq ($(STA),OPENSTA)
ifeq ($(HAVE_PLL),YES)
ifneq ($(SOC):$(PDK):$(TINY_SAFE24_PLL_OFF),TINY:ICS55:YES)
$(error STA=OPENSTA requires a qualified PDK PLL timing profile; HAVE_PLL=YES is unsupported)
endif
endif
endif
DEF_LIST ?= +define+PDK_$(PDK)
DEF_LIST += +define+SIMU_$(SIMU)
@@ -636,7 +644,7 @@ config:
doctor:
@python3 $(ROOT_PATH)/scripts/doctor.py \
--root $(ROOT_PATH) --simu $(SIMU) --synth $(SYNTH) --sta $(STA) \
--pdk $(PDK) --soc $(SOC) --have-sram-macro $(HAVE_SRAM_MACRO) \
--pdk $(PDK) --soc $(SOC) --have-sram-macro $(HAVE_SRAM_MACRO) --have-pll $(HAVE_PLL) \
--formal $(FORMAL) --lock $(LOCK_FILE)
benchmark-report: firmware
@@ -661,7 +669,7 @@ hp-performance-check:
ifeq ($(SOC),TINY)
setup: setup-tiny
setup-tiny:
python3 $(ROOT_PATH)/scripts/setup_tiny.py
python3 $(ROOT_PATH)/scripts/setup_tiny.py --pdk $(PDK)
else
setup: setup-mpw setup-vexiiriscv setup-clusterip setup-ip setup-pdk setup-app
endif
@@ -1181,13 +1189,13 @@ 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 $(if $(filter TINY,$(SOC)),--soc TINY)
python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite smoke --pdk $(if $(filter TINY,$(SOC)),$(PDK),IHP130) $(if $(filter TINY,$(SOC)),--soc TINY)
regress-rtl:
python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite rtl --pdk IHP130 $(if $(filter TINY,$(SOC)),--soc TINY)
python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite rtl --pdk $(if $(filter TINY,$(SOC)),$(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 TINY,$(SOC)),--soc TINY) $(if $(filter YES,$(REGRESS_NETSIM_BOOT_ONLY)),--netsim-boot-only)
python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite pr --pdk $(if $(filter TINY,$(SOC)),$(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)
@@ -1196,7 +1204,7 @@ ifneq ($(SOC),TINY)
endif
regress-nightly:
python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite nightly $(if $(filter TINY,$(SOC)),--soc TINY)
python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite nightly $(if $(filter TINY,$(SOC)),--soc TINY --pdk $(PDK))
sim-asm: asm
$(MAKE) SIM_FIRMWARE_NAME=$(ASM_FIRMWARE_NAME) sim
+8 -5
View File
@@ -22,7 +22,8 @@ An open-source RISC-V SoC platform, from SystemVerilog RTL and firmware to repro
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)
wireless IP. Tiny defaults to ICS55 SAFE24 with a parked PLL and retains
IHP130 compatibility; [Std and Pro](docs/soc-family-positioning.md)
remain roadmap products. Qualification is determined by each profile's retained
verification and physical reports.
@@ -122,7 +123,8 @@ 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. |
| [ICS55 Tiny](configs/ci/ics55-tiny.mk) | Default Tiny platform: 128 KiB SRAM, SAFE24 boot, PLL present but held off. |
| [IHP130 Tiny](configs/ci/ihp130-tiny.mk) | Explicit Tiny compatibility, 128 KiB SRAM, 24 MHz/no PLL. |
| [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 Mini](configs/ci/ics55.mk) | Regression-compatible Mini profile with locked SRAM and PLL disabled. |
@@ -135,9 +137,10 @@ points, not the complete profile or application inventory.
| [Mini MPW](configs/cluster/mini-mpw.mk) | Legacy MPW C0-C3/user-IP compatibility, separate from the product ABI. |
Tiny's [2026-10-07 target refreeze](docs/ip/tiny-soc.md#ics55-default-platform-and-final-timing-gate-2026-10-07)
selects ICS55 with `HAVE_PLL=YES` and SAFE24 startup as the future default.
Its new profile and RTL binding await TINY-ICS55-P1; the IHP130 Tiny entry above
remains the executable compatibility baseline. Locked PLL views can be acquired
selects ICS55 with `HAVE_PLL=YES` and SAFE24 startup as the default.
The profile instantiates a parked PLL; dynamic SYS switching remains R2-P7 and
physical qualification is pending. IHP130 remains explicit compatibility.
Locked PLL views can be acquired
with `python3 physical/pdk/setup.py --pdk ICS55 --component pll` independently
of platform implementation or timing qualification.
+8 -3
View File
@@ -159,9 +159,12 @@ static bool rs_mcu_io(void) {
if ((rs_gpio_get_capabilities(&capabilities) != RS_OK) ||
((capabilities.pad_features & (RS_GPIO_PAD_CAP_PULL_UP | RS_GPIO_PAD_CAP_PULL_DOWN)) !=
0U) ||
(rs_gpio_configure(15U, &pull_up) != RS_ENOTSUP) ||
(rs_gpio_configure(15U, &pull_down) != RS_ENOTSUP) ||
((RS_SOC_GPIO_HAS_PULLS != 0U) ? (RS_GPIO_PAD_CAP_PULL_UP | RS_GPIO_PAD_CAP_PULL_DOWN)
: 0U)) ||
(rs_gpio_configure(15U, &pull_up) !=
((RS_SOC_GPIO_HAS_PULLS != 0U) ? RS_OK : RS_ENOTSUP)) ||
(rs_gpio_configure(15U, &pull_down) !=
((RS_SOC_GPIO_HAS_PULLS != 0U) ? RS_OK : RS_ENOTSUP)) ||
(rs_gpio_configure(15U, &output) != RS_OK) || (rs_gpio_write(15U, true) != RS_OK) ||
(rs_gpio_read(15U, &high) != RS_OK) || !high || (rs_gpio_write(15U, false) != RS_OK) ||
(rs_gpio_read(15U, &high) != RS_OK) || high) {
@@ -256,6 +259,8 @@ int main(void) {
}
if ((rs_archinfo_read(&info) != RS_OK) || (rs_archinfo_validate_build(&info) != RS_OK) ||
(info.soc_id != RS_SOC_ID) || (info.topology != UINT32_C(0x20200001)) ||
(info.technology != RS_SOC_TECHNOLOGY_ID) ||
((info.features0 & UINT32_C(1)) != RS_SOC_PLL_PRESENT) ||
(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);
+6 -4
View File
@@ -67,10 +67,12 @@ For supported profiles and commands, see the root [README](../README.md) and
## Tiny MCU
The [2026-10-07 Tiny refreeze](../docs/ip/tiny-soc.md#ics55-default-platform-and-final-timing-gate-2026-10-07)
sets the **target** default to ICS55 with `HAVE_PLL=YES` and SAFE24 boot.
`ci/ics55-tiny.mk` is planned for TINY-ICS55-P1 and is not yet an executable
profile. IHP130 remains explicit compatibility; do not substitute the Mini
`ci/ics55.mk` profile for Tiny or bypass current configuration guards.
sets the default to ICS55 with `HAVE_PLL=YES` and SAFE24 boot.
`ci/ics55-tiny.mk` implements that entrypoint with the PLL held off; standalone
PLL backend tests do not enable RCU/SYS switching. `make SOC=TINY` selects this
committed profile, and `make SOC=TINY PDK=IHP130` selects explicit compatibility.
Do not substitute Mini's `ci/ics55.mk` for Tiny. Acceptance evidence belongs in
the Tiny ledger, and physical qualification remains pending.
`ci/ihp130-tiny.mk` selects `SOC=TINY`, one RV32IMC Hazard3 (A disabled),
128 KiB SRAM, AXI32/APB4, four DMA channels and the wired peripheral subset.
+18
View File
@@ -0,0 +1,18 @@
SOC := TINY
MINI_MODE := NONE
PDK := ICS55
SIMU := VERILATOR
HAVE_PLL := YES
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
+7 -2
View File
@@ -6,11 +6,16 @@ the root README and subsystem guides.
- [Tiny Gen1 QFN64 R2](ip/tiny-soc.md) freezes dual CPU I/D paths, four
independent 32 KiB main-SRAM groups on the CPU's SYS clock, per-target AXI32
concurrency, eight-channel DMA scheduling and the existing shared-IP/camera
target without changing the package budget. The active roadmap is sequential
`TINY-R2-P0` through `TINY-R2-P11`; legacy `TINY-P0` through `TINY-P12`
target without changing the package budget. After `TINY-ICS55-P1`, the active
roadmap completes remaining Tiny foundation, SPI, PIO-lite and PPALite, then
jointly qualifies the complete product. `TINY-R2-P0..P11` and legacy
`TINY-P0` through `TINY-P12`
remain in the linked contract history. CPU/main-SRAM rates up to 240 MHz
require qualification. These targets remain separate from the existing
24 MHz/four-channel implementation and physical evidence.
- [Tiny ICS55 platform](ip/tiny-ics55-platform.md) defines the default SAFE24
profile, parked PLL, locked 32-macro SRAM mapping, validation commands and
source-bound platform evidence; PLL-driven SYS remains later work.
- [PIO-lite](ip/piolite.md) freezes the separately approved future Tiny
standard programmable-I/O block: two state machines, shared 32x16 program
store, existing GPIO USER ownership, PCLK execution and central DMA V2.1
+4 -2
View File
@@ -3,8 +3,9 @@
## Supported Profiles
Tiny's [2026-10-07 specification default](ip/tiny-soc.md#ics55-default-platform-and-final-timing-gate-2026-10-07)
is ICS55/PLL with SAFE24 boot; platform enablement and its new profile remain
pending. The table describes executable profiles, not planned support.
is ICS55/PLL with SAFE24 boot. The executable default keeps the PLL parked off;
PLL-driven SYS and physical qualification remain later work. The table
describes executable profiles, not qualified operating rates.
For Tiny and its SPI/PIO-lite/PPALite extensions only, pre-final post-synthesis
timing closure is observational. Retain STA attempts/failures and all required
functional, synthesis/mapping and netlist-function evidence. The combined
@@ -13,6 +14,7 @@ This does not change warning/metrics policy or another product's gates.
| Tier | Profile | Automated coverage |
| --- | --- | --- |
| Pull request | `configs/ci/ics55-tiny.mk` | Default Tiny SAFE24/parked PLL; firmware, Verilator/Icarus and JTAG, lint; local Yosys, netlist and OpenSTA |
| 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 |
+3 -2
View File
@@ -11,8 +11,9 @@ automatically cover the new feature.
- Current normative default: TINY/ICS55, `HAVE_PLL=YES`, SAFE24 boot under the
[2026-10-07 Tiny policy](tiny-soc.md#ics55-default-platform-and-final-timing-gate-2026-10-07).
TINY-ICS55-P1 and `configs/ci/ics55-tiny.mk` are pending. The profile below
remains executable IHP130 compatibility, not an ICS55 test result.
`configs/ci/ics55-tiny.mk` now exists with a parked PLL; platform acceptance
belongs to the Tiny ledger. The profile below remains IHP130 compatibility,
not an ICS55 or PIO-lite test result.
- All pre-final post-synthesis timing is observational. Retain failed/unrun
measurements and functional/protocol, synthesis/mapping and netlist-function
gates. PIOLITE-P5 joins R2-P11/SPI-P5/PPALITE-P5 only after all functional
+5 -4
View File
@@ -22,8 +22,9 @@ firmware, configuration, dependency, warning baseline or quality-policy change.
The [2026-10-07 Tiny refreeze](tiny-soc.md#ics55-default-platform-and-final-timing-gate-2026-10-07)
selects default TINY/ICS55, `HAVE_PLL=YES`, SAFE24 boot, retaining explicit
IHP130 compatibility. `configs/ci/ics55-tiny.mk` remains planned until
TINY-ICS55-P1; commands below are existing IHP130 references. Pre-final
IHP130 compatibility. `configs/ci/ics55-tiny.mk` now exists with its PLL parked
off; platform acceptance is tracked in the Tiny ledger. Commands below remain
IHP130 references and do not validate PIO-lite. Pre-final
post-synthesis timing is observational, while functionality, protocol,
synthesis/mapping and netlist-function checks remain required. PIOLITE-P5
joins R2-P11, SPI-P5 and PPALITE-P5 after all functional stages on the same
@@ -34,7 +35,7 @@ PIO implementation and unrelated dependency changes remain outside this refreeze
| Item | Frozen boundary |
| --- | --- |
| Target | TINY, future standard QFN64 product feature |
| Default target / planned profile | ICS55, `HAVE_PLL=YES`, SAFE24 boot; `configs/ci/ics55-tiny.mk` pending TINY-ICS55-P1 |
| Default target / platform profile | ICS55, `HAVE_PLL=YES`, SAFE24 boot; `configs/ci/ics55-tiny.mk`, PLL parked off; no PIO-lite integration |
| Starting profile / PDK | `configs/ci/ihp130-tiny.mk` / IHP130; existing 24 MHz, no-PLL baseline |
| Product integration | `rtl/tiny/top/retrosoc_tiny.sv`, `rtl/tiny/integration/soc_topology.json`, `rtl/tiny/address_map/memory_map.json`, `rtl/tiny/pin_map/pin_map.json`, `rtl/tiny/integration/clock_reset_domains.json` |
| Filelists | `rtl/tiny/filelist/ip.fl`, `inc.fl`, `top.fl`, `tb.fl` and their existing build flow |
@@ -748,7 +749,7 @@ authorized or marked complete by approval of this document.
```text
Use $retrosoc-feature-implementation. Stage: preflight. Feature slug: piolite. Target SoCs: TINY.
Phase: PIOLITE-P1 - ISA Reference Model and Assembler.
Read docs/ip/piolite.md and docs/ip/piolite-verification.md. Default target: ICS55/HAVE_PLL=YES with SAFE24 boot and planned configs/ci/ics55-tiny.mk; require accepted TINY-ICS55-P1 before integrated default-platform work. Current executable reference: configs/ci/ihp130-tiny.mk, IHP130, 24 MHz/no PLL, no PIO. Apply the 2026-10-07 observational pre-final timing policy and mandatory complete-product final campaign; do not invent an executable profile or relabel historical results.
Read docs/ip/piolite.md and docs/ip/piolite-verification.md. Default platform: configs/ci/ics55-tiny.mk, ICS55/HAVE_PLL=YES with external SAFE24 and a parked PLL; verify TINY-ICS55-P1 acceptance in the Tiny ledger before integrated default-platform work. Compatibility: configs/ci/ihp130-tiny.mk, IHP130, 24 MHz/no PLL. Neither platform implements PIO-lite. Apply the 2026-10-07 observational pre-final timing policy and mandatory complete-product final campaign; do not relabel historical results.
Plan only the frozen ISA reference model, deterministic assembler, program metadata/relocation and focused tests, including actual 32-word application and dual-SM footprints. Preserve the two-SM architecture, PCLK tick/packing semantics, GPIO/DMA lifecycle contracts and all R2 phase identifiers.
Do not implement RTL, HAL integration, DMA V2.1, GPIO/RCU wiring, new profiles or later phases. Do not alter the ISA/ABI, expand memory or package scope, download new dependencies, change quality policy, commit or push. Report SPEC_CONFLICT if a frozen requirement cannot be met; do not weaken it. Return the scoped preflight and validation plan for explicit approval before implementation.
```
+3 -2
View File
@@ -10,8 +10,9 @@ evidence under its original scope.
- Current normative default: TINY/ICS55, `HAVE_PLL=YES`, SAFE24 boot under the
[2026-10-07 Tiny policy](tiny-soc.md#ics55-default-platform-and-final-timing-gate-2026-10-07).
TINY-ICS55-P1 and `configs/ci/ics55-tiny.mk` are pending; existing IHP130
commands and historical records are compatibility evidence only.
`configs/ci/ics55-tiny.mk` now exists with a parked PLL; platform acceptance
belongs to the Tiny ledger. Existing IHP130 commands and historical records
are compatibility evidence only; no PPALite integration is delivered.
- Pre-final post-synthesis timing is observational; failed/missing timing does
not become a PASS or a hardware-frequency claim. Source correctness,
functionality, synthesis/mapping and netlist-function requirements remain.
+8 -6
View File
@@ -17,15 +17,17 @@ AXI64/two-dimensional DMA architecture is not imported.
This freeze changes documentation only. It does not change RTL, firmware,
configuration, dependencies, warning baselines, metrics policy or readiness.
The executable Tiny baseline remains 24 MHz/no PLL, four DMA channels and no
PPALite. Source qualification gaps below remain explicit prerequisites.
The IHP130 reference remains 24 MHz/no PLL, four DMA channels and no PPALite;
the ICS55 SAFE24 platform likewise has no PPALite integration. Source
qualification gaps below remain explicit prerequisites.
## Target SoCs and Integration Scope
The [2026-10-07 Tiny refreeze](tiny-soc.md#ics55-default-platform-and-final-timing-gate-2026-10-07)
selects default TINY/ICS55, `HAVE_PLL=YES`, SAFE24 boot, retaining IHP130
compatibility. `configs/ci/ics55-tiny.mk` is planned, not executable until
TINY-ICS55-P1. Existing commands below retain their IHP130 scope. Pre-final
compatibility. `configs/ci/ics55-tiny.mk` now exists with its PLL parked off;
platform acceptance is tracked in the Tiny ledger. Existing commands below
retain their IHP130 scope and do not validate PPALite. Pre-final
post-synthesis timing is observational; source integrity, functionality,
protocol, synthesis/mapping and netlist-function gates are unchanged.
PPALITE-P5 joins R2-P11, SPI-P5 and PIOLITE-P5 after all functional stages on
@@ -36,7 +38,7 @@ by this platform/timing refreeze.
| Item | Frozen boundary |
| --- | --- |
| Target / deferred products | TINY future standard product; MINI/STD/PRO integration deferred |
| Default target / planned profile | ICS55, `HAVE_PLL=YES`, SAFE24 boot; `configs/ci/ics55-tiny.mk` pending TINY-ICS55-P1 |
| Default target / platform profile | ICS55, `HAVE_PLL=YES`, SAFE24 boot; `configs/ci/ics55-tiny.mk`, PLL parked off; no PPALite integration |
| Starting profile / PDK | `configs/ci/ihp130-tiny.mk`, IHP130, existing 24 MHz/no-PLL baseline |
| Product ownership | `rtl/tiny/top`, `rtl/tiny/integration/soc_topology.json`, `rtl/tiny/integration/clock_reset_domains.json`, `rtl/tiny/address_map/memory_map.json` and existing product filelists |
| Shared source | `rtl/ip/multimedia/axi4s_dvp.sv`, `dvp_core.sv`, `dvp_reg.sv`; DVP V2 APB/stream contract and existing 512 B payload CDC FIFO |
@@ -713,7 +715,7 @@ PPALite there. IDs and titles are stable and do not replace earlier roadmaps.
```text
Use $retrosoc-feature-implementation. Stage: preflight. Feature slug: ppalite. Target SoCs: TINY.
Phase: PPALITE-P1 - Stream Pixel Core and Reference Model.
Read docs/ip/ppalite.md and docs/ip/ppalite-verification.md. Default target: ICS55/HAVE_PLL=YES with SAFE24 boot and planned configs/ci/ics55-tiny.mk; require accepted TINY-ICS55-P1 before integrated default-platform work. Current executable reference: configs/ci/ihp130-tiny.mk, IHP130, 24 MHz/no PLL, no PPALite. Apply the 2026-10-07 observational pre-final timing policy and mandatory complete-product final campaign; do not invent an executable profile or relabel historical results.
Read docs/ip/ppalite.md and docs/ip/ppalite-verification.md. Default platform: configs/ci/ics55-tiny.mk, ICS55/HAVE_PLL=YES with external SAFE24 and a parked PLL; verify TINY-ICS55-P1 acceptance in the Tiny ledger before integrated default-platform work. Compatibility: configs/ci/ihp130-tiny.mk, IHP130, 24 MHz/no PLL. Neither platform implements PPALite. Apply the 2026-10-07 observational pre-final timing policy and mandatory complete-product final campaign; do not relabel historical results.
Plan only the frozen standalone pixel/stream core, input validation, 1/2/4 sampling, exact format/row packing, small buffers, APB1.0 and handwritten SVH/C parity, scalar reference, independent stream BFM and focused tests. Preserve Y-only grayscale, RAW contract, resource limits and all earlier phase IDs.
Do not implement DVP source repairs, Tiny routing/DMA/RCU integration, the complete SDK/display application or later phases. Do not add memory replay, RGB-to-gray arithmetic, interpolation, row/frame SRAM, DMA requests/masters, dependencies, profiles or quality-policy changes. Return SPEC_CONFLICT rather than weakening an unmet contract, and provide the scoped preflight/validation plan for explicit approval. Do not commit or push.
```
+3 -2
View File
@@ -10,8 +10,9 @@ Tiny, SPI-SD, XPI, DMA and PIO evidence does not automatically cover this IP.
- Current normative default: TINY/ICS55, `HAVE_PLL=YES`, SAFE24 boot under the
[2026-10-07 Tiny policy](tiny-soc.md#ics55-default-platform-and-final-timing-gate-2026-10-07).
TINY-ICS55-P1 and its planned `configs/ci/ics55-tiny.mk` are pending;
the starting profile below remains executable IHP130 compatibility only.
`configs/ci/ics55-tiny.mk` now exists with a parked PLL; platform acceptance
belongs to the Tiny ledger. The profile below remains IHP130 compatibility;
neither platform implements SPI.
- Before final qualification, post-synthesis timing is observational and all
failures/NOT_RUN cases remain attributed. Functional/source/protocol and
synthesis/mapping/netlist-function gates remain mandatory. SPI-P5 joins
+8 -6
View File
@@ -16,7 +16,8 @@ PSRAM still use XPI and contend there; a separate screen clock/data bus does
not eliminate framebuffer-memory cost. The first application is sequential
capture, verify, display and SD save, not simultaneous double-buffer video.
The committed profile remains 24 MHz/no PLL, four-channel DMA and no SPI.
The IHP130 reference remains 24 MHz/no PLL, four-channel DMA and no SPI;
the ICS55 SAFE24 platform likewise has no SPI integration.
Eight DMA channels, the Gen1 pin map, faster PCLK and new IP are target
contracts. This freeze does not change RTL, firmware, profiles, dependencies,
warning baselines, metrics policy or readiness. It does not implement a new
@@ -26,9 +27,10 @@ GUI framework or replace the legacy XPI LCD transport.
The [2026-10-07 Tiny refreeze](tiny-soc.md#ics55-default-platform-and-final-timing-gate-2026-10-07)
supersedes the original platform and timing-gate scope: default TINY/ICS55,
`HAVE_PLL=YES`, SAFE24 boot; IHP130 remains explicit compatibility. The planned
`configs/ci/ics55-tiny.mk` awaits TINY-ICS55-P1, so the existing commands below
remain IHP130-only executable references. All pre-final post-synthesis timing
`HAVE_PLL=YES`, SAFE24 boot; IHP130 remains explicit compatibility. The
`configs/ci/ics55-tiny.mk` platform now exists with its PLL parked off; its
acceptance is tracked in the Tiny ledger. Existing commands below retain
their IHP130 scope and do not validate SPI. All pre-final post-synthesis timing
is observational; functional/protocol, synthesis/mapping and netlist-function
requirements remain. SPI-P5 joins R2-P11, PIOLITE-P5 and PPALITE-P5 only after
all functional stages, on the same complete-product netlist. Preserve all
@@ -39,7 +41,7 @@ upgrade beyond the separately approved Tiny PLL acquisition.
| Item | Frozen contract |
| --- | --- |
| Target / status | TINY future standard product; MINI/STD/PRO integration deferred |
| Default target / planned profile | ICS55, `HAVE_PLL=YES`, SAFE24 boot; `configs/ci/ics55-tiny.mk` pending TINY-ICS55-P1 |
| Default target / platform profile | ICS55, `HAVE_PLL=YES`, SAFE24 boot; `configs/ci/ics55-tiny.mk`, PLL parked off; no SPI integration |
| Starting profile / PDK | `configs/ci/ihp130-tiny.mk` / IHP130, existing 24 MHz/no-PLL baseline |
| Product sources | `rtl/tiny/top`, `rtl/tiny/integration/soc_topology.json`, `clock_reset_domains.json`, `rtl/tiny/address_map/memory_map.json`, `rtl/tiny/pin_map/pin_map.json` |
| Filelists | Existing `rtl/tiny/filelist` ownership, especially `ip.fl`, `inc.fl`, `top.fl` and `tb.fl` |
@@ -697,7 +699,7 @@ Stable IDs/titles below do not rename, reuse or advance R2/PIO phases.
```text
Use $retrosoc-feature-implementation. Stage: preflight. Feature slug: spi. Target SoCs: TINY.
Phase: SPI-P1 - SPI Core and Register Implementation.
Read docs/ip/spi.md and docs/ip/spi-verification.md. Default target: ICS55/HAVE_PLL=YES with SAFE24 boot and planned configs/ci/ics55-tiny.mk; require accepted TINY-ICS55-P1 before integrated default-platform work. Current executable reference: configs/ci/ihp130-tiny.mk, IHP130, 24 MHz/no PLL, no SPI. Apply the 2026-10-07 observational pre-final timing policy and mandatory complete-product final campaign; do not invent an executable profile or relabel historical results.
Read docs/ip/spi.md and docs/ip/spi-verification.md. Default platform: configs/ci/ics55-tiny.mk, ICS55/HAVE_PLL=YES with external SAFE24 and a parked PLL; verify TINY-ICS55-P1 acceptance in the Tiny ledger before integrated default-platform work. Compatibility: configs/ci/ihp130-tiny.mk, IHP130, 24 MHz/no PLL. Neither platform implements SPI. Apply the 2026-10-07 observational pre-final timing policy and mandatory complete-product final campaign; do not relabel historical results.
Plan only the frozen standalone master/APB1.0 core, packing/phase/lifecycle and source-qualified interface contracts, handwritten SVH/C parity, reference behavior, independent pin BFM and focused tests. Preserve all modes/8-16-bit packing, FIFO sizes, exact quotas, CS/D-C boundaries and bounded close/recovery semantics.
Do not implement DMA V2.2, Tiny routing/RCU/fabric integration, the SDK/display application, new profiles or later phases. Do not change the frozen ABI, increase storage/pads, alter the legacy XPI LCD path, upgrade dependencies, change quality policy, commit or push. Return SPEC_CONFLICT rather than weakening an unmet contract, and provide the scoped preflight and validation plan for explicit approval before implementation.
```
+147
View File
@@ -0,0 +1,147 @@
# Tiny ICS55 SAFE24 platform
This runbook implements the boundary of
[TINY-ICS55-P1](tiny-soc.md#tiny-ics55-p1---default-pdk-and-pll-platform-enablement).
Acceptance and exact artifact roots belong in the [ledger](tiny-soc-verification.md).
The implementation starts from `ecac3559b0ba67fa2630003ad718657969c78af4`.
## Platform and ownership
`configs/ci/ics55-tiny.mk` selects TINY/ICS55, HAVE_PLL=YES, 128 KiB macro SRAM
and external 24 MHz SAFE24. `make SOC=TINY` selects this profile; explicit
`PDK=IHP130` selects the existing compatibility profile. Mini defaults remain
unchanged. This phase does not add a crystal input, change the frozen package
or pin map, add a clock mux, or implement the RCU register bank.
The ASIC wrapper selects `UseIcs55EcosPll=1` and `Ics55Parked=1`. The retained
PLL macro has constant EN=0 and N=32; it cannot drive SYS. The digital monitor
and control cone are unused in this configuration. SAFE24 CPU release never
waits for a PLL or an external peripheral clock. P2's five-edge reset chains,
CPU-last startup, watchdog POR boundary and payload retention remain intact.
Full clock switching and system clock-fault recovery remain R2-P7.
Shared `onchip_ram` defaults still use the legacy ICS55 16 KiB geometry for
Mini. Tiny explicitly selects `Ics55SmallBanks=1`, restricted to the 32-bit
interface, to bind 32 OpenECOS 1024x32 macros. This changes technology selection,
not arbitration or the future four logical bank services. IHP130 remains on
its original 32 4 KiB macros. Memory aperture, byte masks, response semantics
and software bank discovery remain unchanged for Tiny.
Tiny's generated GPIO pad instances explicitly select `ReadWhileDriving=1`.
ICS55's IE and OE are independent; enabling IE while driving preserves Tiny's
pad-input readback contract. The shared wrapper default is still zero for
Mini. Dedicated tests check both settings against native Icarus and functional
Verilator pad views. Firmware tests real technology pull capabilities instead
of assuming the IHP130 lack of pulls. ARCHINFO reports technology 0x02040037
and PLL presence for ICS55; the IHP130 reference remains 0x02010082/no PLL.
Presence does not advertise a working RCU control API; unsupported writes and
HAL operations remain rejected. No address/IRQ/DMA/register ABI is added.
The source lists select native Icarus IO, functional Verilator IO, or synthesis
library inputs explicitly. The unused shared 4096x32 wrapper has only its
official interface stub in Tiny simulation; the netlist audit rejects any
actual instance. Managed sources and dependency versions are unchanged.
## PLL backend verification boundary
The locked OpenECOS PLL revision is
`6ebb1a8f7f4ccbccdb7f587664fdfe63cd39e61b`. Standalone tests select the unparked
backend and use its actual behavioral view, not a pass-through stub. Selectors
5/7 configure N=32/40, SELECT=0 and OD=2 for nominal 192/240 MHz from REF24.
Other selectors disable the backend. Held APPLY is consumed once; each new
request deasserts EN for two reference cycles before restart.
Clock qualification waits 512 reference cycles, discards the first 128-cycle
window and requires four good windows. A registered 16-bit Gray count crosses
through Common's two-stage synchronizer. Expected counts are 1024/1280 with
16/20-edge tolerance. A bad window removes lock; 4096 unqualified reference
cycles exhaust the acquisition budget and require a new request. Source-domain
reset release takes five edges. The interface's lock output is a digital
activity/rate observation, not analog lock or jitter qualification.
The macro has no LOCK output; its Liberty has no timing arcs and its license
is unresolved. Ideal simulation supplies are not a physical ground/rail merge
decision. Reference loss cannot be diagnosed by a stopped reference clock;
the frozen external-reset recovery boundary remains. None of the standalone
tests qualifies CPU/main SRAM at a high rate.
## Validation sequence
Activate the existing locked development environment and use one timestamp
across commands. Run focused tests, RTL/C policy and host checks before the
full Pytest suite or product regressions. Tests requiring a model/simulator
must execute; a skip is not that gate's acceptance.
```sh
source .cache/retrosoc/development/tiny-r2-p1/activate.sh
export BUILD_TIMESTAMP=$(date +%Y-%m-%d-%H-%M)
python3 -m pytest -q tests/test_tiny_ics55_platform.py tests/test_tiny.py \
tests/test_tiny_r2_baseline.py tests/test_tiny_r2_p2.py \
tests/test_clock_reset_domains.py tests/test_mgmt_debug.py \
tests/test_publication_tiny.py tests/test_commercial_flow.py::test_ics55_pll_wrapper
make CONFIG=configs/ci/ics55-tiny.mk SOC=TINY PDK=ICS55 \
rtl-format-check rtl-style-check rtl-readiness-check \
sw-format-check sw-policy-check sw-host-test
ruff check .
python3 -m pytest -q
make CONFIG=configs/ci/ics55-tiny.mk SOC=TINY PDK=ICS55 APP=ci_smoke firmware
tiny_ics_root=$(make -s CONFIG=configs/ci/ics55-tiny.mk SOC=TINY PDK=ICS55 APP=ci_smoke config | awk '$1 == "VARIANT_ROOT" {print $2}')
python3 scripts/tiny_ics55_platform.py --variant-root "$tiny_ics_root" capture
python3 scripts/regress.py --root . --suite pr --soc TINY --pdk ICS55 --dry-run
python3 scripts/regress.py --root . --suite nightly --soc TINY --pdk ICS55 --dry-run
python3 scripts/regress.py --root . --suite pr --soc TINY --pdk IHP130 --dry-run
python3 scripts/regress.py --root . --suite pr --soc MINI --pdk ICS55 --dry-run
python3 scripts/regress.py --root . --suite pr --soc TINY --pdk ICS55
python3 scripts/regress.py --root . --suite pr --soc TINY --pdk IHP130
python3 scripts/tiny_ics55_platform.py --variant-root "$tiny_ics_root" report
```
Tiny PR RTL simulation enables `+tiny_jtag_smoke`: IDCODE/DTMCS, DMI halt and
resume must finish before the testbench can accept firmware TEST_STATUS.
Normal workload replay leaves JTAG untouched. Tiny always requires command
success, valid TEST_STATUS/SIM_TEST_PASS and no forbidden errors; UART or the
Mini boot-only marker is insufficient. Tiny's filtered nightly matrix matches
PR, so verify that equivalence rather than repeating identical long runs.
Replay the retained original P1 HEX using `tiny-r2-baseline-sim` under the new
profile with APP=bringup and explicit TINY_BASELINE_HEX. Each simulator executes
three cold starts; Icarus uses SOC_SIM_TIME=14400 and JOBS=3 and is polled every
30 minutes. If host timeout is exhausted, retain the failed attempt and do not
count an incomplete sample. An identical-model retry may increase only the
host budget, retaining successful cold starts and recording each run's budget;
the actual campaign recovery and its executable driver are in the ledger.
Keep the original image manifest/source/phase and binaries intact.
The P1 label identifies the measurement protocol; the new RTL/config/PDK and
outer phase record identify ICS55-P1. Historical IHP130 samples remain labeled
references, not current-source cross-PDK performance qualification.
## Netlist, STA and evidence
Yosys imports the PLL's library blackbox; it never synthesizes behavioral clock
delays. Its final Verilog and JSON are bound by hashes to a structural report:
32 4 KiB SRAM macros, one constant-disabled PLL, and CPU/SRAM clock connectivity.
The 16 KiB stub must have no instance. A modified audit script or consumed
artifact invalidates the report. Netlist configuration must match SOC/PDK,
capacity, PLL presence, 41667 ps synthesis target and the actual config digest.
OpenSTA accepts only this verified parked Tiny/ICS55 exception to the existing
PLL guard. Active PLL configurations remain unsupported. SYS remains the
41.666666667 ns primary clock at the existing buffer output, with JTAG as the
separate asynchronous domain. No active PLL generated clock is invented.
The original core-only IO/reset exceptions remain unchanged. Reports include
coverage, clocks, SYS setup/hold, SRAM input/return paths and electrical,
recovery/removal, pulse-width and minimum-period observations.
`TINY_ICS55_STA_AUDIT_PASS` means the audit ran, not timing closure. Negative
WNS/TNS and other timing violations remain failures in the report but do not
block this functional phase. Missing arcs, full-chip IO, extracted PVT, package,
power and analog qualification remain final-campaign gaps. The legacy pad
wrapper and Mini padless ECC runs do not qualify the frozen QFN64 product.
Evidence lives under `build/<profile>-<timestamp>-<config-hash>/meta/tiny-ics55-p1/`.
Capture snapshots before final synthesis/STA; a later capture cannot relabel an
older run. Archive source/generated input hashes, consumed models/libraries,
configuration, tool identities, commands/results and original binary identity.
The native report covers synthesis/STA only; functional and matched-workload
acceptance must be joined explicitly in the phase delivery index. Warning
baselines and global observe-mode metrics are not promoted or regenerated here.
+156 -2
View File
@@ -1,11 +1,165 @@
# Tiny MCU Verification Record
## TINY-ICS55-P1 implementation (2026-10-07–08)
Implementation is based on committed source
`ecac3559b0ba67fa2630003ad718657969c78af4` plus the retained working-tree diff.
The worktree was clean at entry. The preflight specification/ledger Git blobs
were `713f3d5a0d04a7b2fc4c66838e9ceaf6c7551263` and
`87bc267759d863d98f200e2740abcaa0dcba28a5`. This record is separate from the
earlier documentation/dependency refreeze and IHP130 R2 measurements.
Implementation and validation are complete for review; human phase acceptance
is still pending by this entry.
The [platform runbook](tiny-ics55-platform.md) defines ownership and commands.
The default `configs/ci/ics55-tiny.mk` selects TINY/ICS55, HAVE_PLL=YES,
128 KiB main SRAM and external 24 MHz SYS. The PLL is physically represented
but disabled; standalone PLL192/240 tests do not enable those SoC rates.
IHP130 remains an explicit no-PLL profile, and Mini defaults are unchanged.
Changes are limited to platform/profile selection, generated identity and
filelists, technology adapters, the standalone PLL digital backend, native
pad readback, JTAG smoke, netlist/STA evidence and matching publication-source
parsing. Tiny explicitly selects 32 locked 1024x32 SRAM macros through a
default-off shared parameter; this is not R2-P4 logical SRAM banking. Tiny's
GPIO input remains enabled while driving, with the legacy Mini default intact.
No register/API, address, IRQ, DMA channel/request, package assignment or
private Crypto storage change is introduced. No new MISRA deviation applies.
Dependency versions, warning signatures and metrics policy are unchanged.
Final validation uses `BUILD_TIMESTAMP=2026-10-07-23-22`:
- Primary CI/evidence variant: `build/ics55-tiny-2026-10-07-23-22-42d5beccb726/`.
Commands, logs and results are in `meta/tiny-ics55-p1/validation/`; the
captured source/tools/views are selected by `meta/tiny-ics55-p1/latest-inputs.json`.
- Matched workload variant: `build/ics55-tiny-2026-10-07-23-22-b4aa59f3e5d9/`,
APP=bringup. Each simulator runs three cold starts of the original retained
P1 HEX, SHA-256 `ba24c2dfd727a905f39b644df254e47262c39ab4960ff2614be44402020758b2`.
Its original image/source manifest remains under
`build/ihp130-tiny-2026-10-05-12-30-fa1c4ce1e303/meta/tiny-r2-p1/binaries/image-ozwo38_b/`.
The seven fixed workloads are empty-window calibration, CPU, memory, DMA
burst 1, DMA burst 16, CPU/DMA contention and descriptor-chain DMA. There
is no recompilation or layout experiment in this comparison.
| Gate | Current result / retained evidence |
| --- | --- |
| Focused checks after the JTAG correction | PASS; 178 platform/provenance/baseline/build tests; earlier memory/debug/clock and publication checks also passed |
| Final policy and formatting | PASS; `validation/policy.json` and `rtl-format-retry.json`; C format/policy/host tests, Ruff, locked dependency schema, RTL style/readiness and changed-file formatting |
| Regression definitions | PASS; `validation/dry-runs.json`; Tiny ICS55 PR/nightly are identical, plus IHP130 Tiny and ICS55 Mini PR expansion |
| Tiny ICS55 PR | PASS; `validation/tiny-ics55-pr.json`; firmware, Verilator SVA/JTAG, native Icarus/JTAG, Yosys, strict Tiny netlist boot and OpenSTA execution |
| Tiny IHP130 PR | PASS; `validation/tiny-ihp130-pr.json`; separate compatibility artifacts, not relabeled ICS55 evidence |
| Shared Mini compatibility | PASS for firmware, memory/pin/topology/clock-domain checks and Verilator SVA lint; `validation/mini-compatibility.json`, `mini-rtl-lint-retry.json`; full Mini PR not run |
| Source / macro audit | PASS; `meta/tiny-ics55-p1/report.json`, `syn/yosys/tiny-ics55-netlist.json`; 32 SRAM macros, 2177 CPU clocked cells on direct SYS, one PLL with constant EN=0 |
| Matched Verilator workload | PASS, three identical samples, same original HEX; all seven workload samples also equal the historical IHP130 reference |
| Matched Icarus workload | PASS after timeout recovery; runs 1/2 at 13862.428/14242.835 s and recovered run 3 at 15379.18 s. All three samples match Verilator and historical IHP130; original exit-124 attempt is retained, with per-run budgets in `matched-report.json` |
| Full Pytest | PASS; 1568 passed / 1 skipped in 3769.75 s on final source; `validation/pytest.json`; the optional publication retrieval test lacks `pypdf` |
| Warning comparison | Non-blocking FAIL observations retained; no signatures regenerated or waived |
| Metrics | Collected on the actual APP=ci_smoke variants; `validation/ics55-ci-metrics.json`, `ihp130-ci-metrics.json`; policy remains observe |
In this table `validation/` is relative to the primary variant's
`meta/tiny-ics55-p1/`. Verilator SVA CI variants use suffixes `2eb98c0e1fae`
(ICS55) and `39ea34c082ac` (IHP130); IHP130's ordinary CI variant uses
`bff91e3529ba`, all with the same fixed timestamp. Tiny netlist `netsim-boot`
uses a retained 220-byte assembly image and TEST_STATUS/SIM_TEST_PASS; it is
not the Mini UART-only boot target or full C netlist qualification.
The native source/input digest is
`9566b3f2c06b21b3e06a4513d0163cc9ffcb63e6d68dd665a31f61be6c1e9def`.
The report rechecks consumed Verilog/JSON/configuration and audit-script hashes,
source snapshots, locked tools and post-capture execution timestamps. The
seven locked Tiny tools and PDK/model inputs retain their existing versions.
Mini's first lint attempt failed at SBT's local Unix socket in the sandbox;
the same source/configuration command passed with that environment restriction
removed. The initial multi-file formatter invocation was unsupported; individual
read-only verification passed without changing source.
The original Icarus attempt is retained as failed at
`meta/tiny-r2-p1/workloads/iverilog/attempt-_fqxpoam/attempt.json` below the
matched variant. Runs 1/2 each completed all seven cases and strict Tiny
TEST_STATUS/SIM_TEST_PASS, with samples identical to Verilator. Run 3 printed
part of its final TCD record before host timeout; the missing completion marker
prevents acceptance. The generated recovery driver
`meta/tiny-ics55-p1/retry_icarus_run3.py` below the primary variant checks original
source/generated-input/model/tool/image hashes, preserves the two completed
run records, and launches a new cold start for repetition 3 only. It retains
the failed attempt and individual per-run timeout values. The recovered run 3
passed in 15379.18 seconds with the same seven samples and strict markers. HEX,
simulator model,
workload parameters and the 100000000-cycle limit are unchanged; this is an
execution-budget recovery, not a compiler, layout or architecture experiment.
The first recovery launch stopped during provenance validation before starting
simulation: the stock runtime validator requires a common timeout for all runs.
The generated recovery adapter now invokes that unchanged validator separately
with each original command's declared budget. The final phase join uses
`collect_matched_report.py` and writes `meta/tiny-ics55-p1/matched-report.json`
below the primary variant, explicitly preserving per-run budgets and the failed
attempt. It does not claim that the ordinary homogeneous-budget baseline report
command accepts a mixed-budget record. Project implementation/validator sources
and all original run records remain unchanged by this operational recovery.
Final SAFE24 core STA observations, in ns:
| PDK | WNS max | TNS max | WNS min / TNS min | Verdict |
| --- | ---: | ---: | --- | --- |
| ICS55 | -41.14 | -508669.53 | 0.00 / 0.00 | Timing FAIL; observational in this phase |
| IHP130 | -94.28 | -1594880.00 | 0.00 / 0.00 | Timing FAIL; compatibility observation |
ICS55 `sta/opensta/sys-setup.rpt` starts at the XPI reset leaf, fanout 4707,
and ends at RX FIFO storage; its SYS hold report's worst reported slack is
+0.08 ns. `coverage.rpt` records 43 inputs without input delays, 52 outputs
without output delays and 93 unconstrained endpoints. These are the retained
core-only analysis boundary, not full-chip IO closure. The clock report has
SYS 41.666666667 ns and JTAG 100 ns; no active PLL generated clock is claimed.
The electrical/recovery/removal/pulse-width/minimum-period and SRAM input/return
reports remain under the same `sta/opensta/` directory.
Direct warning checks on the CI variants report 21 new Yosys signatures for
ICS55, and 14 Yosys plus one Icarus signature for IHP130, relative to the
stored baselines. Lint comparisons report 459/403 signatures respectively.
Neither Tiny profile has a stored RTL-lint baseline, so every normalized
lint signature is classified as new. The reports include unused parameters,
empty ports, vendor model limitations and ABC's Liberty-cache fallback; they are
not counts of defects introduced by this phase. The regression preserves
their failed observation status while its functional commands pass.
The earlier development variant
`build/ics55-tiny-2026-10-07-21-00-42d5beccb726/` retains failed attempts:
missing unused SRAM interface, wrong 16 KiB macro selection, GPIO readback
failure and insufficient constant-disabled PLL proof. Corrected development
Verilator/JTAG, mapped-netlist audit and SAFE24 STA subsequently ran, but those
results are not substituted for the fresh final campaign. NFS clock-skew
warnings are retained; final variants and forced workload compilation avoid
reuse of those earlier simulator outputs.
The intermediate `2026-10-07-23-06` campaign exposed an IHP130 Icarus JTAG
testbench reset-initialization failure. Four-state waveform inspection showed
internal TRST remaining X until release because TCK started after reset. The
opt-in test now clocks TCK during asserted external reset before its TAP/DMI
sequence; no reset RTL changed. A bounded Icarus probe then completed
halt/resume. Ongoing simulations in that campaign were explicitly interrupted
before final source recapture; partial workload passes are not final evidence.
The initial full Pytest was also interrupted and restarted on final source;
its interrupted record stays in that earlier directory. After the correction,
178 focused platform, provenance, baseline and build tests passed.
The required acceptance remains command success, valid sticky TEST_STATUS,
SIM_TEST_PASS and no forbidden error marker. UART alone is insufficient.
Boot-only netlist checks are narrower than full C netlist acceptance. Timing
violations remain failures under the observational pre-final policy; no
behavioral result qualifies physical timing, analog PLL lock, PVT, power,
package or silicon. The PLL has no LOCK output, no characterized timing arcs
and an unresolved upstream license. Its ideal digital supply ties do not
define physical rail integration. Full RCU/source switching remains R2-P7.
The next action is read-only feature review followed by explicit human phase
acceptance. No commit, push, PR or next-phase advancement is authorized here.
## ICS55 default and final timing policy refreeze (2026-10-07)
The [2026-10-07 normative policy](tiny-soc.md#ics55-default-platform-and-final-timing-gate-2026-10-07)
selects TINY/ICS55 with `HAVE_PLL=YES` and SAFE24 startup as the default target.
`configs/ci/ics55-tiny.mk` and TINY-ICS55-P1 platform implementation are pending;
`configs/ci/ihp130-tiny.mk` remains the current executable compatibility baseline.
At refreeze, `configs/ci/ics55-tiny.mk` and TINY-ICS55-P1 platform implementation
were pending; `configs/ci/ihp130-tiny.mk` was the executable compatibility baseline.
The existing 614fa623 commit adds shared ICS55 SRAM inputs, not Tiny enablement.
All pre-final post-synthesis timing results are observations rather than phase
+28 -23
View File
@@ -7,8 +7,9 @@ The 2026-10-07 refreeze selects **ICS55 as the default target PDK with
complete-product final qualification campaign. The
[platform and timing policy](#ics55-default-platform-and-final-timing-gate-2026-10-07)
below supersedes earlier target-PDK and intermediate timing-gate wording.
It does not relabel historical IHP130 results or make the planned ICS55 profile
executable. This refreeze includes locked PLL acquisition, not RTL integration.
It does not relabel historical IHP130 results. The refreeze itself included
locked PLL acquisition only; the subsequent TINY-ICS55-P1 implementation is
described in the [platform runbook](tiny-ics55-platform.md) and verification ledger.
The R2 portion of this contract freezes the performance-only design approved
on 2026-10-04 for Target SoCs: `TINY`, feature slug `tiny-soc`. It preserves the QFN64
@@ -54,12 +55,12 @@ This is a specification freeze, not evidence of implemented or qualified
additional product rollout; Std/Pro integration is outside this change.
Tiny owns its integration in `rtl/tiny`; Mini remains a separate product. The
committed `configs/ci/ihp130-tiny.mk` profile still describes the initial
2026-09-25 implementation: 24 MHz external clock, no PLL, two UARTs and two I2C
controllers. Its RTL, canonical maps, SDK, configuration, generated datasheet
and verification evidence remain the executable baseline until separate
integration work updates them. The baseline sections below retain that
contract and must not be read as the new Gen1 integration or clock qualification.
`configs/ci/ics55-tiny.mk` default implements SAFE24 with a present, explicitly
disabled PLL; `configs/ci/ihp130-tiny.mk` preserves the 24 MHz/no-PLL compatibility
profile. Both retain two UARTs, two I2C controllers and the legacy executable
integration. The original generated datasheet and historical evidence remain
IHP130-specific. The baseline sections below must not be read as completed
Gen1 integration or clock qualification.
Product RTL, address/pin/topology inputs and filelists remain under `rtl/tiny`;
the SDK and application composition retain their existing `crt/` and `app/`
@@ -68,8 +69,9 @@ the current product execution order is defined by the 2026-10-07 policy below.
Legacy `TINY-P0`
through `TINY-P12` keep their original headings and evidence in the archive;
their former non-monotonic schedule is not the current execution plan.
The current profile still selects four DMA channels and forbids PLL/non-24-MHz
Tiny configurations. New configurations require explicit platform enablement.
Current profiles still select four DMA channels and require 24 MHz external
SYS. ICS55 PLL presence does not enable PLL-driven SYS; new operating points
require later clock/reset integration and qualification.
PIO-lite integration in `PIOLITE-P3` requires the applicable accepted R2-P6
DMA/shared-integration and R2-P7 RCU functionality. `PIOLITE-P5` may share a
physical run with R2-P11 only on the same PIO-inclusive source revision and
@@ -91,9 +93,9 @@ integration contracts. It adds ICS55 platform enablement to the former
performance-only scope; unrelated SoCs and PDK rollouts remain excluded.
- TINY-052: the default **target** is TINY/ICS55, `HAVE_PLL=YES`, 128 KiB main
SRAM and SAFE24 boot. The planned committed entrypoint is
`configs/ci/ics55-tiny.mk`; it does not exist yet. The current executable
entrypoint remains `configs/ci/ihp130-tiny.mk`, IHP130, 24 MHz/no PLL.
SRAM and SAFE24 boot. The default entrypoint is `configs/ci/ics55-tiny.mk`,
with its PLL explicitly parked off in P1. The compatibility entrypoint is
`configs/ci/ihp130-tiny.mk`, IHP130, 24 MHz/no PLL.
IHP130 remains an explicit compatibility option, not the future default.
Mini profiles and defaults are unchanged.
- TINY-053: before final complete-product qualification, post-synthesis timing
@@ -182,7 +184,7 @@ lock/jitter proof nor final frequency qualification.
### TINY-ICS55-P1 - Default PDK and PLL Platform Enablement
This is a new, pending implementation prerequisite before remaining R2 work,
This is the platform implementation prerequisite before remaining R2 work,
not a renaming of R2-P1/P2. Introduce the default ICS55 Tiny profile and explicit
PDK selection, reuse the locked SRAM and PLL inputs, adapt filelists, technology
bindings and observation endpoints, and establish SAFE24 PLL-present boot,
@@ -196,7 +198,9 @@ Acceptance requires an executable source-bound platform, actual macro binding,
both simulators and strict Tiny verdicts, dependency/model identity, relevant
synthesis/netlist checks and observational timing with explicit gaps. Physical
timing closure is deferred to the combined final campaign. This refreeze
implements only documentation and dependency acquisition, not this phase.
implemented only documentation and dependency acquisition. The subsequent
implementation and its source-bound acceptance status are recorded separately
in the ledger; neither enables R2-P7 clock switching or final physical qualification.
### Commercial references and reuse boundary
@@ -2195,8 +2199,9 @@ evidence class. Record exact directed/formal commands when those cases are
introduced; do not assume a nonexistent target, a skipped RTL test or another
product's result satisfies Tiny acceptance. Before the combined final campaign,
post-synthesis timing is observational under TINY-053. The commands above
remain the existing IHP130 executable baseline; the proposed ICS55 Tiny profile
and its commands must be introduced and verified by TINY-ICS55-P1.
retain the historical IHP130 compatibility entrypoint. The default ICS55
platform's explicit commands and evidence boundaries are documented in
[the platform runbook](tiny-ics55-platform.md).
## Archived P0-P12 development order and acceptance
@@ -2591,19 +2596,19 @@ concurrency and final physical product qualification are different claims.
## Implementation handoff
The next platform step after the 2026-10-07 refreeze is TINY-ICS55-P1 preflight.
Historical R2-P1/P2 evidence remains IHP130-only. After platform enablement,
The current handoff is review of TINY-ICS55-P1 implementation and its retained
evidence. Historical R2-P1/P2 evidence remains IHP130-only. After acceptance,
resume the remaining Tiny foundation, then SPI, PIO-lite and PPALite functional
phases, followed by the combined final qualification campaign. This handoff
authorizes no implementation or automatic phase advancement.
```text
Use $retrosoc-feature-implementation in preflight mode for feature tiny-soc.
Use $retrosoc-feature-review in read-only review mode for feature tiny-soc.
Target SoCs: TINY.
Phase: TINY-ICS55-P1 - Default PDK and PLL Platform Enablement.
Specification: docs/ip/tiny-soc.md; evidence: docs/ip/tiny-soc-verification.md.
Current executable reference: configs/ci/ihp130-tiny.mk, IHP130, 24 MHz/no PLL. Planned default: configs/ci/ics55-tiny.mk, ICS55, HAVE_PLL=YES, SAFE24 boot; do not pretend this profile already exists. Map the current source/worktree, locked OpenECOS SRAM and pdk_ics55_pll inputs, models, technology bindings and tools. PLL pin: 6ebb1a8f7f4ccbccdb7f587664fdfe63cd39e61b. Its Liberty has no timing arcs and its interface has no LOCK output; retain those qualification gaps.
Plan the smallest platform/profile/filelist/PLL-backend integration and source-bound functional baseline. Preserve QFN64 and IO/power assignments, 128 KiB CPU-rate main SRAM, private Crypto storage, boot/debug, original workload binaries and IHP130/shared-Mini compatibility. No half-rate SRAM fallback. Preserve selector 5/7 mappings for PLL192/240 and reject the old N=2/four-cycle lock assumption.
Default platform: configs/ci/ics55-tiny.mk, ICS55, HAVE_PLL=YES, external SAFE24 with the PLL explicitly parked off. Compatibility: configs/ci/ihp130-tiny.mk, IHP130, 24 MHz/no PLL. Read docs/ip/tiny-ics55-platform.md and the implementation ledger; distinguish the worktree based on ecac3559b0ba67fa2630003ad718657969c78af4 from historical results. Audit actual source, locked macro/model inputs and retained command verdicts. PLL pin: 6ebb1a8f7f4ccbccdb7f587664fdfe63cd39e61b; Liberty has no timing arcs and no LOCK output.
Review default selection, truthful ARCHINFO, 32 x 4 KiB SRAM mapping at CPU SYS, constant-disabled PLL binding, selector 5/7 backend qualification, native and functional pad readback, JTAG halt/resume, strict dual-simulator firmware/netlist results and original matched workload HEX. Preserve QFN64, private Crypto storage, boot/debug and IHP130/shared-Mini compatibility. No half-rate SRAM fallback. Check source/input/tool hashes and distinguish digital PLL rate observation from analog lock.
Full Tiny RCU transitions and dynamic rate reporting remain R2-P7; do not implement later CPU/banking/fabric/DMA/peripheral phases. Preserve dependency versions, warning baselines and metrics policy. Retain required synthesis/mapping/netlist-functional checks and observational STA, with failures and missing inputs explicit. No intermediate post-synthesis timing closure gate; final timing and physical qualification occur only on the complete SPI/PIO-lite/PPALite-inclusive product.
Return a single-phase preflight with exact proposed changes, ownership, validation commands, blockers and acceptance boundaries. Read-only preflight only: no edits, dependency restoration, builds, simulations or STA. Do not commit, push, create a PR or advance a phase. Stop for explicit implementation approval.
Return prioritized findings with file/line evidence and phase readiness; identify failed, unrun or historical gates. Read-only review only: no edits, restoration, builds, simulations or STA. Do not commit, push, create a PR or advance a phase. Stop for human acceptance and separate approval before any next-phase implementation.
```
+5 -3
View File
@@ -6,8 +6,9 @@ This document defines the intended Tiny, Mini, Std, and Pro product ladder.
Tiny and Mini have executable build profiles; Std and Pro remain roadmap
targets. Committed product profiles and retained validation evidence define
implemented support. Tiny's 2026-10-07 specification selects ICS55/PLL as its
default wired-MCU target; current executable Tiny remains IHP130/no PLL until
TINY-ICS55-P1. IHP130 remains explicit compatibility, and Mini defaults are unchanged.
default wired-MCU target. Its executable profile keeps SYS on external SAFE24
and the PLL parked off. IHP130 remains explicit compatibility, and Mini
defaults are unchanged.
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
@@ -44,7 +45,8 @@ verification requirements differ materially.
The [2026-10-07 refreeze](ip/tiny-soc.md#ics55-default-platform-and-final-timing-gate-2026-10-07)
requires ICS55 with `HAVE_PLL=YES`, SAFE24 startup and unchanged CPU-rate
128 KiB SRAM. The planned `configs/ci/ics55-tiny.mk` is not executable yet.
128 KiB SRAM. `configs/ci/ics55-tiny.mk` implements that SAFE24 platform;
PLL192/240 backend tests do not qualify those frequencies as SoC operating points.
After platform enablement, the order is remaining Tiny foundation, SPI,
PIO-lite, PPALite, then one complete-product final physical campaign. Earlier
post-synthesis timing is observational; functionality and source-bound evidence
+4 -2
View File
@@ -51,8 +51,10 @@ Liberty views and prints their hashes. No profile or RTL binding is changed.
The views have no characterized timing arcs or LOCK output; licensing is
undetermined. Functional modeling, actual lock/loss detection, supply binding
and final timing characterization remain separate integration/release work.
Tiny's new default ICS55/PLL specification still requires TINY-ICS55-P1;
current executable Tiny configurations remain IHP130/no PLL.
Tiny's default `configs/ci/ics55-tiny.mk` instantiates one disabled PLL and
32 locked 4 KiB SRAM macros at external SAFE24. Its digital backend tests and
core STA observations do not qualify analog PLL behavior or PLL-driven SYS.
IHP130/no PLL remains explicit Tiny compatibility.
The IHP130 dependency is also the technology source for the independent
`physical/librelane/{mini,tiny}/` core-hardening and full-chip pad-ring flows.
+6 -1
View File
@@ -20,6 +20,11 @@ $(OPENSTA_SDC): $(OPENSTA_SDC_GEN) $(OPENSTA_DOMAIN_MAP) $(OPENSTA_PIN_MAP) | ma
sta: $(OPENSTA_SDC) | manifest
@mkdir -p $(STA_BUILD_ROOT)
ifeq ($(TINY_SAFE24_PLL_OFF),YES)
python3 $(ROOT_PATH)/scripts/tiny_ics55_platform.py --variant-root $(VARIANT_ROOT) verify-netlist \
--netlist $(OPENSTA_NETLIST) --config $(OPENSTA_CONFIG) \
--output $(SYN_BUILD_ROOT)/tiny-ics55-netlist.json
endif
@for input in $(OPENSTA_NETLIST) $(OPENSTA_LIBERTY) $(OPENSTA_LINK_LIBS) $(OPENSTA_SRAM_LIBS) $(OPENSTA_SDC) $(OPENSTA_CONFIG); do \
test -f "$$input" || { echo "OpenSTA input missing: $$input" >&2; exit 1; }; \
done
@@ -40,7 +45,7 @@ sta: $(OPENSTA_SDC) | manifest
--env OPENSTA_TOP=$(OPENSTA_TOP) --env OPENSTA_NETLIST=$(OPENSTA_NETLIST) --env OPENSTA_LIBERTY=$(OPENSTA_LIBERTY) \
--env 'OPENSTA_LINK_LIBS=$(OPENSTA_LINK_LIBS)' --env 'OPENSTA_SRAM_LIBS=$(OPENSTA_SRAM_LIBS)' \
--env OPENSTA_SDC=$(OPENSTA_SDC) --env OPENSTA_REPORT=$(OPENSTA_REPORT) \
--env OPENSTA_METRICS=$(OPENSTA_METRICS) -- \
--env OPENSTA_METRICS=$(OPENSTA_METRICS) --env OPENSTA_TINY_ICS55=$(TINY_SAFE24_PLL_OFF) -- \
$(OPENSTA) -no_init -exit -threads $(OPENSTA_THREADS) $(ROOT_PATH)/physical/smoke/sta/opensta/opensta.tcl
.PHONY: sta
+3
View File
@@ -20,6 +20,9 @@ if {[info exists ::env(OPENSTA_TOP)] && $::env(OPENSTA_TOP) ne ""} {
}
link_design $top_design
read_sdc $::env(OPENSTA_SDC)
if {[info exists ::env(OPENSTA_TINY_ICS55)] && $::env(OPENSTA_TINY_ICS55) eq "YES"} {
source [file join [file dirname [info script]] tiny_ics55_checks.tcl]
}
report_checks -path_delay min_max -sort_by_slack -slack_max 0.0 -group_path_count 1000 -endpoint_path_count 1000 > $::env(OPENSTA_REPORT)
+3
View File
@@ -26,6 +26,9 @@ endif
else ifeq ($(PDK),ICS55)
OPENSTA_LIBERTY := $(ROOT_PATH)/.cache/retrosoc/pdk/ics55/ics55_h7cr_ss.lib
OPENSTA_LINK_LIBS := $(ROOT_PATH)/physical/pdk/icsprout55-pdk/IP/IO/ICsprout_55LLULP1233_IO_251013/liberty/ICSIOA_N55_3P3_ss_1p08_2p97_125c.lib
ifeq ($(SOC),TINY)
OPENSTA_LINK_LIBS += $(ROOT_PATH)/.cache/retrosoc/sources/ics55_ecos_pll/lib/PLL_TOP_max.lib
endif
ifeq ($(HAVE_SRAM_MACRO),YES)
OPENSTA_SRAM_LIBS := \
$(ROOT_PATH)/.cache/retrosoc/pdk/ics55/sram/ics55_ecos_sram_1024x32_m8/lib/ics55_ecos_sram_1024x32_m8_ss1p08v125ccmax.lib \
@@ -0,0 +1,22 @@
# The Python precondition binds the consumed netlist to its structural audit:
# one disabled PLL, 32 SYS SRAM macros and common CPU/SRAM clock connectivity.
# This script adds observations, not PLL timing arcs or new timing exceptions.
set audit_dir [file dirname $::env(OPENSTA_REPORT)]
set pll [get_cells -hierarchical -filter {ref_name == PLL_TOP}]
set ram [get_cells -hierarchical -filter {ref_name == ics55_ecos_sram_1024x32_m8}]
if {[llength $pll] != 1 || [llength $ram] != 32} { error "Tiny ICS55 macro binding mismatch" }
if {[llength [get_clocks clk_system]] != 1} { error "Tiny SYS clock missing" }
report_clock_properties > $audit_dir/clocks.rpt
check_setup -verbose > $audit_dir/coverage.rpt
report_check_types -recovery -removal -min_pulse_width -min_period -max_slew -max_capacitance -max_fanout -violators -verbose > $audit_dir/constraint-violations.rpt
report_checks -path_group clk_system -path_delay max -group_path_count 20 -fields {capacitance slew fanout} > $audit_dir/sys-setup.rpt
report_checks -path_group clk_system -path_delay min -group_path_count 20 -fields {capacitance slew fanout} > $audit_dir/sys-hold.rpt
set ram_inputs {}
set ram_outputs {}
foreach pin [get_pins -of_objects $ram] {
if {[get_property $pin direction] eq "input"} { lappend ram_inputs $pin }
if {[get_property $pin direction] eq "output"} { lappend ram_outputs $pin }
}
report_checks -to $ram_inputs -path_delay min_max -group_path_count 20 > $audit_dir/sram-input-paths.rpt
report_checks -from $ram_outputs -path_delay min_max -group_path_count 20 > $audit_dir/sram-return-paths.rpt
puts "TINY_ICS55_STA_AUDIT_PASS: SAFE24 only; PLL held off; no PLL timing qualification"
@@ -48,6 +48,9 @@ if {$pdk == "IHP130"} {
set pdk_io_lib "${pdk_dir}/IP/IO/ICsprout_55LLULP1233_IO_251013/liberty"
set tech_cells [list "$pdk_cells_lib/ics55_h7cr_tt.lib"]
set tech_macros [list "$pdk_io_lib/ICSIOA_N55_3P3_tt_1p2_3p3_25c.lib"]
if {$soc == "TINY"} {
lappend tech_macros "$root_dir/.cache/retrosoc/sources/ics55_ecos_pll/lib/PLL_TOP_typ.lib"
}
if {$have_sram_macro} {
lappend tech_macros "$pdk_cells_lib/sram/ics55_ecos_sram_1024x32_m8/lib/ics55_ecos_sram_1024x32_m8_tt1p2v25cctyp.lib"
lappend tech_macros "$pdk_cells_lib/sram/ics55_ecos_sram_4096x32_m8/lib/ics55_ecos_sram_4096x32_m8_tt1p2v25cctyp.lib"
@@ -165,6 +165,9 @@ yosys tee -q -o "${report_dir}/${proj_name}_area_logic.rpt" stat -top $top_desig
# final netlist
yosys write_verilog -noattr -noexpr -nohex -nodec $netlist
if {$soc == "TINY" && $pdk == "ICS55"} {
yosys write_json ${netlist}.json
}
# Record the configuration only after the netlist has been written successfully.
set config_tmp "${config}.tmp"
@@ -176,5 +179,7 @@ puts $config_file "SYNTH_RECIPE=$synth_recipe"
puts $config_file "PERIOD_PS=$period_ps"
puts $config_file "HAVE_SRAM_MACRO=$::env(HAVE_SRAM_MACRO)"
puts $config_file "SRAM_SIZE_KIB=$::env(SRAM_SIZE_KIB)"
if {[info exists ::env(HAVE_PLL)]} { puts $config_file "HAVE_PLL=$::env(HAVE_PLL)" }
if {[info exists ::env(CONFIG_DIGEST)]} { puts $config_file "CONFIG_DIGEST=$::env(CONFIG_DIGEST)" }
close $config_file
file rename -force $config_tmp $config
+5
View File
@@ -67,12 +67,17 @@ $(NETLIST): $(SV_FLIST) $(YOSYS_SCRIPTS)
--log $(YOSYS_BUILD)/$(RTL_NAME).log --result $(YOSYS_BUILD)/result-synth.json \
--env PDK=$(PDK) --env SOC=$(SOC) --env SYNTH_RECIPE=$(SYNTH_RECIPE) \
--env HAVE_SRAM_MACRO=$(HAVE_SRAM_MACRO) --env SRAM_SIZE_KIB=$(SRAM_SIZE_KIB) \
--env HAVE_PLL=$(HAVE_PLL) --env CONFIG_DIGEST=$(CONFIG_DIGEST) \
--env YOSYS_TARGET_PERIOD_PS=$(YOSYS_TARGET_PERIOD_PS) \
--env SV_FLIST=$(SV_FLIST) --env TOP_DESIGN=$(TOP_DESIGN) --env CONFIG=$(NETLIST_CONFIG) \
--env PROJ_NAME=$(RTL_NAME) --env WORK=$(YOSYS_TMP) --env BUILD=$(YOSYS_OUT) \
--env REPORTS=$(YOSYS_RPT) --env NETLIST=$(NETLIST) -- \
timeout --foreground --kill-after=5s $(YOSYS_TIMEOUT)s \
yosys -t --perffile $(YOSYS_BUILD)/yosys-perf.json -c $(YOSYS_DIR)/script/synth.tcl
ifeq ($(SOC)-$(PDK),TINY-ICS55)
python3 $(ROOT_PATH)/scripts/tiny_ics55_platform.py --variant-root $(VARIANT_ROOT) audit-netlist \
--netlist $(NETLIST) --config $(NETLIST_CONFIG) --output $(YOSYS_BUILD)/tiny-ics55-netlist.json
endif
$(NETLIST_DEBUG): $(NETLIST)
@test -f $@
+14 -3
View File
@@ -68,8 +68,11 @@ def facts(root: Path = ROOT) -> dict:
ram = instance_parameters(top, "onchip_ram", "u_sram")
dma = instance_parameters(top, "apb4_dma", "u_dma")
cpu = instance_parameters(top, "mgmt_core_wrapper", "u_cpu")
if ram != {"CapacityKiB": 128, "DataWidth": 32, "IdWidth": 1}:
if ram != {"CapacityKiB": 128, "DataWidth": 32, "IdWidth": 1, "Ics55SmallBanks": 1}:
raise ValueError("Tiny SRAM parameters changed")
# The publication remains the reviewed IHP130 compatibility profile;
# selecting ICS55's 4 KiB geometry does not alter its reported SRAM facts.
ram = {key: value for key, value in ram.items() if key != "Ics55SmallBanks"}
if dma != {"NumChannels": 4, "MaxBurstBeats": 16, "FifoDepth": 32, "RequestMask": 0x7F9, "EnableStreams": 0}:
raise ValueError("Tiny DMA parameters changed")
if cpu != {"ExternalIrqCount": 30, "EnableAtomics": 0, "TwoCycleBusErrors": 1, "ResetSyncStages": 5}:
@@ -82,7 +85,15 @@ def facts(root: Path = ROOT) -> dict:
require_snippets(root, SYSCTRL, ["(apb4.paddr[1:0] != 2'd0)", "(apb4.pstrb != 4'hf)",
"if (!s_test_q[31] && apb4.pwdata[31]) s_test_d = apb4.pwdata & 32'h8000_ff01;",
"if (!s_fault_stat_d[0])", "if (rtc_wake_i) s_wake_d = 1'b1;"])
arch = instance_parameters((root / ARCHINFO).read_text(encoding="utf-8").split(".BUILD_ID")[0] + ") u_archinfo (", "apb4_archinfo", "u_archinfo")
from scripts.generate_tiny import platform_values
platform = platform_values(profile["PDK"], profile["HAVE_PLL"] == "YES")
arch_text = (root / ARCHINFO).read_text(encoding="utf-8")
require_snippets(root, ARCHINFO, ['`include "tiny_platform.svh"',
".FEATURES0(`RETROSOC_TINY__FEATURES0)",
".TECHNOLOGY(`RETROSOC_TINY__TECHNOLOGY)"])
arch_text = arch_text.replace("`RETROSOC_TINY__FEATURES0", f"32'h{platform['features0']:08x}")
arch_text = arch_text.replace("`RETROSOC_TINY__TECHNOLOGY", f"32'h{platform['technology']:08x}")
arch = instance_parameters(arch_text.split(".BUILD_ID")[0] + ") u_archinfo (", "apb4_archinfo", "u_archinfo")
if arch != {"REFERENCE_CLOCK_HZ": 24000000, "SRAM_BYTES": 131072, "TOPOLOGY": 0x20200001,
"FEATURES0": 0x7FFE, "TECHNOLOGY": 0x02010082}:
raise ValueError("Tiny discovery parameters changed")
@@ -227,7 +238,7 @@ def collect(config: dict, *, check_snapshot: bool = True) -> dict:
evidence = read(ROOT / BOOK / "evidence.json")
validate_evidence(evidence)
lock = read(ROOT / "dependencies/dependencies.lock.json")
paths = {MAP, TOPOLOGY, PINS, CLOCKS, TOP, RESET_TREE, SYSCTRL, ARCHINFO, config["profile"],
paths = {MAP, TOPOLOGY, PINS, CLOCKS, TOP, RESET_TREE, SYSCTRL, ARCHINFO, "scripts/generate_tiny.py", config["profile"],
"docs/ip/tiny-soc.md", "docs/ip/tiny-soc-verification.md", "dependencies/dependencies.lock.json", "LICENSE"}
paths.update(contract["sources"])
paths.update(b["file"] for b in contract["bindings"])
+31 -15
View File
@@ -12,10 +12,11 @@
`include "mmap_define.svh"
module onchip_ram #(
parameter bit Present = 1'b1,
parameter int unsigned CapacityKiB = 128,
parameter int unsigned DataWidth = 32,
parameter int unsigned IdWidth = 1
parameter bit Present = 1'b1,
parameter int unsigned CapacityKiB = 128,
parameter int unsigned DataWidth = 32,
parameter int unsigned IdWidth = 1,
parameter bit Ics55SmallBanks = 1'b0
) (
// verilog_format: off -- preserve the AXI, APB, and performance grouping
input logic clk_i,
@@ -27,8 +28,10 @@ module onchip_ram #(
// verilog_format: on
);
`ifdef PDK_ICS55
localparam int unsigned TechnologyBankKiB = 16;
localparam int unsigned TechnologyAddrWidth = 12;
// Mini retains its existing 16 KiB macro geometry. Tiny selects 4 KiB
// physical macros without adding logical services or changing arbitration.
localparam int unsigned TechnologyBankKiB = (Ics55SmallBanks && DataWidth == 32) ? 4 : 16;
localparam int unsigned TechnologyAddrWidth = (Ics55SmallBanks && DataWidth == 32) ? 10 : 12;
`else
localparam int unsigned TechnologyBankKiB = 4;
localparam int unsigned TechnologyAddrWidth = 10;
@@ -382,15 +385,27 @@ module onchip_ram #(
assign s_bank_cs[bank] = (s_mem_read || s_mem_write) &&
(s_mem_word_addr[14:TechnologyAddrWidth] == (15 - TechnologyAddrWidth)'(bank));
`ifdef PDK_ICS55
tc_sram_4096x32 u_ram (
.clk_i (clk_i),
.cs_i (s_bank_cs[bank]),
.addr_i(s_mem_word_addr[11:0]),
.data_i(s_mem_wdata),
.mask_i(s_mem_wstrb),
.wren_i(s_mem_write),
.data_o(s_bank_rdata[bank])
);
if (Ics55SmallBanks) begin : gen_small
tc_sram_1024x32 u_ram (
.clk_i (clk_i),
.cs_i (s_bank_cs[bank]),
.addr_i(s_mem_word_addr[9:0]),
.data_i(s_mem_wdata),
.mask_i(s_mem_wstrb),
.wren_i(s_mem_write),
.data_o(s_bank_rdata[bank])
);
end else begin : gen_large
tc_sram_4096x32 u_ram (
.clk_i (clk_i),
.cs_i (s_bank_cs[bank]),
.addr_i(s_mem_word_addr[11:0]),
.data_i(s_mem_wdata),
.mask_i(s_mem_wstrb),
.wren_i(s_mem_write),
.data_o(s_bank_rdata[bank])
);
end
`else
tc_sram_1024x32 u_ram (
.clk_i (clk_i),
@@ -695,6 +710,7 @@ module onchip_ram #(
`ifndef SYNTHESIS
initial begin
if (((DataWidth != 32) && (DataWidth != 64)) || (IdWidth < 1) ||
(Ics55SmallBanks && (DataWidth != 32)) ||
((DataWidth % 8) != 0) ||
((CapacityKiB != 4) && (CapacityKiB != 16) && (CapacityKiB != 32) &&
(CapacityKiB != 64) && (CapacityKiB != 128))) begin
+1
View File
@@ -4,5 +4,6 @@
/tech/tc_clk.sv
/tech/tc_opipsram_delay.sv
/tech/tc_pll.sv
/tech/tc_pll_ics55_ecos.sv
/tech/tc_sram.sv
/tech/tc_usb2_packet_ram.sv
+8 -6
View File
@@ -520,7 +520,9 @@ module tc_io_schmitt_in_pad (
`endif
endmodule
module tc_io_tri_full_pad (
module tc_io_tri_full_pad #(
parameter bit ReadWhileDriving = 1'b0
) (
`ifdef PDK_IHP130
`ifdef USE_POWER_PINS
inout wire iovdd,
@@ -599,15 +601,15 @@ module tc_io_tri_full_pad (
.C (p2c),
.A (),
.PAD(pad),
.IE (~c2p_en),
.CS (cs), // 1: CMOS 0: SCHMI
.IE (ReadWhileDriving || !c2p_en),
.CS (cs), // 1: CMOS 0: SCHMI
.I (c2p),
.OE (c2p_en),
.OD (1'b0),
.PU (pu), // active high
.PD (pd), // active high
.PU (pu), // active high
.PD (pd), // active high
.DS0(1'b0),
.DS1(1'b1) // 8mA
.DS1(1'b1) // 8mA
);
`endif
+26 -1
View File
@@ -8,7 +8,10 @@
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
module tc_pll (
module tc_pll #(
parameter bit UseIcs55EcosPll = 1'b0,
parameter bit Ics55Parked = 1'b0
) (
input logic fref_i,
input logic rst_n_i,
input logic [2:0] cfg_sel_i,
@@ -117,6 +120,27 @@ module tc_pll (
);
`elsif PDK_ICS55
`ifdef HAVE_PLL
`ifdef RETROSOC_SOC__TINY
if (UseIcs55EcosPll) begin : gen_ecos
tc_pll_ics55_ecos #(
.Parked(Ics55Parked)
) u_backend (
.fref_i (fref_i),
.rst_n_i (rst_n_i),
.cfg_sel_i (cfg_sel_i),
.cfg_apply_i (cfg_apply_i),
.pll_capable_o(pll_capable_o),
.pll_lock_o (pll_lock_o),
.pll_clk_o (pll_clk_o)
);
end else begin : gen_unsupported
assign pll_capable_o = 1'b0;
assign pll_lock_o = 1'b0;
assign pll_clk_o = 1'b0;
end
`else
// Retain the legacy Mini experimental binding and instance path. It is not
// the locked OpenECOS backend and does not qualify that macro.
localparam logic [2:0] ICS55_SUPPORTED_SEL = 3'd0;
localparam logic [7:0] ICS55_N = 8'd2;
localparam logic [1:0] ICS55_OD = 2'd2;
@@ -178,6 +202,7 @@ module tc_pll (
.OD (ICS55_OD),
.N (ICS55_N)
);
`endif
`else
assign pll_capable_o = 1'b0;
assign pll_lock_o = 1'b0;
+167
View File
@@ -0,0 +1,167 @@
// Copyright (c) 2026 Yuchi Miao
// SPDX-License-Identifier: MulanPSL-2.0
// Digital output-clock qualification only; the macro has no analog LOCK pin.
// REFCLK must remain live. System source switching belongs to the Tiny RCU.
module tc_pll_ics55_ecos #(
parameter bit Parked = 1'b0
) (
input logic fref_i,
input logic rst_n_i,
input logic [2:0] cfg_sel_i,
input logic cfg_apply_i,
output logic pll_capable_o,
output logic pll_lock_o,
output logic pll_clk_o
);
logic s_ref_rst_n, s_monitor_rst_n, s_pll_rst_n;
logic s_apply_seen_q, s_apply, s_valid_q, s_enable;
logic [ 2:0] s_selection_q;
logic [ 1:0] s_restart_q;
logic [ 9:0] s_settle_q;
logic [12:0] s_budget_q;
logic [ 6:0] s_window_q;
logic s_discard_q, s_lock_q;
logic [2:0] s_good_q;
logic [15:0] s_count_q, s_count_next, s_gray_q, s_gray_next;
logic [15:0] s_gray_ref, s_count_ref, s_last_q, s_delta;
logic [15:0] s_expected, s_tolerance;
logic [7:0] s_n;
// Ideal functional supply connections, not a physical rail/ground merge.
wire s_vdd = 1'b1;
wire s_vss = 1'b0;
rst_sync #(
.STAGE(5)
) u_ref_reset (
.clk_i (fref_i),
.rst_n_i(rst_n_i),
.rst_n_o(s_ref_rst_n)
);
assign s_apply = cfg_apply_i && !s_apply_seen_q;
assign s_enable = !Parked && rst_n_i && s_ref_rst_n && s_valid_q && (s_restart_q == 0) && !s_apply;
assign s_n = (!Parked && s_selection_q == 3'd7) ? 8'd40 : 8'd32;
assign pll_capable_o = 1'b1;
assign pll_lock_o = s_lock_q && s_enable;
(* keep = 1, dont_touch = "true" *)
PLL_TOP u_PLL_TOP (
.AVDD (s_vdd),
.AVSS (s_vss),
.DVDD (s_vdd),
.DVSS (s_vss),
.DVDD_DRV(s_vdd),
.DVSS_DRV(s_vss),
.EN (s_enable),
.BP (1'b0),
.N (s_n),
.SELECT (1'b0),
.OD (2'd2),
.REFCLK (fref_i),
.CKOUT1 (pll_clk_o),
.CKOUT2 (),
.CKTST () // No extra functional domain or package pin.
);
assign s_monitor_rst_n = s_enable && (s_settle_q == 10'd512);
rst_sync #(
.STAGE(5)
) u_pll_reset (
.clk_i (pll_clk_o),
.rst_n_i(s_monitor_rst_n),
.rst_n_o(s_pll_rst_n)
);
assign s_count_next = s_count_q + 16'd1;
bin2gray #(
.DATA_WIDTH(16)
) u_encode (
.bin_i (s_count_next),
.gray_o(s_gray_next)
);
// Register Gray in the source domain: combinational binary-to-Gray glitches
// must not cross into REFCLK. All bits belong to one Gray-coded counter.
always_ff @(posedge pll_clk_o or negedge s_pll_rst_n) begin
if (!s_pll_rst_n) begin
s_count_q <= '0;
s_gray_q <= '0;
end else begin
s_count_q <= s_count_next;
s_gray_q <= s_gray_next;
end
end
cdc_sync #(
.STAGE (2),
.DATA_WIDTH(16)
) u_count_sync (
.clk_i (fref_i),
.rst_n_i(s_monitor_rst_n),
.dat_i (s_gray_q),
.dat_o (s_gray_ref)
);
gray2bin #(
.DATA_WIDTH(16)
) u_decode (
.gray_i(s_gray_ref),
.bin_o (s_count_ref)
);
assign s_delta = s_count_ref - s_last_q;
assign s_expected = (s_selection_q == 3'd7) ? 16'd1280 : 16'd1024;
assign s_tolerance = (s_selection_q == 3'd7) ? 16'd20 : 16'd16;
always_ff @(posedge fref_i or negedge s_ref_rst_n) begin
if (!s_ref_rst_n) begin
s_apply_seen_q <= 1'b0;
s_selection_q <= '0;
s_valid_q <= 1'b0;
s_restart_q <= '0;
s_settle_q <= '0;
s_budget_q <= '0;
s_window_q <= '0;
s_discard_q <= 1'b1;
s_good_q <= '0;
s_lock_q <= 1'b0;
s_last_q <= '0;
end else begin
s_apply_seen_q <= cfg_apply_i;
if (s_apply) begin
s_selection_q <= cfg_sel_i;
s_valid_q <= (cfg_sel_i == 3'd5) || (cfg_sel_i == 3'd7);
s_restart_q <= 2'd2;
s_settle_q <= '0;
s_budget_q <= '0;
s_window_q <= '0;
s_discard_q <= 1'b1;
s_good_q <= '0;
s_lock_q <= 1'b0;
s_last_q <= '0;
end else if (s_valid_q) begin
if (s_restart_q != 0) begin
s_restart_q <= s_restart_q - 2'd1;
end else begin
if (!s_lock_q) s_budget_q <= s_budget_q + 13'd1;
else s_budget_q <= '0;
if (!s_lock_q && s_budget_q == 13'd4095) begin
s_valid_q <= 1'b0;
s_lock_q <= 1'b0;
end else if (s_settle_q != 10'd512) begin
s_settle_q <= s_settle_q + 10'd1;
end else if (s_window_q == 7'd127) begin
s_window_q <= '0;
s_last_q <= s_count_ref;
s_discard_q <= 1'b0;
if (s_discard_q) begin
s_good_q <= '0;
end else if ((s_delta >= s_expected - s_tolerance) &&
(s_delta <= s_expected + s_tolerance)) begin
if (s_good_q < 3'd4) s_good_q <= s_good_q + 3'd1;
if (s_good_q >= 3'd3) s_lock_q <= 1'b1;
end else begin
s_good_q <= '0;
s_lock_q <= 1'b0;
end
end else begin
s_window_q <= s_window_q + 7'd1;
end
end
end
end
end
endmodule
+5 -3
View File
@@ -31,7 +31,8 @@ $(ARCHINFO_METADATA_STAMP): $(LOCK_FILE) $(wildcard $(ARCHINFO_METADATA_SCRIPT))
$(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))
--filelists $(GENERATED_FL_DIR) --pdk $(PDK) --simulator $(SIMU) --synthesis $(SYNTH) \
$(foreach define,$(DEF_LIST),--define $(define))
@touch $@
$(SOC_IRQ_METADATA): $(FILELIST_STAMP)
@@ -50,7 +51,7 @@ check-clock-reset-domains:
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
PDK_FILELIST := $(GENERATED_FL_DIR)/pdk_selected.fl
TINY_TB_FLIST := -f $(GENERATED_FL_DIR)/tb.fl
TOP_DESIGN := retrosoc_tiny_asic
RTL_NAME := retrosoc_tiny_asic
@@ -62,6 +63,7 @@ TINY_DEPFILE := $(SIM_BUILD_ROOT)/sources.d
TINY_LINT_LOG := $(VARIANT_ROOT)/lint/verilator/lint.log
SOC_SIM_TIME ?= 1800
TINY_SIM_TIMEOUT ?= 20000000
TINY_SIM_ARGS ?=
VERILATOR ?= verilator
IVERILOG ?= iverilog
VVP ?= vvp
@@ -110,7 +112,7 @@ check-rtl-lint: rtl-lint
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)
$(TINY_RUN) +firmware=$(SW_BUILD_DIR)/$(SIM_FIRMWARE_NAME).hex +max_cycles=$(TINY_SIM_TIMEOUT) $(TINY_SIM_ARGS)
$(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)'
+6 -5
View File
@@ -2,11 +2,12 @@
Tiny is the independent single-Hazard3 wired MCU product. Its frozen target
contract is [Tiny Gen1 QFN64 R2](../../docs/ip/tiny-soc.md). The 2026-10-07
specification default is ICS55, `HAVE_PLL=YES`, SAFE24 boot; TINY-ICS55-P1
platform integration and `configs/ci/ics55-tiny.mk` are pending. IHP130 remains
explicit compatibility. For the current executable baseline, use
`make CONFIG=configs/ci/ihp130-tiny.mk setup` followed by
`make CONFIG=configs/ci/ihp130-tiny.mk firmware sim`.
default is ICS55, `HAVE_PLL=YES`, SAFE24 boot, with the PLL macro parked off.
Use `make CONFIG=configs/ci/ics55-tiny.mk setup` followed by
`make CONFIG=configs/ci/ics55-tiny.mk firmware sim`. IHP130/no PLL remains
explicit compatibility through `configs/ci/ihp130-tiny.mk`. See the
[platform runbook](../../docs/ip/tiny-ics55-platform.md) for source-bound
validation; backend frequency tests are not SYS switching or timing qualification.
`top` owns product integration, `address_map`, `pin_map` and `integration` own
canonical address, pad, IRQ and clock/reset inputs, `filelist` selects sources,
+8 -4
View File
@@ -10,9 +10,12 @@ module retrosoc_tiny_tb;
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;
logic s_jtag_tck_driver = 1'b0;
logic s_jtag_tms_driver = 1'b1;
logic s_jtag_tdi_driver = 1'b0;
wire s_jtag_tck = s_jtag_tck_driver;
wire s_jtag_tms = s_jtag_tms_driver;
wire s_jtag_tdi = s_jtag_tdi_driver;
wire s_jtag_trst_n = s_rst_n;
wire s_jtag_tdo;
tri1 [31:0] s_gpio;
@@ -23,6 +26,7 @@ module retrosoc_tiny_tb;
integer s_max_cycles = 20000000;
integer s_done_cycles = 0;
logic [ 7:0] s_uart_byte;
`include "tiny_jtag_smoke.svh"
`ifdef RETROSOC_SOC__TINY_NETLIST
wire [7:0] s_test_code = {
@@ -51,7 +55,7 @@ module retrosoc_tiny_tb;
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
if (s_done_cycles >= 128 && s_jtag_checks_done) begin
$display("SIM_TEST_PASS Tiny cycles=%0d", s_cycles);
$finish;
end
+98
View File
@@ -0,0 +1,98 @@
// Pin-level JTAG/DTM/DMI acceptance, opt-in and independent of UART output.
logic s_jtag_checks_done = 1'b0;
task automatic jtag_tick(input bit tms, input bit tdi, output logic tdo);
s_jtag_tms_driver = tms;
s_jtag_tdi_driver = tdi;
#50;
tdo = s_jtag_tdo;
s_jtag_tck_driver = 1'b1;
#50;
s_jtag_tck_driver = 1'b0;
endtask
task automatic jtag_idle(input integer count);
bit ignored;
repeat (count) jtag_tick(0, 0, ignored);
endtask
task automatic jtag_ir(input logic [4:0] instruction);
bit ignored;
jtag_tick(1, 0, ignored);
jtag_tick(1, 0, ignored);
jtag_tick(0, 0, ignored);
jtag_tick(0, 0, ignored);
for (int i = 0; i < 5; i++) jtag_tick(i == 4, instruction[i], ignored);
jtag_tick(1, 0, ignored);
jtag_tick(0, 0, ignored);
endtask
task automatic jtag_dr(input integer width, input logic [40:0] data, output logic [40:0] result);
logic sampled;
result = '0;
jtag_tick(1, 0, sampled);
jtag_tick(0, 0, sampled);
jtag_tick(0, 0, sampled);
for (int i = 0; i < width; i++) begin
jtag_tick(i == width - 1, data[i], sampled);
if (sampled !== 1'b0 && sampled !== 1'b1) $fatal(1, "SIM_TEST_FAIL unknown shifted JTAG TDO");
result[i] = sampled;
end
jtag_tick(1, 0, sampled);
jtag_tick(0, 0, sampled);
endtask
task automatic jtag_dmi(input bit write_access, input logic [6:0] address, input logic [31:0] data,
output logic [31:0] result);
logic [40:0] response;
jtag_dr(41, {address, data, (write_access ? 2'd2 : 2'd1)}, response);
jtag_idle(16);
jtag_dr(41, 41'd0, response);
if (response[1:0] != 0) $fatal(1, "SIM_TEST_FAIL DMI status %0d", response[1:0]);
result = response[33:2];
endtask
initial begin : jtag_smoke
bit ignored;
logic [40:0] response;
logic [31:0] status;
integer attempts;
if ($test$plusargs("tiny_jtag_smoke")) begin
// Exercise reset with a live TCK before releasing the external reset.
// Four-state models must observe an asserted reset, not X -> 1.
repeat (6) jtag_tick(1, 0, ignored);
wait (s_rst_n);
// Five-edge JTAG reset release plus TAP reset; SYS remains independent.
repeat (12) jtag_tick(1, 0, ignored);
jtag_idle(1);
jtag_ir(5'h01);
jtag_dr(32, 41'd0, response);
if (response[31:0] != 32'(`SOC_JTAG_IDCODE))
$fatal(1, "SIM_TEST_FAIL JTAG IDCODE %h", response[31:0]);
jtag_ir(5'h10);
jtag_dr(32, 41'd0, response);
if (response[3:0] != 1 || response[9:4] != 7)
$fatal(1, "SIM_TEST_FAIL DTMCS %h", response[31:0]);
jtag_ir(5'h11);
jtag_dmi(1, 7'h10, 32'h00000001, status);
jtag_dmi(1, 7'h10, 32'h80000001, status);
attempts = 0;
status = 0;
while (!status[9] && attempts < 256) begin
jtag_dmi(0, 7'h11, 32'd0, status);
attempts++;
end
if (!status[9]) $fatal(1, "SIM_TEST_FAIL JTAG halt timeout");
jtag_dmi(1, 7'h10, 32'h40000001, status);
attempts = 0;
status = 0;
while (!status[11] && attempts < 256) begin
jtag_dmi(0, 7'h11, 32'd0, status);
attempts++;
end
if (!status[11]) $fatal(1, "SIM_TEST_FAIL JTAG resume timeout");
jtag_dmi(1, 7'h10, 32'h00000001, status);
$display("SIM_TEST_PASS Tiny JTAG transport halt/resume");
end
s_jtag_checks_done = 1'b1;
end
+2
View File
@@ -1,3 +1,5 @@
/tech/tc_io.sv
/tech/tc_clk.sv
/tech/tc_pll.sv
/tech/tc_pll_ics55_ecos.sv
/tech/tc_sram.sv
@@ -1,5 +1,13 @@
{
"schema_version": 2,
"inactive_macros": {
"ics55_pll": {
"cell": "PLL_TOP",
"scope": "TINY-ICS55-P1: present, EN=0, no SYS connection",
"active_clock_domain": false,
"qualification": "no characterized timing arcs; full RCU remains R2-P7"
}
},
"reset_ports": [
"ext_rst_n_i_pad",
"jtag_trst_n_i_pad"
+15 -4
View File
@@ -1,15 +1,26 @@
# Tiny's first qualified integration is deliberately a fixed MCU profile.
# Both Tiny integrations remain SAFE24; PLL presence is not SYS selection.
ifneq ($(MINI_MODE),NONE)
$(error SOC=TINY requires MINI_MODE=NONE)
endif
ifneq ($(PDK),IHP130)
$(error SOC=TINY currently supports only PDK=IHP130)
ifeq ($(filter $(PDK),IHP130 ICS55),)
$(error SOC=TINY supports only PDK=IHP130 or ICS55)
endif
ifneq ($(HAVE_HP),NO)
$(error SOC=TINY requires HAVE_HP=NO)
endif
ifeq ($(PDK),ICS55)
ifneq ($(HAVE_PLL),YES)
$(error SOC=TINY PDK=ICS55 requires HAVE_PLL=YES with the PLL held off)
endif
ifeq ($(PDK_BEHAV),YES)
$(error SOC=TINY PDK=ICS55 requires the locked technology models, PDK_BEHAV=NO)
endif
override TINY_SAFE24_PLL_OFF := YES
else
ifneq ($(HAVE_PLL),NO)
$(error SOC=TINY requires HAVE_PLL=NO)
$(error SOC=TINY PDK=IHP130 requires HAVE_PLL=NO)
endif
override TINY_SAFE24_PLL_OFF := NO
endif
ifneq ($(HAVE_SRAM_IF),YES)
$(error SOC=TINY requires HAVE_SRAM_IF=YES)
+4 -3
View File
@@ -168,9 +168,10 @@ uart_if u_uart0_if ();
.dma_wait_o (s_dma_wait)
);
onchip_ram #(
.CapacityKiB(128),
.DataWidth (32),
.IdWidth (1)
.CapacityKiB (128),
.DataWidth (32),
.IdWidth (1),
.Ics55SmallBanks(1'b1)
) u_sram (
.clk_i (clk_i),
.rst_n_i (s_sram_rst_n),
+16
View File
@@ -14,6 +14,22 @@ module retrosoc_tiny_asic (
/* verilator lint_on UNUSEDSIGNAL */
gpio_if u_gpio_if ();
xpi_if u_xpi_if ();
`ifdef HAVE_PLL
// P1 preserves the macro but supplies no request or reset release. SYS is
// always the external SAFE24 path; RCU source switching belongs to R2-P7.
tc_pll #(
.UseIcs55EcosPll(1'b1),
.Ics55Parked (1'b1)
) u_pll (
.fref_i (s_ext_clk),
.rst_n_i (1'b0),
.cfg_sel_i (3'd5),
.cfg_apply_i (1'b0),
.pll_capable_o(),
.pll_lock_o (),
.pll_clk_o ()
);
`endif
`include "retrosoc_asic_pad_bindings.svh"
tc_clk_buf u_clock_buffer (
.clk_i(s_ext_clk),
+3 -2
View File
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: MulanPSL-2.0
`include "archinfo_integration_metadata.svh"
`include "archinfo_define.svh"
`include "tiny_platform.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.
@@ -29,8 +30,8 @@ module tiny_archinfo (
.REFERENCE_CLOCK_HZ(24_000_000),
.SRAM_BYTES (131072),
.TOPOLOGY (32'h2020_0001),
.FEATURES0 (32'h0000_7ffe),
.TECHNOLOGY (32'h0201_0082),
.FEATURES0 (`RETROSOC_TINY__FEATURES0),
.TECHNOLOGY (`RETROSOC_TINY__TECHNOLOGY),
.BUILD_ID (`ARCHINFO_INTEGRATION_BUILD_ID),
.CONFIG_ID (`ARCHINFO_INTEGRATION_CONFIG_ID),
.BUILD_STATUS (`ARCHINFO_INTEGRATION_BUILD_STATUS)
+28
View File
@@ -25,6 +25,7 @@ def parse_args() -> argparse.Namespace:
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("--have-pll", choices=("YES", "NO"), default="NO")
parser.add_argument("--formal", choices=("YES", "NO"), default="NO")
parser.add_argument("--require-debug-tools", action="store_true")
parser.add_argument("--lock", type=Path, default=DEFAULT_LOCK)
@@ -85,7 +86,23 @@ def main() -> int:
}
if args.pdk in pdk_names:
source_names.append(pdk_names[args.pdk])
if args.pdk == "ICS55" and args.soc == "TINY" and args.have_pll == "YES":
source_names.append("pdk_ics55_pll")
paths = {name: root / lock["sources"][name]["destination"] for name in source_names}
if args.pdk == "ICS55":
for corner in ("tt", "ss"):
paths[f"ics55_cells_{corner}"] = root / f".cache/retrosoc/pdk/ics55/ics55_h7cr_{corner}.lib"
if args.have_sram_macro == "YES":
for size in ("1024", "4096"):
name = f"ics55_ecos_sram_{size}x32_m8"
for view in (f"verilog/{name}_core.v", f"verilog/{name}_stub.v",
f"lib/{name}_tt1p2v25cctyp.lib", f"lib/{name}_ss1p08v125ccmax.lib"):
paths[f"{name}/{view}"] = root / ".cache/retrosoc/pdk/ics55/sram" / name / view
if args.soc == "TINY" and args.have_pll == "YES":
from physical.pdk.setup import PLL_VIEWS
pll = root / lock["sources"]["pdk_ics55_pll"]["destination"]
for view in PLL_VIEWS:
paths[f"pll/{view}"] = pll / view
if args.pdk == "SKY130" and args.have_sram_macro == "YES":
paths["sky130_openram_sram"] = root / ".cache/retrosoc/pdk/sky130/openram"
tool_results = {
@@ -97,6 +114,17 @@ def main() -> int:
}
failures = sum(value["path"] is None for value in tool_results.values())
failures += sum(not value["exists"] for value in path_results.values())
if "pdk_ics55_pll" in paths and paths["pdk_ics55_pll"].exists():
try:
revision = subprocess.check_output(["git", "-C", str(paths["pdk_ics55_pll"]),
"rev-parse", "HEAD"], text=True).strip()
dirty = subprocess.check_output(["git", "-C", str(paths["pdk_ics55_pll"]),
"status", "--porcelain"], text=True).strip()
matching = revision == lock["sources"]["pdk_ics55_pll"]["revision"] and not dirty
except subprocess.CalledProcessError:
matching = False
path_results["pdk_ics55_pll"]["locked_clean_checkout"] = bool(matching)
failures += int(not matching)
report = {
"schema_version": 1,
"ok": failures == 0,
+44 -9
View File
@@ -53,7 +53,17 @@ def read_topology(path: Path, memory_map: Path) -> dict:
return document
def generate_bindings(document: dict, output: Path) -> None:
def platform_values(pdk: str, have_pll: bool = False) -> dict[str, int]:
if (pdk, have_pll) not in (("IHP130", False), ("ICS55", True)):
raise ValueError("unsupported Tiny PDK/PLL combination")
return {"technology": 0x02040037 if pdk == "ICS55" else 0x02010082,
"features0": 0x7FFE | int(have_pll), "pll_present": int(have_pll),
"gpio_pulls": int(pdk == "ICS55")}
def generate_bindings(document: dict, output: Path, pdk: str = "IHP130",
have_pll: bool = False) -> None:
platform = platform_values(pdk, have_pll)
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",
@@ -80,7 +90,13 @@ def generate_bindings(document: dict, output: Path) -> None:
"#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")
"#define RS_SOC_DMA_CHANNELS 4U\n#define RS_SOC_ID 0x54494E59U\n"
f"#define RS_SOC_TECHNOLOGY_ID 0x{platform['technology']:08X}U\n"
f"#define RS_SOC_PLL_PRESENT {platform['pll_present']}U\n"
f"#define RS_SOC_GPIO_HAS_PULLS {platform['gpio_pulls']}U\n#endif\n")
atomic_write(output / "rtl/tiny_platform.svh", comment +
f"`define RETROSOC_TINY__TECHNOLOGY 32'h{platform['technology']:08x}\n"
f"`define RETROSOC_TINY__FEATURES0 32'h{platform['features0']:08x}\n")
gpio = [comment]
for function in document["gpio_alt_functions"]:
pin = function["pin"]
@@ -119,18 +135,33 @@ def generate_bindings(document: dict, output: Path) -> None:
atomic_write(output / "rtl/tiny_apb_wiring.svh", "\n".join(wiring) + "\n")
def generate_filelists(output: Path, generated: Path, defines: list[str]) -> None:
def generate_filelists(output: Path, generated: Path, defines: list[str],
pdk: str = "IHP130", simulator: str = "VERILATOR",
synthesis: str = "NONE") -> 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"}
"hazard3": ROOT / "rtl/managed/hazard3/hdl", "physical": ROOT / "physical",
"cache": ROOT / ".cache"}
suffix = "_yosys" if synthesis == "YOSYS" else "_verilator" if simulator == "VERILATOR" else ""
pdk_name = "pdk_ihp130.fl" if pdk == "IHP130" else f"pdk_ics55{suffix}.fl"
templates = [*sorted((TINY / "filelist").glob("*.fl")),
ROOT / "rtl/filelist/pdk_ihp130.fl"]
ROOT / "rtl/filelist" / pdk_name]
for template in templates:
content = template.read_text()
for prefix, directory in prefixes.items():
content = re.sub(rf"(?<![^\s+])/{prefix}(?=/|\s|$)", str(directory), content)
if template.name == pdk_name and pdk == "ICS55":
# The shared tc_sram file also declares an unused 16 KiB wrapper.
# Keep its official interface visible to parsers; Tiny must never
# instantiate it (the mapped-netlist audit rejects that case).
content = content.replace("ics55_ecos_sram_4096x32_m8_core.v",
"ics55_ecos_sram_4096x32_m8_stub.v")
atomic_write(output / template.name, content)
include = [generated / "rtl", generated / "memory_map/rtl", generated / "pin_map/rtl",
if template.name == pdk_name:
if pdk == "ICS55" and synthesis != "YOSYS":
content += str(ROOT / ".cache/retrosoc/sources/ics55_ecos_pll/verilog/PLL_TOP.behavioral.v") + "\n"
atomic_write(output / "pdk_selected.fl", content)
include = [TINY / "dv", generated / "rtl", generated / "memory_map/rtl", generated / "pin_map/rtl",
generated / "archinfo"]
atomic_write(output / "def.fl", " ".join([*defines, *(f"+incdir+{p}" for p in include)]) + "\n")
atomic_write(output / "pdk_local.fl", "")
@@ -142,6 +173,9 @@ def main() -> int:
parser.add_argument("--filelists", type=Path)
parser.add_argument("--define", action="append", default=[])
parser.add_argument("--check", action="store_true")
parser.add_argument("--pdk", choices=("IHP130", "ICS55"), default="IHP130")
parser.add_argument("--simulator", choices=("IVERILOG", "VERILATOR"), default="VERILATOR")
parser.add_argument("--synthesis", choices=("NONE", "YOSYS"), default="NONE")
args = parser.parse_args()
document = read_topology(TINY / "integration/soc_topology.json",
TINY / "address_map/memory_map.json")
@@ -153,9 +187,10 @@ def main() -> int:
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)
generate_pin_map(TINY / "pin_map/pin_map.json", output / "pin_map", read_while_driving=True)
generate_bindings(document, output, args.pdk, "+define+HAVE_PLL" in args.define)
generate_filelists(args.filelists.resolve(), output, args.define,
args.pdk, args.simulator, args.synthesis)
return 0
+26 -10
View File
@@ -79,6 +79,14 @@ TINY_PR_COMMANDS = (
("configs/ci/ihp130-tiny.mk", ("APP=ci_smoke", "STA=OPENSTA", "sta")),
)
RTL_COMMANDS = (*PR_COMMANDS[:6], *TINY_PR_COMMANDS[:3])
TINY_PROFILES = {"IHP130": "configs/ci/ihp130-tiny.mk", "ICS55": "configs/ci/ics55-tiny.mk"}
def tiny_commands(pdk: str) -> tuple:
return tuple((TINY_PROFILES[pdk], (*values, "TINY_SIM_ARGS=+tiny_jtag_smoke")
if "sim" in values else values) for _, values in TINY_PR_COMMANDS)
PR_COMMANDS = (*PR_COMMANDS, *TINY_PR_COMMANDS)
SMOKE_COMMANDS = (
("configs/ci/ihp130.mk", RTL_LINT_VALUES),
@@ -179,6 +187,8 @@ def select_regression(
if pdk == "IHP130":
return PR_COMMANDS, PR_PROFILES
profile = PDK_PR_PROFILES[pdk]
if pdk == "ICS55":
return (*pdk_pr_commands(profile), *tiny_commands(pdk)), (profile, TINY_PROFILES[pdk])
return pdk_pr_commands(profile), (profile,)
if suite == "pr":
return PR_COMMANDS, PR_PROFILES
@@ -282,18 +292,24 @@ def main() -> int:
)
args = parser.parse_args()
try:
commands, profiles = select_regression(args.suite, args.pdk)
if args.soc == "TINY":
pdk = args.pdk or "ICS55"
if pdk not in TINY_PROFILES or args.suite == "nightly-extra":
raise ValueError("Tiny supports IHP130/ICS55 smoke, rtl, pr and nightly suites")
commands = tiny_commands(pdk)
if args.suite == "smoke":
commands = commands[:2]
elif args.suite == "rtl":
commands = commands[:3]
profiles = (TINY_PROFILES[pdk],)
else:
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 args.soc == "MINI":
commands = tuple((profile, values) for profile, values in commands
if profile not in TINY_PROFILES.values())
profiles = tuple(profile for profile in profiles if profile not in TINY_PROFILES.values())
if not commands:
parser.error(f"suite {args.suite} has no {args.soc} configuration for this PDK")
if args.netsim_boot_only:
+7 -6
View File
@@ -271,7 +271,7 @@ def render_core_ports(pads: list[Pad]) -> str:
return "\n".join(lines) + "\n"
def render_pad_instance(pad: Pad) -> str | None:
def render_pad_instance(pad: Pad, read_while_driving: bool = False) -> str | None:
instance = f"u_{pad.name}"
if pad.kind == "tri_input":
return (
@@ -297,8 +297,9 @@ def render_pad_instance(pad: Pad) -> str | None:
f".c2p_en({pad.output_enable_signal}), .p2c({pad.input_signal}));"
)
if pad.kind == "full":
parameters = " #(.ReadWhileDriving(1'b1))" if read_while_driving else ""
return (
f" tc_io_tri_full_pad {instance} ({POWER_CONNECTIONS}.pad({pad.name}), "
f" tc_io_tri_full_pad{parameters} {instance} ({POWER_CONNECTIONS}.pad({pad.name}), "
f".c2p({pad.interface}.do_o[{pad.index}]), "
f".c2p_en({pad.interface}.oe_o[{pad.index}]), .p2c({pad.interface}.di_i[{pad.index}]), "
f".cs({pad.interface}.cs_o[{pad.index}]), .pu({pad.interface}.pu_o[{pad.index}]), "
@@ -325,7 +326,7 @@ def render_pad_instance(pad: Pad) -> str | None:
def render_pad_bindings(
pads: list[Pad], power_counts: dict[str, int] | None = None
pads: list[Pad], power_counts: dict[str, int] | None = None, read_while_driving: bool = False
) -> str:
counts = IHP130_POWER_PAD_COUNTS if power_counts is None else power_counts
lines = [
@@ -358,7 +359,7 @@ def render_pad_bindings(
]
)
for pad in pads:
line = render_pad_instance(pad)
line = render_pad_instance(pad, read_while_driving)
if line is not None:
lines.extend(wrap_feature(pad, line))
lines.append("`undef RETROSOC_PAD_POWER_CONNECTIONS")
@@ -461,13 +462,13 @@ def render_profile_bindings(pads: list[Pad], bindings: dict[str, str | None]) ->
return "\n".join(lines) + "\n"
def generate(map_path: Path, output_dir: Path) -> None:
def generate(map_path: Path, output_dir: Path, *, read_while_driving: bool = False) -> None:
pads, profiles = read_map(map_path)
power_counts = read_power_pad_counts(map_path)
rtl_dir = output_dir / "rtl"
atomic_write(rtl_dir / "retrosoc_asic_ports.svh", render_ports(pads))
atomic_write(
rtl_dir / "retrosoc_asic_pad_bindings.svh", render_pad_bindings(pads, power_counts)
rtl_dir / "retrosoc_asic_pad_bindings.svh", render_pad_bindings(pads, power_counts, read_while_driving)
)
atomic_write(rtl_dir / "retrosoc_core_ports.svh", render_core_ports(pads))
atomic_write(rtl_dir / "retrosoc_core_bindings.svh", render_core_bindings(pads))
+5 -1
View File
@@ -4,6 +4,7 @@
from __future__ import annotations
import argparse
import subprocess
import sys
from pathlib import Path
@@ -21,13 +22,16 @@ TINY_SOURCES = (
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--update", action="store_true")
parser.add_argument("--pdk", choices=("IHP130", "ICS55"), default="ICS55")
args = parser.parse_args()
for name in TINY_SOURCES:
for name in (name for name in TINY_SOURCES if not name.startswith("pdk_")):
dependency = source(name)
ensure_git_repo(
dependency["url"], ROOT / dependency["destination"], dependency["revision"],
recursive=dependency.get("recursive", False), update=args.update,
)
subprocess.run([sys.executable, str(ROOT / "physical/pdk/setup.py"), "--pdk", args.pdk,
*(["--update"] if args.update else [])], check=True)
return 0
+275
View File
@@ -0,0 +1,275 @@
#!/usr/bin/env python3
"""Source-bound Tiny ICS55 evidence and a fail-closed SAFE24 netlist audit."""
from __future__ import annotations
import argparse
from datetime import datetime, timezone
import hashlib
from pathlib import Path
import re
import sys
import tarfile
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT))
from scripts import tiny_r2_baseline as b # noqa: E402
from scripts.config_key import variant_id # noqa: E402
from scripts.tiny_r2_feasibility import timing_metrics, path_slack # noqa: E402
PHASE = "TINY-ICS55-P1"
RAM = "ics55_ecos_sram_1024x32_m8"
def verify_locked_views(root: Path) -> dict:
"""Check derived cache bytes against the unchanged locked archives."""
lock = b.read_json(root / "dependencies/dependencies.lock.json")
verified = {}
for key in ("pdk_ics55_h7cr_liberty", "pdk_ics55_sram_1024x32_m8", "pdk_ics55_sram_4096x32_m8"):
spec = lock["archives"][key]
path = root / spec["destination"]
if b.sha256(path) != spec["sha256"]:
raise ValueError(f"locked archive changed: {key}")
package = key.replace("pdk_ics55_sram_", "ics55_ecos_sram_")
seen = set()
with tarfile.open(path) as archive:
for member in archive:
if not member.isfile():
continue
relative = Path(member.name)
if relative.is_absolute() or ".." in relative.parts:
raise ValueError("unsafe archive view name")
target = None
if key == "pdk_ics55_h7cr_liberty":
for corner, tokens in (("tt", ("tt", "1p2", "25")), ("ss", ("ss", "1p08", "125"))):
if member.name.endswith(".lib") and all(t in member.name.lower() for t in tokens):
target = root / f".cache/retrosoc/pdk/ics55/ics55_h7cr_{corner}.lib"
else:
if relative.parts[0] == package:
relative = Path(*relative.parts[1:])
if relative.parts and relative.parts[0] in ("lib", "verilog"):
target = root / ".cache/retrosoc/pdk/ics55/sram" / package / relative
if target is not None:
if not target.is_file() or hashlib.sha256(archive.extractfile(member).read()).hexdigest() != b.sha256(target):
raise ValueError(f"cached view differs from locked archive: {target}")
seen.add(str(target))
if len(seen) != (2 if key == "pdk_ics55_h7cr_liberty" else 11):
raise ValueError(f"incomplete locked view set: {key}")
verified[key] = {"archive": b.artifact(path), "views": sorted(seen)}
from physical.pdk.prepare_ics55_sim_model import patch_muxi2_models, VERILATOR_LINT_OFF, VERILATOR_LINT_ON
pdk = root / lock["sources"]["pdk_ics55"]["destination"]
original = pdk / "IP/STD_cell/ics55_LLSC_H7C_V1p10C100/ics55_LLSC_H7CR/verilog/ics55_LLSC_H7CR.v"
patched, _ = patch_muxi2_models(original.read_text())
expected = VERILATOR_LINT_OFF + patched.rstrip() + "\n" + VERILATOR_LINT_ON
model = root / ".cache/retrosoc/pdk/ics55/ics55_h7cr_functional.v"
if model.read_text() != expected:
raise ValueError("standard-cell functional model differs from the owned locked-source transform")
verified["functional_cells"] = b.artifact(model)
return verified
def inspect_netlist(document: dict) -> dict:
modules = document["modules"]
top = "retrosoc_tiny_asic"
leaves = []
aliases = {}
def canonical(bit):
if bit in aliases:
aliases[bit] = canonical(aliases[bit])
return aliases[bit]
return bit
def alias(a, other):
a, other = canonical(a), canonical(other)
if a == other:
return
if a in ("0", "1", "x", "z"):
a, other = other, a
if a in ("0", "1", "x", "z"):
raise ValueError("conflicting constants at a hierarchy boundary")
aliases[a] = other
def visit(module_name, scope, bound):
module = modules[module_name]
mapping = {}
for port, bits in bound.items():
declaration = module["ports"][port]
for local, external in zip(declaration["bits"], bits):
if isinstance(local, str):
if declaration["direction"] == "output":
alias(external, local)
elif local in mapping:
alias(external, mapping[local])
else:
mapping[local] = external
def net(bit):
return bit if isinstance(bit, str) else mapping.get(bit, f"{scope}:{bit}")
for name, cell in module.get("cells", {}).items():
ports = {port: [net(bit) for bit in bits] for port, bits in cell["connections"].items()}
kind = cell["type"]
path = f"{scope}/{name}"
attrs = modules.get(kind, {}).get("attributes", {})
if kind in modules and not int(str(attrs.get("blackbox", "0")), 2):
visit(kind, path, ports)
else:
leaves.append({"path": path, "type": kind, "ports": ports})
visit(top, top, {})
for c in leaves:
for port, bits in c["ports"].items():
c["ports"][port] = [canonical(bit) for bit in bits]
clock = modules[top]["cells"]["u_clock_buffer"]["connections"]["clk_o"]
if len(clock) != 1 or not isinstance(clock[0], int):
raise ValueError("missing live SYS clock")
system = canonical(f"{top}:{clock[0]}")
if system in ("0", "1", "x", "z"):
raise ValueError("SYS is not a live clock net")
ram = [c for c in leaves if c["type"] == RAM]
pll = [c for c in leaves if c["type"] == "PLL_TOP"]
if any(c["type"] == "ics55_ecos_sram_4096x32_m8" for c in leaves):
raise ValueError("Tiny instantiated the unsupported 16 KiB SRAM interface stub")
if len(ram) != 32 or len(pll) != 1:
raise ValueError("expected 32 main SRAM macros and one PLL")
if any(c["ports"].get("CLK") != [system] for c in ram):
raise ValueError("main SRAM does not share direct SYS")
constants = {"0": 0, "1": 1}
for c in leaves:
if c["type"] in ("TIELOH7R", "TIEHIH7R"):
for bit in c["ports"]["Z"]:
constants[bit] = int(c["type"] == "TIEHIH7R")
enable = pll[0]["ports"].get("EN", [])
if len(enable) != 1 or constants.get(enable[0]) != 0:
raise ValueError("PLL enable is not proven constant zero")
cpu = [c for c in leaves if "u_hazard3_cpu_1port" in c["path"]
and ("CK" in c["ports"] or "CKN" in c["ports"])]
if not cpu or any(c["ports"].get("CK", c["ports"].get("CKN")) != [system] for c in cpu):
raise ValueError("CPU sequential clocks do not share direct SYS")
pll_outputs = {bit for name in ("CKOUT1", "CKOUT2", "CKTST") for bit in pll[0]["ports"].get(name, [])}
if system in pll_outputs:
raise ValueError("PLL drives SYS")
return {"system_net": system, "cpu_clocked_cells": len(cpu),
"sram": [{"path": c["path"], "clock": c["ports"]["CLK"]} for c in ram],
"pll": pll[0], "pll_enable": 0, "qualification": "SAFE24 digital connectivity only"}
def check_configuration(variant: Path, path: Path) -> None:
manifest = b.configuration(variant)
if manifest["configuration"]["PDK"] != "ICS55":
raise ValueError("ICS55 platform evidence cannot consume another PDK")
config = dict(line.split("=", 1) for line in path.read_text().splitlines() if "=" in line)
for key, value in {"PDK": "ICS55", "SOC": "TINY", "HAVE_PLL": "YES",
"HAVE_SRAM_MACRO": "YES", "SRAM_SIZE_KIB": "128",
"TOP_DESIGN": "retrosoc_tiny_asic", "PERIOD_PS": "41667"}.items():
if config.get(key) != value:
raise ValueError(f"consumed netlist configuration mismatch: {key}")
values = [f"{k}={v}" for k, v in manifest["configuration"].items()
if k not in ("SIMU", "SYNTH", "SYNTH_RECIPE", "STA")]
expected = variant_id(manifest["profile"], values, ROOT / "dependencies/dependencies.lock.json",
"2000-01-01-00-00").rsplit("-", 1)[1]
if config.get("CONFIG_DIGEST") != expected or variant.name.rsplit("-", 1)[1] != expected:
raise ValueError("consumed configuration digest mismatch")
def verify_netlist(variant: Path, netlist: Path, config: Path, output: Path) -> dict:
check_configuration(variant, config)
report = b.read_json(output)
if report.get("status") != "passed" or report.get("phase") != PHASE:
raise ValueError("missing successful structural audit")
if report.get("audit_script") != b.artifact(Path(__file__)):
raise ValueError("structural audit was produced by another script")
expected = [netlist, Path(str(netlist) + ".json"), config]
if report.get("inputs") != [b.artifact(path) for path in expected]:
raise ValueError("missing or changed consumed netlist artifacts")
return report
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--variant-root", type=Path, required=True)
parser.add_argument("action", choices=("capture", "audit-netlist", "verify-netlist", "report"))
parser.add_argument("--netlist", type=Path)
parser.add_argument("--config", type=Path)
parser.add_argument("--output", type=Path)
args = parser.parse_args()
variant = args.variant_root.resolve()
parent = variant / "meta/tiny-ics55-p1"
try:
if args.action == "capture":
directory = b.new_attempt(parent, "inputs-")
b.dump(parent / "latest-inputs.json", {"path": str(directory / "identity.json")})
b.dump(directory / "identity.json", {"phase": PHASE, "status": "capturing"})
inputs = b.source_inputs(ROOT, "ICS55")
for path in (ROOT / "tests").rglob("*"):
if path.is_file() and path.suffix in (".py", ".sv", ".svh"):
inputs[str(path.relative_to(ROOT))] = b.sha256(path)
views = verify_locked_views(ROOT)
identity = {"phase": PHASE, "status": "captured", "created_at": datetime.now(timezone.utc).isoformat(), "views": views,
"source": b.snapshot(ROOT, directory, inputs), "tools": b.tools_identity(ROOT)}
if any(b.sha256(ROOT / name) != digest for name, digest in inputs.items()):
raise ValueError("source changed while capturing inputs")
b.dump(directory / "identity.json", identity)
b.dump(parent / "latest-inputs.json", {"path": str(directory / "identity.json")})
elif args.action in ("audit-netlist", "verify-netlist"):
if not all((args.netlist, args.config, args.output)):
parser.error("netlist actions require --netlist, --config and --output")
check_configuration(variant, args.config)
if args.action == "audit-netlist":
net_json = Path(str(args.netlist) + ".json")
report = {"phase": PHASE, "status": "passed", "audit_script": b.artifact(Path(__file__)),
"binding": inspect_netlist(b.read_json(net_json)),
"inputs": [b.artifact(p) for p in (args.netlist, net_json, args.config)]}
b.dump(args.output, report)
else:
verify_netlist(variant, args.netlist, args.config, args.output)
else:
b.dump(parent / "report.json", {"phase": PHASE, "status": "incomplete"})
identity = b.read_json(Path(b.read_json(parent / "latest-inputs.json")["path"]))
if identity.get("status") != "captured":
raise ValueError("missing successful source capture")
inputs = b.read_json(b.check_artifact(identity["source"]["inputs"]))
if any(b.sha256(ROOT / name) != digest for name, digest in inputs.items()):
raise ValueError("captured source changed")
flows = {}
for key in ("snapshot", "patch"):
b.check_artifact(identity["source"][key])
for tool in identity["tools"].values():
b.check_artifact(tool["executable"])
b.check_artifact(tool["archive"])
for name in ("syn/yosys/result-synth.json", "sta/opensta/result-sta.json"):
path = variant / name
result = b.read_json(path)
if result.get("status") != "passed" or result.get("exit_code") != 0:
raise ValueError(f"incomplete flow: {name}")
if datetime.fromisoformat(result["started_at"]) < datetime.fromisoformat(identity["created_at"]):
raise ValueError(f"flow predates captured source: {name}")
flows[name] = b.artifact(path)
sta = variant / "sta/opensta"
mapped = variant / "syn/yosys/out/retrosoc_tiny_asic_yosys.v"
verify_netlist(variant, mapped, mapped.with_suffix(".config"),
variant / "syn/yosys/tiny-ics55-netlist.json")
log = (sta / "opensta.log").read_text()
if "TINY_ICS55_STA_AUDIT_PASS:" not in log or re.search(r"(?im)^Error:|\bFATAL\b|%Error", log):
raise ValueError("incomplete or erroneous STA audit")
metrics = timing_metrics(sta / "timing_metrics.rpt")
report = {"phase": PHASE, "status": "completed", "source": identity,
"flows": flows, "qualification": "SAFE24 synthesis/STA observation only; functional acceptance is separate",
"timing": metrics,
"timing_status": "failed" if any(v < 0 for v in metrics.values()) else "nonnegative_reported_slack",
"sys_setup_slack_ns": path_slack(sta / "sys-setup.rpt"),
"sys_hold_slack_ns": path_slack(sta / "sys-hold.rpt"),
"sta_artifacts": {p.name: b.artifact(p) for p in sta.iterdir() if p.is_file()},
"macro_audit": b.artifact(variant / "syn/yosys/tiny-ics55-netlist.json")}
b.dump(parent / "report.json", report)
print(f"{PHASE} {args.action}: PASS (not physical qualification)")
return 0
except (OSError, ValueError, KeyError) as error:
if args.action == "capture" and "directory" in locals():
b.dump(directory / "identity.json", {"phase": PHASE, "status": "failed", "error": str(error)})
print(f"{PHASE}: {error}", file=sys.stderr)
return 1
if __name__ == "__main__":
raise SystemExit(main())
+29 -13
View File
@@ -38,7 +38,7 @@ TOOL_PROGRAMS = {"verilator": "verilator", "verible": "verible-verilog-format",
SOURCE_PATHS = ("Makefile", "configs/ci/ihp130-tiny.mk", "dependencies/dependencies.lock.json",
"requirements", "rtl/tiny", "rtl/ip", "rtl/tech", "rtl/mk/software.mk",
"crt", "app/apps/ci_smoke", "app/apps/bringup", "scripts",
"physical/smoke", "physical/librelane/tiny")
"physical/smoke", "physical/librelane/tiny", "physical/pdk")
CASE_FIELDS = {"id", "name", "cycles", "instructions", "kernel_cycles", "kernel_calls",
"payload_bytes", "checksum", "cpu_wait", "dma_wait", "sram_reads",
"sram_writes", "sram_rbeats", "sram_wbeats", "sram_stalls", "sram_errors"}
@@ -75,7 +75,7 @@ def digest_map(value: dict) -> str:
return hashlib.sha256(json.dumps(value, sort_keys=True).encode()).hexdigest()
def source_inputs(root: Path) -> dict[str, str]:
def source_inputs(root: Path, pdk: str = "IHP130") -> dict[str, str]:
names = subprocess.check_output(
["git", "ls-files", "--cached", "--others", "--exclude-standard", "-z", "--",
*SOURCE_PATHS], cwd=root,
@@ -83,7 +83,14 @@ def source_inputs(root: Path) -> dict[str, str]:
paths = {root / name for name in names if name and not name.endswith(
(".md", ".png", ".jpg", ".svg", ".pdf"))}
lock = read_json(root / "dependencies/dependencies.lock.json")
for name in TINY_SOURCES:
selected_sources = list(TINY_SOURCES)
if pdk == "ICS55":
selected_sources = [name if name != "pdk_ihp130" else "pdk_ics55" for name in selected_sources]
selected_sources.append("pdk_ics55_pll")
paths.add(root / "configs/ci/ics55-tiny.mk")
paths.update(p for p in (root / ".cache/retrosoc/pdk/ics55").rglob("*")
if p.is_file() and p.suffix in (".v", ".lib"))
for name in selected_sources:
spec = lock["sources"][name]
checkout = root / spec["destination"]
revision = subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=checkout,
@@ -100,7 +107,7 @@ def source_inputs(root: Path) -> dict[str, str]:
paths.update((base / "sg13g2_stdcell/lib").glob("*slow_1p08V_125C.lib"))
paths.update((base / "sg13g2_io/lib").glob("*slow_1p08V_3p0V_125C.lib"))
else:
for suffix in ("*.v", "*.sv", "*.vh", "*.svh", "*.c", "*.h"):
for suffix in ("*.v", "*.sv", "*.vh", "*.svh", "*.c", "*.h", "*.lib", "*.lef"):
paths.update(checkout.rglob(suffix))
return {str(path.relative_to(root)): sha256(path) for path in sorted(paths) if path.is_file()}
@@ -126,13 +133,22 @@ def runtime_inputs(root: Path, variant: Path, simulator: str) -> dict[str, str]:
def configuration(variant: Path) -> dict:
manifest = read_json(variant / "meta/manifest.json")
config = manifest["configuration"]
if manifest["profile"] != "ihp130-tiny" or any(
config.get(key) != expected for key, expected in COMPATIBILITY.items()
expected = dict(COMPATIBILITY)
if manifest["profile"] == "ics55-tiny":
expected.update(PDK="ICS55", HAVE_PLL="YES")
if manifest["profile"] not in ("ihp130-tiny", "ics55-tiny") or any(
config.get(key) != value for key, value in expected.items()
):
raise ValueError("baseline requires the committed Tiny/IHP130 24 MHz macro profile")
raise ValueError("baseline requires an explicit Tiny 24 MHz macro profile")
return manifest
def variant_source_inputs(root: Path, variant: Path) -> dict:
if configuration(variant)["configuration"]["PDK"] == "ICS55":
return source_inputs(root, "ICS55")
return source_inputs(root)
def tools_identity(root: Path) -> dict:
cache_value = os.environ.get("RETROSOC_DEVELOPMENT_CACHE")
if not cache_value:
@@ -325,7 +341,7 @@ def build_image(args: argparse.Namespace) -> int:
try:
manifest = configuration(variant)
tools = tools_identity(root)
before = source_inputs(root)
before = variant_source_inputs(root, variant)
# Archive generated C headers and the preprocessed flat linker input too.
generated = {str(path.relative_to(root)): sha256(path)
for path in (variant / "generated/tiny").rglob("*") if path.is_file()}
@@ -355,7 +371,7 @@ def build_image(args: argparse.Namespace) -> int:
ebss = re.search(r"^([0-9a-f]+)(?: [0-9a-f]+)? [A-Za-z] _ebss$", symbols, re.MULTILINE)
if ebss is None or not 0x30000000 <= int(ebss[1], 16) <= 0x3001F000:
raise ValueError("image does not preserve at least 4 KiB of stack headroom")
if before != source_inputs(root):
if before != variant_source_inputs(root, variant):
raise ValueError("source changed while compiling the retained image")
if any(not (root / name).is_file() or sha256(root / name) != digest
for name, digest in generated.items()):
@@ -421,12 +437,12 @@ def simulate(args: argparse.Namespace) -> int:
firmware_source=image["source"], manifest=configuration(variant), tools=tools_identity(root))
if args.simulator == "iverilog":
record["runtime_tool"] = vvp_identity(args.vvp, record["tools"]["iverilog"])
before = source_inputs(root)
before = variant_source_inputs(root, variant)
command = args.command[1:] if args.command[:1] == ["--"] else args.command
if not command:
raise ValueError("a fresh simulator compilation command is required")
execute(root, directory, "compile-model", command, quiet=True)
if before != source_inputs(root):
if before != variant_source_inputs(root, variant):
raise ValueError("source changed while compiling the simulator")
runtime = runtime_inputs(root, variant, args.simulator)
record["rtl_source"] = snapshot(root, directory / "inputs", before | runtime)
@@ -472,7 +488,7 @@ def simulate(args: argparse.Namespace) -> int:
dump(output, record)
if errors:
raise ValueError("; ".join(errors))
if before != source_inputs(root) or runtime != runtime_inputs(root, variant, args.simulator):
if before != variant_source_inputs(root, variant) or runtime != runtime_inputs(root, variant, args.simulator):
raise ValueError("source or generated inputs changed during simulation")
validate_runs(record["runs"])
validate_runtime_tool(root, record)
@@ -511,7 +527,7 @@ def report(args: argparse.Namespace) -> int:
check_artifact(attempt["model"])
source = attempt["rtl_source"]
inputs = read_json(check_artifact(source["inputs"]))
if inputs != source_inputs(root) | runtime_inputs(root, variant, simulator):
if inputs != variant_source_inputs(root, variant) | runtime_inputs(root, variant, simulator):
raise ValueError(f"stale {simulator} source inputs")
check_artifact(source["snapshot"])
validate_runs(attempt["runs"])
+1 -1
View File
@@ -44,7 +44,7 @@ def require_variant(root: Path, variant: Path) -> dict:
raise ValueError("evidence must remain below repository build variants")
manifest = baseline.configuration(variant)
config = manifest["configuration"]
if config["APP"] != "ci_smoke" or config["MGMT_CPU_CLK_HZ"] != "24000000":
if config["PDK"] != "IHP130" or config["APP"] != "ci_smoke" or config["MGMT_CPU_CLK_HZ"] != "24000000":
raise ValueError("feasibility requires the 24 MHz Tiny ci_smoke variant")
return manifest
+5 -3
View File
@@ -5,7 +5,8 @@
`include "onchip_ram_define.svh"
module onchip_ram_tb #(
parameter int unsigned CapacityKiB = 128
parameter bit Ics55SmallBanks = 1'b0,
parameter int unsigned CapacityKiB = 128
);
localparam logic [31:0] SramBase = `SOC_ADDR_SRAM_BASE;
localparam logic [31:0] SramEnd = `SOC_ADDR_SRAM_END;
@@ -34,8 +35,9 @@ module onchip_ram_tb #(
always #5 clk_i = ~clk_i;
onchip_ram #(
.Present (1'b1),
.CapacityKiB(CapacityKiB)
.Ics55SmallBanks(Ics55SmallBanks),
.Present (1'b1),
.CapacityKiB (CapacityKiB)
) u_dut (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
+45
View File
@@ -0,0 +1,45 @@
`timescale 1ns / 1ps
module tc_ics55_gpio_readback_tb;
logic drive = 0, value = 0, external_value = 0;
wire tiny_read, legacy_read;
tri tiny_pad, legacy_pad;
assign tiny_pad = drive ? 1'bz : external_value;
assign legacy_pad = drive ? 1'bz : external_value;
tc_io_tri_full_pad #(
.ReadWhileDriving(1'b1)
) u_tiny (
.pad (tiny_pad),
.c2p (value),
.c2p_en(drive),
.p2c (tiny_read),
.cs (1'b1),
.pu (1'b0),
.pd (1'b0)
);
tc_io_tri_full_pad u_legacy (
.pad (legacy_pad),
.c2p (value),
.c2p_en(drive),
.p2c (legacy_read),
.cs (1'b1),
.pu (1'b0),
.pd (1'b0)
);
initial begin
#20;
if ({tiny_read, legacy_read} !== 2'b00) $fatal(1, "input low");
external_value = 1;
#20;
if ({tiny_read, legacy_read} !== 2'b11) $fatal(1, "input high");
drive = 1;
value = 1;
#20;
if (tiny_pad !== 1'b1 || legacy_pad !== 1'b1 || tiny_read !== 1'b1 || legacy_read !== 1'b0)
$fatal(1, "output readback or legacy behavior");
value = 0;
#20;
if ({tiny_pad, legacy_pad, tiny_read, legacy_read} !== 4'b0000) $fatal(1, "output low");
$display("SIM_TEST_PASS ICS55 GPIO readback and legacy compatibility");
$finish;
end
endmodule
+114
View File
@@ -0,0 +1,114 @@
`timescale 1ns / 1ps
module tc_pll_ics55_ecos_tb;
logic s_ref = 0;
logic s_reset_n = 0;
logic [2:0] s_selection = 0;
logic s_apply = 0;
wire s_capable, s_locked, s_clock;
integer s_edges = 0;
tc_pll #(
.UseIcs55EcosPll(1'b1)
) u_dut (
.fref_i (s_ref),
.rst_n_i (s_reset_n),
.cfg_sel_i (s_selection),
.cfg_apply_i (s_apply),
.pll_capable_o(s_capable),
.pll_lock_o (s_locked),
.pll_clk_o (s_clock)
);
always #20.833333 s_ref = ~s_ref;
always @(posedge s_clock) s_edges++;
task automatic cycle(input integer count);
repeat (count) @(negedge s_ref);
endtask
task automatic configure(input logic [2:0] selection);
@(negedge s_ref);
s_selection = selection;
s_apply = 1;
cycle(1);
s_apply = 0;
endtask
task automatic await_lock;
integer waited;
waited = 0;
while (!s_locked && waited < 2000) begin
cycle(1);
waited++;
end
if (!s_locked || !s_capable) $fatal(1, "PLL did not qualify");
endtask
task automatic measure(input integer multiplier);
integer before_edges;
before_edges = s_edges;
cycle(128);
if (s_edges-before_edges < multiplier*128-2 ||
s_edges-before_edges > multiplier*128+2)
$fatal(1, "wrong measured PLL rate");
endtask
initial begin
cycle(2);
s_reset_n = 1;
cycle(8);
if (s_locked || s_clock) $fatal(1, "unrequested PLL started");
configure(5);
cycle(510);
if (s_locked) $fatal(1, "early lock without clock observations");
await_lock();
measure(8);
configure(7);
cycle(1);
if (s_locked) $fatal(1, "reconfiguration retained stale lock");
await_lock();
measure(10);
// A held APPLY is one command, not an endless restart.
@(negedge s_ref);
s_selection = 5;
s_apply = 1;
cycle(1);
await_lock();
measure(8);
s_apply = 0;
force u_dut.gen_ecos.u_backend.u_PLL_TOP.N = 8'd40;
cycle(136);
if (s_locked) $fatal(1, "wrong-frequency output retained lock");
release u_dut.gen_ecos.u_backend.u_PLL_TOP.N;
await_lock();
measure(8);
// Stop both polarities. No internal model-ready signal is consulted.
force u_dut.gen_ecos.u_backend.pll_clk_o = 1'b0;
cycle(136);
if (s_locked) $fatal(1, "stopped-low output retained lock");
release u_dut.gen_ecos.u_backend.pll_clk_o;
await_lock();
force u_dut.gen_ecos.u_backend.pll_clk_o = 1'b1;
cycle(136);
if (s_locked) $fatal(1, "stopped-high output retained lock");
cycle(4100);
if (u_dut.gen_ecos.u_backend.u_PLL_TOP.EN) $fatal(1, "acquisition timeout left PLL enabled");
release u_dut.gen_ecos.u_backend.pll_clk_o;
configure(7);
await_lock();
measure(10);
configure(0);
cycle(1500);
if (s_locked || u_dut.gen_ecos.u_backend.u_PLL_TOP.EN) $fatal(1, "invalid selection accepted");
configure(5);
await_lock();
@(negedge s_ref);
s_reset_n = 0;
#1;
if (s_locked) $fatal(1, "reset retained lock");
cycle(10);
s_reset_n = 1;
cycle(20);
if (s_locked) $fatal(1, "reset replayed old configuration");
$display("SIM_TEST_PASS ICS55 PLL backend");
$finish;
end
initial begin
#1000000;
$fatal(1, "SIM_TEST_TIMEOUT PLL backend");
end
endmodule
+7 -2
View File
@@ -3,6 +3,7 @@ from __future__ import annotations
import copy
import json
import re
from pathlib import Path
import shutil
@@ -81,6 +82,7 @@ def source_tree(tmp_path):
@pytest.mark.parametrize("path,before,after", [
(tiny.TOP, ".CapacityKiB(128)", ".CapacityKiB(32)"),
(tiny.TOP, ".Ics55SmallBanks(1'b1)", ".Ics55SmallBanks(1'b0)"),
(tiny.TOP, ".DataWidth (32)", ".DataWidth (64)"),
(tiny.TOP, ".NumChannels (4)", ".NumChannels (8)"),
(tiny.TOP, ".EnableStreams(1'b0)", ".EnableStreams(1'b1)"),
@@ -93,8 +95,11 @@ def source_tree(tmp_path):
def test_changed_hardware_or_isa_cannot_retain_publication_claim(source_tree, path, before, after):
file = source_tree / path
text = file.read_text(encoding="utf-8")
assert before in text
file.write_text(text.replace(before, after, 1), encoding="utf-8")
# Parameter alignment is formatting, while the altered value must still
# fail the source-bound publication contract.
pattern = r"\s*".join(re.escape(char) for char in before if not char.isspace())
assert re.search(pattern, text)
file.write_text(re.sub(pattern, lambda _: after, text, count=1), encoding="utf-8")
with pytest.raises(ValueError):
tiny.facts(source_tree)
+188
View File
@@ -0,0 +1,188 @@
"""Tiny ICS55 SAFE24 platform and the real locked PLL behavioral view."""
from __future__ import annotations
import os
import json
from pathlib import Path
import shutil
import subprocess
import pytest
from scripts.check_simulation import DEFAULT_FAILURE
from scripts.generate_tiny import generate_filelists, platform_values
from scripts.rtl.generate_memory_map import generate as generate_memory_map
from scripts.tiny_ics55_platform import inspect_netlist, RAM
from scripts import tiny_ics55_platform as platform
ROOT = Path(__file__).resolve().parents[1]
COMMON = ROOT / "rtl/managed/clusterip/common/rtl"
PLL = ROOT / ".cache/retrosoc/sources/ics55_ecos_pll/verilog/PLL_TOP.behavioral.v"
def run(command, **kwargs):
p = subprocess.run(command, text=True, capture_output=True, **kwargs)
assert p.returncode == 0, p.stdout + p.stderr
assert not DEFAULT_FAILURE.search(p.stdout + p.stderr), p.stdout + p.stderr
return p.stdout
@pytest.mark.parametrize("simulator", ["iverilog", "verilator"])
def test_actual_pll_backend(simulator, tmp_path):
assert shutil.which(simulator) and PLL.is_file(), "required locked input unavailable"
sources = [COMMON / p for p in ["utils/register.sv", "clkrst/rst_sync.sv", "cdc/cdc_sync.sv",
"utils/bin2gray.sv", "utils/gray2bin.sv"]]
sources += [ROOT / "rtl/tech/tc_pll.sv", ROOT / "rtl/tech/tc_pll_ics55_ecos.sv",
PLL, ROOT / "tests/rtl/tc_pll_ics55_ecos_tb.sv"]
flags = [f"-I{COMMON}", "-DSV_ASSRT_DISABLE", "-DPDK_ICS55", "-DHAVE_PLL",
"-DRETROSOC_SOC__TINY"]
binary = tmp_path / "sim"
top = "tc_pll_ics55_ecos_tb"
if simulator == "iverilog":
command = [simulator, "-g2012", "-s", top, *flags, *map(str, sources), "-o", str(binary)]
launch = ["vvp", str(binary)]
else:
command = [simulator, "--binary", "--timing", "--assert", "-Wno-fatal", "--top-module", top,
*flags, *map(str, sources), "--Mdir", str(tmp_path / "obj"), "-o", str(binary)]
launch = [str(binary)]
run(command, timeout=180, env={**os.environ, "CCACHE_DISABLE": "1"})
assert "SIM_TEST_PASS ICS55 PLL backend" in run(launch, timeout=60)
@pytest.mark.parametrize("pdk,pll,technology", [("IHP130", False, 0x02010082), ("ICS55", True, 0x02040037)])
def test_identity(pdk, pll, technology):
values = platform_values(pdk, pll)
assert values["technology"] == technology
assert values["features0"] == 0x7FFE | pll
@pytest.mark.parametrize("simulator,synthesis", [("VERILATOR", "NONE"), ("IVERILOG", "NONE"), ("IVERILOG", "YOSYS")])
def test_flow_specific_inputs(tmp_path, simulator, synthesis):
generate_filelists(tmp_path, tmp_path / "generated", ["+define+HAVE_PLL"], "ICS55", simulator, synthesis)
text = (tmp_path / "pdk_selected.fl").read_text()
assert "IHP-Open-PDK" not in text and "/cache/" not in text.replace("/.cache/", "")
assert ("PLL_TOP.behavioral.v" in text) == (synthesis == "NONE")
assert ("ics55_io_sim_cells.sv" in text) == (simulator == "VERILATOR" and synthesis == "NONE")
def test_default_and_compatibility_configurations():
output = run(["make", "-s", "SOC=TINY", "config"], cwd=ROOT)
assert "ics55-tiny.mk" in output and "ICS55" in output
output = run(["make", "-s", "CONFIG=configs/ci/ihp130-tiny.mk", "config"], cwd=ROOT)
assert "IHP130" in output
result = subprocess.run(["make", "-s", "CONFIG=configs/ci/ics55.mk", "SOC=MINI",
"HAVE_PLL=YES", "TINY_SAFE24_PLL_OFF=YES", "STA=OPENSTA", "config"],
cwd=ROOT, capture_output=True, text=True)
assert result.returncode != 0 and "qualified PDK PLL timing profile" in result.stderr
@pytest.mark.parametrize("changed", [None, "netlist", "json", "config", "script", "missing"])
def test_consumed_netlist_provenance(tmp_path, monkeypatch, changed):
monkeypatch.setattr(platform, "check_configuration", lambda *args: None)
paths = [tmp_path / name for name in ("net.v", "net.v.json", "net.config")]
for path in paths:
path.write_text("original")
report = {"phase": platform.PHASE, "status": "passed",
"audit_script": platform.b.artifact(Path(platform.__file__)),
"inputs": [platform.b.artifact(path) for path in paths]}
if changed in ("netlist", "json", "config"):
paths[("netlist", "json", "config").index(changed)].write_text("changed")
elif changed == "script":
report["audit_script"] = platform.b.artifact(paths[0])
elif changed == "missing":
report["inputs"].pop()
output = tmp_path / "audit.json"
output.write_text(json.dumps(report))
if changed:
with pytest.raises(ValueError):
platform.verify_netlist(tmp_path, paths[0], paths[2], output)
else:
assert platform.verify_netlist(tmp_path, paths[0], paths[2], output) == report
@pytest.fixture
def mapped_netlist():
def cell(kind, **ports):
return {"type": kind, "connections": {k: [v] for k, v in ports.items()}}
cells = {"u_clock_buffer": cell("BUF", clk_o=2),
"u_soc.u_cpu.u_hazard3_cpu_1port.core.pc_reg": cell("DFF", CK=2),
"pll": cell("PLL_TOP", EN=3, CKOUT1=4, CKOUT2=5, CKTST=6),
"low": cell("TIELOH7R", Z=3)}
cells.update({f"bank{i}": cell(RAM, CLK=2) for i in range(32)})
return {"modules": {"retrosoc_tiny_asic": {"ports": {}, "cells": cells}}}
def test_safe24_structural_binding(mapped_netlist):
result = inspect_netlist(mapped_netlist)
assert len(result["sram"]) == 32 and result["cpu_clocked_cells"] == 1
assert result["pll_enable"] == 0
def test_hierarchical_constant_output_is_resolved(mapped_netlist):
modules = mapped_netlist["modules"]
modules["constant_zero"] = {"ports": {"result": {"direction": "output", "bits": ["0"]}}, "cells": {}}
modules["retrosoc_tiny_asic"]["cells"]["low"] = {
"type": "constant_zero", "connections": {"result": [3]}}
assert inspect_netlist(mapped_netlist)["pll_enable"] == 0
@pytest.mark.parametrize("mutation", ["missing_pll", "missing_ram", "live_pll", "unknown_enable",
"divided_ram", "wrong_cpu", "pll_sys"])
def test_unsafe_or_incomplete_mapping_is_rejected(mapped_netlist, mutation):
cells = mapped_netlist["modules"]["retrosoc_tiny_asic"]["cells"]
if mutation == "missing_pll":
del cells["pll"]
elif mutation == "missing_ram":
del cells["bank0"]
elif mutation == "live_pll":
cells["low"]["type"] = "TIEHIH7R"
elif mutation == "unknown_enable":
cells["pll"]["connections"]["EN"] = [99]
elif mutation == "divided_ram":
cells["bank0"]["connections"]["CLK"] = [99]
elif mutation == "wrong_cpu":
cells["u_soc.u_cpu.u_hazard3_cpu_1port.core.pc_reg"]["connections"]["CK"] = [99]
else:
cells["pll"]["connections"]["CKOUT1"] = [2]
with pytest.raises(ValueError):
inspect_netlist(mapped_netlist)
def test_tiny_uses_32_actual_ics55_small_macros(tmp_path):
assert shutil.which("verilator"), "required simulator unavailable"
generated = tmp_path / "map"
generate_memory_map(ROOT / "rtl/tiny/address_map/memory_map.json", generated, "YES", 128)
sram = ROOT / ".cache/retrosoc/pdk/ics55/sram"
sources = [COMMON / p for p in ["interface/axi4_if.sv", "interface/apb4_if.sv",
"interface/axi4_addr_gen.sv", "utils/register.sv", "tech/ram.sv"]]
sources += [ROOT / p for p in ["rtl/tech/tc_sram.sv", "rtl/ip/memory/onchip_ram_reg.sv",
"rtl/ip/memory/onchip_ram.sv", "tests/rtl/onchip_ram_tb.sv"]]
sources += [sram / "ics55_ecos_sram_1024x32_m8/verilog/ics55_ecos_sram_1024x32_m8_core.v",
sram / "ics55_ecos_sram_4096x32_m8/verilog/ics55_ecos_sram_4096x32_m8_stub.v"]
binary = tmp_path / "sim"
run(["verilator", "--binary", "--timing", "--assert", "-Wno-fatal", "--top-module", "onchip_ram_tb",
"-GCapacityKiB=128", "-GIcs55SmallBanks=1", "-DPDK_ICS55", "-DHAVE_SRAM_MACRO", "-DSV_ASSRT_DISABLE",
*(f"-I{p}" for p in [generated / "rtl", ROOT / "rtl/ip/memory", COMMON, COMMON / "interface"]),
*map(str, sources), "--Mdir", str(tmp_path / "obj"), "-o", str(binary)],
timeout=180, env={**os.environ, "CCACHE_DISABLE": "1"})
assert "on-chip SRAM AXI4 test passed capacity_kib=128" in run([str(binary)], timeout=60)
@pytest.mark.parametrize("simulator", ["iverilog", "verilator"])
def test_gpio_readback_preserves_legacy_default(tmp_path, simulator):
assert shutil.which(simulator), "required simulator unavailable"
model = ROOT / ("rtl/tech/ics55_io_sim_cells.sv" if simulator == "verilator" else
"physical/pdk/icsprout55-pdk/IP/IO/ICsprout_55LLULP1233_IO_251013/verilog/icsIOA_N55_3P3.v")
sources = [str(ROOT / "rtl/tech/tc_io.sv"), str(model), str(ROOT / "tests/rtl/tc_ics55_gpio_readback_tb.sv")]
binary = tmp_path / "sim"
if simulator == "iverilog":
compile_command = [simulator, "-g2012", "-DPDK_ICS55", "-Dfunctional", "-s",
"tc_ics55_gpio_readback_tb", *sources, "-o", str(binary)]
launch = ["vvp", str(binary)]
else:
compile_command = [simulator, "--binary", "--timing", "-Wno-fatal", "-DPDK_ICS55",
"--top-module", "tc_ics55_gpio_readback_tb", *sources,
"--Mdir", str(tmp_path / "obj"), "-o", str(binary)]
launch = [str(binary)]
run(compile_command, timeout=120, env={**os.environ, "CCACHE_DISABLE": "1"})
assert "SIM_TEST_PASS ICS55 GPIO" in run(launch, timeout=30)
+2 -2
View File
@@ -281,7 +281,7 @@ def test_invalid_runtime_fails_before_compilation_or_simulation(locked_icarus, m
image_path.write_text("{}")
image = {"source": {}, "artifacts": {"tiny_baseline.hex": {"sha256": "fixture"}}}
monkeypatch.setattr(baseline, "retained_image", lambda *_: (image_path, image))
monkeypatch.setattr(baseline, "configuration", lambda *_: {})
monkeypatch.setattr(baseline, "configuration", lambda *_: {"configuration": {"PDK": "IHP130"}})
monkeypatch.setattr(baseline, "execute", lambda *_args, **_kwargs: pytest.fail("tool execution preceded rejection"))
args = argparse.Namespace(root=root, variant_root=root / "build/variant", simulator="iverilog",
hex=None, vvp=selected, command=["unexpected-build"])
@@ -322,7 +322,7 @@ def test_runtime_retention_and_actual_launch_are_rechecked(locked_icarus, mutati
def test_image_retains_compiler_command_after_postprocessing(tmp_path: Path, monkeypatch) -> None:
variant = tmp_path / "build/variant"
monkeypatch.setattr(baseline, "configuration", lambda *_: {})
monkeypatch.setattr(baseline, "configuration", lambda *_: {"configuration": {"PDK": "IHP130"}})
monkeypatch.setattr(baseline, "tools_identity", lambda *_: {})
monkeypatch.setattr(baseline, "source_inputs", lambda *_: {})
monkeypatch.setattr(baseline, "snapshot", lambda *_: {})