Troubleshooting

Gotchas encountered running Flow A, kept here so they don’t have to be re-discovered per design.

Environment / install

externally-managed-environment on pip install --user fusesoc

Ubuntu 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 found running a design from a local checkout

FuseSoC doesn’t scan the working directory for .core files by default — add --cores-root . to the fusesoc run command.

Synthesis

Edalize’s Yosys backend crashes with a TypeError before reading yosys_template

The Tool-API Yosys backend requires a non-empty arch value in the .core file’s tools.yosys section, even when a full custom yosys_template overrides its entire built-in flow anyway. Set arch: generic as a placeholder.

OpenSTA/OpenROAD fails to parse the mapped netlist, or complains about signed

Yosys still emits the signed qualifier 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 always blocks instead of real cells

dfflibmap must run before abc in 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_annotate has 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 -max and -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. from posedge to negedge if the DUT captures on posedge) 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_power numbers look implausible (too high, or don’t respond to the clock period)

report_power with 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 use read_vcd before report_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 placed during global_placement

Run place_pins before global_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_placement moves standard cells to satisfy its density target but does not move already-placed pins along with them. Call place_pins a second time, after global_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 -density close to your actual target utilization to global_placement.

detailed_route/TritonRoute crashes or segfaults

Confirmed (via a null-pointer dereference in dmesg, not memory exhaustion) to be unsafe inside the OpenROAD GUI process. Run pnr.tcl headless (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 routing

Reloading a written .def in a fresh openroad session does not restore the linked-network/global-route state that extract_parasitics/estimate_parasitics need. Keep floorplanning through PPA reporting in one uninterrupted script/session rather than splitting it across separate invocations.

$sdf_annotate fails to match instances between the netlist and SDF

<design>_route.v and <design>_route.sdf must be written from the same live pnr.tcl session — instance names must match exactly between the two files.

Icarus’s default min:typ:max delay selection has nothing to pick for “typ”

write_sdf needs the -include_typ flag; without it, OpenROAD only emits min/max delays.

Multi-file designs (Bender / read_slang)

sorry: Overriding the default variable lifetime is not yet supported

Icarus Verilog does not support a per-declaration automatic/ static lifetime 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_slang handles 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-elab

Recent 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 correct Bender.yml should prevent in the first place. --compat-mode is deprecated/always-on (safe to drop too); --keep-hierarchy is 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 directory

Path is relative to the caller’s working directory, which differs between synth.tcl (run by Edalize from build/<core>_<version>/synth-yosys/, three levels below the project root) and ppa_report.tcl/pnr.tcl (run standalone from the project root). Use ../../../scripts/yosys_common.tcl in synth.tcl and plain scripts/yosys_common.tcl in the other two — this is already correct in the versions under flow-a/scripts/; this entry exists for when the source line gets copy-pasted between contexts by hand.

parameter '<NAME>' not found in '<tb>.dut' during gate-level simulation

Synthesis 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 #(...) dutdut 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 simulation

A 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 $display output 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 X when sampled: Verilog’s 4-state ternary (cond ? "PASS" : "FAIL") bitwise-blends both string literals when cond itself evaluates to X, 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 #1 sized 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 in timing_fmax.rpt/ timing_postroute.rpt before re-running.

FuseSoC doesn’t notice RTL edits when the .core file’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 under filesets: (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 practice fusesoc run always re-invokes iverilog fresh 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 -W fails on a warning

The 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. Run sphinx-build -W docs docs/_build/html locally before pushing to catch these early.