Flow A: Overview¶
Flow A is the fully open-source EDA/PDK quadrant of the thesis (Yosys + OpenROAD + KLayout, Sky130). This page explains the pipeline architecture; Running Flow A on Your Own SV Design shows how to point it at a design of your own.
Pipeline stages¶
RTL (design.v)
-> functional simulation (Icarus, via FuseSoC)
-> synthesis (Yosys, via FuseSoC, target library from the PDK)
-> post-synthesis functional simulation (Icarus, manual)
-> post-synthesis PPA analysis (OpenROAD/OpenSTA, manual)
-> place & route (OpenROAD, manual — standalone script)
-> post-route SDF-back-annotated functional simulation (Icarus, manual)
-> post-route PPA analysis (OpenROAD/OpenSTA, real OpenRCX parasitics)
Why FuseSoC only covers two stages¶
FuseSoC/Edalize is used only for the RTL simulation and synthesis
stages, via Edalize’s Icarus and Yosys Tool-API backends. Place & route is
run as a standalone OpenROAD Tcl script, not through Edalize’s
OpenROAD backend, because that backend is built specifically to drive the
separate OpenROAD-flow-scripts (ORFS) project via its own
Makefile/config.mk format — a different, heavier toolchain than the
hand-written Tcl flow used here. Everything from place-and-route onward
(PPA analysis, gate-level simulation, SDF back-annotation) is likewise run
by invoking openroad / iverilog directly, not through FuseSoC.
This means for every design the flow needs:
a FuseSoC core file (
<design>.core) that describes two targets:sim(RTL functional simulation) andsynth(Yosys synthesis) — see Running Flow A on Your Own SV Design;three standalone Tcl scripts driven directly by
openroad:ppa_report.tcl(post-synthesis, pre-route PPA),pnr.tcl(floorplan → placement → CTS → routing → post-route PPA), and optionally a power-with-real-activity variant of each;a handful of manual Icarus invocations for gate-level simulation (post-synthesis and post-route/SDF), since neither of those stages goes through FuseSoC.
Output layout¶
FuseSoC-driven stages land under build/<core-name>_<version>/<stage>/.
Everything driven directly by the standalone OpenROAD script (pnr.tcl)
writes to the project root instead, since it isn’t run through
FuseSoC/Edalize and has no build-directory convention to follow:
<design>/
├── constraints/
│ └── <design>.sdc
├── golden/ # expected-output reference data
├── <design>.core # FuseSoC core file
├── rtl/
│ └── <design>.v
├── scripts/
│ ├── synth.tcl # Yosys synthesis (custom template)
│ ├── ppa_report.tcl # post-synthesis PPA
│ └── pnr.tcl # place & route + post-route PPA
├── tb/
│ └── tb_<design>.sv
├── build/
│ └── <core-name>_<version>/
│ ├── sim-icarus/ # RTL sim output
│ ├── synth-yosys/ # synthesis output (netlist, reports)
│ └── gls-icarus/ # gate-level sims (created manually)
├── <design>_placed.def # from pnr.tcl, project root
├── <design>_route.v # post-route netlist
├── <design>_route.sdf # post-route SDF (real delays)
├── <design>_route.spef # extracted parasitics
├── timing_postroute.rpt
├── timing_postroute_hold.rpt
└── power_postroute.rpt
Why the flow works this way¶
A few design choices recur throughout the scripts and are worth knowing
up front, since they explain “why is this step here” the first time you
read synth.tcl or pnr.tcl:
``dfflibmap`` before ``abc``. Generic/behavioral flip-flops must be mapped to real library cells before ABC’s technology mapping runs, since OpenSTA/OpenROAD’s Verilog reader only understands gate instantiations, not behavioral
alwaysblocks.Stripping ``signed`` from the mapped netlist. Yosys still emits the
signedqualifier on port/wire declarations in the gate-level netlist; OpenSTA/OpenROAD’s Verilog reader does not support it, and it carries no structural meaning once the netlist is fully mapped, so it is safe (and required) to strip.Two ``place_pins`` calls in ``pnr.tcl``. The first is required before
global_placement(which hard-errors if a port has no location yet); the second is required afterglobal_placement, because that step does not move already-placed pins along with the cells it re-positions.``-density`` below the default in ``global_placement``. OpenROAD’s default target density (0.70) lets the placer satisfy the density constraint by compacting the netlist into a wirelength-optimal blob covering only part of the die, instead of spreading uniformly the way a commercial placer does by default. Setting
-densityclose to the actual target utilization forces a uniform spread.``pnr.tcl`` must run headless.
detailed_routeis known to segfault inside the OpenROAD GUI process (confirmed via a null-pointer dereference indmesg, not memory exhaustion). Every command up to (not including) routing can be typed into the GUI’s Scripting console for visual inspection if needed.Post-route PPA reporting stays inside the same live ``openroad`` session as place & route. Reloading a written
.defin a fresh session does not restore the linked-network/global-route state thatextract_parasitics/estimate_parasiticsneed — a standalone reload-and-report script fails with[ERROR EST-0005] Run global_route before estimating parasitics. This is whypnr.tclkeeps floorplanning through PPA reporting in one uninterrupted script.Vectorless vs. real-activity power.
report_powerwith no VCD loaded uses OpenSTA’s default/vectorless switching-activity assumption, which is not tied to the clock period in a physically meaningful way and can be off by an order of magnitude. For a trustworthy figure, capture a VCD from an already-verified-passing gate-level simulation and feed it toreport_powerviaread_vcd— see Running Flow A on Your Own SV Design for the pattern.
See Troubleshooting for the full list of gotchas encountered
running this flow, including the gate-level simulation flags
(-gspecify, -ginterconnect) and a testbench-timing pitfall that
only shows up once real SDF delays are annotated.