mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-04 22:32:36 +00:00
feat: add Tiny R2 reset distribution and feasibility analysis
- Implemented a new TCL script for Tiny R2 feasibility checks, reporting clock properties, path delays, and reset loads. - Created a SystemVerilog module for the Tiny R2 reset tree, managing multiple reset signals for the system and its leaves. - Developed a Python script for capturing and analyzing feasibility metrics, ensuring compliance with reset-load and clock frequency requirements. - Added testbenches for the Tiny R2 reset tree and FIFO, validating reset behavior and timing contracts. - Introduced unit tests for feasibility analysis, ensuring correct handling of clock rates and reset load conditions.
This commit is contained in:
24 files changed
+1209
-66
No files matched your search
@@ -0,0 +1,156 @@
|
|||||||
|
# Tiny R2-P2 reset distribution and clock feasibility
|
||||||
|
|
||||||
|
This is the implementation record boundary for
|
||||||
|
[TINY-R2-P2](tiny-soc.md#tiny-r2-p2---reset-distribution-and-cpusram-clock-feasibility).
|
||||||
|
Results and remaining gates belong in the [verification ledger](tiny-soc-verification.md).
|
||||||
|
The starting revision is `f06c039e790e38ccaf336ff21b68619010e7cc9e`.
|
||||||
|
Use `configs/ci/ihp130-tiny.mk`, TINY, IHP130, 24 MHz/no PLL. Higher rates
|
||||||
|
below are analysis constraints, not executable profiles or qualified clocks.
|
||||||
|
|
||||||
|
## Reset ownership
|
||||||
|
|
||||||
|
`tiny_reset_tree` reuses the locked Common `rst_sync`. Every chain has five
|
||||||
|
stages and asserts asynchronously. Cold release traverses POR, system and
|
||||||
|
the leaf chains. All 17 leaf resets must be released before the core/debug
|
||||||
|
wrapper starts its own five-edge hart release. The nominal cold sequence is
|
||||||
|
POR at edge 5, system at edge 10, leaves at edge 15 and hart at edge 20.
|
||||||
|
These counts are behavioral sequencing, not a metastability or physical-skew
|
||||||
|
bound. A stopped clock never completes a release.
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
ext[RESET_N] --> por[POR: five edges]
|
||||||
|
por --> system[System: five edges]
|
||||||
|
watchdog[Watchdog reset request] --> system
|
||||||
|
system --> leaves[17 SYS reset leaves: five edges]
|
||||||
|
leaves --> ready[All leaves released]
|
||||||
|
ready --> hart[Hart: five edges]
|
||||||
|
hart --> cpu[CPU and request frontend]
|
||||||
|
por --> timer[Watchdog functional timer]
|
||||||
|
trst[JTAG_TRST_N] --> jtag[JTAG: five TCK edges]
|
||||||
|
```
|
||||||
|
|
||||||
|
Leaf zero belongs to the fabric, APB bridge, error responders and common tick.
|
||||||
|
Leaves 1 through 16 follow the existing `soc_topology.json` APB target order:
|
||||||
|
GPIO, UART0, UART1, TIMER0, TIMER1, I2C0, I2C1, XPI, DMA, SYSCTRL, CLINT,
|
||||||
|
SRAM, ARCHINFO, PWM, RTC and WDG. Generated APB interfaces and each IP's
|
||||||
|
explicit reset input use the same leaf. AXI interface reset arrays retain
|
||||||
|
their existing owner/target order. All clocks still use SYS; reset partitioning
|
||||||
|
does not add a clock domain, divider, clock gate or SRAM CDC.
|
||||||
|
|
||||||
|
The watchdog functional timer stays on POR reset; its APB state uses the WDG
|
||||||
|
leaf. A watchdog request therefore survives the system reset it triggers.
|
||||||
|
Hart-only debug reset keeps its existing accepted-transfer drain and leaves
|
||||||
|
other clients running. The shared core/debug/reset wrappers add only the
|
||||||
|
`ResetSyncStages` RTL parameter: default 3 preserves Mini behavior, while Tiny
|
||||||
|
selects 5 for hart, JTAG and DMI release. There is no new software ABI.
|
||||||
|
|
||||||
|
Common FIFO payload storage, pointer/count/empty behavior, flush precedence
|
||||||
|
and warm-flush handshakes are unchanged. The FIFO's mixed asynchronous-control
|
||||||
|
process already omits payload clearing; P2 does not rewrite that managed
|
||||||
|
process or claim to have removed a payload clear. Local reset loads are
|
||||||
|
partitioned instead. Empty-read masking and stale-data isolation are exercised
|
||||||
|
with directed cases and an independent queue scoreboard.
|
||||||
|
|
||||||
|
Main SRAM payload is not reset. The existing SRAM protocol fixture now also
|
||||||
|
holds a read response, resets the controller, checks that the stale response
|
||||||
|
is cancelled and verifies the previously written word after release. Crypto
|
||||||
|
is not integrated in the selected Tiny baseline; its six private-bank and
|
||||||
|
physical FIFO erasure contracts remain unchanged. CPU release does not wait
|
||||||
|
for Crypto READY or external AUDIO/PIXCLK clocks.
|
||||||
|
|
||||||
|
Leaf synchronizer instances have narrow `keep`/`keep_hierarchy` attributes to
|
||||||
|
prevent synthesis from merging identical reset drivers. The feasibility audit
|
||||||
|
requires 17 distinct instances and driver identities. These attributes do not
|
||||||
|
create a timing exception or establish a routed reset tree.
|
||||||
|
|
||||||
|
## Reproducible analysis
|
||||||
|
|
||||||
|
Before changing RTL, the implementation captured clean source, tools and inputs
|
||||||
|
and ran fresh firmware, balanced Yosys synthesis and core STA. The baseline is
|
||||||
|
`build/ihp130-tiny-2026-10-06-09-33-19f1e9c571ee/`; its
|
||||||
|
`meta/tiny-r2-p2/baseline-artifacts.json` binds the netlist, config, SDC,
|
||||||
|
commands/results and archived source. The preceding P1 timing numbers remain
|
||||||
|
separately attributed even when numerically equal.
|
||||||
|
|
||||||
|
Use a new timestamp/variant for a candidate. With `APP=ci_smoke`, build firmware
|
||||||
|
first, capture its inputs, then run fresh synthesis and STA:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
source .cache/retrosoc/development/tiny-r2-p1/activate.sh
|
||||||
|
export BUILD_TIMESTAMP=$(date +%Y-%m-%d-%H-%M)
|
||||||
|
make CONFIG=configs/ci/ihp130-tiny.mk SOC=TINY PDK=IHP130 APP=ci_smoke firmware
|
||||||
|
tiny_candidate_root=$(make -s CONFIG=configs/ci/ihp130-tiny.mk SOC=TINY PDK=IHP130 APP=ci_smoke config | awk '$1 == "VARIANT_ROOT" {print $2}')
|
||||||
|
python3 scripts/tiny_r2_feasibility.py --variant-root "$tiny_candidate_root" capture
|
||||||
|
make -j1 CONFIG=configs/ci/ihp130-tiny.mk SOC=TINY PDK=IHP130 APP=ci_smoke SYNTH=YOSYS STA=OPENSTA synth sta
|
||||||
|
python3 scripts/tiny_r2_feasibility.py --variant-root "$tiny_candidate_root" run \
|
||||||
|
--baseline-root build/ihp130-tiny-2026-10-06-09-33-19f1e9c571ee
|
||||||
|
```
|
||||||
|
|
||||||
|
The runner creates unique attempts below `meta/tiny-r2-p2/`. It rejects missing
|
||||||
|
or stale captures, synthesis older than the capture, incomplete native flows,
|
||||||
|
changed artifacts, and missing audit markers. Every point retains the exact
|
||||||
|
command, libraries, analysis SDC, metrics, reset loads/endpoints, net capacitance,
|
||||||
|
CPU and SRAM input/return paths, clock binding, constraint violations and
|
||||||
|
coverage report. Direct load counts exclude the output driver itself; a
|
||||||
|
structural all-arcs endpoint count is a different measure.
|
||||||
|
Native max/min WNS includes asynchronous recovery/removal checks. Separate
|
||||||
|
`sys-setup.rpt` and `sys-hold.rpt` report the SYS data-path slacks; neither
|
||||||
|
relabels a recovery violation as a data setup result.
|
||||||
|
|
||||||
|
Both actual netlists are analyzed at SYS24/96/192/240 MHz (periods
|
||||||
|
41.666666667/10.416666667/5.208333333/4.166666667 ns) in slow, typical and fast
|
||||||
|
views. The 24 MHz SDC is retained byte-for-byte; other points replace only its
|
||||||
|
single SYS clock period. Existing uncertainty, clock grouping and port/reset
|
||||||
|
exceptions are retained. CPU and all 32 main-SRAM A_CLK loads share the same
|
||||||
|
SYS driver. No profile, PLL or main-SRAM divider changes result from this sweep.
|
||||||
|
The current peripherals also share SYS. This sweep does not model the later
|
||||||
|
R2-P7 SYS/MEM/PCLK partition or the future four 32 KiB SRAM frontends; its
|
||||||
|
failures and path measurements belong to the actual current netlist.
|
||||||
|
|
||||||
|
The fast SRAM view is 1.32 V/-55 C, paired with the actual standard-cell and
|
||||||
|
IO fast -40 C views. Typical uses 1.20 V/25 C and slow uses 1.08 V/125 C.
|
||||||
|
The locked SRAM libraries provide setup/hold, clock-to-output and pulse-width
|
||||||
|
arcs but no explicit minimum-period entry. Missing characterization, table
|
||||||
|
extrapolation and uncovered checks remain gaps; nonnegative reported slack
|
||||||
|
does not establish a qualified maximum frequency. These ideal-clock,
|
||||||
|
unextracted analyses exclude CTS, routing, package and board effects.
|
||||||
|
|
||||||
|
## Functional and compatibility acceptance
|
||||||
|
|
||||||
|
Run focused tests and policy before the wider regression. Required fixtures
|
||||||
|
must actually execute with the selected tools; a missing tool or skipped
|
||||||
|
fixture cannot satisfy a gate.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
python3 -m pytest -q tests/test_tiny_r2_p2.py tests/test_tiny.py \
|
||||||
|
tests/test_mgmt_debug.py tests/test_i2c.py tests/test_clock_reset_domains.py \
|
||||||
|
'tests/test_onchip_ram.py::test_onchip_ram_axi4_capacity_protocol_and_performance[128]'
|
||||||
|
make CONFIG=configs/ci/ihp130-tiny.mk SOC=TINY PDK=IHP130 rtl-format-check rtl-style-check rtl-readiness-check
|
||||||
|
ruff check .
|
||||||
|
python3 -m pytest -q
|
||||||
|
python3 scripts/regress.py --root . --suite pr --soc TINY --pdk IHP130 --dry-run
|
||||||
|
python3 scripts/regress.py --root . --suite nightly --soc TINY --pdk IHP130 --dry-run
|
||||||
|
python3 scripts/regress.py --root . --suite pr --soc TINY --pdk IHP130
|
||||||
|
make CONFIG=configs/ci/ihp130-debug.mk SOC=MINI PDK=IHP130 SIMU=VERILATOR debug-sim
|
||||||
|
```
|
||||||
|
|
||||||
|
The additional Mini command checks existing shared-wrapper compatibility;
|
||||||
|
it is not a Tiny external-JTAG qualification or a new product rollout.
|
||||||
|
Its locked OpenOCD is restored separately under
|
||||||
|
`.cache/retrosoc/development/tiny-r2-p2-debug`; select that executable explicitly
|
||||||
|
without changing the P1 environment. Dependency versions remain unchanged.
|
||||||
|
|
||||||
|
For matched-binary workloads, follow the [P1 runbook](tiny-soc-r2-baseline.md)
|
||||||
|
with a new variant and explicitly select the original
|
||||||
|
`image-ozwo38_b/tiny_baseline.hex`. Both simulators perform three cold starts.
|
||||||
|
Compare the measured windows separately from the intentionally longer reset
|
||||||
|
startup. Icarus has a 14400-second per-run wall budget and is polled every
|
||||||
|
30 minutes. Command success, valid TEST_STATUS, `SIM_TEST_PASS Tiny`, complete
|
||||||
|
records and absence of forbidden errors remain mandatory; UART alone cannot pass.
|
||||||
|
|
||||||
|
P2 acceptance requires functional evidence, mapped reset partitioning and
|
||||||
|
source-bound attempted frequency results with explicit gaps. Warning and
|
||||||
|
metrics policies are unchanged. Failed timing remains failed, and neither
|
||||||
|
this phase nor its safe-frequency functional branch qualifies a physical
|
||||||
|
operating point or authorizes R2-P3 and later work.
|
||||||
@@ -8,8 +8,10 @@ or register ABI version. The selected executable baseline remains
|
|||||||
their physical acceptance do not exist merely because this plan names them.
|
their physical acceptance do not exist merely because this plan names them.
|
||||||
|
|
||||||
The R2 freeze itself changed documentation only. Separately authorized
|
The R2 freeze itself changed documentation only. Separately authorized
|
||||||
R2-P1 implementation and validation are recorded below; the other R2
|
R2-P1 implementation and validation are recorded below. Its functional and
|
||||||
implementation and qualification phases remain **pending**.
|
measurement baseline was accepted with the explicit R2-P2 implementation
|
||||||
|
approval on 2026-10-06; the recorded physical gaps remain open. R2-P2 work is
|
||||||
|
recorded separately below; later implementation and qualification remain pending.
|
||||||
Historical `TINY-P0` through `TINY-P12` identifiers and evidence are preserved
|
Historical `TINY-P0` through `TINY-P12` identifiers and evidence are preserved
|
||||||
under their original scope; the mapping in the main contract transfers
|
under their original scope; the mapping in the main contract transfers
|
||||||
outstanding obligations without renaming old results or declaring them passed.
|
outstanding obligations without renaming old results or declaring them passed.
|
||||||
@@ -171,8 +173,8 @@ not a prerequisite to the architectural improvements in R2-P3 through R2-P5.
|
|||||||
| Active phase | Required evidence and completion boundary | Status |
|
| Active phase | Required evidence and completion boundary | Status |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| TINY-R2-P0 - Performance Contract and Roadmap Freeze | Reviewable requirements, exact old/new phase mapping, invariant package/IO counts, same-frequency CPU/main-SRAM ownership, explicit performance budgets, links and commands. Record actual documentation checks separately. | Documentation freeze only; no hardware result |
|
| TINY-R2-P0 - Performance Contract and Roadmap Freeze | Reviewable requirements, exact old/new phase mapping, invariant package/IO counts, same-frequency CPU/main-SRAM ownership, explicit performance budgets, links and commands. Record actual documentation checks separately. | Documentation freeze only; no hardware result |
|
||||||
| TINY-R2-P1 - Reproducible Baseline, Constraints and Measurements | Reviewed source/profile/lock/tool identity; current 24 MHz functional and workload baseline; clocks, reset endpoints, constraints and timing-exception audit; counters and comparison methodology. Historical WNS is a risk reference, not a refreshed measurement. | Implementation and baseline evidence ready for review; timing and warning failures recorded; phase acceptance pending human review |
|
| TINY-R2-P1 - Reproducible Baseline, Constraints and Measurements | Reviewed source/profile/lock/tool identity; current 24 MHz functional and workload baseline; clocks, reset endpoints, constraints and timing-exception audit; counters and comparison methodology. Historical WNS is a risk reference, not a refreshed measurement. | Functional/measurement baseline accepted by explicit P2 approval on 2026-10-06; timing, warning and physical gaps retained |
|
||||||
| TINY-R2-P2 - Reset Distribution and CPU/SRAM Clock Feasibility | Reset distribution and payload-reset semantics; locked IHP130 main-SRAM macro timing, candidate common CPU/SRAM periods and representative paths; early clock/reset feasibility with explicit gaps. No final routed/PVT pass is implied. | Pending |
|
| TINY-R2-P2 - Reset Distribution and CPU/SRAM Clock Feasibility | Reset distribution and payload-reset semantics; locked IHP130 main-SRAM macro timing, candidate common CPU/SRAM periods and representative paths; early clock/reset feasibility with explicit gaps. No final routed/PVT pass is implied. | Implementation and evidence ready for review; all analyzed rates retain timing failures; phase acceptance pending human review |
|
||||||
| TINY-R2-P3 - Software and DMA Scheduling | Compiler and placement A/B results, finite DMA ownership, burst/chunk scheduling, WS2812 refill budget and workload correctness on the available platform. | Pending |
|
| TINY-R2-P3 - Software and DMA Scheduling | Compiler and placement A/B results, finite DMA ownership, burst/chunk scheduling, WS2812 refill budget and workload correctness on the available platform. | Pending |
|
||||||
| TINY-R2-P4 - Dual-Port Hazard3 and Four-Bank Local SRAM | Actual separate I/D paths, four independent 32 KiB bank frontends, SYS-clocked physical main-SRAM macros, local latency/fairness, FENCE.I, debug/reset and early inventory/SDC/macro-binding updates. | Pending |
|
| TINY-R2-P4 - Dual-Port Hazard3 and Four-Bank Local SRAM | Actual separate I/D paths, four independent 32 KiB bank frontends, SYS-clocked physical main-SRAM macros, local latency/fairness, FENCE.I, debug/reset and early inventory/SDC/macro-binding updates. | Pending |
|
||||||
| TINY-R2-P5 - Per-Target Concurrent Fabric | Cross-target concurrency, one combined transaction per target, central-DMA read/write overlap, AW/W binding, errors, simultaneous faults/counters and accepted-transfer drain. | Pending |
|
| TINY-R2-P5 - Per-Target Concurrent Fabric | Cross-target concurrency, one combined transaction per target, central-DMA read/write overlap, AW/W binding, errors, simultaneous faults/counters and accepted-transfer drain. | Pending |
|
||||||
@@ -537,6 +539,152 @@ versions, warning baselines and metrics policy are unchanged by this repair.
|
|||||||
The earlier failed timing qualification and all unrun physical/debug/PVT gates
|
The earlier failed timing qualification and all unrun physical/debug/PVT gates
|
||||||
remain open. No phase acceptance or subsequent-phase authorization is implied.
|
remain open. No phase acceptance or subsequent-phase authorization is implied.
|
||||||
|
|
||||||
|
## R2-P2 implementation evidence (2026-10-06)
|
||||||
|
|
||||||
|
The approved starting revision is `f06c039e790e38ccaf336ff21b68619010e7cc9e`
|
||||||
|
on `dev`, clean before work. Target `TINY`, profile `configs/ci/ihp130-tiny.mk`,
|
||||||
|
PDK `IHP130`, 24 MHz/no PLL remain fixed. The
|
||||||
|
[reset/feasibility runbook](tiny-soc-r2-reset-feasibility.md) owns the commands,
|
||||||
|
reset-leaf mapping, measurement endpoints and qualification boundaries.
|
||||||
|
|
||||||
|
Before editing RTL, fresh firmware, Yosys and STA completed under
|
||||||
|
`build/ihp130-tiny-2026-10-06-09-33-19f1e9c571ee/` (`APP=ci_smoke`).
|
||||||
|
`meta/tiny-r2-p2/baseline-inputs/identity.json` retains the clean source/tools;
|
||||||
|
`baseline-artifacts.json` additionally binds generated inputs, netlist/config,
|
||||||
|
SDC and native flow results. The source input digest is
|
||||||
|
`336aa85c3f213104d0d4b875ae18a427a403bb829c775904784311fd865cbfd0` and netlist
|
||||||
|
SHA-256 is `7caefc1d53aeec3d506efea6d90a7dc3845dd8177a6bba262e9fbd3814d34631`.
|
||||||
|
Fresh setup WNS/TNS are -455.18/-13049600 ns, hold WNS/TNS zero. These new
|
||||||
|
records do not change the attribution of the equal P1 timing values.
|
||||||
|
|
||||||
|
The implementation partitions reset across the existing 16 APB targets and
|
||||||
|
one fabric leaf, with five-edge local releases and CPU-last startup. Shared
|
||||||
|
debug wrappers retain default three-stage behavior and Tiny selects five.
|
||||||
|
No FIFO/CDC managed source, Crypto erasure, memory capacity, clock operating
|
||||||
|
point, register/HAL ABI, dependency version or quality policy changes.
|
||||||
|
|
||||||
|
Development checks passed both simulators' reset/FIFO fixtures, shared
|
||||||
|
three/five-stage hart reset, Tiny protocol/generator checks, I2C recovery,
|
||||||
|
clock metadata and 128 KiB SRAM protocol/reset readback. The first four-state
|
||||||
|
fixtures lacked a post-elaboration reset edge; their startup failures were
|
||||||
|
fixed in test initialization, not by changing the design. RTL format/style and
|
||||||
|
readiness passed. Common's FIFO and warm-flush fixtures passed in both
|
||||||
|
simulators with output redirected below the candidate build tree.
|
||||||
|
|
||||||
|
The initial candidate under `build/ihp130-tiny-2026-10-06-10-05-19f1e9c571ee/`
|
||||||
|
completed synthesis/STA at aggregate max-delay WNS -94.28 ns and TNS
|
||||||
|
-1594880 ns; min-delay WNS/TNS zero. These native metrics include recovery
|
||||||
|
checks, rather than only data setup. Timing still fails. The worst path is the XPI leaf
|
||||||
|
reset driver's recovery path. The first multi-point audit stopped because
|
||||||
|
Yosys renamed a leaf wire to its APB interface alias. The audit was
|
||||||
|
corrected to resolve actual mapped leaf registers and consumed nets; this
|
||||||
|
partial campaign is not complete feasibility acceptance. A subsequent probe
|
||||||
|
also rejected an invalid Tcl name-escaping expression. The final audit resolves
|
||||||
|
the preserved leaf instances, their actual output-register pins and consumed
|
||||||
|
interface nets without relying on source-level aliases or unsafe name escaping.
|
||||||
|
|
||||||
|
Final synthesis/analysis evidence uses
|
||||||
|
`build/ihp130-tiny-2026-10-06-10-25-19f1e9c571ee/meta/tiny-r2-p2/`.
|
||||||
|
`feasibility-rke9efn7/report.json` completed all 24 source-bound points: two
|
||||||
|
netlists, three corners, and four common SYS periods. Every point retains
|
||||||
|
the exact libraries/SDC, commands, metrics, reset loads/endpoints, CPU/SRAM
|
||||||
|
paths and constraint/coverage reports. No frequency point passed timing.
|
||||||
|
|
||||||
|
| Corner | Baseline aggregate WNS at 24 MHz (ns) | Candidate aggregate WNS at 24 MHz (ns) | Candidate SYS data setup slack (ns) | Candidate SYS data hold slack (ns) |
|
||||||
|
| --- | ---: | ---: | ---: | ---: |
|
||||||
|
| Slow | -455.18 | -94.28 | -87.78 | 0.22 |
|
||||||
|
| Typical | -299.07 | -51.46 | -48.16 | 0.11 |
|
||||||
|
| Fast | -185.71 | -19.94 | -19.18 | 0.02 |
|
||||||
|
|
||||||
|
| Candidate corner | SYS96 aggregate WNS (ns) | SYS192 aggregate WNS (ns) | SYS240 aggregate WNS (ns) |
|
||||||
|
| --- | ---: | ---: | ---: |
|
||||||
|
| Slow | -125.53 | -130.74 | -131.78 |
|
||||||
|
| Typical | -82.71 | -87.91 | -88.96 |
|
||||||
|
| Fast | -51.19 | -56.39 | -57.44 |
|
||||||
|
|
||||||
|
The baseline root reset directly drives 17211 loads; the candidate root drives
|
||||||
|
85 leaf-synchronizer reset pins. All 17 leaf instances and output-register
|
||||||
|
drivers remain distinct. Largest leaf loads are XPI 4710, PWM 4193 and DMA
|
||||||
|
3212. These residual loads and their recovery/data-path violations remain
|
||||||
|
explicit early feasibility limits, not a routed reset-tree pass. Both netlists
|
||||||
|
retain exactly 32 main macros driven directly by SYS. The fast SRAM/standard-cell
|
||||||
|
temperature mismatch, missing explicit macro minimum period, and ideal-clock,
|
||||||
|
unextracted analysis limitations remain as documented in the runbook.
|
||||||
|
|
||||||
|
The fresh baseline reports 207695 cells and 9107192.081398 square micrometers
|
||||||
|
of library area; the final candidate reports 208551 cells and 9109365.619198
|
||||||
|
square micrometers. These are native balanced-synthesis observations, including
|
||||||
|
normal build-identity constants, not extracted die area or a power claim.
|
||||||
|
Final metric collection retains the unchanged observe policy. Synthetic
|
||||||
|
negative-test result JSONs were initially found by the generic collector below
|
||||||
|
the full-Pytest scratch directory. After tests completed, that directory was
|
||||||
|
moved to the separate `ihp130-tiny-2026-10-06-12-38-19f1e9c571ee` build variant,
|
||||||
|
with its original path preserved as a symlink. `test-fixture-relocation.json`
|
||||||
|
records the move and the original metric file; final native firmware, synthesis
|
||||||
|
and timing values are unchanged, and synthetic test flows are excluded.
|
||||||
|
|
||||||
|
OpenOCD 0.12.0-1 and the SBT 2.0.5 launcher bundle are restored through the existing locked helper
|
||||||
|
in `.cache/retrosoc/development/tiny-r2-p2-debug`; P1's environment is unchanged.
|
||||||
|
The initial OpenOCD download's sandbox DNS failure and the initial Mini debug
|
||||||
|
attempt's SBT local-socket permission failure are retained separately. These
|
||||||
|
are environment failures, not hardware acceptance. The retry with locked tools
|
||||||
|
and local-socket permission passed `DEBUG_GDB_PASS` in 47.323 seconds under
|
||||||
|
`build/ihp130-debug-2026-10-06-10-15-b4a322720e40/sim/verilator/debug/`.
|
||||||
|
The locked VexiiRiscv project selected sbt 1.10.0 on Ubuntu Java 17.0.18,
|
||||||
|
as recorded in the generation log; its project tool selection was not changed.
|
||||||
|
This validates shared-wrapper default compatibility, not Tiny external JTAG.
|
||||||
|
|
||||||
|
The final focused suite passed 45 cases; source format/style/readiness and
|
||||||
|
Ruff passed. The Tiny PR campaign completed with exit zero in 2185.548 seconds
|
||||||
|
under `BUILD_TIMESTAMP=2026-10-06-10-26`. Both ordinary RTL simulations passed
|
||||||
|
at 3744281 cycles; Icarus took 1603.291 seconds. Netlist boot passed at 29373
|
||||||
|
cycles in 147.240 seconds. The PR synthesis and STA commands completed, while
|
||||||
|
warning observations and timing failures retain their existing policy boundary.
|
||||||
|
This is not a full-C netlist or physical pass.
|
||||||
|
|
||||||
|
The first full Pytest run recorded 1500 passed, 2 failed, 10 fixture errors and
|
||||||
|
one skipped PDF test. Both root causes were static consumers of the approved
|
||||||
|
reset parameter change: the Tiny publication collector required the old exact
|
||||||
|
three-parameter CPU dictionary, and the debug-flow test required the old
|
||||||
|
unparameterized instance spelling. The collector now explicitly requires
|
||||||
|
`ResetSyncStages=5` and includes the reset tree in source tracking; its negative
|
||||||
|
tests reject a wrong stage count and preserve the reviewed-snapshot check.
|
||||||
|
The debug test checks actual parameter forwarding and unchanged default three
|
||||||
|
stages. These compatibility updates do not alter RTL or publication identity;
|
||||||
|
no PDF or publication snapshot is refreshed. The focused consumer suite passed
|
||||||
|
31 tests. Final full Pytest passed 1517 tests with one skipped PDF retrieval
|
||||||
|
test (`pypdf` unavailable) in 3624.06 seconds. The initial failed run remains
|
||||||
|
recorded separately; focused and full-suite counts overlap.
|
||||||
|
|
||||||
|
`consumer-inputs-rv35_5et/identity.json` in the final analysis root retains the
|
||||||
|
post-flow consumer/test increment and verifies that every archived RTL,
|
||||||
|
generator, STA and workload source input is unchanged. Original timing inputs
|
||||||
|
and reports are preserved, rather than relabeled with these later test changes.
|
||||||
|
|
||||||
|
P1-image Verilator replay passed all three runs under
|
||||||
|
`build/ihp130-tiny-2026-10-06-10-27-fa1c4ce1e303/meta/tiny-r2-p1/workloads/verilator/attempt-88f2f259/`.
|
||||||
|
All measured records match the original P1 image baseline; terminal cycles
|
||||||
|
increase from 18458948 to 18458959, separately recording the intentional
|
||||||
|
11-cycle reset-startup change. Icarus attempt
|
||||||
|
`workloads/iverilog/attempt-e147iu49` under the same workload root passed all
|
||||||
|
three runs at 18458959 cycles, in 11275.215, 11254.774 and 11341.696 wall
|
||||||
|
seconds. All three logs satisfy command success, TEST_STATUS/SIM_TEST_PASS
|
||||||
|
and forbidden-marker checks. The workload root's `meta/tiny-r2-p1/baseline-report.json`
|
||||||
|
passed cross-simulator aggregation with the unchanged original HEX and identical
|
||||||
|
measured records. This directory/record retains the P1 measurement protocol
|
||||||
|
identity; its separate RTL-source snapshot identifies the P2 candidate.
|
||||||
|
|
||||||
|
Implementation and reproducible P2 evidence are ready for human review.
|
||||||
|
Timing qualification still fails at all analyzed rates, including 24 MHz;
|
||||||
|
the current executable branch is functionally validated, not physically qualified.
|
||||||
|
Full C netlist execution, routed/PVT/IO/package/silicon qualification, exhaustive
|
||||||
|
CDC/RDC and Tiny external-JTAG qualification remain unrun. No standalone formal
|
||||||
|
qualification is claimed; this phase used directed reset/protocol checks and
|
||||||
|
an independent FIFO scoreboard with unchanged Common implementations.
|
||||||
|
No Required-rule MISRA deviation was added. Publication PDF rebuilding and
|
||||||
|
snapshot advancement were not part of the consumer-compatibility repair.
|
||||||
|
No P2 acceptance or later-phase authorization is claimed.
|
||||||
|
|
||||||
## Archived P6/P10 planning and documentation evidence
|
## Archived P6/P10 planning and documentation evidence
|
||||||
|
|
||||||
The remainder of this section preserves the prior planning and P10 document
|
The remainder of this section preserves the prior planning and P10 document
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
# Source-bound Tiny R2 feasibility only. No timing exceptions are added here.
|
||||||
|
source [file join [file dirname [info script]] opensta.tcl]
|
||||||
|
set output $::env(TINY_R2_OUTPUT)
|
||||||
|
set candidate $::env(TINY_R2_CANDIDATE)
|
||||||
|
report_clock_properties > $output/clocks.rpt
|
||||||
|
report_checks -path_group clk_system -path_delay max -group_path_count 1 -fields {capacitance slew fanout} > $output/sys-setup.rpt
|
||||||
|
report_checks -path_group clk_system -path_delay min -group_path_count 1 -fields {capacitance slew fanout} > $output/sys-hold.rpt
|
||||||
|
report_check_types -recovery -removal -min_pulse_width -min_period -max_slew -max_capacitance -max_fanout -violators -verbose > $output/constraint-violations.rpt
|
||||||
|
check_setup -verbose > $output/coverage.rpt
|
||||||
|
|
||||||
|
set counts [open $output/reset-loads.tsv w]
|
||||||
|
puts $counts "name\tdrivers\tdirect_loads\tstructural_endpoints"
|
||||||
|
proc reset_report {label nets {drivers {}}} {
|
||||||
|
global output counts
|
||||||
|
if {[llength $nets] != 1} { error "expected one reset net for $label" }
|
||||||
|
set net [lindex $nets 0]
|
||||||
|
if {[llength $drivers] == 0} {
|
||||||
|
set drivers [get_pins -of_objects $net -filter {direction == output}]
|
||||||
|
}
|
||||||
|
if {[llength $drivers] != 1} { error "expected one reset driver for $label" }
|
||||||
|
set direct {}
|
||||||
|
foreach pin [get_fanout -from $drivers -flat -pin_levels 1 -trace_arcs all] {
|
||||||
|
if {[get_property $pin direction] eq "input"} { lappend direct $pin }
|
||||||
|
}
|
||||||
|
set endpoints [get_fanout -from $drivers -flat -endpoints_only -trace_arcs all]
|
||||||
|
puts $counts "$label\t[get_full_name [lindex $drivers 0]]\t[llength $direct]\t[llength $endpoints]"
|
||||||
|
report_net [get_full_name $net] > $output/$label-net.rpt
|
||||||
|
report_checks -through $drivers -path_delay min_max -group_path_count 10 -fields {capacitance slew fanout} > $output/$label-paths.rpt
|
||||||
|
set file [open $output/$label-endpoints.rpt w]
|
||||||
|
foreach pin $endpoints { puts $file [get_full_name $pin] }
|
||||||
|
close $file
|
||||||
|
}
|
||||||
|
reset_report system [get_nets -quiet {u_soc.s_rst_n}]
|
||||||
|
set leaf_instances [get_cells -hierarchical -quiet {*u_leaf_rst_sync}]
|
||||||
|
if {$candidate} {
|
||||||
|
if {[llength $leaf_instances] != 17} { error "reset leaves merged or missing" }
|
||||||
|
set output_register_pins [get_pins -hierarchical -quiet {*rst_n_o_reg/Q}]
|
||||||
|
for {set index 0} {$index < 17} {incr index} {
|
||||||
|
set selected {}
|
||||||
|
foreach instance $leaf_instances {
|
||||||
|
set name [get_full_name $instance]
|
||||||
|
if {[regexp {gen_leaf\[([0-9]+)\][.]u_leaf_rst_sync$} $name -> leaf_index] && $leaf_index == $index} {
|
||||||
|
lappend selected $instance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if {[llength $selected] != 1} { error "leaf index missing or duplicated: $index" }
|
||||||
|
set name [get_full_name [lindex $selected 0]]
|
||||||
|
set driver {}
|
||||||
|
foreach pin $output_register_pins {
|
||||||
|
if {[get_full_name $pin] eq "$name/rst_n_o_reg/Q"} { lappend driver $pin }
|
||||||
|
}
|
||||||
|
if {[llength $driver] != 1} { error "mapped leaf output register missing: $name" }
|
||||||
|
# Yosys may rename an alias to its APB interface. Resolve the consumed
|
||||||
|
# net through the preserved leaf instance, not a source-level wire name.
|
||||||
|
set outputs [get_pins -of_objects $selected -filter {direction == output}]
|
||||||
|
reset_report leaf-$index [get_nets -of_objects $outputs] $driver
|
||||||
|
}
|
||||||
|
} elseif {[llength $leaf_instances] != 0} {
|
||||||
|
error "pre-change netlist already contains reset leaves"
|
||||||
|
}
|
||||||
|
close $counts
|
||||||
|
|
||||||
|
set clock_source [get_pins -quiet {u_clock_buffer/clk_o}]
|
||||||
|
if {[llength $clock_source] != 1} { error "system clock source missing" }
|
||||||
|
set macro_clocks {}
|
||||||
|
foreach pin [get_fanout -from $clock_source -flat -pin_levels 1 -trace_arcs all] {
|
||||||
|
if {[string match {*/A_CLK} [get_full_name $pin]]} { lappend macro_clocks $pin }
|
||||||
|
}
|
||||||
|
if {[llength $macro_clocks] != 32} { error "expected 32 direct SYS main-SRAM clock loads" }
|
||||||
|
set macros [get_cells -hierarchical -filter {ref_name == RM_IHPSG13_1P_1024x32_c2_bm_bist}]
|
||||||
|
if {[llength $macros] != 32} { error "main-SRAM macro binding differs from 32" }
|
||||||
|
set file [open $output/sram-clock-pins.rpt w]
|
||||||
|
foreach pin $macro_clocks { puts $file [get_full_name $pin] }
|
||||||
|
close $file
|
||||||
|
set macro_inputs {}
|
||||||
|
set macro_outputs {}
|
||||||
|
foreach pin [get_pins -of_objects $macros] {
|
||||||
|
set name [get_full_name $pin]
|
||||||
|
if {[string match {*/A_DOUT*} $name]} { lappend macro_outputs $pin }
|
||||||
|
if {[regexp {/A_(ADDR|DIN|BM|WEN|REN|MEN)} $name]} { lappend macro_inputs $pin }
|
||||||
|
}
|
||||||
|
if {[llength $macro_inputs] == 0 || [llength $macro_outputs] == 0} { error "SRAM data timing pins missing" }
|
||||||
|
report_checks -to $macro_inputs -path_delay min_max -group_path_count 20 -fields {capacitance slew fanout} > $output/sram-input-paths.rpt
|
||||||
|
report_checks -from $macro_outputs -path_delay min_max -group_path_count 20 -fields {capacitance slew fanout} > $output/sram-return-paths.rpt
|
||||||
|
set cpu_pins [get_pins -hierarchical -quiet {*u_cpu*/Q}]
|
||||||
|
if {[llength $cpu_pins] == 0} { error "CPU sequential launch pins missing" }
|
||||||
|
report_checks -from $cpu_pins -path_delay min_max -group_path_count 20 -fields {capacitance slew fanout} > $output/cpu-paths.rpt
|
||||||
|
puts "TINY_R2_FEASIBILITY_PASS: source-bound reports; 32 SYS macros; [llength $leaf_instances] reset leaves"
|
||||||
@@ -23,6 +23,7 @@ TOPOLOGY = "rtl/tiny/integration/soc_topology.json"
|
|||||||
PINS = "rtl/tiny/pin_map/pin_map.json"
|
PINS = "rtl/tiny/pin_map/pin_map.json"
|
||||||
CLOCKS = "rtl/tiny/integration/clock_reset_domains.json"
|
CLOCKS = "rtl/tiny/integration/clock_reset_domains.json"
|
||||||
TOP = "rtl/tiny/top/retrosoc_tiny.sv"
|
TOP = "rtl/tiny/top/retrosoc_tiny.sv"
|
||||||
|
RESET_TREE = "rtl/tiny/top/tiny_reset_tree.sv"
|
||||||
SYSCTRL = "rtl/tiny/top/tiny_sysctrl.sv"
|
SYSCTRL = "rtl/tiny/top/tiny_sysctrl.sv"
|
||||||
ARCHINFO = "rtl/tiny/top/tiny_archinfo.sv"
|
ARCHINFO = "rtl/tiny/top/tiny_archinfo.sv"
|
||||||
FAMILIES = {"xpi", "sram", "gpio", "dma", "timer", "pwm", "rtc", "wdg", "uart", "i2c", "clint", "archinfo"}
|
FAMILIES = {"xpi", "sram", "gpio", "dma", "timer", "pwm", "rtc", "wdg", "uart", "i2c", "clint", "archinfo"}
|
||||||
@@ -71,8 +72,9 @@ def facts(root: Path = ROOT) -> dict:
|
|||||||
raise ValueError("Tiny SRAM parameters changed")
|
raise ValueError("Tiny SRAM parameters changed")
|
||||||
if dma != {"NumChannels": 4, "MaxBurstBeats": 16, "FifoDepth": 32, "RequestMask": 0x7F9, "EnableStreams": 0}:
|
if dma != {"NumChannels": 4, "MaxBurstBeats": 16, "FifoDepth": 32, "RequestMask": 0x7F9, "EnableStreams": 0}:
|
||||||
raise ValueError("Tiny DMA parameters changed")
|
raise ValueError("Tiny DMA parameters changed")
|
||||||
if cpu != {"ExternalIrqCount": 30, "EnableAtomics": 0, "TwoCycleBusErrors": 1}:
|
if cpu != {"ExternalIrqCount": 30, "EnableAtomics": 0, "TwoCycleBusErrors": 1, "ResetSyncStages": 5}:
|
||||||
raise ValueError("Tiny CPU integration changed")
|
raise ValueError("Tiny CPU integration changed")
|
||||||
|
require_snippets(root, RESET_TREE, [".STAGE(5)", "assign cpu_ready_rst_n_o = &leaf_rst_n_o;"])
|
||||||
require_snippets(root, "rtl/ip/core/mgmt_core_wrapper.sv", [".EXTENSION_C(1)", ".EXTENSION_A(EnableAtomics)"])
|
require_snippets(root, "rtl/ip/core/mgmt_core_wrapper.sv", [".EXTENSION_C(1)", ".EXTENSION_A(EnableAtomics)"])
|
||||||
require_snippets(root, TOP, ["u_masters_axi4_if[2]", ".axi4(u_masters_axi4_if[0])",
|
require_snippets(root, TOP, ["u_masters_axi4_if[2]", ".axi4(u_masters_axi4_if[0])",
|
||||||
".axi4(u_masters_axi4_if[1])", ".cfg_apb4(u_sram_apb4_if)",
|
".axi4(u_masters_axi4_if[1])", ".cfg_apb4(u_sram_apb4_if)",
|
||||||
@@ -225,7 +227,7 @@ def collect(config: dict, *, check_snapshot: bool = True) -> dict:
|
|||||||
evidence = read(ROOT / BOOK / "evidence.json")
|
evidence = read(ROOT / BOOK / "evidence.json")
|
||||||
validate_evidence(evidence)
|
validate_evidence(evidence)
|
||||||
lock = read(ROOT / "dependencies/dependencies.lock.json")
|
lock = read(ROOT / "dependencies/dependencies.lock.json")
|
||||||
paths = {MAP, TOPOLOGY, PINS, CLOCKS, TOP, SYSCTRL, ARCHINFO, config["profile"],
|
paths = {MAP, TOPOLOGY, PINS, CLOCKS, TOP, RESET_TREE, SYSCTRL, ARCHINFO, config["profile"],
|
||||||
"docs/ip/tiny-soc.md", "docs/ip/tiny-soc-verification.md", "dependencies/dependencies.lock.json", "LICENSE"}
|
"docs/ip/tiny-soc.md", "docs/ip/tiny-soc-verification.md", "dependencies/dependencies.lock.json", "LICENSE"}
|
||||||
paths.update(contract["sources"])
|
paths.update(contract["sources"])
|
||||||
paths.update(b["file"] for b in contract["bindings"])
|
paths.update(b["file"] for b in contract["bindings"])
|
||||||
|
|||||||
@@ -13,7 +13,8 @@
|
|||||||
module mgmt_core_wrapper #(
|
module mgmt_core_wrapper #(
|
||||||
parameter int ExternalIrqCount = 30,
|
parameter int ExternalIrqCount = 30,
|
||||||
parameter bit EnableAtomics = 1'b1,
|
parameter bit EnableAtomics = 1'b1,
|
||||||
parameter bit TwoCycleBusErrors = 1'b0
|
parameter bit TwoCycleBusErrors = 1'b0,
|
||||||
|
parameter int ResetSyncStages = 3
|
||||||
) (
|
) (
|
||||||
// verilog_format: off -- preserve reviewed column alignment
|
// verilog_format: off -- preserve reviewed column alignment
|
||||||
input logic clk_i,
|
input logic clk_i,
|
||||||
@@ -68,7 +69,8 @@ module mgmt_core_wrapper #(
|
|||||||
// verilog_format: on
|
// verilog_format: on
|
||||||
|
|
||||||
mgmt_debug_wrapper #(
|
mgmt_debug_wrapper #(
|
||||||
.JtagIdcode(`SOC_JTAG_IDCODE)
|
.JtagIdcode (`SOC_JTAG_IDCODE),
|
||||||
|
.ResetSyncStages(ResetSyncStages)
|
||||||
) u_mgmt_debug_wrapper (
|
) u_mgmt_debug_wrapper (
|
||||||
.clk_i (clk_i),
|
.clk_i (clk_i),
|
||||||
.rst_n_i (rst_n_i),
|
.rst_n_i (rst_n_i),
|
||||||
|
|||||||
@@ -11,7 +11,9 @@
|
|||||||
// Converts Debug Module reset requests into a management-hart-only reset.
|
// Converts Debug Module reset requests into a management-hart-only reset.
|
||||||
// The request is retained until the AHB-Lite bridge has completed its current
|
// The request is retained until the AHB-Lite bridge has completed its current
|
||||||
// AXI4 transfer, so a debugger cannot discard an accepted management access.
|
// AXI4 transfer, so a debugger cannot discard an accepted management access.
|
||||||
module mgmt_debug_reset (
|
module mgmt_debug_reset #(
|
||||||
|
parameter int ResetSyncStages = 3
|
||||||
|
) (
|
||||||
input logic clk_i,
|
input logic clk_i,
|
||||||
input logic rst_n_i,
|
input logic rst_n_i,
|
||||||
input logic reset_req_i,
|
input logic reset_req_i,
|
||||||
@@ -55,7 +57,9 @@ module mgmt_debug_reset (
|
|||||||
.dat_i (s_reset_active_d),
|
.dat_i (s_reset_active_d),
|
||||||
.dat_o (s_reset_active_q)
|
.dat_o (s_reset_active_q)
|
||||||
);
|
);
|
||||||
rst_sync u_core_rst_sync (
|
rst_sync #(
|
||||||
|
.STAGE(ResetSyncStages)
|
||||||
|
) u_core_rst_sync (
|
||||||
.clk_i (clk_i),
|
.clk_i (clk_i),
|
||||||
.rst_n_i(rst_n_i && !s_reset_active_q),
|
.rst_n_i(rst_n_i && !s_reset_active_q),
|
||||||
.rst_n_o(core_rst_n_o)
|
.rst_n_o(core_rst_n_o)
|
||||||
|
|||||||
@@ -11,7 +11,8 @@
|
|||||||
// Hazard3 JTAG-DTM and Debug Module integration. The Debug Module is in the
|
// Hazard3 JTAG-DTM and Debug Module integration. The Debug Module is in the
|
||||||
// management-core clock domain and exposes abstract commands only (no SBA).
|
// management-core clock domain and exposes abstract commands only (no SBA).
|
||||||
module mgmt_debug_wrapper #(
|
module mgmt_debug_wrapper #(
|
||||||
parameter logic [31:0] JtagIdcode = 32'hDEAD_BEEF
|
parameter logic [31:0] JtagIdcode = 32'hDEAD_BEEF,
|
||||||
|
parameter int ResetSyncStages = 3
|
||||||
) (
|
) (
|
||||||
input logic clk_i,
|
input logic clk_i,
|
||||||
input logic rst_n_i,
|
input logic rst_n_i,
|
||||||
@@ -67,7 +68,9 @@ module mgmt_debug_wrapper #(
|
|||||||
.clk_i(jtag_tck_i),
|
.clk_i(jtag_tck_i),
|
||||||
.clk_o(s_jtag_tck_buf)
|
.clk_o(s_jtag_tck_buf)
|
||||||
);
|
);
|
||||||
rst_sync u_jtag_rst_sync (
|
rst_sync #(
|
||||||
|
.STAGE(ResetSyncStages)
|
||||||
|
) u_jtag_rst_sync (
|
||||||
.clk_i (s_jtag_tck_buf),
|
.clk_i (s_jtag_tck_buf),
|
||||||
.rst_n_i(jtag_trst_n_i),
|
.rst_n_i(jtag_trst_n_i),
|
||||||
.rst_n_o(s_jtag_trst_n_sync)
|
.rst_n_o(s_jtag_trst_n_sync)
|
||||||
@@ -78,7 +81,9 @@ module mgmt_debug_wrapper #(
|
|||||||
.dat_i (s_dmihardreset_req),
|
.dat_i (s_dmihardreset_req),
|
||||||
.dat_o (s_dmihardreset_sync)
|
.dat_o (s_dmihardreset_sync)
|
||||||
);
|
);
|
||||||
rst_sync u_dmi_rst_sync (
|
rst_sync #(
|
||||||
|
.STAGE(ResetSyncStages)
|
||||||
|
) u_dmi_rst_sync (
|
||||||
.clk_i (clk_i),
|
.clk_i (clk_i),
|
||||||
.rst_n_i(rst_n_i && !s_dmihardreset_sync),
|
.rst_n_i(rst_n_i && !s_dmihardreset_sync),
|
||||||
.rst_n_o(s_dmi_rst_n)
|
.rst_n_o(s_dmi_rst_n)
|
||||||
@@ -146,7 +151,9 @@ module mgmt_debug_wrapper #(
|
|||||||
.sbus_rdata (dbg_sbus_rdata_i)
|
.sbus_rdata (dbg_sbus_rdata_i)
|
||||||
);
|
);
|
||||||
|
|
||||||
mgmt_debug_reset u_mgmt_debug_reset (
|
mgmt_debug_reset #(
|
||||||
|
.ResetSyncStages(ResetSyncStages)
|
||||||
|
) u_mgmt_debug_reset (
|
||||||
.clk_i (clk_i),
|
.clk_i (clk_i),
|
||||||
.rst_n_i (rst_n_i),
|
.rst_n_i (rst_n_i),
|
||||||
.reset_req_i (s_sys_reset_req || s_hart_reset_req),
|
.reset_req_i (s_sys_reset_req || s_hart_reset_req),
|
||||||
|
|||||||
@@ -437,6 +437,7 @@
|
|||||||
"tiny_archinfo.sv",
|
"tiny_archinfo.sv",
|
||||||
"tiny_axi42apb4.sv",
|
"tiny_axi42apb4.sv",
|
||||||
"tiny_axi4_fabric.sv",
|
"tiny_axi4_fabric.sv",
|
||||||
|
"tiny_reset_tree.sv",
|
||||||
"tiny_sysctrl.sv"
|
"tiny_sysctrl.sv"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -39,6 +39,14 @@ the contract history. The committed RTL/profile still implements the initial
|
|||||||
24 MHz/no-PLL/four-channel design; no faster CPU/SRAM or PLL timing support
|
24 MHz/no-PLL/four-channel design; no faster CPU/SRAM or PLL timing support
|
||||||
follows from this freeze or the `HAVE_PLL` selector alone.
|
follows from this freeze or the `HAVE_PLL` selector alone.
|
||||||
|
|
||||||
|
The R2-P2 candidate keeps that executable clock/memory configuration and adds
|
||||||
|
five-edge local reset release, separate reset leaves for the existing targets,
|
||||||
|
and CPU-last startup. Shared debug-wrapper defaults remain unchanged for Mini.
|
||||||
|
The [reset/feasibility runbook](../../docs/ip/tiny-soc-r2-reset-feasibility.md)
|
||||||
|
defines the source-bound audit and analysis-only frequency sweep. Results and
|
||||||
|
phase acceptance remain in the verification ledger; this is not routed timing
|
||||||
|
or high-frequency qualification.
|
||||||
|
|
||||||
The separately approved [PIO-lite extension](../../docs/ip/piolite.md) is part
|
The separately approved [PIO-lite extension](../../docs/ip/piolite.md) is part
|
||||||
of the future standard Tiny product, with its own `PIOLITE-P0` through
|
of the future standard Tiny product, with its own `PIOLITE-P0` through
|
||||||
`PIOLITE-P5` phases. It adds two state machines, shared 32 x 16-bit program
|
`PIOLITE-P5` phases. It adds two state machines, shared 32 x 16-bit program
|
||||||
|
|||||||
@@ -2,5 +2,6 @@
|
|||||||
/top/tiny_axi42apb4.sv
|
/top/tiny_axi42apb4.sv
|
||||||
/top/tiny_sysctrl.sv
|
/top/tiny_sysctrl.sv
|
||||||
/top/tiny_archinfo.sv
|
/top/tiny_archinfo.sv
|
||||||
|
/top/tiny_reset_tree.sv
|
||||||
/top/retrosoc_tiny.sv
|
/top/retrosoc_tiny.sv
|
||||||
/top/retrosoc_tiny_asic.sv
|
/top/retrosoc_tiny_asic.sv
|
||||||
@@ -8,10 +8,10 @@
|
|||||||
{
|
{
|
||||||
"name": "system",
|
"name": "system",
|
||||||
"clock": "clk_i",
|
"clock": "clk_i",
|
||||||
"reset": "s_rst_n",
|
"reset": "system_rst_n_o",
|
||||||
"reset_source": "rst_n_i",
|
"reset_source": "s_system_req_n",
|
||||||
"reset_primitive": "rst_sync",
|
"reset_primitive": "rst_sync",
|
||||||
"path": "rtl/tiny/top/retrosoc_tiny.sv",
|
"path": "rtl/tiny/top/tiny_reset_tree.sv",
|
||||||
"instance": "u_system_rst_sync",
|
"instance": "u_system_rst_sync",
|
||||||
"sta": {
|
"sta": {
|
||||||
"source_port": "extclk_i_pad",
|
"source_port": "extclk_i_pad",
|
||||||
@@ -36,6 +36,25 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"reset_distribution": {
|
||||||
|
"release_edges": 5,
|
||||||
|
"por": "u_soc.u_reset_tree.u_por_rst_sync",
|
||||||
|
"system": "u_soc.u_reset_tree.u_system_rst_sync",
|
||||||
|
"system_source": "por_rst_n_o && !watchdog_reset_req_i",
|
||||||
|
"leaf_instance_pattern": "u_soc.u_reset_tree.gen_leaf[%d].u_leaf_rst_sync",
|
||||||
|
"leaf_names": [
|
||||||
|
"fabric", "gpio", "uart0", "uart1", "timer0", "timer1", "i2c0", "i2c1",
|
||||||
|
"xpi", "dma", "sysctrl", "clint", "sram", "archinfo", "pwm", "rtc", "wdg"
|
||||||
|
],
|
||||||
|
"cpu_prerequisite": "all leaf resets released",
|
||||||
|
"hart": "u_soc.u_cpu.u_mgmt_debug_wrapper.u_mgmt_debug_reset.u_core_rst_sync",
|
||||||
|
"jtag": "u_soc.u_cpu.u_mgmt_debug_wrapper.u_jtag_rst_sync",
|
||||||
|
"dmi": "u_soc.u_cpu.u_mgmt_debug_wrapper.u_dmi_rst_sync",
|
||||||
|
"watchdog_functional_reset": "POR only; APB uses wdg leaf",
|
||||||
|
"main_sram_payload_reset": "none",
|
||||||
|
"crypto": "not integrated; physical erasure contract unchanged",
|
||||||
|
"clock_domain": "system"
|
||||||
|
},
|
||||||
"crossings": [
|
"crossings": [
|
||||||
{
|
{
|
||||||
"name": "debug_dmi",
|
"name": "debug_dmi",
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ module retrosoc_tiny (
|
|||||||
output logic [7:0] test_code_o
|
output logic [7:0] test_code_o
|
||||||
);
|
);
|
||||||
logic s_por_rst_n, s_rst_n;
|
logic s_por_rst_n, s_rst_n;
|
||||||
|
logic [16:0] s_leaf_rst_n;
|
||||||
|
logic s_fabric_rst_n, s_cpu_ready_rst_n;
|
||||||
|
logic s_master_rst_n [2];
|
||||||
|
logic s_target_rst_n [5];
|
||||||
logic s_debug_halted;
|
logic s_debug_halted;
|
||||||
logic [31:0] s_irq;
|
logic [31:0] s_irq;
|
||||||
logic s_fault_valid, s_fault_master, s_fault_write;
|
logic s_fault_valid, s_fault_master, s_fault_write;
|
||||||
@@ -34,7 +38,8 @@ module retrosoc_tiny (
|
|||||||
logic [4:0] s_tick_count_d, s_tick_count_q;
|
logic [4:0] s_tick_count_d, s_tick_count_q;
|
||||||
logic s_tick;
|
logic s_tick;
|
||||||
logic s_unused_uart1_tx_stall, s_unused_uart1_rx_stall;
|
logic s_unused_uart1_tx_stall, s_unused_uart1_rx_stall;
|
||||||
uart_if u_uart0_if ();
|
`include "tiny_apb_interfaces.svh"
|
||||||
|
uart_if u_uart0_if ();
|
||||||
uart_if u_uart1_if ();
|
uart_if u_uart1_if ();
|
||||||
i2c_if u_i2c0_if ();
|
i2c_if u_i2c0_if ();
|
||||||
i2c_if u_i2c1_if ();
|
i2c_if u_i2c1_if ();
|
||||||
@@ -45,7 +50,7 @@ module retrosoc_tiny (
|
|||||||
dma_req_if u_dma_req_if ();
|
dma_req_if u_dma_req_if ();
|
||||||
rtc_if u_rtc_if (
|
rtc_if u_rtc_if (
|
||||||
.rtc_clk_i (clk_i),
|
.rtc_clk_i (clk_i),
|
||||||
.rtc_rst_n_i(s_rst_n)
|
.rtc_rst_n_i(s_rtc_rst_n)
|
||||||
);
|
);
|
||||||
// The watchdog reset pulse must survive resetting the bus/CPU. Its timer
|
// The watchdog reset pulse must survive resetting the bus/CPU. Its timer
|
||||||
// uses POR reset while its APB register domain uses the system reset.
|
// uses POR reset while its APB register domain uses the system reset.
|
||||||
@@ -60,7 +65,7 @@ module retrosoc_tiny (
|
|||||||
.USER_WIDTH(1)
|
.USER_WIDTH(1)
|
||||||
) u_masters_axi4_if[2] (
|
) u_masters_axi4_if[2] (
|
||||||
.aclk (clk_i),
|
.aclk (clk_i),
|
||||||
.aresetn(s_rst_n)
|
.aresetn(s_master_rst_n)
|
||||||
);
|
);
|
||||||
axi4_if #(
|
axi4_if #(
|
||||||
.ADDR_WIDTH(32),
|
.ADDR_WIDTH(32),
|
||||||
@@ -69,49 +74,56 @@ module retrosoc_tiny (
|
|||||||
.USER_WIDTH(1)
|
.USER_WIDTH(1)
|
||||||
) u_targets_axi4_if[5] (
|
) u_targets_axi4_if[5] (
|
||||||
.aclk (clk_i),
|
.aclk (clk_i),
|
||||||
.aresetn(s_rst_n)
|
.aresetn(s_target_rst_n)
|
||||||
);
|
);
|
||||||
axi4_stream_if #(
|
axi4_stream_if #(
|
||||||
.DATA_WIDTH(32)
|
.DATA_WIDTH(32)
|
||||||
) u_i2s_tx_axis_if (
|
) u_i2s_tx_axis_if (
|
||||||
.aclk (clk_i),
|
.aclk (clk_i),
|
||||||
.aresetn(s_rst_n)
|
.aresetn(s_dma_rst_n)
|
||||||
);
|
);
|
||||||
axi4_stream_if #(
|
axi4_stream_if #(
|
||||||
.DATA_WIDTH(32)
|
.DATA_WIDTH(32)
|
||||||
) u_i2s_rx_axis_if (
|
) u_i2s_rx_axis_if (
|
||||||
.aclk (clk_i),
|
.aclk (clk_i),
|
||||||
.aresetn(s_rst_n)
|
.aresetn(s_dma_rst_n)
|
||||||
);
|
);
|
||||||
axi4_stream_if #(
|
axi4_stream_if #(
|
||||||
.DATA_WIDTH(32)
|
.DATA_WIDTH(32)
|
||||||
) u_dvp_rx_axis_if (
|
) u_dvp_rx_axis_if (
|
||||||
.aclk (clk_i),
|
.aclk (clk_i),
|
||||||
.aresetn(s_rst_n)
|
.aresetn(s_dma_rst_n)
|
||||||
);
|
);
|
||||||
axi4_stream_if #(
|
axi4_stream_if #(
|
||||||
.DATA_WIDTH(32)
|
.DATA_WIDTH(32)
|
||||||
) u_crypto_tx_axis_if (
|
) u_crypto_tx_axis_if (
|
||||||
.aclk (clk_i),
|
.aclk (clk_i),
|
||||||
.aresetn(s_rst_n)
|
.aresetn(s_dma_rst_n)
|
||||||
);
|
);
|
||||||
axi4_stream_if #(
|
axi4_stream_if #(
|
||||||
.DATA_WIDTH(32)
|
.DATA_WIDTH(32)
|
||||||
) u_crypto_rx_axis_if (
|
) u_crypto_rx_axis_if (
|
||||||
.aclk (clk_i),
|
.aclk (clk_i),
|
||||||
.aresetn(s_rst_n)
|
.aresetn(s_dma_rst_n)
|
||||||
);
|
);
|
||||||
`include "tiny_apb_interfaces.svh"
|
assign s_fabric_rst_n = s_leaf_rst_n[0];
|
||||||
|
assign s_master_rst_n = '{s_cpu_ready_rst_n, s_dma_rst_n};
|
||||||
|
assign s_target_rst_n = '{
|
||||||
|
s_sram_rst_n,
|
||||||
|
s_xpi_rst_n,
|
||||||
|
s_fabric_rst_n,
|
||||||
|
s_fabric_rst_n,
|
||||||
|
s_fabric_rst_n
|
||||||
|
};
|
||||||
|
|
||||||
rst_sync u_por_rst_sync (
|
tiny_reset_tree u_reset_tree (
|
||||||
.clk_i (clk_i),
|
.clk_i (clk_i),
|
||||||
.rst_n_i(rst_n_i),
|
.rst_n_i (rst_n_i),
|
||||||
.rst_n_o(s_por_rst_n)
|
.watchdog_reset_req_i(u_wdg_if.reset_req_o),
|
||||||
);
|
.por_rst_n_o (s_por_rst_n),
|
||||||
rst_sync u_system_rst_sync (
|
.system_rst_n_o (s_rst_n),
|
||||||
.clk_i (clk_i),
|
.leaf_rst_n_o (s_leaf_rst_n),
|
||||||
.rst_n_i(s_por_rst_n && !u_wdg_if.reset_req_o),
|
.cpu_ready_rst_n_o (s_cpu_ready_rst_n)
|
||||||
.rst_n_o(s_rst_n)
|
|
||||||
);
|
);
|
||||||
assign s_tick = s_tick_count_q == 5'd23;
|
assign s_tick = s_tick_count_q == 5'd23;
|
||||||
assign s_tick_count_d = s_tick ? 5'd0 : (s_tick_count_q + 5'd1);
|
assign s_tick_count_d = s_tick ? 5'd0 : (s_tick_count_q + 5'd1);
|
||||||
@@ -119,17 +131,18 @@ rst_sync u_por_rst_sync (
|
|||||||
.DATA_WIDTH(5)
|
.DATA_WIDTH(5)
|
||||||
) u_tick_reg (
|
) u_tick_reg (
|
||||||
.clk_i (clk_i),
|
.clk_i (clk_i),
|
||||||
.rst_n_i(s_rst_n),
|
.rst_n_i(s_fabric_rst_n),
|
||||||
.dat_i (s_tick_count_d),
|
.dat_i (s_tick_count_d),
|
||||||
.dat_o (s_tick_count_q)
|
.dat_o (s_tick_count_q)
|
||||||
);
|
);
|
||||||
mgmt_core_wrapper #(
|
mgmt_core_wrapper #(
|
||||||
.ExternalIrqCount (30),
|
.ExternalIrqCount (30),
|
||||||
.EnableAtomics (1'b0),
|
.EnableAtomics (1'b0),
|
||||||
.TwoCycleBusErrors(1'b1)
|
.TwoCycleBusErrors(1'b1),
|
||||||
|
.ResetSyncStages (5)
|
||||||
) u_cpu (
|
) u_cpu (
|
||||||
.clk_i (clk_i),
|
.clk_i (clk_i),
|
||||||
.rst_n_i (s_rst_n),
|
.rst_n_i (s_cpu_ready_rst_n),
|
||||||
.irq_i (s_irq),
|
.irq_i (s_irq),
|
||||||
.jtag_tck_i (jtag_tck_i),
|
.jtag_tck_i (jtag_tck_i),
|
||||||
.jtag_tms_i (jtag_tms_i),
|
.jtag_tms_i (jtag_tms_i),
|
||||||
@@ -141,7 +154,7 @@ rst_sync u_por_rst_sync (
|
|||||||
);
|
);
|
||||||
tiny_axi4_fabric u_fabric (
|
tiny_axi4_fabric u_fabric (
|
||||||
.clk_i (clk_i),
|
.clk_i (clk_i),
|
||||||
.rst_n_i (s_rst_n),
|
.rst_n_i (s_fabric_rst_n),
|
||||||
.masters (u_masters_axi4_if),
|
.masters (u_masters_axi4_if),
|
||||||
.targets (u_targets_axi4_if),
|
.targets (u_targets_axi4_if),
|
||||||
.perf_enable_i (s_perf_enable),
|
.perf_enable_i (s_perf_enable),
|
||||||
@@ -160,7 +173,7 @@ rst_sync u_por_rst_sync (
|
|||||||
.IdWidth (1)
|
.IdWidth (1)
|
||||||
) u_sram (
|
) u_sram (
|
||||||
.clk_i (clk_i),
|
.clk_i (clk_i),
|
||||||
.rst_n_i (s_rst_n),
|
.rst_n_i (s_sram_rst_n),
|
||||||
.mem_axi4 (u_targets_axi4_if[0]),
|
.mem_axi4 (u_targets_axi4_if[0]),
|
||||||
.cfg_apb4 (u_sram_apb4_if),
|
.cfg_apb4 (u_sram_apb4_if),
|
||||||
.perf_enable_i(s_perf_enable),
|
.perf_enable_i(s_perf_enable),
|
||||||
@@ -168,7 +181,7 @@ rst_sync u_por_rst_sync (
|
|||||||
);
|
);
|
||||||
apb4_xpi u_xpi (
|
apb4_xpi u_xpi (
|
||||||
.clk_i (clk_i),
|
.clk_i (clk_i),
|
||||||
.rst_n_i (s_rst_n),
|
.rst_n_i (s_xpi_rst_n),
|
||||||
.mem_axi4 (u_targets_axi4_if[1]),
|
.mem_axi4 (u_targets_axi4_if[1]),
|
||||||
.apb4 (u_xpi_apb4_if),
|
.apb4 (u_xpi_apb4_if),
|
||||||
.xpi (xpi),
|
.xpi (xpi),
|
||||||
@@ -179,7 +192,7 @@ rst_sync u_por_rst_sync (
|
|||||||
);
|
);
|
||||||
tiny_axi42apb4 u_apb_bridge (
|
tiny_axi42apb4 u_apb_bridge (
|
||||||
.clk_i (clk_i),
|
.clk_i (clk_i),
|
||||||
.rst_n_i(s_rst_n),
|
.rst_n_i(s_fabric_rst_n),
|
||||||
.axi4 (u_targets_axi4_if[2]),
|
.axi4 (u_targets_axi4_if[2]),
|
||||||
`include "tiny_apb_connections.svh"
|
`include "tiny_apb_connections.svh"
|
||||||
);
|
);
|
||||||
@@ -187,19 +200,19 @@ rst_sync u_por_rst_sync (
|
|||||||
.Response(2'b11)
|
.Response(2'b11)
|
||||||
) u_decode_error (
|
) u_decode_error (
|
||||||
.clk_i (clk_i),
|
.clk_i (clk_i),
|
||||||
.rst_n_i(s_rst_n),
|
.rst_n_i(s_fabric_rst_n),
|
||||||
.axi4 (u_targets_axi4_if[3])
|
.axi4 (u_targets_axi4_if[3])
|
||||||
);
|
);
|
||||||
axi4_error_slave #(
|
axi4_error_slave #(
|
||||||
.Response(2'b10)
|
.Response(2'b10)
|
||||||
) u_protocol_error (
|
) u_protocol_error (
|
||||||
.clk_i (clk_i),
|
.clk_i (clk_i),
|
||||||
.rst_n_i(s_rst_n),
|
.rst_n_i(s_fabric_rst_n),
|
||||||
.axi4 (u_targets_axi4_if[4])
|
.axi4 (u_targets_axi4_if[4])
|
||||||
);
|
);
|
||||||
tiny_sysctrl u_sysctrl (
|
tiny_sysctrl u_sysctrl (
|
||||||
.clk_i (clk_i),
|
.clk_i (clk_i),
|
||||||
.rst_n_i (s_rst_n),
|
.rst_n_i (s_sysctrl_rst_n),
|
||||||
.apb4 (u_sysctrl_apb4_if),
|
.apb4 (u_sysctrl_apb4_if),
|
||||||
.fault_valid_i (s_fault_valid),
|
.fault_valid_i (s_fault_valid),
|
||||||
.fault_addr_i (s_fault_addr),
|
.fault_addr_i (s_fault_addr),
|
||||||
@@ -217,7 +230,7 @@ rst_sync u_por_rst_sync (
|
|||||||
);
|
);
|
||||||
tiny_archinfo u_archinfo (
|
tiny_archinfo u_archinfo (
|
||||||
.clk_i (clk_i),
|
.clk_i (clk_i),
|
||||||
.rst_n_i(s_rst_n),
|
.rst_n_i(s_archinfo_rst_n),
|
||||||
.apb4 (u_archinfo_apb4_if)
|
.apb4 (u_archinfo_apb4_if)
|
||||||
);
|
);
|
||||||
apb4_gpio #(
|
apb4_gpio #(
|
||||||
@@ -226,7 +239,7 @@ rst_sync u_por_rst_sync (
|
|||||||
.HasPullDown (gpio_pad_caps_pkg::HasPullDown)
|
.HasPullDown (gpio_pad_caps_pkg::HasPullDown)
|
||||||
) u_gpio (
|
) u_gpio (
|
||||||
.clk_i (clk_i),
|
.clk_i (clk_i),
|
||||||
.rst_n_i (s_rst_n),
|
.rst_n_i (s_gpio_rst_n),
|
||||||
.apb4 (u_gpio_apb4_if),
|
.apb4 (u_gpio_apb4_if),
|
||||||
.gpio (u_gpio_if),
|
.gpio (u_gpio_if),
|
||||||
.user_gpio(u_user_gpio_if)
|
.user_gpio(u_user_gpio_if)
|
||||||
@@ -247,7 +260,7 @@ rst_sync u_por_rst_sync (
|
|||||||
`include "tiny_irq.svh"
|
`include "tiny_irq.svh"
|
||||||
apb4_uart u_uart0 (
|
apb4_uart u_uart0 (
|
||||||
.clk_i (clk_i),
|
.clk_i (clk_i),
|
||||||
.rst_n_i (s_rst_n),
|
.rst_n_i (s_uart0_rst_n),
|
||||||
.apb4 (u_uart0_apb4_if),
|
.apb4 (u_uart0_apb4_if),
|
||||||
.uart (u_uart0_if),
|
.uart (u_uart0_if),
|
||||||
.dma_tx_stall_o(s_dma_stall[2]),
|
.dma_tx_stall_o(s_dma_stall[2]),
|
||||||
@@ -255,7 +268,7 @@ apb4_uart u_uart0 (
|
|||||||
);
|
);
|
||||||
apb4_i2c u_i2c0 (
|
apb4_i2c u_i2c0 (
|
||||||
.clk_i (clk_i),
|
.clk_i (clk_i),
|
||||||
.rst_n_i (s_rst_n),
|
.rst_n_i (s_i2c0_rst_n),
|
||||||
.apb4 (u_i2c0_apb4_if),
|
.apb4 (u_i2c0_apb4_if),
|
||||||
.i2c (u_i2c0_if),
|
.i2c (u_i2c0_if),
|
||||||
.dma_tx_stall_o(s_dma_stall[4]),
|
.dma_tx_stall_o(s_dma_stall[4]),
|
||||||
@@ -263,14 +276,14 @@ apb4_uart u_uart0 (
|
|||||||
);
|
);
|
||||||
apb4_timer u_timer0 (
|
apb4_timer u_timer0 (
|
||||||
.clk_i (clk_i),
|
.clk_i (clk_i),
|
||||||
.rst_n_i (s_rst_n),
|
.rst_n_i (s_timer0_rst_n),
|
||||||
.debug_halted_i(s_debug_halted),
|
.debug_halted_i(s_debug_halted),
|
||||||
.apb4 (u_timer0_apb4_if),
|
.apb4 (u_timer0_apb4_if),
|
||||||
.irq_o (s_timer0_irq)
|
.irq_o (s_timer0_irq)
|
||||||
);
|
);
|
||||||
apb4_uart u_uart1 (
|
apb4_uart u_uart1 (
|
||||||
.clk_i (clk_i),
|
.clk_i (clk_i),
|
||||||
.rst_n_i (s_rst_n),
|
.rst_n_i (s_uart1_rst_n),
|
||||||
.apb4 (u_uart1_apb4_if),
|
.apb4 (u_uart1_apb4_if),
|
||||||
.uart (u_uart1_if),
|
.uart (u_uart1_if),
|
||||||
.dma_tx_stall_o(s_unused_uart1_tx_stall),
|
.dma_tx_stall_o(s_unused_uart1_tx_stall),
|
||||||
@@ -278,7 +291,7 @@ apb4_uart u_uart0 (
|
|||||||
);
|
);
|
||||||
apb4_i2c u_i2c1 (
|
apb4_i2c u_i2c1 (
|
||||||
.clk_i (clk_i),
|
.clk_i (clk_i),
|
||||||
.rst_n_i (s_rst_n),
|
.rst_n_i (s_i2c1_rst_n),
|
||||||
.apb4 (u_i2c1_apb4_if),
|
.apb4 (u_i2c1_apb4_if),
|
||||||
.i2c (u_i2c1_if),
|
.i2c (u_i2c1_if),
|
||||||
.dma_tx_stall_o(s_dma_stall[6]),
|
.dma_tx_stall_o(s_dma_stall[6]),
|
||||||
@@ -286,14 +299,14 @@ apb4_uart u_uart0 (
|
|||||||
);
|
);
|
||||||
apb4_timer u_timer1 (
|
apb4_timer u_timer1 (
|
||||||
.clk_i (clk_i),
|
.clk_i (clk_i),
|
||||||
.rst_n_i (s_rst_n),
|
.rst_n_i (s_timer1_rst_n),
|
||||||
.debug_halted_i(s_debug_halted),
|
.debug_halted_i(s_debug_halted),
|
||||||
.apb4 (u_timer1_apb4_if),
|
.apb4 (u_timer1_apb4_if),
|
||||||
.irq_o (s_timer1_irq)
|
.irq_o (s_timer1_irq)
|
||||||
);
|
);
|
||||||
apb4_clint u_clint (
|
apb4_clint u_clint (
|
||||||
.clk_i (clk_i),
|
.clk_i (clk_i),
|
||||||
.rst_n_i (s_rst_n),
|
.rst_n_i (s_clint_rst_n),
|
||||||
.timebase_tick_i(s_tick),
|
.timebase_tick_i(s_tick),
|
||||||
.apb4 (u_clint_apb4_if),
|
.apb4 (u_clint_apb4_if),
|
||||||
.clint (u_clint_if)
|
.clint (u_clint_if)
|
||||||
@@ -326,7 +339,7 @@ apb4_uart u_uart0 (
|
|||||||
.EnableStreams(1'b0)
|
.EnableStreams(1'b0)
|
||||||
) u_dma (
|
) u_dma (
|
||||||
.clk_i (clk_i),
|
.clk_i (clk_i),
|
||||||
.rst_n_i (s_rst_n),
|
.rst_n_i (s_dma_rst_n),
|
||||||
.dma_xfer_done_o(s_dma_done),
|
.dma_xfer_done_o(s_dma_done),
|
||||||
.irq_o (s_dma_irq),
|
.irq_o (s_dma_irq),
|
||||||
.hw_trg (u_dma_req_if),
|
.hw_trg (u_dma_req_if),
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
// Copyright (c) 2026 Yuchi Miao
|
||||||
|
// SPDX-License-Identifier: MulanPSL-2.0
|
||||||
|
|
||||||
|
// One SYS clock, separate reset loads. Leaf zero owns the fabric; the remaining
|
||||||
|
// leaves follow the canonical APB target order. This is not a clock-domain split.
|
||||||
|
module tiny_reset_tree #(
|
||||||
|
parameter int LeafCount = 17
|
||||||
|
) (
|
||||||
|
input logic clk_i,
|
||||||
|
input logic rst_n_i,
|
||||||
|
input logic watchdog_reset_req_i,
|
||||||
|
output logic por_rst_n_o,
|
||||||
|
output logic system_rst_n_o,
|
||||||
|
output logic [LeafCount-1:0] leaf_rst_n_o,
|
||||||
|
output logic cpu_ready_rst_n_o
|
||||||
|
);
|
||||||
|
logic s_system_req_n;
|
||||||
|
|
||||||
|
assign s_system_req_n = por_rst_n_o && !watchdog_reset_req_i;
|
||||||
|
// The core/debug wrapper adds its five local release edges after every
|
||||||
|
// functional endpoint is ready. No external peripheral clock is a prerequisite.
|
||||||
|
assign cpu_ready_rst_n_o = &leaf_rst_n_o;
|
||||||
|
|
||||||
|
rst_sync #(
|
||||||
|
.STAGE(5)
|
||||||
|
) u_por_rst_sync (
|
||||||
|
.clk_i (clk_i),
|
||||||
|
.rst_n_i(rst_n_i),
|
||||||
|
.rst_n_o(por_rst_n_o)
|
||||||
|
);
|
||||||
|
rst_sync #(
|
||||||
|
.STAGE(5)
|
||||||
|
) u_system_rst_sync (
|
||||||
|
.clk_i (clk_i),
|
||||||
|
.rst_n_i(s_system_req_n),
|
||||||
|
.rst_n_o(system_rst_n_o)
|
||||||
|
);
|
||||||
|
for (genvar leaf = 0; leaf < LeafCount; leaf++) begin : gen_leaf
|
||||||
|
// Preserve independent reset drivers through the existing Yosys flow.
|
||||||
|
// P2 validates the mapped drivers and loads; this is not a timing exception.
|
||||||
|
(* keep = 1, keep_hierarchy = 1 *)
|
||||||
|
rst_sync #(
|
||||||
|
.STAGE(5)
|
||||||
|
) u_leaf_rst_sync (
|
||||||
|
.clk_i (clk_i),
|
||||||
|
.rst_n_i(system_rst_n_o),
|
||||||
|
.rst_n_o(leaf_rst_n_o[leaf])
|
||||||
|
);
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
@@ -91,15 +91,18 @@ def generate_bindings(document: dict, output: Path) -> None:
|
|||||||
f"assign u_gpio_if.{mode}_oe_i[{pin}] = {value['oe']};"))
|
f"assign u_gpio_if.{mode}_oe_i[{pin}] = {value['oe']};"))
|
||||||
atomic_write(output / "rtl/tiny_gpio.svh", "\n".join(gpio) + "\n")
|
atomic_write(output / "rtl/tiny_gpio.svh", "\n".join(gpio) + "\n")
|
||||||
ports, declarations, routes, decode = [], [comment], [comment], [comment]
|
ports, declarations, routes, decode = [], [comment], [comment], [comment]
|
||||||
|
reset_assignments = []
|
||||||
for index, target in enumerate(document["apb_targets"]):
|
for index, target in enumerate(document["apb_targets"]):
|
||||||
name = target["name"]
|
name = target["name"]
|
||||||
ports.append(f"apb4_if.master {name}")
|
ports.append(f"apb4_if.master {name}")
|
||||||
declarations.append(f"apb4_if u_{name}_apb4_if (.pclk(clk_i), .presetn(s_rst_n));")
|
declarations.append(f"logic s_{name}_rst_n;")
|
||||||
|
declarations.append(f"apb4_if u_{name}_apb4_if (.pclk(clk_i), .presetn(s_{name}_rst_n));")
|
||||||
|
reset_assignments.append(f"assign s_{name}_rst_n = s_leaf_rst_n[{index + 1}];")
|
||||||
routes.append(f".{name}(u_{name}_apb4_if)")
|
routes.append(f".{name}(u_{name}_apb4_if)")
|
||||||
decode.append(f"if ({' || '.join(f'`SOC_ADDR_IS_{r}(s_addr_q)' for r in target['regions'])}) "
|
decode.append(f"if ({' || '.join(f'`SOC_ADDR_IS_{r}(s_addr_q)' for r in target['regions'])}) "
|
||||||
f"s_select = {len(document['apb_targets'])}'b{1 << index:016b};")
|
f"s_select = {len(document['apb_targets'])}'b{1 << index:016b};")
|
||||||
atomic_write(output / "rtl/tiny_apb_ports.svh", ",\n".join(ports) + "\n")
|
atomic_write(output / "rtl/tiny_apb_ports.svh", ",\n".join(ports) + "\n")
|
||||||
atomic_write(output / "rtl/tiny_apb_interfaces.svh", "\n".join(declarations) + "\n")
|
atomic_write(output / "rtl/tiny_apb_interfaces.svh", "\n".join(declarations + reset_assignments) + "\n")
|
||||||
atomic_write(output / "rtl/tiny_apb_connections.svh", ",\n".join(routes[1:]) + "\n")
|
atomic_write(output / "rtl/tiny_apb_connections.svh", ",\n".join(routes[1:]) + "\n")
|
||||||
atomic_write(output / "rtl/tiny_apb_decode.svh", "\n".join(decode) + "\n")
|
atomic_write(output / "rtl/tiny_apb_decode.svh", "\n".join(decode) + "\n")
|
||||||
wiring = [comment]
|
wiring = [comment]
|
||||||
|
|||||||
@@ -0,0 +1,241 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Retain reset-load and same-frequency CPU/SRAM feasibility; never qualify a rate."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
import math
|
||||||
|
from pathlib import Path
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
sys.path.insert(0, str(ROOT))
|
||||||
|
from scripts import tiny_r2_baseline as baseline # noqa: E402
|
||||||
|
|
||||||
|
PHASE = "TINY-R2-P2"
|
||||||
|
RATES = (24, 96, 192, 240)
|
||||||
|
CORNERS = {
|
||||||
|
"slow": ("slow_1p08V_125C", "slow_1p08V_3p0V_125C", "slow_1p08V_125C"),
|
||||||
|
"typ": ("typ_1p20V_25C", "typ_1p2V_3p3V_25C", "typ_1p20V_25C"),
|
||||||
|
"fast": ("fast_1p32V_m40C", "fast_1p32V_3p6V_m40C", "fast_1p32V_m55C"),
|
||||||
|
}
|
||||||
|
MACRO = "RM_IHPSG13_1P_1024x32_c2_bm_bist"
|
||||||
|
SDC_CLOCK = re.compile(r"^(create_clock -name clk_system -period )([0-9.]+)( \$clk_system_pin)$", re.MULTILINE)
|
||||||
|
|
||||||
|
|
||||||
|
def source_inputs(root: Path) -> dict:
|
||||||
|
inputs = baseline.source_inputs(root)
|
||||||
|
for path in (root / "tests").rglob("*"):
|
||||||
|
if path.is_file() and path.suffix in (".py", ".sv", ".svh"):
|
||||||
|
inputs[str(path.relative_to(root))] = baseline.sha256(path)
|
||||||
|
return inputs
|
||||||
|
|
||||||
|
|
||||||
|
def generated_inputs(root: Path, variant: Path) -> dict:
|
||||||
|
return {str(p.relative_to(root)): baseline.sha256(p)
|
||||||
|
for p in (variant / "generated/tiny").rglob("*") if p.is_file()}
|
||||||
|
|
||||||
|
|
||||||
|
def require_variant(root: Path, variant: Path) -> dict:
|
||||||
|
if not variant.is_relative_to(root / "build"):
|
||||||
|
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":
|
||||||
|
raise ValueError("feasibility requires the 24 MHz Tiny ci_smoke variant")
|
||||||
|
return manifest
|
||||||
|
|
||||||
|
|
||||||
|
def candidate_sdc(original: str, mhz: int) -> str:
|
||||||
|
if mhz not in RATES or len(SDC_CLOCK.findall(original)) != 1:
|
||||||
|
raise ValueError("expected one baseline system clock and a frozen candidate rate")
|
||||||
|
if abs(float(SDC_CLOCK.search(original)[2]) - 1000 / 24) > 1e-6:
|
||||||
|
raise ValueError("source SDC is not the 24 MHz baseline")
|
||||||
|
if mhz == 24:
|
||||||
|
return original
|
||||||
|
return SDC_CLOCK.sub(lambda m: m[1] + f"{1000 / mhz:.12g}" + m[3], original)
|
||||||
|
|
||||||
|
|
||||||
|
def timing_metrics(path: Path) -> dict:
|
||||||
|
values = {}
|
||||||
|
for line in path.read_text().splitlines():
|
||||||
|
key, _, value = line.partition("=")
|
||||||
|
values[key] = float(value.split()[-1])
|
||||||
|
if (set(values) != {"wns_min", "wns_max", "tns_min", "tns_max"}
|
||||||
|
or not all(math.isfinite(value) for value in values.values())):
|
||||||
|
raise ValueError("incomplete STA measurements")
|
||||||
|
return values
|
||||||
|
|
||||||
|
|
||||||
|
def reset_loads(path: Path, candidate: bool) -> dict:
|
||||||
|
rows = {}
|
||||||
|
for line in path.read_text().splitlines()[1:]:
|
||||||
|
name, driver, direct, endpoints = line.split("\t")
|
||||||
|
if name in rows or min(int(direct), int(endpoints)) < 0:
|
||||||
|
raise ValueError("invalid reset-load accounting")
|
||||||
|
rows[name] = {"driver": driver, "direct_loads": int(direct), "structural_endpoints": int(endpoints)}
|
||||||
|
expected = {"system"} | ({f"leaf-{index}" for index in range(17)} if candidate else set())
|
||||||
|
if set(rows) != expected or len({r["driver"] for r in rows.values()}) != len(rows):
|
||||||
|
raise ValueError("reset drivers merged or missing")
|
||||||
|
return rows
|
||||||
|
|
||||||
|
|
||||||
|
def path_slack(path: Path) -> float:
|
||||||
|
match = re.search(r"(?m)^\s*([-+]?\d+(?:\.\d+)?)\s+slack\b", path.read_text())
|
||||||
|
if match is None:
|
||||||
|
raise ValueError(f"missing constrained SYS path: {path}")
|
||||||
|
return float(match[1])
|
||||||
|
|
||||||
|
|
||||||
|
def capture(args: argparse.Namespace) -> int:
|
||||||
|
root, variant = args.root.resolve(), args.variant_root.resolve()
|
||||||
|
manifest = require_variant(root, variant)
|
||||||
|
parent = variant / "meta/tiny-r2-p2"
|
||||||
|
directory = baseline.new_attempt(parent, "inputs-")
|
||||||
|
inputs = source_inputs(root) | generated_inputs(root, variant)
|
||||||
|
record = {"phase": PHASE, "created_at": datetime.now(timezone.utc).isoformat(),
|
||||||
|
"manifest": manifest, "tools": baseline.tools_identity(root),
|
||||||
|
"source": baseline.snapshot(root, directory, inputs)}
|
||||||
|
baseline.dump(directory / "identity.json", record)
|
||||||
|
baseline.dump(parent / "latest-inputs.json", {"path": str(directory / "identity.json")})
|
||||||
|
print(f"Tiny P2 captured candidate inputs: {directory}")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def consumed_files(variant: Path) -> dict:
|
||||||
|
return {name: baseline.artifact(variant / name) for name in (
|
||||||
|
"meta/manifest.json", "syn/yosys/result-synth.json", "sta/opensta/result-sta.json",
|
||||||
|
"syn/yosys/out/retrosoc_tiny_asic_yosys.v", "syn/yosys/out/retrosoc_tiny_asic_yosys.config",
|
||||||
|
"sta/opensta/retrosoc_core.sdc")}
|
||||||
|
|
||||||
|
|
||||||
|
def run(args: argparse.Namespace) -> int:
|
||||||
|
root, variant = args.root.resolve(), args.variant_root.resolve()
|
||||||
|
old_variant = args.baseline_root.resolve()
|
||||||
|
require_variant(root, variant)
|
||||||
|
require_variant(root, old_variant)
|
||||||
|
parent = variant / "meta/tiny-r2-p2"
|
||||||
|
directory = baseline.new_attempt(parent, "feasibility-")
|
||||||
|
output = directory / "report.json"
|
||||||
|
baseline.dump(parent / "latest-feasibility.json", {"path": str(output)})
|
||||||
|
record = {"phase": PHASE, "status": "running", "points": [],
|
||||||
|
"qualification": "early unextracted feasibility only; no qualified operating point"}
|
||||||
|
try:
|
||||||
|
identity_path = Path(baseline.read_json(parent / "latest-inputs.json")["path"])
|
||||||
|
identity = baseline.read_json(identity_path)
|
||||||
|
before = source_inputs(root) | generated_inputs(root, variant)
|
||||||
|
if before != baseline.read_json(baseline.check_artifact(identity["source"]["inputs"])):
|
||||||
|
raise ValueError("candidate inputs changed after capture")
|
||||||
|
for key in ("inputs", "snapshot", "patch"):
|
||||||
|
baseline.check_artifact(identity["source"][key])
|
||||||
|
record.update(candidate_identity=baseline.artifact(identity_path), tools=baseline.tools_identity(root))
|
||||||
|
original = baseline.read_json(old_variant / "meta/tiny-r2-p2/baseline-artifacts.json")
|
||||||
|
for key in ("inputs", "snapshot", "patch"):
|
||||||
|
baseline.check_artifact(original["source"][key])
|
||||||
|
for artifact in original["artifacts"].values():
|
||||||
|
baseline.check_artifact(artifact)
|
||||||
|
record["baseline_identity"] = baseline.artifact(old_variant / "meta/tiny-r2-p2/baseline-artifacts.json")
|
||||||
|
record["inputs"] = {"baseline": consumed_files(old_variant), "candidate": consumed_files(variant)}
|
||||||
|
for role, files in record["inputs"].items():
|
||||||
|
for key in ("syn/yosys/result-synth.json", "sta/opensta/result-sta.json"):
|
||||||
|
result = baseline.read_json(Path(files[key]["path"]))
|
||||||
|
if result.get("status") != "passed" or result.get("exit_code") != 0:
|
||||||
|
raise ValueError(f"{role} lacks completed synthesis/STA")
|
||||||
|
if role == "candidate":
|
||||||
|
synth = baseline.read_json(Path(files["syn/yosys/result-synth.json"]["path"]))
|
||||||
|
if datetime.fromisoformat(synth["started_at"]) < datetime.fromisoformat(identity["created_at"]):
|
||||||
|
raise ValueError("candidate synthesis predates its captured inputs")
|
||||||
|
libraries = root / "physical/pdk/IHP-Open-PDK/ihp-sg13g2/libs.ref"
|
||||||
|
script = root / "physical/smoke/sta/opensta/tiny_r2_feasibility.tcl"
|
||||||
|
record["script"] = baseline.artifact(script)
|
||||||
|
record["macro_views"] = {}
|
||||||
|
for corner, (std, io, ram) in CORNERS.items():
|
||||||
|
paths = [libraries / f"sg13g2_stdcell/lib/sg13g2_stdcell_{std}.lib",
|
||||||
|
libraries / f"sg13g2_io/lib/sg13g2_io_{io}.lib",
|
||||||
|
libraries / f"sg13g2_sram/lib/{MACRO}_{ram}.lib"]
|
||||||
|
text = paths[2].read_text()
|
||||||
|
record["macro_views"][corner] = {
|
||||||
|
"libraries": [baseline.artifact(p) for p in paths],
|
||||||
|
"explicit_minimum_period": bool(re.search(r"\b(?:minimum_period|min_period)\b", text)),
|
||||||
|
"setup": "setup_rising" in text, "hold": "hold_rising" in text,
|
||||||
|
"clock_to_output": "rising_edge" in text, "pulse_width": "min_pulse_width" in text,
|
||||||
|
"temperature_pairing": ram + "; stdcell " + std,
|
||||||
|
}
|
||||||
|
for role, files in record["inputs"].items():
|
||||||
|
original_sdc = Path(files["sta/opensta/retrosoc_core.sdc"]["path"]).read_text()
|
||||||
|
for mhz in RATES:
|
||||||
|
point = directory / role / corner / str(mhz)
|
||||||
|
point.mkdir(parents=True)
|
||||||
|
sdc = point / "analysis.sdc"
|
||||||
|
baseline.atomic_write(sdc, candidate_sdc(original_sdc, mhz))
|
||||||
|
environment = {
|
||||||
|
"OPENSTA_TOP": "retrosoc_tiny_asic",
|
||||||
|
"OPENSTA_NETLIST": files["syn/yosys/out/retrosoc_tiny_asic_yosys.v"]["path"],
|
||||||
|
"OPENSTA_LIBERTY": str(paths[0]), "OPENSTA_LINK_LIBS": str(paths[1]),
|
||||||
|
"OPENSTA_SRAM_LIBS": str(paths[2]), "OPENSTA_SDC": str(sdc),
|
||||||
|
"OPENSTA_REPORT": str(point / "timing.rpt"),
|
||||||
|
"OPENSTA_METRICS": str(point / "timing_metrics.rpt"),
|
||||||
|
"TINY_R2_OUTPUT": str(point), "TINY_R2_CANDIDATE": str(int(role == "candidate")),
|
||||||
|
}
|
||||||
|
command = [sys.executable, str(root / "scripts/run_flow.py"), "--tool", "opensta-p2",
|
||||||
|
"--log", str(point / "sta.log"), "--result", str(point / "result.json")]
|
||||||
|
for key, value in environment.items():
|
||||||
|
command += ["--env", f"{key}={value}"]
|
||||||
|
command += ["--", record["tools"]["opensta"]["executable"]["path"],
|
||||||
|
"-no_init", "-exit", "-threads", "2", str(script)]
|
||||||
|
subprocess.run(command, check=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, timeout=1800)
|
||||||
|
log = (point / "sta.log").read_text()
|
||||||
|
if "TINY_R2_FEASIBILITY_PASS:" not in log or re.search(r"(?im)^Error:|\bFATAL\b", log):
|
||||||
|
raise ValueError(f"STA audit incomplete: {point}")
|
||||||
|
metrics = timing_metrics(point / "timing_metrics.rpt")
|
||||||
|
record["points"].append({"role": role, "corner": corner, "sys_mhz": mhz,
|
||||||
|
"metrics": metrics,
|
||||||
|
"sys_setup_path_slack_ns": path_slack(point / "sys-setup.rpt"),
|
||||||
|
"sys_hold_path_slack_ns": path_slack(point / "sys-hold.rpt"),
|
||||||
|
"reset_loads": reset_loads(point / "reset-loads.tsv", role == "candidate"),
|
||||||
|
"timing": "failed" if any(v < 0 for v in metrics.values()) else "nonnegative_reported_slack",
|
||||||
|
"artifacts": {p.name: baseline.artifact(p) for p in point.iterdir() if p.is_file()}})
|
||||||
|
baseline.dump(output, record)
|
||||||
|
print(f"{role} {corner} SYS={mhz} MHz: WNS={metrics['wns_max']} ns; not qualification", flush=True)
|
||||||
|
if before != source_inputs(root) | generated_inputs(root, variant):
|
||||||
|
raise ValueError("candidate inputs changed during feasibility")
|
||||||
|
for files in record["inputs"].values():
|
||||||
|
for artifact in files.values():
|
||||||
|
baseline.check_artifact(artifact)
|
||||||
|
for views in record["macro_views"].values():
|
||||||
|
for artifact in views["libraries"]:
|
||||||
|
baseline.check_artifact(artifact)
|
||||||
|
for key in ("candidate_identity", "baseline_identity", "script"):
|
||||||
|
baseline.check_artifact(record[key])
|
||||||
|
for tool in record["tools"].values():
|
||||||
|
baseline.check_artifact(tool["executable"])
|
||||||
|
record["status"] = "completed"
|
||||||
|
except (OSError, ValueError, KeyError, subprocess.SubprocessError) as error:
|
||||||
|
record.update(status="failed", error=str(error))
|
||||||
|
print(f"Tiny P2 feasibility: {error}", file=sys.stderr)
|
||||||
|
finally:
|
||||||
|
baseline.dump(output, record)
|
||||||
|
return int(record["status"] != "completed")
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
parser = argparse.ArgumentParser(description=__doc__)
|
||||||
|
parser.add_argument("--root", type=Path, default=ROOT)
|
||||||
|
parser.add_argument("--variant-root", type=Path, required=True)
|
||||||
|
subparsers = parser.add_subparsers(dest="action", required=True)
|
||||||
|
subparsers.add_parser("capture")
|
||||||
|
execute = subparsers.add_parser("run")
|
||||||
|
execute.add_argument("--baseline-root", type=Path, required=True)
|
||||||
|
args = parser.parse_args()
|
||||||
|
try:
|
||||||
|
return capture(args) if args.action == "capture" else run(args)
|
||||||
|
except (OSError, ValueError, KeyError, subprocess.SubprocessError) as error:
|
||||||
|
print(f"Tiny P2: {error}", file=sys.stderr)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
`timescale 1ns / 1ps
|
`timescale 1ns / 1ps
|
||||||
|
|
||||||
module mgmt_debug_reset_tb;
|
module mgmt_debug_reset_tb #(
|
||||||
|
parameter int ResetSyncStages = 3
|
||||||
|
);
|
||||||
logic s_clk;
|
logic s_clk;
|
||||||
logic s_rst_n;
|
logic s_rst_n;
|
||||||
logic s_reset_req;
|
logic s_reset_req;
|
||||||
@@ -10,7 +12,9 @@ module mgmt_debug_reset_tb;
|
|||||||
|
|
||||||
always #5 s_clk = ~s_clk;
|
always #5 s_clk = ~s_clk;
|
||||||
|
|
||||||
mgmt_debug_reset u_mgmt_debug_reset (
|
mgmt_debug_reset #(
|
||||||
|
.ResetSyncStages(ResetSyncStages)
|
||||||
|
) u_mgmt_debug_reset (
|
||||||
.clk_i (s_clk),
|
.clk_i (s_clk),
|
||||||
.rst_n_i (s_rst_n),
|
.rst_n_i (s_rst_n),
|
||||||
.reset_req_i (s_reset_req),
|
.reset_req_i (s_reset_req),
|
||||||
@@ -27,7 +31,7 @@ module mgmt_debug_reset_tb;
|
|||||||
|
|
||||||
repeat (2) @(posedge s_clk);
|
repeat (2) @(posedge s_clk);
|
||||||
s_rst_n = 1'b1;
|
s_rst_n = 1'b1;
|
||||||
repeat (4) @(posedge s_clk);
|
repeat (ResetSyncStages + 1) @(posedge s_clk);
|
||||||
#1;
|
#1;
|
||||||
assert (s_core_rst_n && s_reset_done)
|
assert (s_core_rst_n && s_reset_done)
|
||||||
else $fatal(1, "initial reset release failed");
|
else $fatal(1, "initial reset release failed");
|
||||||
@@ -46,7 +50,7 @@ module mgmt_debug_reset_tb;
|
|||||||
#1;
|
#1;
|
||||||
assert (!s_core_rst_n && !s_reset_done)
|
assert (!s_core_rst_n && !s_reset_done)
|
||||||
else $fatal(1, "core reset did not assert");
|
else $fatal(1, "core reset did not assert");
|
||||||
repeat (4) @(posedge s_clk);
|
repeat (ResetSyncStages + 1) @(posedge s_clk);
|
||||||
#1;
|
#1;
|
||||||
assert (s_core_rst_n && s_reset_done)
|
assert (s_core_rst_n && s_reset_done)
|
||||||
else $fatal(1, "core reset did not release");
|
else $fatal(1, "core reset did not release");
|
||||||
@@ -62,7 +66,7 @@ module mgmt_debug_reset_tb;
|
|||||||
else $fatal(1, "held reset released too early");
|
else $fatal(1, "held reset released too early");
|
||||||
|
|
||||||
s_reset_req = 1'b0;
|
s_reset_req = 1'b0;
|
||||||
repeat (4) @(posedge s_clk);
|
repeat (ResetSyncStages + 1) @(posedge s_clk);
|
||||||
#1;
|
#1;
|
||||||
assert (s_core_rst_n && s_reset_done)
|
assert (s_core_rst_n && s_reset_done)
|
||||||
else $fatal(1, "held reset did not release");
|
else $fatal(1, "held reset did not release");
|
||||||
|
|||||||
@@ -381,6 +381,26 @@ module onchip_ram_tb #(
|
|||||||
$fatal(1, "performance counters did not record the directed traffic");
|
$fatal(1, "performance counters did not record the directed traffic");
|
||||||
end
|
end
|
||||||
|
|
||||||
|
// System reset cancels a held response but must preserve SRAM payload.
|
||||||
|
write_single(SramBase + 32'h300, `AXI4_BURST_SIZE_4BYTES, 32'h5A36_C9E7, 4'hf, `AXI4_RESP_OKAY);
|
||||||
|
@(negedge clk_i);
|
||||||
|
mem_axi4.araddr = SramBase + 32'h300;
|
||||||
|
mem_axi4.arvalid = 1'b1;
|
||||||
|
do @(posedge clk_i); while (!mem_axi4.arready);
|
||||||
|
@(negedge clk_i);
|
||||||
|
mem_axi4.arvalid = 1'b0;
|
||||||
|
mem_axi4.rready = 1'b0;
|
||||||
|
wait (mem_axi4.rvalid);
|
||||||
|
@(negedge clk_i);
|
||||||
|
rst_n_i = 1'b0;
|
||||||
|
#1;
|
||||||
|
if (mem_axi4.rvalid || mem_axi4.bvalid) $fatal(1, "reset retained a stale SRAM response");
|
||||||
|
repeat (3) @(posedge clk_i);
|
||||||
|
@(negedge clk_i);
|
||||||
|
rst_n_i = 1'b1;
|
||||||
|
repeat (2) @(negedge clk_i);
|
||||||
|
read_single(SramBase + 32'h300, `AXI4_BURST_SIZE_4BYTES, 32'h5A36_C9E7, `AXI4_RESP_OKAY);
|
||||||
|
|
||||||
$display("on-chip SRAM AXI4 test passed capacity_kib=%0d", CapacityKiB);
|
$display("on-chip SRAM AXI4 test passed capacity_kib=%0d", CapacityKiB);
|
||||||
$finish;
|
$finish;
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -0,0 +1,115 @@
|
|||||||
|
`timescale 1ns / 1ps
|
||||||
|
module tiny_r2_fifo_reset_tb;
|
||||||
|
logic s_clk = 0, s_reset_n = 1, s_watchdog = 0;
|
||||||
|
logic s_por_n, s_system_n, s_ready_n;
|
||||||
|
logic [16:0] s_leaf_n;
|
||||||
|
logic s_flush = 0, s_push = 0, s_pop = 0, s_full, s_empty;
|
||||||
|
logic [7:0] s_input = 0, s_output;
|
||||||
|
logic [2:0] s_count;
|
||||||
|
logic [7:0] s_model [4];
|
||||||
|
int s_head = 0, s_tail = 0, s_used = 0;
|
||||||
|
logic [31:0] s_random = 32'h12345678;
|
||||||
|
tiny_reset_tree u_tree (
|
||||||
|
.clk_i (s_clk),
|
||||||
|
.rst_n_i (s_reset_n),
|
||||||
|
.watchdog_reset_req_i(s_watchdog),
|
||||||
|
.por_rst_n_o (s_por_n),
|
||||||
|
.system_rst_n_o (s_system_n),
|
||||||
|
.leaf_rst_n_o (s_leaf_n),
|
||||||
|
.cpu_ready_rst_n_o (s_ready_n)
|
||||||
|
);
|
||||||
|
fifo #(
|
||||||
|
.DATA_WIDTH (8),
|
||||||
|
.BUFFER_DEPTH(4)
|
||||||
|
) u_fifo (
|
||||||
|
.clk_i (s_clk),
|
||||||
|
.rst_n_i(s_leaf_n[6]),
|
||||||
|
.flush_i(s_flush),
|
||||||
|
.push_i (s_push),
|
||||||
|
.full_o (s_full),
|
||||||
|
.dat_i (s_input),
|
||||||
|
.pop_i (s_pop),
|
||||||
|
.empty_o(s_empty),
|
||||||
|
.dat_o (s_output),
|
||||||
|
.cnt_o (s_count)
|
||||||
|
);
|
||||||
|
|
||||||
|
task automatic check_visible;
|
||||||
|
if (s_count !== 3'(s_used) || s_empty !== (s_used == 0) || s_full !== (s_used == 4))
|
||||||
|
$fatal(1, "FIFO control disagrees with independent queue");
|
||||||
|
if (s_output !== ((s_used == 0) ? 8'd0 : s_model[s_head]))
|
||||||
|
$fatal(1, "stale or incorrect FIFO payload became visible");
|
||||||
|
endtask
|
||||||
|
|
||||||
|
task automatic cycle;
|
||||||
|
bit take_push, take_pop;
|
||||||
|
take_push = s_push && ((s_used < 4) || (s_pop && s_used != 0));
|
||||||
|
take_pop = s_pop && s_used != 0;
|
||||||
|
if (!s_leaf_n[6] || s_flush) begin
|
||||||
|
s_head = 0;
|
||||||
|
s_tail = 0;
|
||||||
|
s_used = 0;
|
||||||
|
end else begin
|
||||||
|
if (take_pop) begin
|
||||||
|
s_head = (s_head + 1) % 4;
|
||||||
|
s_used--;
|
||||||
|
end
|
||||||
|
if (take_push) begin
|
||||||
|
s_model[s_tail] = s_input;
|
||||||
|
s_tail = (s_tail + 1) % 4;
|
||||||
|
s_used++;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
#5 s_clk = 1;
|
||||||
|
#1;
|
||||||
|
check_visible();
|
||||||
|
#4 s_clk = 0;
|
||||||
|
endtask
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
#1 s_reset_n = 0;
|
||||||
|
#1 s_reset_n = 1;
|
||||||
|
repeat (16) cycle();
|
||||||
|
s_push = 1;
|
||||||
|
for (int value = 1; value <= 4; value++) begin
|
||||||
|
s_input = 8'(value);
|
||||||
|
cycle();
|
||||||
|
end
|
||||||
|
s_pop = 1;
|
||||||
|
s_input = 8'h55;
|
||||||
|
cycle(); // Full simultaneous pop/push must accept replacement.
|
||||||
|
s_flush = 1;
|
||||||
|
cycle(); // Flush wins over both operations; data output becomes zero.
|
||||||
|
s_flush = 0;
|
||||||
|
s_pop = 0;
|
||||||
|
s_input = 8'ha5;
|
||||||
|
cycle();
|
||||||
|
#2 s_watchdog = 1;
|
||||||
|
#1;
|
||||||
|
s_head = 0;
|
||||||
|
s_tail = 0;
|
||||||
|
s_used = 0;
|
||||||
|
check_visible();
|
||||||
|
#50; // Reset and empty masking must not need a running clock.
|
||||||
|
check_visible();
|
||||||
|
s_watchdog = 0;
|
||||||
|
s_push = 0;
|
||||||
|
repeat (11) cycle();
|
||||||
|
for (int trial = 0; trial < 1024; trial++) begin
|
||||||
|
s_random ^= s_random << 13;
|
||||||
|
s_random ^= s_random >> 17;
|
||||||
|
s_random ^= s_random << 5;
|
||||||
|
s_push = s_random[0];
|
||||||
|
s_pop = s_random[1];
|
||||||
|
s_flush = (s_random[7:2] == 0);
|
||||||
|
s_input = s_random[15:8];
|
||||||
|
cycle();
|
||||||
|
end
|
||||||
|
$display("SIM_TEST_PASS Tiny R2 FIFO reset");
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
initial begin
|
||||||
|
#20000;
|
||||||
|
$fatal(1, "SIM_TEST_TIMEOUT Tiny R2 FIFO reset");
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
`timescale 1ns / 1ps
|
||||||
|
module tiny_r2_reset_tb;
|
||||||
|
logic s_clk = 0;
|
||||||
|
logic s_reset_n = 1;
|
||||||
|
logic s_watchdog = 0;
|
||||||
|
logic s_por_n, s_system_n, s_ready_n;
|
||||||
|
logic [16:0] s_leaf_n;
|
||||||
|
logic s_hart_req = 0, s_idle = 1, s_core_n, s_done;
|
||||||
|
|
||||||
|
tiny_reset_tree u_tree (
|
||||||
|
.clk_i (s_clk),
|
||||||
|
.rst_n_i (s_reset_n),
|
||||||
|
.watchdog_reset_req_i(s_watchdog),
|
||||||
|
.por_rst_n_o (s_por_n),
|
||||||
|
.system_rst_n_o (s_system_n),
|
||||||
|
.leaf_rst_n_o (s_leaf_n),
|
||||||
|
.cpu_ready_rst_n_o (s_ready_n)
|
||||||
|
);
|
||||||
|
mgmt_debug_reset #(
|
||||||
|
.ResetSyncStages(5)
|
||||||
|
) u_hart (
|
||||||
|
.clk_i (s_clk),
|
||||||
|
.rst_n_i (s_ready_n),
|
||||||
|
.reset_req_i (s_hart_req),
|
||||||
|
.bridge_idle_i(s_idle),
|
||||||
|
.core_rst_n_o (s_core_n),
|
||||||
|
.reset_done_o (s_done)
|
||||||
|
);
|
||||||
|
|
||||||
|
task automatic cycle;
|
||||||
|
#5 s_clk = 1;
|
||||||
|
#1;
|
||||||
|
if (s_core_n && s_leaf_n !== 17'h1ffff) $fatal(1, "CPU released before all leaves");
|
||||||
|
#4 s_clk = 0;
|
||||||
|
endtask
|
||||||
|
|
||||||
|
task automatic cold_release;
|
||||||
|
for (int edge_count = 1; edge_count <= 20; edge_count++) begin
|
||||||
|
cycle();
|
||||||
|
if (s_por_n !== (edge_count >= 5)) $fatal(1, "POR release edge %0d", edge_count);
|
||||||
|
if (s_system_n !== (edge_count >= 10)) $fatal(1, "system release edge %0d", edge_count);
|
||||||
|
if (s_leaf_n !== ((edge_count >= 15) ? 17'h1ffff : 17'd0))
|
||||||
|
$fatal(1, "leaf release edge %0d", edge_count);
|
||||||
|
if (s_core_n !== (edge_count >= 20)) $fatal(1, "hart release edge %0d", edge_count);
|
||||||
|
end
|
||||||
|
endtask
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
// Generate a real reset edge after elaboration in four-state simulation.
|
||||||
|
#1 s_reset_n = 0;
|
||||||
|
#1 s_reset_n = 1;
|
||||||
|
cold_release();
|
||||||
|
#2 s_reset_n = 0;
|
||||||
|
#1;
|
||||||
|
if ({s_por_n, s_system_n, s_leaf_n, s_core_n} !== '0)
|
||||||
|
$fatal(1, "asynchronous stopped-clock assertion failed");
|
||||||
|
#2 s_reset_n = 1;
|
||||||
|
#100;
|
||||||
|
if ({s_por_n, s_system_n, s_leaf_n, s_core_n} !== '0)
|
||||||
|
$fatal(1, "stopped clock allowed release");
|
||||||
|
repeat (4) cycle();
|
||||||
|
if (s_por_n) $fatal(1, "released before five edges");
|
||||||
|
#2 s_reset_n = 0;
|
||||||
|
#1 s_reset_n = 1;
|
||||||
|
cold_release();
|
||||||
|
|
||||||
|
#2 s_watchdog = 1;
|
||||||
|
#1;
|
||||||
|
if (!s_por_n || s_system_n || s_leaf_n != 0 || s_core_n)
|
||||||
|
$fatal(1, "watchdog did not preserve POR and reset the system");
|
||||||
|
repeat (8) cycle();
|
||||||
|
s_watchdog = 0;
|
||||||
|
for (int edge_count = 1; edge_count <= 15; edge_count++) begin
|
||||||
|
cycle();
|
||||||
|
if (!s_por_n || s_system_n !== (edge_count >= 5) ||
|
||||||
|
s_leaf_n !== ((edge_count >= 10) ? 17'h1ffff : 17'd0) ||
|
||||||
|
s_core_n !== (edge_count >= 15))
|
||||||
|
$fatal(1, "watchdog release sequencing");
|
||||||
|
end
|
||||||
|
|
||||||
|
s_idle = 0;
|
||||||
|
s_hart_req = 1;
|
||||||
|
cycle();
|
||||||
|
s_hart_req = 0;
|
||||||
|
repeat (4) begin
|
||||||
|
cycle();
|
||||||
|
if (!s_core_n || s_done || s_leaf_n != 17'h1ffff)
|
||||||
|
$fatal(1, "hart request discarded accepted work or reset other clients");
|
||||||
|
end
|
||||||
|
s_idle = 1;
|
||||||
|
cycle();
|
||||||
|
if (s_core_n || s_done) $fatal(1, "idle hart did not assert reset");
|
||||||
|
cycle(); // Retained request clears; five release edges follow.
|
||||||
|
for (int edge_count = 1; edge_count <= 5; edge_count++) begin
|
||||||
|
cycle();
|
||||||
|
if (s_leaf_n != 17'h1ffff || s_core_n !== (edge_count == 5))
|
||||||
|
$fatal(1, "hart-only release or peripheral preservation failed");
|
||||||
|
end
|
||||||
|
if (!s_done) $fatal(1, "hart reset completion missing");
|
||||||
|
$display("SIM_TEST_PASS Tiny R2 reset");
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
initial begin
|
||||||
|
#10000;
|
||||||
|
$fatal(1, "SIM_TEST_TIMEOUT Tiny R2 reset");
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
@@ -6,11 +6,13 @@ import shutil
|
|||||||
import subprocess
|
import subprocess
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
ROOT = Path(__file__).resolve().parents[1]
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
|
||||||
|
|
||||||
def test_management_debug_reset_waits_for_the_ahbl_bridge(tmp_path: Path) -> None:
|
@pytest.mark.parametrize("stages", [3, 5])
|
||||||
|
def test_management_debug_reset_waits_for_the_ahbl_bridge(tmp_path: Path, stages: int) -> None:
|
||||||
iverilog = shutil.which("iverilog")
|
iverilog = shutil.which("iverilog")
|
||||||
vvp = shutil.which("vvp")
|
vvp = shutil.which("vvp")
|
||||||
if iverilog is None or vvp is None:
|
if iverilog is None or vvp is None:
|
||||||
@@ -31,6 +33,7 @@ def test_management_debug_reset_waits_for_the_ahbl_bridge(tmp_path: Path) -> Non
|
|||||||
f"-I{ROOT / 'rtl/managed/clusterip/common/rtl'}",
|
f"-I{ROOT / 'rtl/managed/clusterip/common/rtl'}",
|
||||||
"-s",
|
"-s",
|
||||||
"mgmt_debug_reset_tb",
|
"mgmt_debug_reset_tb",
|
||||||
|
f"-Pmgmt_debug_reset_tb.ResetSyncStages={stages}",
|
||||||
"-o",
|
"-o",
|
||||||
str(simulation),
|
str(simulation),
|
||||||
*(str(source) for source in sources),
|
*(str(source) for source in sources),
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ def test_tiny_configuration_and_inventory_are_derived(data):
|
|||||||
assert data["facts"]["dma"]["NumChannels"] == 4
|
assert data["facts"]["dma"]["NumChannels"] == 4
|
||||||
assert data["facts"]["dma"]["RequestMask"] == 0x7F9
|
assert data["facts"]["dma"]["RequestMask"] == 0x7F9
|
||||||
assert data["facts"]["dma"]["EnableStreams"] == 0
|
assert data["facts"]["dma"]["EnableStreams"] == 0
|
||||||
|
assert data["facts"]["cpu"]["ResetSyncStages"] == 5
|
||||||
|
assert tiny.RESET_TREE in data["source_paths"]
|
||||||
assert len(data["catalog"]) == 14 and len(data["regions"]) == 20 and len(data["pads"]) == 52
|
assert len(data["catalog"]) == 14 and len(data["regions"]) == 20 and len(data["pads"]) == 52
|
||||||
assert len(data["interrupts"]) == 14 and len(data["gpio"]) == 32
|
assert len(data["interrupts"]) == 14 and len(data["gpio"]) == 32
|
||||||
assert not any(p.startswith("rtl/mini/") for p in data["source_paths"])
|
assert not any(p.startswith("rtl/mini/") for p in data["source_paths"])
|
||||||
@@ -49,9 +51,26 @@ def test_common_register_default_remains_mini_compatible():
|
|||||||
assert "SOC_SYSCTRL_TEST_STATUS_OFFSET" in constants
|
assert "SOC_SYSCTRL_TEST_STATUS_OFFSET" in constants
|
||||||
|
|
||||||
|
|
||||||
|
def test_reset_update_does_not_bypass_reviewed_snapshot(monkeypatch):
|
||||||
|
original = tiny.subprocess.check_output
|
||||||
|
checked = []
|
||||||
|
|
||||||
|
def changed_snapshot(command, **kwargs):
|
||||||
|
if command[:3] == ["git", "diff", "--name-only"]:
|
||||||
|
assert tiny.RESET_TREE in command
|
||||||
|
checked.append(command)
|
||||||
|
return tiny.RESET_TREE + "\n"
|
||||||
|
return original(command, **kwargs)
|
||||||
|
|
||||||
|
monkeypatch.setattr(tiny.subprocess, "check_output", changed_snapshot)
|
||||||
|
with pytest.raises(ValueError, match="technical sources differ from reviewed snapshot"):
|
||||||
|
tiny.collect(tiny.read(ROOT / tiny.BOOK / "tiny.json"))
|
||||||
|
assert len(checked) == 1
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def source_tree(tmp_path):
|
def source_tree(tmp_path):
|
||||||
sources = [tiny.TOP, tiny.ARCHINFO, tiny.SYSCTRL, "rtl/ip/core/mgmt_core_wrapper.sv", "configs/ci/ihp130-tiny.mk",
|
sources = [tiny.TOP, tiny.RESET_TREE, tiny.ARCHINFO, tiny.SYSCTRL, "rtl/ip/core/mgmt_core_wrapper.sv", "configs/ci/ihp130-tiny.mk",
|
||||||
tiny.MAP, tiny.TOPOLOGY, (tiny.BOOK / "source-contract.json").as_posix()]
|
tiny.MAP, tiny.TOPOLOGY, (tiny.BOOK / "source-contract.json").as_posix()]
|
||||||
for relative in sources:
|
for relative in sources:
|
||||||
target = tmp_path / relative
|
target = tmp_path / relative
|
||||||
@@ -66,6 +85,7 @@ def source_tree(tmp_path):
|
|||||||
(tiny.TOP, ".NumChannels (4)", ".NumChannels (8)"),
|
(tiny.TOP, ".NumChannels (4)", ".NumChannels (8)"),
|
||||||
(tiny.TOP, ".EnableStreams(1'b0)", ".EnableStreams(1'b1)"),
|
(tiny.TOP, ".EnableStreams(1'b0)", ".EnableStreams(1'b1)"),
|
||||||
(tiny.TOP, ".EnableAtomics (1'b0)", ".EnableAtomics (1'b1)"),
|
(tiny.TOP, ".EnableAtomics (1'b0)", ".EnableAtomics (1'b1)"),
|
||||||
|
(tiny.TOP, ".ResetSyncStages (5)", ".ResetSyncStages (3)"),
|
||||||
("configs/ci/ihp130-tiny.mk", "ISA := RV32IM", "ISA := RV32IMC"),
|
("configs/ci/ihp130-tiny.mk", "ISA := RV32IM", "ISA := RV32IMC"),
|
||||||
(tiny.ARCHINFO, "32'h5449_4e59", "32'h4d49_4e49"),
|
(tiny.ARCHINFO, "32'h5449_4e59", "32'h4d49_4e49"),
|
||||||
(tiny.SYSCTRL, "!s_test_q[31] && apb4.pwdata[31]", "s_test_q[31] && apb4.pwdata[31]"),
|
(tiny.SYSCTRL, "!s_test_q[31] && apb4.pwdata[31]", "s_test_q[31] && apb4.pwdata[31]"),
|
||||||
|
|||||||
@@ -1384,7 +1384,9 @@ def test_hazard3_debug_flow_is_locked_and_uses_remote_bitbang() -> None:
|
|||||||
assert ".BRANCH_PREDICTOR (1)" in wrapper
|
assert ".BRANCH_PREDICTOR (1)" in wrapper
|
||||||
assert ".BREAKPOINT_TRIGGERS(2)" in wrapper
|
assert ".BREAKPOINT_TRIGGERS(2)" in wrapper
|
||||||
assert ".HAVE_SBA(0)" in debug_wrapper
|
assert ".HAVE_SBA(0)" in debug_wrapper
|
||||||
assert "mgmt_debug_reset u_mgmt_debug_reset" in debug_wrapper
|
assert re.search(r"mgmt_debug_reset\s*#\(\s*\.ResetSyncStages\(ResetSyncStages\)\s*\)\s+u_mgmt_debug_reset", debug_wrapper)
|
||||||
|
assert re.search(r"parameter\s+int\s+ResetSyncStages\s*=\s*3", wrapper)
|
||||||
|
assert re.search(r"parameter\s+int\s+ResetSyncStages\s*=\s*3", debug_wrapper)
|
||||||
assert "--timeout $(SOC_SIM_TIME)" in verilator_makefile
|
assert "--timeout $(SOC_SIM_TIME)" in verilator_makefile
|
||||||
assert "--require-debug-tools" in verilator_makefile
|
assert "--require-debug-tools" in verilator_makefile
|
||||||
assert "HAVE_DEBUG" not in verilator_makefile
|
assert "HAVE_DEBUG" not in verilator_makefile
|
||||||
|
|||||||
@@ -31,6 +31,11 @@ def test_tiny_sources_and_abi_are_independent(tmp_path: Path) -> None:
|
|||||||
document = read_topology(TINY / "integration/soc_topology.json",
|
document = read_topology(TINY / "integration/soc_topology.json",
|
||||||
TINY / "address_map/memory_map.json")
|
TINY / "address_map/memory_map.json")
|
||||||
generate_bindings(document, output)
|
generate_bindings(document, output)
|
||||||
|
interfaces = (output / "rtl/tiny_apb_interfaces.svh").read_text()
|
||||||
|
for index, target in enumerate(document["apb_targets"], start=1):
|
||||||
|
name = target["name"]
|
||||||
|
assert f".presetn(s_{name}_rst_n)" in interfaces
|
||||||
|
assert f"assign s_{name}_rst_n = s_leaf_rst_n[{index}];" in interfaces
|
||||||
generate_filelists(filelists, output, ["+define+RETROSOC_SOC__TINY"])
|
generate_filelists(filelists, output, ["+define+RETROSOC_SOC__TINY"])
|
||||||
generate_memory_map(TINY / "address_map/memory_map.json", output / "memory_map", "YES", 128)
|
generate_memory_map(TINY / "address_map/memory_map.json", output / "memory_map", "YES", 128)
|
||||||
resolved = parse_filelists(filelists / name for name in
|
resolved = parse_filelists(filelists / name for name in
|
||||||
|
|||||||
@@ -0,0 +1,124 @@
|
|||||||
|
"""Tiny reset distribution and early timing-feasibility contracts."""
|
||||||
|
|
||||||
|
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 import tiny_r2_feasibility as feasibility
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
COMMON = ROOT / "rtl/managed/clusterip/common/rtl"
|
||||||
|
|
||||||
|
|
||||||
|
def run(command: list[str], log: Path, **kwargs) -> str:
|
||||||
|
result = subprocess.run(command, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, **kwargs)
|
||||||
|
log.write_text(result.stdout)
|
||||||
|
assert result.returncode == 0, result.stdout
|
||||||
|
assert not DEFAULT_FAILURE.search(result.stdout), result.stdout
|
||||||
|
return result.stdout
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("simulator", ["iverilog", "verilator"])
|
||||||
|
@pytest.mark.parametrize("fixture,marker", [
|
||||||
|
("tiny_r2_reset_tb", "SIM_TEST_PASS Tiny R2 reset"),
|
||||||
|
("tiny_r2_fifo_reset_tb", "SIM_TEST_PASS Tiny R2 FIFO reset"),
|
||||||
|
])
|
||||||
|
def test_reset_and_fifo_contracts(tmp_path: Path, simulator: str, fixture: str, marker: str) -> None:
|
||||||
|
assert shutil.which(simulator), f"required simulator unavailable: {simulator}"
|
||||||
|
sources = [COMMON / "utils/register.sv", COMMON / "clkrst/rst_sync.sv",
|
||||||
|
COMMON / "utils/fifo.sv", ROOT / "rtl/tiny/top/tiny_reset_tree.sv",
|
||||||
|
ROOT / "rtl/ip/core/mgmt_debug_reset.sv", ROOT / f"tests/rtl/{fixture}.sv"]
|
||||||
|
includes = [f"-I{COMMON}", "-DSV_ASSRT_DISABLE"]
|
||||||
|
output = tmp_path / "sim"
|
||||||
|
if simulator == "iverilog":
|
||||||
|
assert shutil.which("vvp"), "required Icarus runtime unavailable"
|
||||||
|
command = [simulator, "-g2012", "-s", fixture, *includes,
|
||||||
|
*(str(p) for p in sources), "-o", str(output)]
|
||||||
|
launch = ["vvp", str(output)]
|
||||||
|
else:
|
||||||
|
command = [simulator, "--binary", "--timing", "--assert", "-Wno-fatal",
|
||||||
|
"--top-module", fixture, *includes, *(str(p) for p in sources),
|
||||||
|
"--Mdir", str(tmp_path / "obj"), "-o", str(output)]
|
||||||
|
launch = [str(output)]
|
||||||
|
run(command, tmp_path / "compile.log", env={**os.environ, "CCACHE_DISABLE": "1"}, timeout=120)
|
||||||
|
content = run(launch, tmp_path / "sim.log", timeout=30)
|
||||||
|
assert marker in content
|
||||||
|
assert "SIM_TEST_FAIL" not in content and "SIM_TEST_TIMEOUT" not in content
|
||||||
|
|
||||||
|
|
||||||
|
def test_reset_inventory_has_one_clock_and_all_existing_targets() -> None:
|
||||||
|
topology = json.loads((ROOT / "rtl/tiny/integration/soc_topology.json").read_text())
|
||||||
|
inventory = json.loads((ROOT / "rtl/tiny/integration/clock_reset_domains.json").read_text())
|
||||||
|
reset = inventory["reset_distribution"]
|
||||||
|
assert reset["release_edges"] == 5
|
||||||
|
assert reset["leaf_names"] == ["fabric", *(target["name"] for target in topology["apb_targets"])]
|
||||||
|
assert len(reset["leaf_names"]) == 17
|
||||||
|
assert {domain["name"] for domain in inventory["domains"]} == {"system", "jtag"}
|
||||||
|
assert reset["clock_domain"] == "system" and reset["main_sram_payload_reset"] == "none"
|
||||||
|
|
||||||
|
|
||||||
|
SDC = ("create_clock -name clk_system -period 41.666666667 $clk_system_pin\n"
|
||||||
|
"create_clock -name clk_jtag -period 100 $clk_jtag_port\n"
|
||||||
|
"set_false_path -from [all_inputs]\nset_clock_uncertainty -setup 0.2 [all_clocks]\n")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("mhz", [24, 96, 192, 240])
|
||||||
|
def test_analysis_only_changes_the_system_period(mhz: int) -> None:
|
||||||
|
output = feasibility.candidate_sdc(SDC, mhz)
|
||||||
|
assert output.splitlines()[1:] == SDC.splitlines()[1:]
|
||||||
|
assert float(output.splitlines()[0].split()[4]) == pytest.approx(1000 / mhz)
|
||||||
|
if mhz == 24:
|
||||||
|
assert output == SDC
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("sdc,mhz", [(SDC, 48), (SDC + SDC, 96),
|
||||||
|
(SDC.replace("clk_system", "clk_other"), 96),
|
||||||
|
(SDC.replace("41.666666667", "5"), 96)])
|
||||||
|
def test_feasibility_rejects_wrong_source_clock_or_rate(sdc: str, mhz: int) -> None:
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
feasibility.candidate_sdc(sdc, mhz)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("content", ["wns_max=wns max -1.0\n", "", "wns_min=0\nwns_max=nan\ntns_min=0\ntns_max=0\n"])
|
||||||
|
def test_incomplete_or_invalid_metrics_cannot_pass(tmp_path: Path, content: str) -> None:
|
||||||
|
report = tmp_path / "metrics.rpt"
|
||||||
|
report.write_text(content)
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
feasibility.timing_metrics(report)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("mutation", ["none", "missing", "merged"])
|
||||||
|
def test_reset_loads_require_seventeen_independent_leaves(tmp_path: Path, mutation: str) -> None:
|
||||||
|
report = tmp_path / "loads.tsv"
|
||||||
|
lines = ["name\tdrivers\tdirect_loads\tstructural_endpoints", "system\troot/Q\t85\t1000"]
|
||||||
|
lines += [f"leaf-{i}\tleaf{i}/Q\t10\t20" for i in range(17)]
|
||||||
|
if mutation == "missing":
|
||||||
|
lines.pop()
|
||||||
|
if mutation == "merged":
|
||||||
|
lines[-1] = "leaf-16\tleaf0/Q\t10\t20"
|
||||||
|
report.write_text("\n".join(lines))
|
||||||
|
if mutation == "none":
|
||||||
|
assert len(feasibility.reset_loads(report, True)) == 18
|
||||||
|
else:
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
feasibility.reset_loads(report, True)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("content,expected", [(" -1.25 slack (VIOLATED)\n", -1.25),
|
||||||
|
(" 0.50 slack (MET)\n", 0.5),
|
||||||
|
("No paths found.\n", None)])
|
||||||
|
def test_sys_data_slack_is_separate_from_aggregate_recovery(tmp_path, content, expected) -> None:
|
||||||
|
path = tmp_path / "sys-setup.rpt"
|
||||||
|
path.write_text(content)
|
||||||
|
if expected is None:
|
||||||
|
with pytest.raises(ValueError, match="missing constrained"):
|
||||||
|
feasibility.path_slack(path)
|
||||||
|
else:
|
||||||
|
assert feasibility.path_slack(path) == expected
|
||||||
Reference in new issue
Block a user