Skip to content

Asic gate - #409

Merged
tinebp merged 11 commits into
masterfrom
asic-gate
Sep 3, 2026
Merged

Asic gate#409
tinebp merged 11 commits into
masterfrom
asic-gate

Conversation

@tinebp

@tinebp tinebp commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

No description provided.

tinebp and others added 11 commits September 2, 2026 18:43
DBG_FLAGS carried -DVCD_OUTPUT inline, so the textual trace, the waveform,
and -g -O0 all rode on one ifdef DEBUG. A waveform therefore cost -O0 plus
eight log streams, a textual trace cost a full VCD dump, and DEBUG+SAIF
tripped the VCD/SAIF mutual-exclusion #error even though those two are not
in conflict. 127c00d worked around the last part in rtlsim alone, by
making -DVCD_OUTPUT conditional on SAIF -- a dependency from a debug
variable onto a power-analysis one, which left the other four backends
unable to trace and capture activity in the same run.

Give VCD its own ifdef VCD block mirroring SAIF's, across all five
Verilator backends and the unittest harness. --trace/--trace-structs move
with it; -g -O0 stays with DEBUG. The #error is preserved as the single
enforcement point and now fires only on a genuine --vcd --saif request.

blackbox.sh gains --vcd, propagated in both build_driver and run_app since
the test target relinks the model, and rejects --vcd on simx as it already
rejects --saif.

Verified on rtlsim: --vcd alone writes trace.vcd with no --debug, --saif
alone writes trace.saif, --debug=1 --saif writes both run.log and the SAIF
and passes, and --vcd --saif is the only combination reaching the #error.

BREAKING: --debug alone no longer writes trace.vcd; pass --vcd.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Four graphics memory modules declared a local VX_lsu_mem_if named
mem_bus_if and bound it to a .lsu_mem_if port on a module that also has a
mem_bus_if port. Verilator and Vivado accept it; sv2v's scoper resolves
the binding to the port instead of the local and aborts.

Yosys cannot read SystemVerilog interfaces, so sv2v is unavoidable on
that flow -- this blocked ASIC synthesis of the graphics units outright.
Renaming the local to lsu_mem_if resolves it with no functional change:
gfx_tex on rtlsim is bit-identical before and after (instrs=49168,
cycles=137275).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The seed table was computed with SystemVerilog `real` in an initial
block. Verilator and Vivado constant-fold it, but yosys has no
floating-point support and cannot elaborate the module at all.

The replacement is exact integer arithmetic, chosen to be bit-identical
to the rounded floating-point result rather than merely close:

    seed_rom[i] = (2^33 * SEED_N + d) / (2 * d),  d = 2*SEED_N + 2*i + 1

which is the round-to-nearest of 2^32 * SEED_N / d. Verified equal to the
previous table for every entry.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…n.mk

A partially-copied directory got Makefile, common.mk and *.in. Any other
included fragment was left behind, so an include that resolves in the
source tree fails from build/ -- invisible until someone runs the target
there. The flattened DUT trees' catalog.mk and build.mk were missing from
every build tree, which broke `make -C build/hw/syn/xilinx/dut <dut>` and
the fpga_gate along with it.

Also stop .gitignore's `/.*` from swallowing .github. Ignore rules do not
apply to tracked files, so the existing workflows were unaffected and the
rule looked harmless -- but a NEW workflow or action was silently dropped
from every commit, with nothing to notice.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
31 per-DUT Makefiles were 31 copies of the same eight assignments,
differing only in their values. They are now one catalog.mk per flow
(_PRJ/_IP/_CFG/_INC/_PKG/_EXT per DUT, with the include and package
fragments shared) plus one build.mk that the dispatcher copies in as the
work tree's Makefile -- the role the per-DUT files played. Adding a DUT
touches catalog.mk only.

The dispatcher's 21 (resp. 10) identical target blocks collapse to one
pattern rule over $(DUTS), which also gains `%-power` and `list`.

Semantics-preserving, checked rather than assumed: every DUT's resolved
variables were dumped before and after and diffed. 20 of 21 matched
exactly; the differing one was rtu_top, which lost its unittest include
and two packages in the first pass. rtu_top is removed here instead --
its wrapper duplicated hw/unittest/rtu_core and nothing referenced it.

`override CONFIGS +=` on the flows' mandatory defines, so a CONFIGS on a
make command line adds to -DSYNTHESIS -DVIVADO and the DUT's own config
rather than replacing them. Command-line variables win over `+=` with no
warning; fpga_gate only escaped this because it passes CONFIGS through
the environment, which appends.

Drops the tcu-sparse-amo-rvc synthesis case, which no DUT provided.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Four asap7_* files sat in ci/ because that is where the installer was
written, not because they belong to CI: they are standard-cell
collateral, and hw/syn/libs/ is where the rest of it lives.

configure copies only Makefile, *.mk and *.in out of a hw* directory, so
the manifest and preparation script are not in the build tree. They are
inputs rather than build artifacts, so install.sh.in reads them from the
source tree through @VORTEX_HOME@ instead of being copied; only the
installer itself is instantiated.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…fixes

The flow could only synthesize whatever TOP_LEVEL_ENTITY it was handed.
dut/catalog.mk now declares the twelve DUTs -- the same
hw/unittest/*/VX_*_top.sv wrappers the Xilinx flow synthesizes -- as a
top, an include path and a define set each, and dut/Makefile forwards
them to the flow. No per-DUT Makefiles are needed: the flow already
includes hw/syn/extensions.mk, which derives each extension's RTL_PKGS
and include paths from the resolved XCONFIGS.

PREFIX defaults to the DUT name so each gets its own tree. That is
load-bearing, not hygienic: $(BUILD_DIR)/src is cached and is NOT
regenerated when EXTRA_INCLUDE changes, so two DUTs sharing a tree
silently synthesize the first one's sources.

synth_summary.py collapses reports/{stat_lib,sram_area,worst_slack,tns,
power}.rpt into one synth_summary.csv, mirroring what the Xilinx flow's
project.tcl already writes. Format knowledge stays next to the tool that
produces it, so a consumer needs no parser per report.

Two measurement bugs, both of which made a number look right while being
wrong:

- report_wns is worst NEGATIVE slack and clamps at zero, so every design
  that closed reported 0.00 and an Fmax derived from it was the target
  clock read back. run_sta.tcl now also emits report_worst_slack, which
  is signed: om at 800 MHz closes with +0.0318 ns, i.e. 820.9 MHz. ABC
  maps to the target period and stops, so closing with picoseconds of
  margin is the normal outcome and the sign is the whole signal -- 2
  digits of precision is not enough to see it.

- A CONFIGS on a make command line silently defeats every `CONFIGS +=`
  in a makefile. dut/Makefile passed each DUT's defines that way, which
  killed the flow's own `CONFIGS += -DSYNTHESIS -DASIC -DYOSYS`, so the
  ASIC RAM path was never taken and arrays inferred to flip-flops: the
  cache DUT came out at 1.08M cells, 122,724 um^2 and zero SRAM area
  against an actual 423k / 47,068 / 16,307. The dispatcher now uses the
  flow's documented EXTRA_CONFIGS channel and the mandatory defines are
  `override`, so the trap cannot be re-armed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
fpga_gate.py was 856 lines of which the great majority -- catalog
loading, config_hash, threshold resolution, known_issue, the resumable
session, the scheduler, the gate and the report -- says nothing about
Vivado. Only the metric names, the build invocation, the log markers and
"what makes two runs comparable" do.

Those move behind a Tool adapter in ci/synth_gate.py; fpga_gate.py
becomes an entry point that pins --tool=xilinx. The Yosys adapter lands
in the next commit.

No behaviour change, verified against the previous script rather than
argued: identical --list output, identical config_hash for all twelve
builds (11 of 12 still matching their recorded baselines -- tensor was
already stale from the impl_strategy addition, under both versions), and
identical verdicts and report tables across unchanged/worse/better
metrics, record mode, and the build-failure path.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The open-source counterpart of fpga_gate: the same golden/threshold/
ratchet discipline over sv2v -> Yosys -> ABC -> OpenSTA on ASAP7 (RVT,
TT), gating post-synthesis Fmax and standard-cell area. Same DUTs as the
Xilinx gate -- the hw/unittest wrappers, declared once in
hw/syn/yosys/dut/catalog.mk -- so `make -C hw/syn/yosys/dut om` and the
gate build the same thing, and a divergence between the two tools is a
finding rather than an artefact of two DUT lists.

Fmax does not mean on this flow what it means on Vivado. ABC maps to the
target period and stops, so a DUT that closes does so with picoseconds of
margin and its Fmax sits just above the clock it was built for by
construction. Cell area is the metric that actually moves; the
target-frequency check is the real Fmax assertion.

Tool versions are part of config_hash, which they are not on the Vivado
side: Yosys/ABC move area between releases by more than the 5%
tolerance, so a toolchain bump must read as STALE ("re-record") rather
than as a regression blaming the RTL. The strings are normalised to drop
the git sha and the compiler that built the binary, so rebuilding the
same yosys does not invalidate every number.

CI: its own nightly workflow, not a ci.yml cell. A DUT is 1-2 hours, so
eleven in one cell would be a day -- the builds fan out to one standalone
hosted job each, fail-fast disabled so one regression cannot cancel the
ten other measurements. Like fpga_gate.yml it pins master and skips
itself when master has not moved, but a hosted runner keeps no state
between runs, so the "already gated" marker is an actions-cache entry
keyed by SHA and spec hash rather than a file in ~/.cache. synth_report.py
joins the per-job reports into one summary table.

Baselines recorded for the five characterised DUTs; the other six carry a
known_issue naming what is missing, so the nightly builds and reports
them without asserting numbers nobody has reviewed. The cross-check
against the FPGA goldens is what says the memory inference is right:
cache 89 BRAM / 16,307 um^2 SRAM, raster 10 / 3,728, tex 0 / 0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
continuous_integration.md 3.5 covers both gates and what a Tool adapter
supplies; 4.5 is the new fan-out workflow. synthesis_analysis.md gains
the dut/ dispatcher and the gate, plus the two things a reader needs
before trusting an Fmax from this flow: ABC closes to the target, and
report_wns clamps at zero.

AGENTS.md gets the synthesis baselines under the same never-hand-edit
rule as the perf ones, and the note that the two gates measure the same
modules -- so the DUT catalogs must stay in step.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@tinebp
tinebp merged commit fd92b0a into master Sep 3, 2026
1 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant