Cross-oracle check: najaeda structural backend vs. reference-yosys
Run date: 2026-07-14
Design
reference-naja is a second structural backend (see
docs/backend-sdk.md and
docs/reference-naja-backend.md) that reproduces the
reference-yosys CDC/RDC rule set on a different toolchain: najaeda's bundled
slang frontend and SNL netlist, with no Yosys or Icarus binary. At the time of
this comparison it implemented REF-CDC-001/002/003 and REF-RDC-001;
REF-XPROP-001 (power-on X) was not yet implemented and is out of scope for
this comparison. (The backend has since gained REF-XPROP-001 support, see
docs/reference-naja-backend.md; the 72/72 result
below predates it and is unaffected, since this artifact's candidates can
trigger only REF-RDC-001.)
To test agreement where the two scopes overlap, we ran reference-naja fresh
against every candidate in the frozen 72-candidate artifact at
artifacts/reset-replication-v0.1/. The reference-yosys structural verdict for
each candidate is already recorded in that artifact (report.json's
structural block and the top-level manifest.json index), so no Yosys or
Icarus run is needed: this is a comparison against a frozen oracle, not a
re-evaluation. Each candidate's manifest.toml names reference-yosys as its
backend; the harness bypasses that field and calls ReferenceNajaBackend
directly. The check is committed as
tests/test_reference_naja.py::ReferenceNajaCrossOracleTests.
The 72 candidates come from eight reset-sensitive tasks, three model
configurations, and three samples per cell. They are a reset-release taskpack,
so the only rule any candidate can trigger is REF-RDC-001; the CDC rules are
exercised by the controlled witness pairs in tests/test_reference_naja.py, not
here.
Result
| Comparison | Count |
|---|---|
| Candidates | 72 |
| Status agreement | 72 / 72 (100.0%) |
| Rule-set agreement | 72 / 72 (100.0%) |
| Disagreement | 0 / 72 |
Status agreement and rule-set agreement are identical: on every candidate the
two oracles emit exactly the same rule set (empty on the 57 mutual passes,
{REF-RDC-001} on the 15 mutual fails). There is no candidate where the two
agree on pass/fail but disagree on which rule fired.
| yosys → naja | Count | Interpretation |
|---|---|---|
| pass → pass | 57 | agree (no finding) |
| fail → fail | 15 | agree (REF-RDC-001) |
| pass → fail | 0 | — |
| fail → pass | 0 | — |
History (kept honest). Before the wide-vector reset-synchronizer fallback
was ported into _reset_synchronizer_regs, this comparison stood at 54 / 72
(75.0%), with 18 pass(yosys) → fail(naja) disagreements — all one
false-positive class, described below. Porting the fallback resolved all 18 and
regressed none of the 54 that already agreed, giving the 72 / 72 above. The 15
mutual REF-RDC-001 fails and 39 of the mutual passes are unchanged; the 18
formerly-disagreeing candidates moved from fail(naja) to pass(naja),
lifting the mutual-pass count from 39 to 57.
Resolved: the wide-vector reset-synchronizer class
The 18 former disagreements were a single false-positive class, and one the
backend already anticipated. Earlier, the ReferenceNajaBackend class docstring
and _reset_synchronizer_regs noted that the Yosys reference has an extra "one
wide vector-shift $adff cell" reset-synchronizer fallback that was
deliberately not ported, on the assumption that "naja lowers explicit
multi-flop reset synchronizers as separate scalar instances in every fixture
observed."
That assumption held for svgap's own example fixtures but not for these
generated candidates. The openai-frontier-a candidates write the
reset synchronizer as a single packed vector:
logic [1:0] reset_sync;
always_ff @(posedge clk or negedge global_reset_n) begin
if (!global_reset_n) reset_sync <= 2'b00;
else reset_sync <= {reset_sync[0], 1'b1};
end
// downstream registers reset on reset_sync[1]
naja lowers reset_sync as one 2-bit sequential instance (..._dffrn__w2), not
two scalar flops. The original _reset_synchronizer_regs required a single-bit
first and second stage (len(q_nets) == 1), so it never recognized this
one-instance synchronizer, and the synchronizer's own register was then flagged
by REF-RDC-001 as an unmarked async-reset consumer. The claude-* candidates
write the same synchronizer as two named scalar flops, which naja lowers as two
scalar instances that the recognizer already accepted — hence the clean model
split.
This was confirmed structurally: for openai-frontier-a--sample-01/reset_config
the flagged cell was the 2-bit reset_sync register itself, and the downstream
config_out register (which resets on the synchronized reset_sync[1], an
internal net rather than a declared reset port) was correctly not flagged. The
design is a textbook async-assert / sync-deassert reset synchronizer;
reference-yosys is right to pass it and reference-naja's earlier fail was a false
positive.
The fallback is now ported. _reset_synchronizer_regs recognizes the wide
single-instance form directly, reproducing the Yosys structural test
(d_bits[0] == inactive constant and d_bits[1:] == q_bits[:-1]): a single
multi-bit sequential instance under one shared async reset, whose bit-0 D loads
the reset's inactive constant and whose every higher bit D is the previous bit's
Q. Because naja routes each of those D bits through a transparent assign alias
(where Yosys has no such cell), the D nets are resolved through the assign glue
before the constant/shift test — reproducing Yosys's resolved-bit view. The match
is deliberately narrow: an ordinary wide async-reset data register (whose D
bits resolve to external data, not to the inactive constant or prior Q bits)
fails both the constant-at-bit-0 and the shift-chain checks and is still flagged,
so the recognizer does not wave through any packed register that merely carries
an async reset. This over-acceptance guard is covered by
ReferenceNajaWideVectorResetTests in tests/test_reference_naja.py.
Frontend-coverage note: SystemVerilog function return
The pilot (docs/pilot-result.md) recorded two functionally
passing Gray-counter candidates that Yosys 0.66 rejected because they used a
SystemVerilog function with an explicit return statement — a construct Yosys's
native Verilog frontend does not parse. Those raw candidates are not in this
checkout (reports/generated/ is gitignored), so we reproduced the construct in
a minimal isolated snippet: an automatic gray-to-binary helper with an
explicit return, matching the Gray-counter task's shape.
- Yosys (0.67+post here; same frontend limitation as 0.66) rejects it:
ERROR: syntax error, unexpected TOK_IDon thereturnline. - najaeda / slang accepts it, elaborating a top design with the expected instances.
This is a positive result: najaeda's slang frontend covers at least this class
of candidate that the Yosys reference reports as tool_error. It does not
change any verdict in the table above (none of the 72 frozen candidates use the
construct), but it means the two pilot Gray-counter tool_error cases would
plausibly have elaborated under reference-naja. The snippet is not committed;
it is reproducible from the description above.
Defensible claim
On the frozen 72-candidate reset-replication artifact, the najaeda structural backend reproduces the reference-yosys verdict on all 72 candidates (rule-for-rule). The one former false-positive class — a packed-vector reset synchronizer that naja lowers as a single multi-bit register — is now recognized by the ported wide vector-shift fallback, closing the last disagreement; before the port the figure was 54/72.
This is a scope-overlap agreement measurement on one frozen taskpack. It is not a claim that the two backends are interchangeable in general, that 100% is their asymptotic agreement, or that reference-naja is signoff-grade. Agreement with the frozen verdicts is compatibility; it is neither independent validation of reference-yosys nor production truth. The frontend-capability evidence issue #1 asks for (function-return and enum-syntax fixtures) stays open follow-up work outside this comparison. It does establish that on this taskpack they agree exactly, and that the modeling difference that had separated them is understood down to the netlist and resolved rather than merely characterized.
Threats to validity
- One taskpack, all reset-release: the CDC rules (REF-CDC-001/002/003) are not exercised by this artifact and are covered only by the controlled witness pairs, which are far smaller than 72 candidates.
- The frozen
structuralfield is treated as ground truth. It is the reference-yosys verdict, itself a narrow structural oracle, not silicon signoff; agreement with it is agreement with a reference tool, not with physical reality. - The 72/72 agreement is measured after porting the wide vector-shift fallback,
which was validated against exactly the packed-vector coding style present in
this taskpack (all
openai-frontier-a). A synchronizer written in some third style neither taskpack sample exercises — a wider chain, a different injection bit, or a non-shift topology — could still fall outside both the scalar and the wide recognizer; the recognizer matches the specific Yosys structural test, not every conceivable synchronizer. Agreement on this taskpack is exact, but it remains conditional on the two coding styles it actually contains. - The
return-syntax result uses a reconstructed snippet, not the original pilot candidates, which are not in this checkout.