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. synth.tcl already strips it as its last step; if you hit this error on a netlist from a custom script, strip it yourself (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 extraction (write_spef) in the one pnr.tcl session. Reporting does not need that state: post_route.tcl reloads the routed netlist and reads the written SPEF instead.

$sdf_annotate fails to match instances between the netlist and SDF

<design>_route.sdf must be written from the same routed netlist as the <design>_route.v you simulate — instance names must match exactly. pnr.tcl writes <design>_route.v; post_route.tcl reloads that file and writes the SDF, so the two stay consistent as long as you do not re-run pnr.tcl without re-running post_route.tcl (make sdf handles this through its dependencies).

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 (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 .core file’s rtl fileset, which a complete fileset prevents 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 #(...) 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 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.

synth.tcl reports no RTL files from the .core file's filesets

The synth target has no filesets: entry, or the files in them lack file_type: systemVerilogSource (Edalize ignores files whose type it doesn’t recognise as Verilog/SystemVerilog). Check the target lists filesets: [rtl] and the fileset sets the file type.

unknown TECH '<name>' / $PDK_ROOT is not set

Every flow script loads scripts/tech/$TECH.tcl through scripts/tech.tcl (TECH defaults to sky130hd). Export PDK_ROOT/PDK as in Getting Started, and run tclsh scripts/tech_query.tcl check to list any missing PDK file.

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.

Signoff, timing and repair

report_checks / write_sdf ignore the extracted wires (setup slack too good, every SDF delay 0)

OpenSTA only uses parasitics after read_spef. Use scripts/post_route.tcl (make postroute), not reports made inside pnr.tcl.

Input hold is not checked, or the testbench fails hold at the first vector

set_input_delay with -max leaves min delays unset, so input hold paths are unconstrained. Use set_input_delay without -max, and launch the testbench inputs at +IN_DLY equal to the SDC input delay (a mismatch causes hold races and false SDF failures).

Hold fails at the ss corner but passes at tt

Input-port-to-flop paths: the clock tree is slow at ss while the external input delay is fixed. Repair with a hold margin (HOLD_MARGIN, default 0.35 ns) so the paths keep margin at the slower corner; check with make multicorner.

Setup fails at the ss corner but passes at tt

repair_timing only fixes violations in the library it reads, which is tt by default. Run make pnr-ss (repair against ss_100C_1v60; the default 0.5 ns SETUP_MARGIN is applied), then make multicorner. In the reference run this moved ss setup from −2.26 ns to +0.29 ns. If it still fails, raise SETUP_MARGIN or relax the clock period.

Pin count mismatch / every cell NoMatch in LVS

The CDL written by write_cdl is not the right cell schematic for the KLayout deck. lvs.sh builds the schematic from the PDK SPICE through lvs_prep.py and runs the deck with scale=true, convert_subckts=true and schematic_simplify=true.

LVS MISMATCH with the top cell Skipped, only tie or diode cells differ

Tie cells (conb_1) and antenna diodes (diode_2) have no device the layout extraction recognises. lvs_prep.py rewrites the tie cell and drop_cells.py removes the diodes from the schematic. LVS then no longer checks those cells.

LVS shows dangling power pins on cells inserted by the repair steps

Run global_connect before write_def/write_cdl (already in pnr.tcl): cells created after pdngen have no power connections until then.

DRC reports li.5 (or other) on a few standard cells

Run the same deck on the bare library cell (command in Flow A: Signoff and Results). If the cell alone violates, it is a PDK library finding, not caused by the flow. probe_p_8, probec_p_8 and lpflow_* cells are excluded with dont_use_cells.

Antenna violations remain after detailed route

Antenna repair works on the global-route estimate. Raise ANT_MARGIN (percent) for more diodes; expect a different set of violating nets rather than zero. See Flow A: Signoff and Results.

A run directory vanished or a second launch broke the first

run_flow.sh refuses an existing tag. Start a new tag instead of deleting runs/<tag>; removing a directory under a running job makes the job fail with missing-file errors in logs/.

klayout fails with ModuleNotFoundError: pya in a helper script

PATH order: with oss-cad-suite/environment sourced, the wrong python3 is found. Run helper scripts with ~/klayout-venv/bin/python.

Windows/WSL: /tmp is cleared

Keep scripts and outputs you need under the project directory, not /tmp.