Skip to content

Verilator as a first-class simulator: integration branch#3

Open
nanavati wants to merge 61 commits into
mainfrom
verilator-ci
Open

Verilator as a first-class simulator: integration branch#3
nanavati wants to merge 61 commits into
mainfrom
verilator-ci

Conversation

@nanavati

@nanavati nanavati commented Jul 4, 2026

Copy link
Copy Markdown
Owner

Integration branch for the complete "Verilator as a first-class simulator" stack: the full set of compiler, testsuite, and CI changes under which the BSC testsuite runs clean under Verilator (verilog-only checkparallel/fullparallel with zero unexpected failures on Verilator 5.032; the only FAILs are bsc.long_tests/log2_loop, which fails identically under iverilog).

This PR is the assembled whole, for review of the end state and for running the branch as a unit. The pieces land upstream individually — this branch is the integration of:

Upstream PRs already open (B-Lang-org/bsc)

PR Content
B-Lang-org#937 testsuite: filter iverilog 12+ "Excess hex digits" warnings
B-Lang-org#990 testsuite: reflect/TypeOf uses $display so verilator output isn't clobbered
B-Lang-org#991 testsuite: shape-matched load files for BRAM0Test's degenerate BRAMs
B-Lang-org#992 testsuite: run backend-independent dump checks regardless of $ctest
B-Lang-org#993 testsuite: sort InputClocksSameDomain output (same-cycle cross-module order)
B-Lang-org#994 testsuite: XFAIL b925 Verilog output under verilator (same-time warning order; upstream issue B-Lang-org#989)
B-Lang-org#995 testsuite: fix MPEG4 (sort ordering, check_vcd arg, bad bitstream sentinel)
B-Lang-org#996 Bluesim: TOP_CXXFLAGS overrides C++ flags for the model/schedule file
B-Lang-org#997 Add -system-verilog-output; emit SystemVerilog string parameters
B-Lang-org#998 Emit SystemVerilog-safe identifiers, with a warning (fixes h264 on verilator)
B-Lang-org#999 Support polymorphic import "BDPI" functions over the DPI path (-use-dpi)
B-Lang-org#1000 Add -dump-formats: select waveform dump formats at link time (vcd/fst/fsdb/none)
B-Lang-org#1001 Verilog: render one-port-per-net inout ports plainly (upstream issue B-Lang-org#327)
B-Lang-org#1004 CI: run the Verilog testsuite with Verilator (non-blocking) — draft, the last to land

Fork PR this stack builds on

Not yet carved into upstream PRs (present here)

Validation on this branch

  • Verilator 5.032, verilog-only, all 864 testsuite directories: zero unexpected failures, zero XPASS, zero ERROR (log2_loop excepted, as under iverilog).
  • iverilog: unchanged/clean throughout.
  • Verilator 4.038 (ubuntu-22.04's version) compatibility census: the flow, DPI (including polymorphic), and tracing all work; ~20 remaining failure root causes are Verilator 4 defects fixed in 5.x (fatal $readmem, escaped-identifier lexing, BLKANDNBLK, $fclose({...}), VL_SHIFTL_WWQ, ...). The CI wiring keeps 22.04 as a non-blocking compatibility canary.

🤖 Generated with Claude Code

nanavati and others added 30 commits July 2, 2026 01:17
simCOpt moves block-local defs into their using function; the order came
from the Id sort, which is hierarchy-dependent. Sort by base name.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The port list came from an AId-keyed map, whose Ord is run-dependent.
Sort by base name.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
tsort breaks ties by the run-dependent AId Ord. Rank def nodes by
id-name around the tsort so the statement order is stable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The member defs were ordered by an AId-keyed map, whose Ord is
run-dependent. Sort by base name.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When linking a Bluesim simulation (bsc -sim -e <top>), bsc generates
the per-module and whole-design-model C++ source, then compiles and
links it into a shared object. The new -sim-codegen-only flag stops
after generation: it writes the .cxx/.h files to the simulation
directory without compiling or linking them. This lets an external
build system own the C++ compile and link as separate steps, so that
identical per-module C++ can be compiled once and shared across
simulation executables instead of being recompiled inside every link.

The flag also disables the stale-object reuse analysis so the
generated file set is deterministic and complete on every run, and is
rejected when linking with a non-Bluesim backend. The top-module
runtime-suitability checks (always_enabled interfaces, top-level
arguments and parameters) are skipped under the flag, since the
generated model is never executed and any module may serve as the
codegen root.

Note: the new Flags field changes the .ba serialization, so .ba files
are not interchangeable across this change.

Includes a testsuite test under bsc.bluesim/codegen_only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The old name implied a simulation top; the flag emits one module's Bluesim
C++ with no top-level. Pure rename: flag, Flags field, S0096 error, golden
-help/-print-flags-raw output, and the codegen_only test dir.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Check that block-codegen of a module matches a normal -sim build, and that
a submodule's C++ is identical across hierarchies. Also cover the
always_enabled case (rejected as a top with G0062, accepted as a
block-codegen root).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A reusable block is never run as a top, so generate the root as it would
appear as a submodule: mkScheduleStmts emits no statements for the root's
own interface-method nodes (their enable/argument ports then go unreferenced
and are optimized away), and simBlocksToC skips the "model_" wrapper.

The result is byte-identical to the C++ the module gets as a submodule, so
the object can be built once and shared across simulations.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Extend the shared test_c_veri worker: for every multi-module test,
regenerate each bsc submodule on its own with -block-codegen and confirm the
.cxx/.h are byte-identical to the in-design version. parallel.exp also adds
an explicit check for mkGCD.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Skip modules that cannot stand alone as a root (imported BDPI functions),
and skip the check under -show-timestamps/-show-version, which embed build
metadata that legitimately differs between bsc invocations.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a fixture family that steps each "access count" 0 -> 1 -> 2 -> n: rules
reading a shared value, method arguments, method count, method kind,
submodule instances, wide method ports named out of sorted order, a
kitchen-sink module, and subinterface forwarding. Each is a submodule of the
testbench, so the shared check confirms every module's per-module C++ is
byte-identical. This exposed the member-def ordering case the rest of the
corpus missed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Record in DEVELOP.md why -block-codegen output is byte-identical to a
module's submodule form (the property that makes object reuse sound), with
one-line pointers at the load-bearing sites.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Closes the last case where -block-codegen output differed from submodule
form. moveDefsOntoStack keeps a def as a member iff two functions read it. A
top interface method's CAN_FIRE is read by both its RDY function and the
module's own schedule (arbitration), so it stays a member; as a submodule
that arbitration moves to the parent, leaving one reader, so it moves to a
local. Drop those own-schedule reads (interface-method CAN_FIREs only) so
block-codegen matches. Gated on -block-codegen; normal output unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Code generation is now its own mode alongside -g (elaborate) and -e
(link): "bsc -sim -c M" generates M's Bluesim C++ from M.ba, named for
the cc -c contract (compile, don't link).  The blockCodegen field
remains as an internal driver bit, so every codegen site is untouched
and the output is byte-identical to -block-codegen's.  Conflicts (-e, a
source file, -systemc, non-.ba files, no backend) are rejected at
decode with errors S0096-S0099.  Tests and goldens migrate to the new
spelling; the .ba tag is bumped for the new Flags field.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
-c M now writes only M's generated files (the cc -c contract: one
module in, that module's outputs out); submodules get theirs from their
own -c invocations, and link remains the closure that generates
whatever is missing.  The SimSystem is still loaded in full; only which
files are written changes, never their bytes, so byte-identity with the
submodule form is unaffected.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A module linked as the top is generated in "top form" -- the form the
link's own schedule and model_ expect -- unlike the block form that -c
produces and that submodules reuse.  Record the form in the .h options
descriptor so reuse never mixes them: a block-form artifact is not
taken as a link's top (previously a C++ compile error at the schedule
file), and a top-form artifact is regenerated rather than reused as a
submodule (previously reused; generate shareable objects with -c).  A
top from an earlier link of the same design still matches and is
still reused.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Define "block form" and "top form" up front in the DEVELOP.md
byte-identity section, and reword the SimCOpt comment that relied on
the opaque phrase "own-schedule reads".

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The -u up-to-date check accepted any .ba newer than its source, even
one that cannot be used: elaborated for a different backend (e.g. left
by "-verilog -elab -g"; a following -sim compile would skip codegen and
then fail the link with a backend mismatch), written by another BSC
version (previously a link-time error), or unreadable.  Such a .ba now
forces re-elaboration instead.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
-verilog now sets genABin, as -sim always has, so every backend keeps
the elaborated .ba alongside its generated code; -no-elab (after the
backend flag) suppresses it.  This gives Verilog flows the elaboration
artifact needed for .ba -> .v generation and link-time checks.  Golden
outputs gain the "Elaborated ... module file created" lines.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The hidden -elab-verilog flag stored the generated VProgram in the .ba
(abmi_vprogram) "for accessing in bluetcl", but nothing ever read it
back -- bluetcl included.  Delete the flag, the field, and its
serialization, and bump the .ba format tag for the layout change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
"bsc -verilog -c M" generates M.v from M.ba, reviving the vGenMods hook
that vLink left commented out ("XXX Until we allow re-generation of
Verilog files").  genModuleVerilog now returns the written file names
(its VProgram result lost its only consumer with abmi_vprogram).
Root-only, like the Bluesim mode: each named module's .v is generated
from its own .ba, found among the command-line .ba files or on the
search path.  The output is byte-identical to the .v written by -g for
the same elaboration under the same generation flags.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Verilog linking already loaded the design's .ba hierarchy and discarded
it ("XXX Until we allow re-generation of Verilog files").  Now the link
regenerates any module .v that is missing or older than its .ba, reuses
the rest (reported like Bluesim's object reuse), and passes the module
.v files to the simulator explicitly.  A .v given on the command line is
the user's to provide and is never regenerated; file arguments can be
glob patterns (bsc forwards them to the simulator's shell unexpanded),
so module names are matched against the patterns -- "*.v" keeps its
longstanding meaning of "the user provides every module".  When no .ba
hierarchy can be found, the link warns (S0099) and uses the .v files as
found, as before.  This makes a link self-sufficient regardless of which
subset of the design was pre-generated with -c: -c is point codegen,
link is the closure.

Rather than adding a third variant of generated-file staleness checking,
the timestamp conventions move to a shared StaleUtils (missing product
is never fresh, equal times are fresh), used by the -u check, Bluesim
object reuse, and the new VFileUtils; the checks now agree by
construction, and each keeps only its own validity details (options
descriptor for Bluesim objects, .ba usability for -u).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Describe the three compilation stages and the -c flag (generating code
from a module's elaborated .ba file); note that .ba files are written
by default for both back ends (-no-elab suppresses them); and describe
how linking reuses up-to-date generated files and regenerates the
rest, for both Bluesim and Verilog.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The link's .ba check is all-or-nothing per design: if any module's .ba
is missing -- a submodule's while the top's is present, or the top's
while a submodule's is present -- the link warns (S0099) and uses the
.v files as found, never regenerating part of a design it cannot fully
see.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Polymorphic BDPI imports previously failed with S0015 ("Polymorphic types
are not yet supported with DPI").  SystemVerilog DPI-C imports need concrete
widths, so a polymorphic import is now monomorphized: for each concrete width
at which it is used, BSC emits one fixed-width DPI import (name-mangled by the
polymorphic-operand widths) that links, via a "c_identifier =" alias, to a
generated C wrapper which forwards to the single user C function.

A fixed-width packed operand ("bit [W-1:0]") is passed on the C side as the
word-canonical svBitVecVal* for every width, which matches the polymorphic C
function's word-pointer ABI, so each wrapper just casts and forwards.  This
avoids DPI open arrays entirely (whose Verilator C runtime is unreliable --
svGetArrayPtr can return NULL for reg-backed values) and depends only on core
DPI features, so it works on old Verilator too (verified on 5.032 and 5.049).

Details:
- ForeignFunctions: DPIInst, width mangling, getDPIInstantiations (scans the
  package for concrete uses), and a per-width mkDPIDeclarations.
- Verilog/GenABin: VDPI carries the c_identifier alias and the underlying C
  function name.
- AVerilogUtil: vco carries the ff-map and def widths; call names are mangled
  by concrete width at all three emission sites (vExpr, vForeignCall,
  vDefMpd).
- AVerilog: feed instantiations to mkDPIDeclarations.
- DPIWrappers: generate the wrappers from the monomorphized VDPIs (one file
  per C function, so the link step finds them by the existing name).
- bsc/bsc_build_vsim_verilator: generate wrappers once widths are known and
  hand the wrapper C sources to Verilator to compile.

Monomorphic and non-foreign codegen is unchanged.  The whole
bsc.codegen/foreign suite passes under -use-dpi with Verilator.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Verilator merges all files into one $unit, so file-scope import "DPI-C"
declarations collide ("Duplicate declaration of function") when a testbench
and DUT both use the same foreign function. Emit the imports as module-body
items (new VMDPI) instead, so each module is self-contained. Fixes multi-module
designs for both monomorphic and polymorphic imports; inert for the non-DPI flow.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…iming lock

Designs that instantiate a generated clock or reset (mkAbsoluteClock, clock
dividers, gated clocks, ...) rely on delay-based library Verilog (ClockGen.v
etc.) that only simulates under Verilator's --timing, and their tops drive
their own clocks, needing a timing-aware harness.  Verilator support for both
is incomplete, so such designs are locked by default (BSC_VERILATOR_ENABLE_TIMING
is the back door to try them).

Whether a design needs timing is analyzed at link time from the design's .ba
hierarchy: any module instantiating a submodule with an output clock or reset
(instGensClockOrReset, evaluated over every module's state instances) marks
the design, and the verdict is passed to the build script as
BSC_VSIM_NEEDS_TIMING=0|1.  sim_main.cpp gains a BSC_VERILATOR_TIMING harness
variant for generated-clock designs (advancing simulated time to each
scheduled event, with the async reset negedge SyncResetA needs), used when
the back door enables --timing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Under Verilator, expect-fail the designs Verilator cannot build: clock/reset
generators and no-main tops need a delay-based (--timing) harness, which the
build script refuses by default (BSC_VERILATOR_ENABLE_TIMING is the back
door; see bsc_build_vsim_verilator).  The refusal is detected after the link
by its distinctive message and recorded as a per-design XFAIL
(verilator_link_lock_reason), and the run/compare steps are skipped; the
no-main path (link_verilog_no_main_pass) XFAILs directly.

Adds the verilator_no_timing / verilator_link_locked helpers (verilog.tcl)
and guards the two hand-driven example flows (Amba_dmac, MacTestBench).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
BSC can emit a foreign function literally named srandom(), which collides
with the SystemVerilog built-in srandom() method: Verilator internal-errors
("Classless rand-thing should have failed in V3LinkDot"), while iverilog
treats it as an ordinary user function.  There is no back door, so under
Verilator XFAIL the link (and skip run/compare) for any design whose linked
Verilog calls srandom() -- e.g. bsc.codegen/foreign/battery's TestActionValues.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
nanavati and others added 29 commits July 3, 2026 17:23
…link

Foreign functions compile to DPI-C (-use-dpi) instead of VPI; this is orthogonal
to the simulator, except that Verilator requires it (no VPI) and iverilog cannot
use it (no DPI).  Introduce a first-class use_dpi flag derived from the simulator
(verilator=1, iverilog=0; other simulators via the USE_DPI env var) and apply
-use-dpi only at the Verilog compile/link points (bsc_compile_verilog and
friends, bsc_link_verilog) via a dpi_flag helper -- never globally through
BSC_OPTIONS -- so it cannot leak into flag/error-dump output and is never given
to iverilog or the C backend.

Also make the VPI-wrapper tests DPI-aware: the VPI wrapper mechanism
(compile-time vpi_wrapper_* files, a link-time startup array, the "VPI wrapper
files created" message) does not exist under DPI, so bsc.options and
bsc.driver/depend skip those checks when useDPI.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two comparisons hard-coded VPI-only output that legitimately differs
under -use-dpi:

- bsc.options/verilog-e: the -vsim /bin/echo probe captures the exact
  link command bsc hands the vsim script.  Under -use-dpi bsc appends
  -dpi to that command, so filter the token out before comparing against
  the simulator-agnostic golden (no-op under iverilog).

- bsc.names/signal_names (check_ats FFunc): the compile log line
  "VPI wrapper files created: ..." is absent under -use-dpi (the DPI
  wrapper is built at link, not compile), so drop that line from the
  filtered comparison when useDPI.

Both now pass under verilator and iverilog.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Under -use-dpi the BDPI import becomes a DPI-C function called directly (my_time(...)), not a VPI $imported_ system-task wrapper.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
sim_verilog_status: when only a nonzero (error) status is expected, accept any nonzero exit. Simulators differ on the code -- verilator's $fatal aborts with 134 where iverilog exits 1.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
bsc_build_vsim_verilator: make the generated build's -j configurable via BSC_VERILATOR_BUILD_JOBS (default unbounded for fast standalone builds). parallel.mk sets it to 1 so concurrent test dirs don't each fan out and oversubscribe the machine.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…bered

A newline-less $write puts verilator's "$finish" banner on the same line as the value, and clean_verilator_output strips it along with the value. $display adds a trailing newline so all simulators agree; this test checks type reflection, so the newline is immaterial to what it exercises.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rename the internal flag systemVerilogTasks -> systemVerilogOutput.  -system-verilog-output is now the canonical spelling and -system-verilog-tasks is kept as a deprecated, backward-compatible spelling of it (both are Toggles setting the same field, so -no-system-verilog-tasks still works).

Under the flag, a String module parameter is emitted as a SystemVerilog 'parameter string' rather than an untyped bit-vector parameter, so SV consumers (VCS, verilator) treat it as a string.  parameter string is IEEE 1800-2005+ (rejected by classic Verilog), hence opt-in.  Verilog task emission ($error/$fatal via the former -system-verilog-tasks) is unchanged.

Update the -print-flags-raw golden for the field rename.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… order)

The separately-synthesized InputClocksSameDomain puts its rg1/rg2 displays
in independent always blocks, so their order within a timestamp is not
guaranteed across simulators (verilator orders them differently than
iverilog/Bluesim).  The output leads with a fixed-width timestamp, so
sort_output canonicalizes the same-cycle order while preserving time order,
letting all three simulators share the goldens.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
RegFile and prod_con differ from the iverilog/Bluesim goldens only by the
order of $displays within a timestamp (the design and testbench run in
separately-synthesized modules, so same-cycle cross-module order is not
guaranteed across simulators).  Both goldens lead with a fixed-width
timestamp, so enable sort_output to canonicalize the same-cycle order while
preserving time order -- all three simulators then share the goldens.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Mark the Verilog output comparisons that differ under verilator for legitimate,
LRM-permitted reasons that cannot be reconciled by sorting.  All of these XFAIL
under verilator and pass normally under iverilog/Bluesim (0 XPASS on both).

Deferred $finish drops a final same-timestamp $display (verilator's VL_FINISH
sets a flag and stops the run before emitting the coincident display, whereas
iverilog/Bluesim complete the timestamp).  Both behaviors are legal, but we
prefer the Bluesim-equivalent full output; the designs have no display-free
cycle for $finish to fire on, so they cannot be reshaped into pure reorders:
  - relax_method_urgency/BypassFIFO (final "Data enqued")
  - bugs/b898 Bug898_2 (final "Counter incremented at 55")
  - driver/no_filenames (final timestamp's two submodule lines)
  - misc/fwrite FOpen/FOpen_MCD/FWrites (one extra $fwrite line; also MCD
    $fopen channel numbering -- verilator allocates top-down from 1<<30 vs the
    iverilog/IEEE bottom-up from 2)

2-state (verilator) vs 4-state (iverilog) X/Z:
  - interra/OVL assertNeverUnknown2/assertNeverUnknownAsync2 (the
    "contains X or Z" violation cannot fire under 2-state)
  - misc/divmod DivideByZero/DivideByZeroWide (yields 0, not x)
  - verilog/parameters ModulePort_WithStaticValue_TopLevel (the undriven
    top-level port reads 0, not z)
  - verilog/tasks LogicBetweenTasks3 v-bug and interfacecalls
    sysInterfaceTaskCalls (the captured GitHub B-Lang-org#559/B-Lang-org#215 "bug output" is
    iverilog-specific: X under iverilog, all-1s or a different wrong order
    under verilator)

Other per-simulator manifestations:
  - verilog/tasks LogicBetweenTasks2 (B-Lang-org#559 zero-delay-between-tasks: verilator
    schedules the inlined logic differently and computes a different s1 value)
  - verilog/tasks sysErrorTest ($error/$fatal render differently and the $fatal
    message is dropped under verilator)
  - lib/SquareRoot ($random stimulus differs; verilator does not implement the
    IEEE 1364 reference algorithm -- DUT self-checks still pass)
  - typechecker/display DisplayRealLiteral (bare-real $display uses LRM default
    %g, which strips trailing zeros -- verilator and VCS agree on 0.5; iverilog
    keeps 0.500000 and is the outlier, so the golden holds the standard form and
    iverilog is the veribug here)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A design that manages its own VCD via $dumpfile/$dumpvars/$dumpon/$dumpoff
aborts under verilator ("Turning on wave traces requires
Verilated::traceEverOn(true) call before time 0"), because verilator requires
tracing to be armed before time 0.  Add a +bsctrace run-time plusarg to the
verilator harness (sim_main.cpp) that arms Verilated::traceEverOn(true) without
opening the harness dump.vcd (that remains +bscvcd's job); it parallels the
build-time --trace.

Wire the two tests whose designs drive their own VCD to pass +bsctrace:
  - bsc.verilog/vcd DumpOnOff (verilog-only) via an explicit sim_verilog call.
  - bsc.bluesim/vcd VCDTest1/VCDTest2 (test_c_veri) via sim_options, using
    test_c_veri_bsv_modules_options.

+bsctrace is scoped to just these dumping tests rather than armed globally:
Verilated::traceEverOn makes verilator compute traced signals on every eval,
so arming it on every verilator sim would be a needless per-cycle cost.
iverilog supports $dump* natively and ignores the unknown plusarg, and Bluesim
likewise ignores it, so it is safe to pass unconditionally at these call sites.
These tests now execute under verilator (were aborting) and still pass under
iverilog and Bluesim.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
vector_interfaces and relax-schedule each generate a compile-time,
backend-independent dump (-dwrapper_typecheck / -dvschedinfo) and then check it,
but generated it with compile_object_pass -- which is guarded by
"if {$ctest == 1}" -- while the consuming find_n_strings / compare_file check is
ungated.  Under a -no-ctest run (e.g. a verilog-only run) the setup is skipped,
so the check runs against a file that was never created and fails spuriously.
(The failure was previously masked by stale dump files left over from earlier
-ctest runs.)

Generate the dump with an ungated compile_pass ... -verilog instead.  Both dumps
are backend-independent -- the .sched is byte-identical under -verilog, and the
wrapper-typecheck dump has the same match count -- so the goldens are unchanged
and both tests now run and pass under -ctest and -no-ctest alike.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ng order)

Under verilator, Test2/3/4's Verilog output differs from the golden purely by
the order of two coincident FIFO error_checks warnings: the fifo and zfifo
instances are separate always blocks, and verilator emits their same-time
$displays in the opposite order from iverilog.  Confirmed (by temporarily
timestamping the warnings) that both simulators fire every warning at the
identical simulation time; only the intra-timestamp order of the two coincident
warnings differs, which is unspecified per the Verilog LRM.

The root cause is that the FIFO primitive warnings carry no $time
(B-Lang-org/bsc issue B-Lang-org#989); until they do, the same-time order cannot be
canonicalized, so XFAIL the affected Verilog outputs under verilator.  Test1
(size 1) does not exhibit the reorder and still matches, so it is left as-is.
The Bluesim .c.out XFAILs (cbug) are unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Introduce a link-time -dump-formats flag selecting which waveform formats a
simulation is built to support: a comma-separated subset of {vcd,fst,fsdb},
"none" for no dumping, defaulting to "vcd" (so existing behavior is unchanged).
At run time +bscvcd/+bscfst/+bscfsdb select which compiled-in format to dump.
The value is threaded to the per-simulator build script via the
BSC_VSIM_TRACE_FORMATS environment variable, and each backend translates it to
its own mechanism or errors if it can't provide the format.

Rework src/Verilog/main.v (used by iverilog/VCS/etc.) so that VCD, FST, and FSDB
are additive rather than fsdb-xor-vcd: VCD is always compiled in, FSDB stays
behind `ifdef BSC_FSDB (the Verdi PLI), and FST fires $dumpvars into a .fst file
(the on-disk format is chosen by the simulator at run time, e.g. iverilog's
vvp -fst).  A request for a format that was not compiled in now prints a clear
error instead of silently doing nothing.

This wires up iverilog FST end to end (bsc -dump-formats fst, run with +bscfst
and vvp -fst, producing a valid dump.fst).  bsc_build_vsim_verilator validates
the requested formats and errors on fst/fsdb (verilator FST is a follow-up;
--trace vs --trace-fst is a build-time, mutually-exclusive choice there).

Flags/FlagsDecode gain the field, default, decoder (with EBadArgFlag validation)
and -print-flags-raw entry; GenABin gains the matching positional slot; the
bsc.options goldens are updated for the new flag in -help/-print-flags output.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Teach bsc_build_vsim_vcs to honor -dump-formats: VCD is native ($dumpvars, no
extra flags), and fsdb is enabled by passing -kdb -debug_access+all (modern VCS
bundles the Verdi FSDB writer, so no external Verdi PLI / VERDI_HOME is needed)
plus +define+BSC_FSDB, which makes the main.v harness call $fsdbDumpvars.  VCS
does not produce FST, so fst (and any unknown format) is rejected with a clear
error.

NOTE: verified only by construction -- shell logic and the standard -kdb flags
were checked, but this was not exercised against a real VCS installation (none
available here); the -debug_access level may need adjustment on a given VCS.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wire FST support for verilator.  bsc_build_vsim_verilator maps -dump-formats to
verilator's trace flag: vcd -> --trace, fst -> --trace-fst, none -> no tracing
(a faster build).  --trace and --trace-fst are mutually exclusive, so a request
for both vcd and fst is rejected; fsdb (not a verilator format) already errors.

sim_main.cpp selects the writer class from verilator's VM_TRACE_FST macro
(VerilatedFstC vs VerilatedVcdC) and dumps to dump.fst/dump.vcd accordingly.
The runtime dump trigger matches the built format (+bscfst for an FST build,
+bscvcd for a VCD build); passing the other format's plusarg, or any +bsc<fmt>
to a -dump-formats none build, now prints a clear error instead of silently
producing nothing.  tfp is now a VM_TRACE-guarded global and step() no longer
takes it, so the none build compiles with no trace class at all.

Verified: -dump-formats fst produces a valid dump.fst (fst2vcd round-trips);
default vcd still produces dump.vcd; wrong-plusarg and none builds error; and
the normal verilator VCD/tracing tests (verilog/vcd, bluesim/vcd, mcd/Hierarchy)
are unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
With -dump-formats none, the Bluesim codegen now stubs out the per-signal VCD
dumping machinery: dump_VCD_defs and dump_VCD collapse to empty methods (they
must still exist -- they are pure-virtual on Module), and the per-member/port
vcd_write_def / vcd_write_x / change-detection / full-value emission and the
per-submodule recursion are not generated.  The VCD helper prototypes are
likewise omitted from the class declaration.  Default (vcd) output is unchanged.

Measured on a 1000-register module: generated C++ shrinks ~20% (426KB -> 343KB;
the VCD recursion was ~22% of the module file).  Note this is a code-size
reduction, not a compile-time one -- the g++ time is dominated by optimizing
the schedule/model file, which this does not change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The generated model_<top>.cxx (the schedule) is dispatch code: compiling it at
the default -O3 is disproportionately slow -- its g++ time grows much faster
than the file size and dwarfs the rest of the link -- while buying no measurable
simulation speed, since the hot compute lives in the per-module files.

Allow its compiler flags to be overridden with the TOP_CXXFLAGS environment
variable, falling back to CXXFLAGS (and then the built-in -O3) when unset, so
default behavior is unchanged.  The per-module files continue to use CXXFLAGS.

Measured on a 1000-register design: model_*.cxx compiles 21s at -O3 vs 2s at
-O1 with identical simulation output, and 2M-cycle run times equal within
noise; end-to-end bsc link time drops 35s -> 17s with TOP_CXXFLAGS=-O1.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A -dump-formats none build was only honest under verilator; elsewhere a
runtime dump request silently misbehaved: the main.v simulators (iverilog,
VCS, ...) still dumped VCD on +bscvcd (the $dumpvars trigger was compiled in
unconditionally), and a Bluesim none build given -V silently wrote a
headers-only VCD with no signals.

Now "none" uniformly means: the harness dump trigger is not compiled in, and
a runtime dump request errors loudly.
 - main.v: the +bscvcd/+bscfst dump branch is compiled out under a new
   BSC_NO_DUMP define, replaced by a clear $display error.
 - bsc_build_vsim_iverilog: new -dump-formats handling (vcd/fst are free at
   build time, fsdb rejected); defines BSC_NO_DUMP for "none".
 - bsc_build_vsim_vcs: defines BSC_NO_DUMP when no format is requested
   (VCD is native to vcs, so this is the only honest way to honor "none").
 - Bluesim: the generated model's dump_VCD_defs reports "built with
   -dump-formats none" on stderr when dumping is requested (it runs once, at
   VCD-header time), instead of silently producing an empty VCD.

Verified: iverilog none + +bscvcd errors with no dump.vcd; Bluesim none + -V
errors; default vcd behavior unchanged on both (real dumps, tests green:
verilog/vcd 4/0, bluesim/vcd 26/0, b925 26/0).  VCS is by construction (not
installed here): same main.v define mechanism as iverilog.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…tinel)

Three fixes that together make MPEG4 pass under Bluesim, iverilog and
verilator:

1. The Verilog output differed under verilator only by the order of two
   coincident $displays from separately-synthesized modules (a single swap in
   30k lines; the outputs are sorted-equal).  Enable sort_output on the
   Verilog comparison so all simulators share the golden.

2. The $check_vcd=0 argument (intended to disable the VCD check "because the
   files are too large") was landing in the sort_output slot, so the giant
   VCD-dump run had silently been executing all along.  Plumb it to the right
   position on both the Bluesim and Verilog calls.

3. bitstream.txt ended with a malformed 'ffffffff' sentinel in an 8-bit
   stream: Bluesim's "Malformed value" load error was baked into the .c
   golden, and iverilog 12+ newly warns "Excess hex digits" at load, breaking
   the .v comparison.  Fix the data (-> 'ff', the value Verilog simulators
   truncated to anyway; the address is never read), drop the error from the
   .c golden, and accept Bluesim's now-accurate gap warning (the file has
   always been one entry short of the declared range).  Also update the
   testbench comments that documented the old loader error.

Verified: verilator CTEST=1 8/0 (Bluesim + sorted Verilog), iverilog 4/0,
no VCD-dump runs, 0 XPASS.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Iverilog 12+ warns about excess hex digits when $readmemh data is wider than
the target register, which breaks output comparisons against goldens produced
by older iverilog.  Filter the warning in clean_iverilog_output, alongside the
existing $readmem warning filters.

This is B-Lang-org/bsc PR B-Lang-org#937 applied locally; it complements the MPEG4
root-cause fix (the one known offender in this tree) by shielding any other
test data with wide readmem entries.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…rilator)

BSV's parser rejects Verilog-2001 keywords as identifiers, but words that are
reserved only in SystemVerilog pass through and are emitted verbatim -- so a
design with a register named "process" (bsc.bsv_examples/h264) produces
Verilog that no SystemVerilog-mode consumer can parse (verilator with DPI
fails with "syntax error ... expecting '{").

Three-part fix:

1. The Verilog printer emits identifiers that collide with SV-reserved words
   as escaped identifiers (\global ), which are legal in both Verilog-2001
   and SystemVerilog, so the output parses under either language level.  The
   set is the IEEE 1800-2017 reserved words minus Verilog-2001, plus the std
   package class names.  (Of these, BSV's parser currently admits process,
   semaphore, mailbox, global, soft, strong and weak; the parser's own
   keyword table stops at SV 3.1.)

2. Escaping cannot help with the std built-in class names (process,
   semaphore, mailbox): verilator resolves even the escaped identifier to
   the std class.  Since generated code never uses the std package, pass
   --no-std to verilator.

3. Verilog generation warns once per colliding identifier (G0129, with the
   identifier's source position), since users may prefer to rename.  The
   warning is suppressible and can be promoted to an error with
   -promote-warnings G0129 for a strict "reserved word" policy; reserving
   the words outright in the parser was rejected as a breaking change
   (h264 itself would stop compiling).

Adds bsc.verilog/svkeywords testing all seven admitted identifiers: compile
(expecting 7 G0129 warnings), link and run identically under Bluesim,
iverilog and verilator.

Verified: h264 now compiles, links and runs under verilator (previously the
last verilator-specific failure besides log2_loop); signal_names, vcd, b925
and Hierarchy regressions unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Noinline functions and Classic foreign functions both appear as ANoInlineFun
instantiations, and the .ba hierarchy walk followed both like submodules,
demanding a .ba.  A noinline function is elaborated by bsc and has one (so
its Verilog can be checked and regenerated at link), but a Classic foreign
function is bound to hand-written Verilog -- e.g. the library's Fork, shipped
as $BLUESPECDIR/Verilog/Fork.v with no elaboration -- so a .ba never exists
and the walk failed on any design using one.

Follow a function module's .ba when it can be found, and otherwise record it
as a foreign leaf (like a BVI import), whose Verilog is the user's or the
library's to provide.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Verilog linking warns (S0099) and falls back to "the .v files as found" when
the design's .ba hierarchy cannot be loaded.  For verilator that is not safe:
the link analyzes the hierarchy to configure the build (whether the design
needs a delay-based --timing harness), and a wrong guess is a broken
simulation.  So when the resolved simulator is verilator, report the real
reason the hierarchy could not be loaded (e.g. a missing submodule .ba,
S0040) as an error instead.

Two cases keep the warn-and-link-as-found behavior even under verilator:
other simulators (they consume no link-time analysis), and a top module with
no .ba at all -- that is the hand-written-Verilog-top flow (e.g. a Verilog
testbench around generated modules), for which a .ba never exists; the
main.v harness drives such designs, so the timing analysis default is
correct.  The error is reserved for the genuinely unsafe case: a loadable
design with a missing piece, which can neither be checked nor regenerated.

The simulator is resolved once, before the hierarchy check, and passed to
vSimLink.  The vlink_regen scenarios cover all three behaviors: submodule
.ba missing under a loadable top errors under verilator (and warns
elsewhere); a missing top .ba warns and links as found everywhere.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
BRAM0Test exercises zero-width-address BRAMs (one location) but loaded them
all from the shared 8-entry bram2.txt.  $readmem data beyond the memory range
is only a warning under iverilog but a runtime fatal under verilator, so the
test aborted there.  Load the one-location BRAMs from a one-entry bram1.txt
instead (the 8-bit-address BRAMs keep bram2.txt; underfilling is tolerated
everywhere).  The loaded values are overwritten before ever being read, so
the expected output is unchanged.

Verified: 10/0 under both verilator (previously aborted) and iverilog.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
bsc bound every inout port of a generated module to its internal net
with a port expression in the module header -- ".iioo(x$INOUT)", or
".arg(arg)" for a module argument -- a header form some tools (notably
Verilator, which rejects it outright) cannot parse.  When the port is
the only one on its net the expression serves no purpose: rename the
net to the port name throughout the module (renameInoutPorts, a pass
over the final VModule) and emit a plain port.  This makes the common
wrapper/pass-through patterns (e.g. a BVI module wrapped and
re-exported) buildable by such tools, and gives the net the
documented, attribute-controlled port name in waveforms instead of an
internal name.

Truly shorted boundary inouts -- two or more ports on one net, e.g. a
module argument re-exported as an interface inout, or two boundary
inouts joined by mkConnection -- have no plain rendering and keep the
port-expression form; the alternatives are no more portable (Verilator
rejects port aliases, SystemVerilog alias and the V1995 tran primitive
alike, and iverilog rejects alias too).

The basicinout.pl -verilog-filter fixture (a user-space rewrite of
exactly the rendering this commit fixes) now passes already-plain
ports through instead of dying, which also makes it idempotent -- the
"basicinout Bug" double-application XFAIL becomes a pass.  Golden .v
files with one-port-per-net inouts are regenerated (the old filter
renamed the header and instance connections but not the wire
declaration; the compiler rename covers all of them).

bsc.verilog/inout and bsc.verilog/filter pass under iverilog
(171/0/0/0 and 10/0/0/0).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
bsc renders a shorted boundary inout -- two or more ports on one net,
e.g. a module argument re-exported as an interface inout, or two
boundary inouts joined by mkConnection -- as Verilog port aliases
(".X1(net), .X2(net)" in the module header; one-port-per-net inouts
are plain ports since the previous commit).  Verilator rejects that
header syntax outright, and supports none of the alternative
renderings either: SystemVerilog alias and the V1995 tran primitive
are also unsupported (Verilator 5.032), and iverilog rejects alias
too, so the port-alias form bsc emits is already the most portable.
Designs compiled with -no-inline-inout-connect additionally pull in
the library InoutConnect.v, whose header is itself a port alias.

Detect the situation from the design's .ba hierarchy at link time
(modUsesInoutPortExpressions, mirroring the needs-timing analysis) and
tell the build script via BSC_VSIM_INOUT_PORTS, which refuses with a
clear error instead of a Verilator syntax error.  Each module's
InoutConnect inlining is judged by the flags that produced its .v
(this link's flags for a .v it regenerated, the .ba-stored flags
otherwise); shorted boundary ports are a topological property no
conforming rendering avoids, so every module in the hierarchy is
judged; zero-width inouts are ignored (they never reach the port
list); and inout expressions that defeat the net resolution
conservatively lock.

The testsuite records the refusal as a per-design XFAIL through the
same post-link lock detection as the --timing lock
(verilator_link_lock_reason "verilator-inout-ports").  With this,
bsc.verilog/inout and bsc.verilog/filter run clean under verilator (90
PASS / 29 XFAIL / 0 FAIL / 0 XPASS and 10/0/0/0): designs whose inout
wiring Verilator can build -- internal tristate buses, wrapped BVI
modules (TbBoth's three-driver bus now simulates correctly) -- pass,
and the genuinely unbuildable shorts are tracked XFAILs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Verilator 4.x falsely flags shared counters updated from generated
always blocks (e.g. the OVL error_count in std_ovl_task.h) as circular
logic, and UNOPTFLAT is fatal by default, so every OVL checker fails to
build.  Verilator 5.x does not warn on these at all, so the waiver is
inert there.  (UNOPTFLAT is a performance lint, not a correctness one.)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add a Verilator testsuite job to the release build/test matrix on every
platform: iverilog is replaced as the Verilog simulator
(TEST_BSC_VERILOG_SIM=verilator) and the run is Verilog-only
(CTEST=0 SYSTEMCTEST=0), since the Bluesim and SystemC backends are
already covered by the main testsuite job.  Verilator is installed from
the distribution (apt/brew), so the matrix brackets the supported
range: ubuntu-22.04 ships Verilator 4.038 at the old end, Homebrew
tracks the current release at the new end.

The job is informational for now (continue-on-error): the testsuite is
not yet expected to be clean under Verilator until the remaining
enablement changes land, and Verilator 4.038 additionally predates
several upstream Verilator fixes.  The job gives visibility without
blocking merges, and can be promoted to a required check once it is
green on the supported floor (Verilator >= 5).

The job runs only in the release matrix (verilator_testsuite: true);
the GHC-version matrices skip it, since Verilator results do not vary
by GHC version.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.

2 participants