Troubleshooting¶
Gotchas encountered running Flow A, kept here so they don’t have to be re-discovered per design.
Environment / install¶
externally-managed-environmentonpip install --user fusesocUbuntu 24.04 blocks system-wide/user pip installs (PEP 668). Use a venv instead — see Getting Started.
ImportError: cannot import name 'walk_tool_packages' from 'edalize.edatool'Version mismatch between FuseSoC and the auto-installed Edalize. Fix:
pip install --upgrade edalize.this core was not foundrunning a design from a local checkoutFuseSoC doesn’t scan the working directory for
.corefiles by default — add--cores-root .to thefusesoc runcommand.
Synthesis¶
- Edalize’s Yosys backend crashes with a
TypeErrorbefore readingyosys_template The Tool-API Yosys backend requires a non-empty
archvalue in the.corefile’stools.yosyssection, even when a full customyosys_templateoverrides its entire built-in flow anyway. Setarch: genericas a placeholder.- OpenSTA/OpenROAD fails to parse the mapped netlist, or complains about
signed Yosys still emits the
signedqualifier on port/wire declarations after synthesis; OpenSTA/OpenROAD’s Verilog reader doesn’t support it. Strip it after synthesis (it carries no structural meaning at this point):sed -i 's/ signed / /g' <mapped netlist>.v
- Registers left as behavioral
alwaysblocks instead of real cells dfflibmapmust run beforeabcin the synthesis script. Without it, OpenSTA/OpenROAD’s Verilog reader — which only understands gate instantiations — fails to parse the netlist.
Gate-level simulation¶
- 100% “Unable to match ModPath” errors during SDF-annotated simulation
Missing
-gspecify. It’s disabled by default in Icarus; without it, specify blocks are parsed but ignored, so$sdf_annotatehas no ModPath objects to attach delays to. The simulation may still report a “pass” in this state — that’s a false positive caused by zero real delay ever being applied. Verify real annotation actually happened by checking the SDF verbose log for nonzero “Putting delay:” lines, or by checking the “Unable to match ModPath” error count dropped to 0%.Could not find net. Did you run iverilog with '-ginterconnect'?Missing
-ginterconnect, required for SDF INTERCONNECT (net/wire delay) matching.- Functional mismatches appear only in the post-route SDF-annotated simulation, not at RTL or post-synthesis
Check static timing first (
report_worst_slack -maxand-min) — if both are comfortably MET, it is likely not a real setup/hold violation but a testbench-timing artifact: the stimulus is being driven on the same clock edge the DUT’s own input-capture register uses, leaving zero margin once real nonzero SDF cell/interconnect delays are applied. Moving the stimulus-driving block to the opposite edge (e.g. fromposedgetonegedgeif the DUT captures onposedge) typically resolves it, giving roughly half a clock period of margin. Confirm with a controlled, single-variable experiment (only change the stimulus edge, re-run against the same routed netlist and SDF file) before concluding this is the cause — a checker sampling-edge race can look similar but does not go away when the checker (rather than the stimulus) is moved to the other edge.- Vectorless
report_powernumbers look implausible (too high, or don’t respond to the clock period) report_powerwith no VCD loaded uses OpenSTA’s default/vectorless switching-activity assumption, which can overstate real power by an order of magnitude and isn’t meaningfully tied to the clock period. Capture a VCD from an already-verified-passing gate-level simulation and useread_vcdbeforereport_power— see the real-activity power section in Running Flow A on Your Own SV Design. A useful sanity check once you have both numbers: real-activity power should generally go up, not down, from pre-route to post-route (added interconnect parasitics add switching capacitance) — if the vectorless numbers show the opposite trend, that’s further evidence they weren’t physically meaningful to begin with.
Place & route¶
GPL-0326 <port> toplevel port is not placedduringglobal_placementRun
place_pinsbeforeglobal_placement— every top-level port needs a placed location first.- IO pins end up clustered on one edge instead of spread across all four
global_placementmoves standard cells to satisfy its density target but does not move already-placed pins along with them. Callplace_pinsa second time, afterglobal_placement.- Placer compacts the whole design into part of the die instead of spreading uniformly
The default target density (0.70) lets the placer satisfy the density constraint with a wirelength-optimal blob rather than a uniform spread. Pass an explicit
-densityclose to your actual target utilization toglobal_placement.detailed_route/TritonRoute crashes or segfaultsConfirmed (via a null-pointer dereference in
dmesg, not memory exhaustion) to be unsafe inside the OpenROAD GUI process. Runpnr.tclheadless (openroad -no_init -exit ...), not with-gui. Everything up to (not including) routing can still be typed into the GUI’s Scripting console for visual inspection if needed.[ERROR EST-0005] Run global_route before estimating parasitics for global routingReloading a written
.defin a freshopenroadsession does not restore the linked-network/global-route state thatextract_parasitics/estimate_parasiticsneed. Keep floorplanning through PPA reporting in one uninterrupted script/session rather than splitting it across separate invocations.$sdf_annotatefails to match instances between the netlist and SDF<design>_route.vand<design>_route.sdfmust be written from the same livepnr.tclsession — instance names must match exactly between the two files.- Icarus’s default min:typ:max delay selection has nothing to pick for “typ”
write_sdfneeds the-include_typflag; without it, OpenROAD only emits min/max delays.
Multi-file designs (Bender / read_slang)¶
sorry: Overriding the default variable lifetime is not yet supportedIcarus Verilog does not support a per-declaration
automatic/staticlifetime override (e.g.automatic integer x;inside a procedural block with a different default lifetime) — this is a real Icarus limitation, not a SystemVerilog error;read_slanghandles the same construct correctly. Fix: remove the qualifier if the variable is always written before read in every iteration where it’s used (usually safe — check this holds before removing it blindly).error: '--ignore-unknown-modules' no longer supported with sv-elabRecent
read_slang/sv-elab builds have removed this flag. Drop it rather than searching for a replacement — it’s only needed when a referenced module genuinely isn’t in the flist, which a correctBender.ymlshould prevent in the first place.--compat-modeis deprecated/always-on (safe to drop too);--keep-hierarchyis explicitly flagged experimental/may-crash by the tool itself — avoid unless you specifically need preserved hierarchy for reporting.couldn't read file "scripts/yosys_common.tcl": no such file or directoryPath is relative to the caller’s working directory, which differs between
synth.tcl(run by Edalize frombuild/<core>_<version>/synth-yosys/, three levels below the project root) andppa_report.tcl/pnr.tcl(run standalone from the project root). Use../../../scripts/yosys_common.tclinsynth.tcland plainscripts/yosys_common.tclin the other two — this is already correct in the versions underflow-a/scripts/; this entry exists for when thesourceline gets copy-pasted between contexts by hand.parameter '<NAME>' not found in '<tb>.dut'during gate-level simulationSynthesis flattens/resolves module parameters, so the mapped/routed netlist’s top module no longer declares them. A testbench that worked fine for RTL sim with
dut #(.PARAM(PARAM)) dut (...)will fail to elaborate against any post-synthesis or post-route netlist. Fix: in the gate-level copy of the testbench only, drop the parameter override entirely (dut #(...) dut→dut dut) — this changes nothing functionally as long as the mapped netlist was synthesized with the same parameter value as the default.Unable to bind wire/reg/memory '<dut>.<submodule>.<signal>'during gate-level simulationA hierarchical debug reference into an internal submodule signal (
dut.u_sub.internal_signal) in the testbench. Synthesis renames/flattens internal nets, so these references can’t resolve against a gate-level netlist even when the design itself is correct. Remove or comment out these references in the gate-level copy of the testbench only — never edit the original RTL testbench used for pre-synthesis simulation.- Garbled/non-hex characters (e.g.
@AA@) in$displayoutput during SDF-annotated simulation, alongside functional mismatches Distinct from the clock-edge race described above (that one produces clean-but-wrong hex values on a clocked design) — this pattern, on a combinational path, means the output is still partially
Xwhen sampled: Verilog’s 4-state ternary (cond ? "PASS" : "FAIL") bitwise-blends both string literals whenconditself evaluates toX, producing corrupted text rather than picking one string. Root cause is almost always insufficient settling time between driving inputs and sampling outputs in the testbench — a delay like#1sized for a zero-delay functional model is nowhere near enough once real SDF gate/wire delays are annotated on a multi-ns combinational path. Fix: increase the testbench’s settling delay to at least 2x the real critical-path delay found intiming_fmax.rpt/timing_postroute.rptbefore re-running.- FuseSoC doesn’t notice RTL edits when the
.corefile’s fileset only lists a Bender-generated flist Expected, not a bug: when RTL sources are read via
iverilog_options: [-f, <flist>]rather than individually declared underfilesets:(see Running Flow A on Your Own SV Design), FuseSoC’s own change-tracking only sees the flist file itself, not the sources it references. In practicefusesoc runalways re-invokesiverilogfresh regardless, so this doesn’t currently cause stale results — but it means any future incremental/cached build tooling added to this flow would need to hash the flist’s referenced files, not just the flist, to stay correct.
Documentation build¶
sphinx-build -Wfails on a warningThe CI workflow (
.github/workflows/docs.yml) treats every Sphinx warning as an error. Common causes: a.. toctree::entry pointing at a page that doesn’t exist yet, or a duplicate section label across pages. Runsphinx-build -W docs docs/_build/htmllocally before pushing to catch these early.