mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-04 22:32:36 +00:00
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:
56 files changed
+1675
-172
No files matched your search
@@ -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`,
|
||||
|
||||
@@ -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
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
@@ -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.
|
||||
|
||||
@@ -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
@@ -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
@@ -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 |
|
||||
|
||||
@@ -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
@@ -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.
|
||||
```
|
||||
|
||||
@@ -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
@@ -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.
|
||||
```
|
||||
|
||||
@@ -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
@@ -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.
|
||||
```
|
||||
|
||||
@@ -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.
|
||||
@@ -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
@@ -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.
|
||||
```
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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 $@
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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:
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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
@@ -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"])
|
||||
|
||||
@@ -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,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),
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -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 *_: {})
|
||||
|
||||
Reference in new issue
Block a user