Plan — RTL-source provenance (ADR 0021 Phase 2)
2026-07-07 — end-to-end closed (PR pending on
ws-a-abc-new-provenance). The WS-A↔WS-B loop works: behavioral RTL →sim→ a signal resolves to its RTL line.src/synth.rsmaps viaabc_newand emits\src; A1/A2 landed — the provenance fork was moved togpu-eda/yowasp-yosys, whose CI builds + validates + releases the wheel, andjacquardfetches that pinned release on first use (no PAT, no artifact expiry). WS-B B0–B2 + B3 (xsources, trace-signals) done; B3 timing-report remains (see below). aigpdk mapping needed two fixes the sky130 A0 didn't surface:read_liberty -lib+hierarchy -purge_libbefore a singleabc_newpass. The per-section markers below predate this and are being folded in at merge.
Status: Active — in flight (updated 2026-07-06; per-section ✅ markers below
are authoritative). WS-A: A0 is GO — the forked provenance wasm builds and
carries origins through the in-process WASI abc_new/aiger2 XAIGER-"y" path
(100% \src coverage on comb + seq2); remaining A1 (harden/pin) + A2 (distribute)
- the
src/synth.rs→abc_newintegration. WS-B: B0–B2 done (sverilogparsecapture →netlistdb.cell_src→AIG::aigpin_src_locations); B3 (surface in--trace-signals/ timing /xsources-xroots) is next. WS-A and WS-B are independent; full end-to-end needs A1+A2 and B3.
ADRs: 0021 Phase 2 (the roadmap this realises), 0014 (AIG core the provenance rides through), 0018 (wasm distribution).
Predecessors: ADR 0021 Phase 1 (the on-ramp — sim/cosim synthesize
behavioral RTL via embedded YoWASP Yosys). This plan makes the results of that
simulation speak RTL source locations instead of flattened gate names.
Tracking: #162 Phase 2.
Goal
Thread \src (RTL source file:line, Yosys's source-provenance attribute)
from synthesis all the way to Jacquard's user-facing outputs, so that
--trace-signals, timing-violation reports, and X-debugging (xsources/xroots)
report RTL source locations rather than post-synthesis flattened gate names.
"Why is spiflash.ctrl.\$auto$…$1234 X?" becomes "spiflash.v:88".
Why it's two independent problems
- The toolchain must emit
\src. Stock YoWASP Yosys drops source provenance through std-cell mapping (abc). Carrying it needs a patched toolchain — the origin-shell\srcpass-through: berkeley-abc #487 (vOrigins/&origins) +robtaylor/yosys@src-retention-y-ext. - Jacquard must ingest
\src. Even with a perfect provenance netlist, Jacquard throws it away today:sverilogparsediscards all attributes (vendor/eda-infra-rs/sverilogparse/src/sverilognom.rs:44— "we regard attributes as comments"), andnetlistdbhas no source-location field.
These are independent and should be de-risked in parallel: WS-B (ingestion)
can be prototyped end-to-end against a hand-annotated (* src=… *) netlist
before the real toolchain (WS-A) exists. Only invest in the heavy wasm build
(A1) once both the A0 spike passes and WS-B is proven.
WS-A — Provenance-carrying toolchain
Reframed after reading origin-shell (2026-07-04). The gate is not "does
\srcsurvive in-process abc" — origins ride the XAIGER"y"channel (an in-memory AIGER round-trip via theaiger2reader/writer, keyed by object id), so external-vs-in-process abc is irrelevant to the channel. origin-shell's real lesson: provenance cannot survive the classicabc/BLIF path at all (BLIF has no object identity), so the std-cell flow must move toabc9/abc_new. Data path (origin-shell POC):abc_new: write_xaiger2 → ABC (&read; &origins; &dch -f; &nf) → read_xaiger2.This means two things Jacquard's own
synth.rsmust change, independent of the patched wasm:
src/synth.rsmaps with classicabc -liberty(lines 241/244) → must become theabc_newflow (&dch -f; &nf,scratchpad -set abc9.origins_max N) to carry origins.- It writes with
write_verilog -noattr(line 247) —-noattrstrips\src. Must drop it (attribute-selective if needed).QoR caveat:
abc9std-cell mapping is "the road less travelled" upstream (YosysHQ/yosys#5679removedabc9 -liberty; the yosys fork carries theaiger2std-cell path). Bare&nfis 9–22% worse area than classicabc;&dch -f; &nfrecovers to parity. So the flow change carries a QoR validation obligation, not just a wiring change.
A′ — De-risk the abc_new flow on the stock wasm first (no patched toolchain)
abc_new/&nf exists in the stock yosys.wasm we already ship (just without
the &origins patch). So the flow migration + its QoR risk can be proven before
any fork/build work, moving the "road less travelled" risk off the patched-wasm
critical path.
- In
src/synth.rs, switch the twoabc -liberty aigpdk_nomem.libpasses to theabc_new&dch -f; &nfflow againstaigpdk_nomem.lib; keep-noattrfor now (no origins to preserve yet). - Exit: the on-ramp still produces a correct aigpdk netlist via
abc_new, and QoR (cell count / area fromstat) is at parity with the classic-abcbaseline on the counter/assert/mem designs.
A′ spike result (2026-07-05, stock
yowasp-yosys 0.64) — the early signal fired. Ranabc_new -script +&dch,-f;&nf -liberty aigpdk_nomem.libon a small design (scratchpad/tmp/claude/aprime):
- Combinational-only (adder) → works (maps to
$_NAND_/$_XNOR_/…).- Sequential (a
posedgeflop) → FAILS:ERROR: Bad connection $auto$ff.cc:337:slice$150/D ~ \d [1](abc_new marked "experimental").- classic
abcbaseline on the same design → works, 33 cells (23 AND2 + 4 DFF + 6 INV).The
ff.ccerror is precisely the flop-handling thatrobtaylor/yosys@src-retention-y-extfixes (origin-shell lists "theff.cc/memory_map/mem.ccfixes"). So A′ cannot be fully de-risked on the stock wasm — the yosys fork is required even to makeabc_newfunction on sequential logic, not just to carry origins. Consequence: A′ folds into A0 — the first real test is building the forked wasm and running theabc_newflow there (getting flow-correctness + QoR + origins in one shot), since stock can only validate the purely-combinational sub-case.
A0 — GATING: build the forked wasm, check \src survives
With A′ proving the abc_new flow works, A0 adds the origins: build the
patched wasm and confirm \src rides the XAIGER "y" channel through mapping.
The spike is the build (the wasm is the A1 artifact — no throwaway work).
Build inputs — ✅ ALL WIRED + VERIFIED 2026-07-06 (the two repoints below are
done; recon was against Codeberg YoWASP/yosys, 2026-07-04). Chain:
robtaylor/yowasp-yosys @ yowasp-yosys-integration (NEW; mirror of Codeberg, base develop-0.64)
└─ yosys-src → robtaylor/yosys@src-retention-y-ext (bcc5698)
└─ abc → robtaylor/abc@origin-tracking-clean (2daf32f2, #487) ← abc is nested in yosys-src
└─ yosys-slang-src → povik/yosys-slang (SV frontend, upstream)
- ✅
yowaspoverlay: createdrobtaylor/yowasp-yosys(full mirror; defaultdevelop= clean mirror,yowasp-yosys-integrationcarries the repoint), repointedyosys-src→robtaylor/yosys@src-retention-y-ext. - ✅ Inside the yosys fork: repointed
robtaylor/yosys@src-retention-y-ext'sabcsubmoduleYosysHQ/abc→robtaylor/abc@origin-tracking-clean(commitbcc5698). Without this the build would have yosys-side\srcretention but stock abc, and provenance would die at mapping.
Build mechanics — ⚠️ CORRECTED 2026-07-06 (supersedes the "CMake + wasi-sdk 33"
recon; that was develop HEAD, the WRONG base for our fork). Two hard constraints
force the develop-0.64 Makefile recipe, not develop's CMake:
robtaylor/yosys@src-retention-y-extis Makefile-only (noCMakeLists.txt) —develop'scmake -S yosys-srccannot build the patched fork at all.develop's CMake migration (61073ed "CI: switch to CMake based builds") dropped yosys-slang — its wasm has noread_slang, the SV frontend the on-ramp depends on.developalso moved to wasi-sdk 33.
So base the overlay's integration branch on develop-0.64: build.sh is the
Makefile flow (make -f yosys-src/Makefile CONFIG=wasi, -flto, -Wl,-z,stack-size=8M)
- wasi-sdk 27 + flex-2.6.4 built from source + yosys-slang built via cmake
and whole-archive-linked (
libyosys-slang.a) — the exact recipe that built the pinnedyowasp-yosys 0.64the on-ramp uses. It hardcodes x86_64-linux wasi-sdk → Linux/Docker/CI only (done: the A0 CI runsubuntu-latest).
A0 CI (authored 2026-07-06): robtaylor/yowasp-yosys/.github/workflows/provenance-wasm.yml
— build (compile → WASI, link slang, smoke read_slang/abc_new, upload wasm+wheel)
provenance-check(origin-shell'sabc_neworigins flow oncomb+seq2vs sky130,\srccoverage; seq2@0% fails). Residual risk CI will surface: develop-0.64's yosys-slang pin4e53d772targets yosys 0.64 (Apr); our fork's yosys is ~Jun 2026, so slang may need a newer pin to compile.
- Repoint (steps 1–2 above), run
build.sh, and run the A′abc_newsynth flow withorigins_maxset and-noattrdropped on a small multi-module design; measure the\srccoverage — the % of mapped cells carrying asrcattribute. Crib origin-shell'stest/src_coverage.shharness (loadswrite_jsonoutput, counts'src' in cell['attributes']over mapped cells) — reuse it near verbatim. - Go/no-go: high
\srccoverage on mapped cells → you already hold the provenance wasm (A1 done); proceed to harden (A1) + distribute (A2). Low/zero → theaiger2"y"channel or abc&originsisn't functioning in the wasm build; diagnose before investing further.
✅ A0 GO (2026-07-06, CI run
28779240456). The provenance wasm builds and carries origins:comb 4/4 = 100%,seq2 88/88 = 100%\srccoverage on mapped sky130 cells. Origins survive the in-process WASIabc_newround-trip (including sequential logic) — the ADR's key open risk is resolved. Getting here also required rebasingrobtaylor/abc@origin-tracking-cleanonto currentberkeley-abc/master(it was 97 commits behind, missing the#ifdef __wasmguards around abc'ssystem()calls →wasm-ld: undefined symbol: system). CI:robtaylor/yowasp-yosys/.github/workflows/provenance-wasm.yml.
- Fallbacks if it fails: (a) debug the
aiger2/&originspath in the wasm build (the channel is XAIGER round-trip, not exec, so the failure is in the reader/writer or the patch, not "in-process abc" per se); (b) Nix native toolchain — origin-shell is already a Nix flake with the validated flow, so a native back-end forjacquard sim --synth-backend nixis the lowest-risk way to ship provenance if the wasm path proves stubborn; (c) native-only provenance, on-ramp wasm stays provenance-free.
A1 — Harden & pin the provenance build
The A0 build, productionized. Thin harness fork, not our own build recipe:
YoWASP/yosys is just a build harness, so the overlay's whole delta is the two
submodule repoints — trivial to rebase when upstream moves, and it beats
hand-rolling a WASI/LTO build (where subtle divergence bites hardest). The durable
maintenance is keeping the existing yosys/abc patch branches rebased on
upstream — unavoidable in any approach, already owned. Per
[~/.claude/FORKED_DEPS_WORKFLOW.md], carry each of the three on an
-integration branch.
Pinning — track our branches, don't freeze SHAs. These are our actively
developed branches (we'll tweak the \src patches, and want to fold in upstream
review of abc#487), so pinning them to frozen SHAs would fight normal development.
Instead:
- Track by branch, not SHA, for the three forks we own (
yosys/abc/yowaspoverlay) — new patches and upstream-review changes flow through automatically; when a patch merges upstream, retarget the branch to upstream. - Pin only genuinely-third-party build inputs we don't control and need
reproducible: wasi-sdk version, flex, and the rest of
build.sh's toolchain. - Stamp provenance into each built artifact, not the source: when CI builds a
released wasm, record the exact
yosys/abc/yowaspSHAs (release notes / asset metadata). That gives release-level reproducibility — any shipped wasm is recreatable — without freezing the dev branches. Develop freely; ship traceably.
Exit strategy: upstream the patches (abc#487 → yosys → a YoWASP build knob) to shrink the forks toward zero.
- Exit: a pinned, CI-reproducible wasm that runs the existing
synth_scriptand emits(* src *)on mapped cells; byte-diff vs the stock wasm shows only provenance additions. - Escape hatch (own-recipe): only if A0 shows
build.shis gnarly/undermaintained enough that rebasing the overlay is painful — then reimplement a minimal pinned WASI build in-repo. Default is the thin fork.
A2 — Distribute the provenance wasm
Publish as a Jacquard release asset (shares the Phase-4 fetch-from-release
mechanism). Decide: is provenance the default on-ramp wasm, or an opt-in
(--yosys-wasm/a provenance flag) larger asset? Recommend opt-in first.
WS-B — Jacquard \src ingestion (startable now with a synthetic netlist)
B0 — Capture attributes in sverilogparse
Stop discarding (* … *) (sverilognom.rs:44). Parse src (and keep the door
open for other attrs) and attach to the cell/wire in the SVerilog AST.
vendor/eda-infra-rs is a vendored submodule → fork + integration branch per
~/.claude/FORKED_DEPS_WORKFLOW.md.
- Exit: a
(* src="f.v:12" *)-annotated netlist round-trips throughSVerilog::parse_*with the attribute retrievable per cell.
✅ B0 DONE (2026-07-06).
gpu-eda/eda-infra-rs@jacquard-integrationa0772f4;SVerilogCellgainssrc: Option<CompactString>.skip_whitespace_and_commentno longer treats(* ... *)as whitespace;leading_attributescaptures/skips at the 3 grammar leading edges (module / port / module-body item). Proven bysverilogparsetest_src_attribute(+tests/attributes.v): parse + round-trip. Caveat: attributes are now only accepted at those leading edges (narrows vs the old "ignored anywhere" — fine for structural netlists).
B1 — Carry source location through netlistdb
Add an optional per-cell (and where meaningful, per-net) source_loc on
NetlistDB, populated from B0. Keep it Option — most cells post-synthesis may
have 0 provenance.
- Exit:
NetlistDB::from_sverilog_fileexposes\srcfor annotated cells; zero overhead / behaviour change when absent.
✅ B1 DONE (2026-07-06).
gpu-eda/eda-infra-rs@jacquard-integration4f7f0ec;NetlistDB.cell_src: Vec<Option<CompactString>>parallel tocelltypes/cellnames, populated ininsert_cellfromSVerilogCell.srcthrough flattening.Nonefor top cell + synthesised inverters + un-annotated cells. Proven bynetlistdb/tests/provenance.{rs,v}.
B2 — Preserve provenance through AIG / staging / flatten
Thread a mapping from AIG nodes / endpoint groups back to \src so a
sim-visible signal can be traced to a source line. Provenance is lossy by
nature (abc merges/splits nodes): design for 0, 1, or many source locations
per signal; never assert exactly one.
- Exit: given an annotated netlist, a chosen output/endpoint resolves to its
source location(s) through the built
FlattenedScript.
✅ B2 DONE (2026-07-06, Jacquard
68cc938b).AIG::aigpin_src_locationscomposes the AIG's existing per-pin cell-origin map (aigpin_cell_origins, built for SDF back-annotation) withnetlistdb.cell_src(B1) — no new state threaded through the AIG builder. Returns 0/1/many de-duplicated locations; a primary output resolves directly. The resolver is AIG-level (the AIG is retained alongside the FlattenedScript). Proven byaig::path_mapping_tests+prov_annotated.v.
B3 — Surface \src in the user-facing outputs
Add source locations to the three consumers, gated on availability (fall back to today's hierarchical gate name when absent):
-
--trace-signals(docs/signal-tracing.md) — annotate traced signals. -
Timing-violation reports (
docs/timing-violations.md). -
X-debugging
xsources/xroots(docs/x-debugging.md) — the highest-value target: report the RTL line of an X-source, not the flattened gate. -
Exit: an end-to-end run on a synthetic annotated netlist prints source locations in all three; a design with no provenance is unchanged.
⏳ B3 IN PROGRESS (2026-07-06) — 2 of 3 consumers done.
- ✅
xsources(ec7b648f): optionalsrcon theXSourcerecord fromcell_src[cell_id]; schema1.0→1.1. (xprop_demo_synth.gvalready carried(* src *)→ provenance flows through the real pipeline.)- ✅
--trace-signals(ca55c786): logs each traced net's RTLsrcvia the B2 resolver at registration.- ⏳ timing-violation reports — deferred (largest; governed schema). Path:
word_id → cell_idviadff_constraints; addsrctoDffSiteName→ViolationRecord. A word packs many DFFs → 0/1/many. ADR 0008 permits additive → bumpSCHEMA_VERSION1.2.0→1.3.0+ ADR note.
Sequencing
A′ abc_new on STOCK wasm (flow+QoR) ─► A0 patched wasm + \src coverage ─(go)─► A1 harden/pin ─► A2 distribute ─┐
├─► integrate: on-ramp emits + surfaces \src
B0 parser ─► B1 netlistdb ─► B2 AIG ─► B3 outputs ─────────────────────────────────────────────────────────┘ (validated on synthetic netlist first)
- Parallel: the whole A-track and B0–B3 have no dependency; B is provable against a hand-written annotated netlist, and A′ against the stock wasm.
- A′ first within WS-A: proves the
abc_newflow + QoR on the stock wasm before any fork/build, moving the "road less travelled" QoR risk off the patched-build critical path. - Barrier: full end-to-end (RTL → provenance wasm → surfaced source lines) needs A1+A2 and B0–B3.
- Kill-switch: if A0 fails, WS-B still delivers value for any externally produced provenance netlist (DC/native Yosys with origins); the on-ramp just doesn't auto-generate it until a fallback toolchain lands.
Risks / open questions
abc9/aiger2"y"channel in the wasm build — the gating unknown (A0): does the XAIGER origins round-trip function in the WASI-built yosys+abc?- QoR of the
abc_newstd-cell flow — abc9 std-cell mapping is unofficial upstream;&nfalone is 9–22% worse area,&dch -f; &nfrecovers to parity. A′ must confirm parity on Jacquard's designs before committing the flow change. sverilogparsefork maintenance — vendored submodule; carry the patch on a-integrationbranch per FORKED_DEPS_WORKFLOW; upstreamable (attribute capture is generally useful).- Does
\srcsurvive Yosysflatten(separate from abc)? origin-shell targets the full flow, but confirm in A′/A0, since the on-ramp flattens. - Provenance granularity — post-optimization a gate may map to 0/1/many source lines; the reporting (B3) and IR (B2) must not assume 1:1.
- Asset size — a second (provenance) wasm ~doubles the fetched-asset story; A2's opt-in-vs-default call interacts with Phase-4 distribution.
Non-goals
- No change to the emulator AIG/boomerang core (ADR 0014/0015).
- Not blocked on upstream YoWASP adopting the patches (we build our own wasm).
- Full SVA / Verific-grade provenance is out of scope (bounded by the patched open-source toolchain).