Findings — measured data
Everything in this appendix was measured on the artifacts this repository generates, by the tools in tools/ and the flow runs under flow/ — never transcribed from documentation. Each finding names its instrument. Statuses use the scoreboard's vocabulary; rows the re-anchored checker suite has not yet re-measured are listed in the scoreboard as TODO rather than duplicated here.
The generation chain
The chain reproduces from pinned sources. Chisel elaboration (Chipyard, pinned by commit) → FIRRTL → SystemVerilog (firtool, pinned release, checksum-verified) → synthesis (Yosys under Hammer) → place-and-route (OpenROAD) → signoff (Magic, KLayout, Netgen). Every stage runs from this repository; the data-provenance appendix carries the pins.
The elaboration declares the design's headline facts. From the emitted device tree: ISA rv32imaczicsr_zifencei_zihpm_xrocket; one Rocket core; 4 KiB instruction cache (64 sets × 64 B); 16 KiB data memory at 0x8000_0000; PMP with 8 regions at granularity 4; 1 hardware breakpoint; debug module at 0x0 (JTAG); CLINT at 0x200_0000; PLIC at 0xC00_0000 with one device source; UART at 0x1002_0000; 64 KiB boot ROM at 0x1_0000. The ChipTop boundary is 18 signals: UART pair, custom_boot, JTAG ×5, reset_io, clock_uncore, clock_tap, and the serial TileLink port (32-bit phits + link clock).
The RTL, measured (L3)
Census over the emitted SystemVerilog (322 files, 80 K lines including simulation collateral; the design cone is ~230 modules):
- Zero
casex/casez,force-class constructs, UDPs,fork, negedge blocks, delays-in-design. - 303
always @(posedge …)blocks, uniformly non-blocking; the census's blocking-assignment sites are in one DPI harness file outside the design cone. (An earlier count of 608 also swept firtool'send // always @(posedge)closing comments — the census now strips comments, and the checker regression pins the honest number.) - One
always @*block in the entire design —EICG_wrapper, the clock-gate model, a deliberate latch (the ICG primitive, L3/02). - 23
'bxliterals, all one idiom: behavioural memory models yielding X on a disabled read. - 212
initialblocks, every one simulation-only (SYNTHESIS/RANDOMIZE-guarded or the initializer idiom) — no register carries a power-up value. (The earlier 425 double-counted closing comments, as above.) - 3,938 assertion mentions across 85 files (the TileLink monitors and friends) — generator-emitted verification collateral, an adequacy asset (L3/04).
Synthesis (L2)
From the Hammer/Yosys run of the tiny Rocket configuration:
- 51,359 standard-cell instances drawn from 96 cell types; 10,416 flops; zero unmapped cells; 228 modules, 9.4 MB mapped netlist.
- Three SRAM macro kinds, five instances: 2 ×
sram22_2048x32m8w8(data memory banks), 2 ×sram22_512x32m4w8(icache data), 1 ×sram22_64x32m4w8(icache tags). All memories resolved to macros; the behavioural*_extmodels appear nowhere in the mapped netlist. - A flop-mapping interaction worth its own line: sky130 has no synchronous-reset or enable flop, so the synthesis script must let Yosys synthesise those flop types from what exists. A
-map-onlydiscipline (correct for pad libraries, which carry no flops) leaves thousands of abstract$_SDFFE_*cells in the netlist, and the failure surfaces only at P&R ("LEF master not found"). The corrected mapping adds ≈ 6,900 cells of reset/enable gating — cells that were previously "free" only because they were placeholders that could never have been placed.
The constraint set (L1)
The generated SDC, measured: one create_clock (clock_uncore, 50 ns, 2 ns uncertainty), one trivial clock group, zero false paths, zero case analysis, zero multicycle paths. The JTAG (jtag_TCK) and serial-link (serial_tl_0_clock_in) clock domains are absent — their paths are unanalysed, not excepted. This is F3's content: for a generated constraint set the audit's weight falls on completeness, there being no assertions to audit.
The independent hardening run
The same RTL was also hardened through a second, independently configured flow (librelane driving the same underlying tools, with the PDK's OpenRAM-family macros — 20 instances banked behind hand-written muxes — in place of SRAM22). It ran end to end and produced a GDS, and its value is exactly that its results are independent measurements of the same design under different physical decisions:
- Timing risk concentrates at the fast corners. Setup passed at all nine corners with 3–10 ns of margin while hold failed at the six fast and typical corners (−0.05 … −0.09 ns worst slack), the slow corners passing — the corner where this design's risk concentrates, measured rather than guessed. (The rocket-sram22 run then closed all nine corners at the conservative 100 ns clock; the domain-validity caveat is F2.)
- Domain violations are real. 6,186 max-slew, 526 max-cap, 185 max-fanout violations on stretched nets in the loose floorplan — F2's evidence that "inside the characterisation range" is a hypothesis that fails in practice and must be checked, not assumed.
- Fill dominates the census. Fill insertion took the instance count from 271,473 placed cells to 3,564,849 — a 13× multiplication by cells that compute nothing, L2/02's deletion classes made vivid.
- The met4 story. Detailed routing settled at 14 violations, all on the layer carrying the PDN straps (9 spacing, 5 shorts); LVS independently caught one of the shorts electrically (one net of 84,586 mismatched, all 84,051 devices matching — a slew-repair buffer chain extracted onto the power rail). Congestion, DRC, and LVS told one consistent story from three directions.
The flow of record (rocket-sram22)
The layout the book's physical layers name: upstream's floorplan (3588 × 2992 µm ≈ 10.73 mm², five rotated SRAM22 macros) hardened through librelane. Measured on run gds3:
- Detailed routing: 0 violations — the first fully clean route of this design in any configuration; the floorplan (rotated macros, real channels) is what changed.
- Timing: all nine corners pass at the 100 ns clock — hold +0.05…+0.26 ns, setup +25.6…+28.3 ns. The clock is the conservative-verification choice made concrete: at 50 ns, hold passes everywhere and setup fails on unrepaired high-fanout nets (worst path ≈ 83 ns).
- The F2 rows stay open: 19,110 max-slew, 1,145 max-cap, 1,817 max-fanout violations — the priced cost of skipping OpenROAD's
repair_design, which is unrunnable on 32 GB hardware: it retains ~600 KB per iteration (independent of repairs performed) and was OOM-killed at the same iteration in four runs across two flows, once taking the machine with it. Every flow launch now runs underulimit -v. - BEOL DRC (KLayout, tiled): 150 flow-owned violations, all at macro interfaces. The raw report says 90,081 — of which 89,931 sit strictly inside the SRAM macros: bitcell geometry checked against logic rules it was never meant to satisfy, because the deck's
sram_excludeguards FEOL only and SRAM22's GDS carries noareaidwaiver markers. Coordinate clustering against the macro boxes separates the two classes in minutes and belongs in the layer checker. - DRC needed deck surgery to run at all: the PDK deck's monolithic deep mode wants >14 GiB and 5–16 h (died twice in the 16M-polygon
mconblock); enabling the deck's own commented-out tiling (500 µm tiles,deepoff) runs the same rules in 1 h 44 m at 2.5 GB peak. The tiled deck is inflow/rocket-sram22/. - LVS is blocked on this design: Magic cannot read the SRAM22 GDS at all (
sky130_fd_bd_sramcells, unknown layer 64/44) — which also explains why upstream's own flow setsdrc.magic.generate_only: true. Connectivity signoff needs a non-Magic extraction route; until then the row is TODO, not green.
The signoff-coverage summary this adds to F8: for macro-bearing sky130 designs, open-tools signoff is one engine deep exactly where it claims to be redundant — Magic hard-stops on the macros, KLayout needs surgery and has no bitcell waiver mechanism, and LVS inherits Magic's limits.
Instruction-for-instruction against the golden model
Spike co-simulation closes the L3 oracle gap: tools/run-sim.sh cosim runs the design with every committed instruction checked against spike, the RISC-V golden ISA model, per commit — PC, register writeback, CSR effects. A pure-compute RV32IMAC image (sim/cotest.c) runs to a tohost pass with 1,631 committed instructions and zero mismatches — the same 1,631 every time it clears the boot window. The RTL the flow hardens refines the ISA, verified instruction-by-instruction — the executable precursor to L3's refinement proof, with the core's own trace port as the α anchor.
Two boundaries fell out, both diagnosed rather than papered over (COSIM-NOTES): ISA co-simulation ends at MMIO — a UART poll loop diverges because the golden model has no UART, the fundamental scope of core-level co-sim — and the boot/wake window needs deterministic Verilator reset init (+verilator+rand+reset+0) to escape X-propagation nondeterminism.
The cosim tooling itself is a finding: cospike is written and CI'd for 64-bit out-of-order cores, and took eight portability patches to run RV32/in-order/custom-extension (cospike-rv32.patch) — the ISA-string custom-extension strip, an RV64 page-level assert, the xlen-1 interrupt bit, sign-vs-zero-extension in the PC and writeback compares, and the pre-boot X-cause guard. Each is a real incompatibility, of a piece with F8's theme: the ecosystem's verification tooling assumes the mainstream target.
The design executes
The functional gap is closed: the generated SoC runs programs. Chipyard's Verilator harness (tools/run-sim.sh) simulates the full ChipTop with the SimTSI host model driving the serial TileLink port — L5/03's load path exercised literally — and the UART adapter echoing TX:
- A bare-metal image (built with stock clang:
--target=riscv32, freestanding, lld — no riscv-gcc needed) is loaded over the serial link into the DTIM at0x8000_0000; the core boots from the ROM, takes the TSI wake interrupt, jumps, and runs it. - The image prints
hello from rocketover the UART and exits through the riscv-teststohostprotocol — pass signalled,$finish, exit 0. ~1 ms of simulated time, ~80 s of wall clock. - The B3 statement of L5/01 is thereby instantiated end to end (load path → execution → observable output → clean exit), and the scoreboard row
L5/b3-smokeruns the simulation itself — the check is the execution.
The harness rested on three small portability findings, recorded in tools/run-sim.sh: Spike v1.1.0 needs -include cstdint under GCC 13 and does not install libriscv.a (the simulator links it for trace disassembly); the radiance generator's make hook unconditionally injects GPU collateral from un-checked-out submodules into every config's simulator build; and Verilator bakes $RISCV include paths at Verilation time, so the env must be right before Verilating, not just at link.
Signoff engines disagree (F8)
On one and the same layout: Magic DRC reported 0 violations; KLayout DRC reported 4 (2 × met4 minimum width, 2 × met4 minimum spacing). Netgen LVS meanwhile flagged the power short the geometric engines split on. Additional engine-behaviour findings from the same runs: Magic's GDS stream-out emitted the SRAM macro's internal subcells as 13 top-level cells (breaking any consumer that resolves a single top; KLayout's stream-out of the same design has exactly one), and the flow's nine-corner STA step runs all corners in parallel against the fill-expanded netlist — exhausting a 31 GB machine and dying on SIGKILL, with each corner completing cleanly when run alone. The register's conclusion (F8): a single engine's verdict is one witness, never the verdict.
Upstream flow drift
The framework's own physical-flow path (Hammer with its sky130 plugin, the documented tutorial configuration) did not run as shipped against current components: ten distinct interface breakages stood between the documented invocation and a result — a PDK workaround that crashes once the bug it patches is fixed upstream; four SRAM-collateral mismatches (lib-file naming, gzipped GDS, six of thirteen cache entries naming macros that no longer exist, a stale generated memory mapping whose make rule cannot rebuild it); the flop-mapping discipline above; and four OpenROAD command-interface changes (source flags, place_cell → place_inst, place_pins -random obsoleted — the last warning and placing nothing, so the failure surfaced three steps downstream as an unplaced-port error). All ten fixes are small patches to open Python; the finding is not that the flow is broken but that pinned-flow reproducibility decays by default, and only patchable flows can be walked forward — the property the toolchain was chosen for.
Memory macro placement
The physical-design facts that constrain any floorplan of this configuration, measured across both hardening runs: the SRAM22 macros bring the memory to 5 instances (the OpenRAM alternative needs 20 banked instances at 0.142 mm²/KiB); macro power pins sit on met2 while a met4/met5 strap plan connects macro grids at met3→met4 by default — a mismatch that yields an empty macro power grid unless the grid is built from the pin layers up; and a packed central macro block fails global routing where edge-pinned banks with wide channels route. The upstream reference floorplan places ChipTop in 3588 × 2992 µm ≈ 10.7 mm² with rotated macros — comfortably inside a ~15 mm² pad-frame budget.
Category-1 scoping pass: the checks we could run
A sweep over the per-instance-checkable side conditions we had not yet run — the point of the scoping pass being to have run the check for each category-1 tool, on the assumption (generous, this pass) that a passing check means the tool did its job on this instance. Four results:
- L3 comb-checks — PASS. Yosys-elaborating the design cone (via the slang plugin) finds exactly one inferred latch —
EICG_wrapper, the deliberate clock-gate primitive — and zero combinational cycles over the ~230-module cone. The L3/02 side conditions hold, and the "naive netlist" oracle L2 shares is well-defined. (tools/combcheck.ys.) - G6 tied-fill — PASS. The routed DEF has no
FILLSsection and the flow ran no metal-density-fill step: the only fill is standard-cell (decap/fill/tap), rail-tied by construction (L2 W4). No floating conductor exists to relay coupling, so M2's tied-fill precondition holds and the grounded screen (PDN + tied cell fill) is present by construction. The harder half — G5, shielding coverage between coupled nets — still needs the coupling graph. - G3 antenna — FINDING, and a correction. The librelane flow does run antenna checks (steps 39 and 45; the earlier "check skipped" note was about the Hammer flow's
CheckDesignAntennaProperties, a different step). Pre-repair: 1,197 net / 1,817 pin violations; 1,178 protection diodes inserted; post-repair residual: 1 net / 7 pin. So G3 is enforced and checked, with a small unconverged residual — the same family as the 150 DRC violations and F2, the repair steps' incomplete closure. - CEC / F5 — ρ is recoverable from the artifact. The synthesis certificate (CEC) is the category-1 check for L2. A first look at the flop instance names (all anonymised
_NNNNN_) suggested ρ was lost — but that is the wrong object: the register's identity lives in its Q-net name, and 10,873 of 10,915 (99.6%) carry the RTL register's hierarchical name (\system.…output_chain_3.sync_1). So ρ is a checkable hypothesis readable from the existing netlist — no synthesis instrumentation, no tool patch. The 36 anonymous Q-nets plus any constant-folded/merged registers are the residue (theopt_dff/opt_mergedeviations L2/03 names). Feeding the Q-net names as eqy's register matching (a one-linerename) leaves only the per-cone SAT run at 61k cells — an afternoon of eqy-tuning, not the "instrument the synthesis" effort first assumed. F5 downgrades to "ρ recoverable; only the verifying CEC is unrun."
The pass's shape matches the methodology: the runnable category-1 checks either pass (L3 comb, G6) or surface a priced residual (G3), and the one that can't be run cheaply (CEC) turns out to be blocked not by tooling but by the anonymisation the book predicted — so its discharge is genuine L2 proof work, not a check we skipped.
Interpretation discipline
Three habits these findings enforce, recorded so they survive the people who learned them:
- A green single-engine verdict is one witness (F8). Signoff claims are stated engine-independently and measured per layout.
- "By construction" is a claim to check. The one-elaboration property (which made the old F7 dissolve, since it holds by construction) is real and valuable, and the re-elaboration diff is what keeps it a fact rather than a memory.
- Absence of constraints is not absence of risk (F3). A minimal generated SDC moves the audit from exceptions to completeness; unconstrained-is-unchecked is the sharper failure mode.