Flow A: Signoff and Results¶
This page documents everything Flow A does after the basic
place-and-route steps in Running Flow A on Your Own SV Design: how timing is
constrained and reported with real wires, how the netlist is repaired,
how the layout is checked (DRC, LVS, antenna, multi-corner timing),
and how runs are isolated so results are reproducible. All numbers on
this page come from the reference run sign_p28_hm35 of
examples/custom_myfir (Sky130 HD, 28 ns clock) unless stated
otherwise.
What a run produces¶
make check sim synth ppa gls pnr sdf power # functional + PPA
make drc lvs # signoff (after pnr)
make pnr runs scripts/pnr.tcl and then scripts/post_route.tcl
(the $(ROUTE_SDF) dependency). make drc / make lvs depend on
<design>.gds, which make builds through the gds target
(KLayout stream-out, needs the klayout venv from Getting Started).
One source of truth for timing constraints¶
constraints/<design>.sdc is the only place the clock period,
uncertainty, input/output delays, driving cell and output load are
defined. scripts/load_constraints.tcl reads it and is sourced by
ppa_report.tcl, pnr.tcl, post_route.tcl,
power_activity.tcl and power_activity_postsynth.tcl, so the
pre-route, post-route and power numbers always use the same
constraints. For a purely combinational design (clk_port ""),
load_constraints.tcl falls back to the flat budget_ns delay
budget instead.
Rules that make the numbers trustworthy:
set_input_delay/set_output_delaycarry no-max, so they apply to min and max and input hold is checked and repaired.The testbench launches
DIN/VINat+IN_DLYafter the clock edge, withIN_DLYequal to the SDC input delay. A mismatch gives false SDF-simulation failures or hold races.make checkverifies that the SDC period equals the testbenchTS.scripts/run_flow.sh --period Nrewrites both consistently (even integers only, because the testbench usesTS/2).
Post-route timing with real wires¶
OpenSTA ignores extracted parasitics unless read_spef is called, so
reports and write_sdf produced inside pnr.tcl would show
zero wire delay. scripts/post_route.tcl therefore reloads the routed
netlist (<design>_route.v), reads <design>_route.spef, sets
propagated clocks, and only then writes timing_postroute.rpt,
timing_postroute_hold.rpt, power_postroute.rpt and
<design>_route.sdf. It runs in seconds and can be repeated freely
(make postroute).
Netlist repair in pnr.tcl¶
Step |
Why |
|---|---|
|
Wire RC for repair and CTS (signal on |
|
Keeps the resizer away from cells the flow excludes (see DRC). Yosys gets the same list. |
|
Fixes slew/cap/fanout and buffers high-fanout nets ( |
CTS, then |
Timing after CTS uses the real clock tree. |
|
See Margins below. |
|
Antenna repair runs on the global-route estimate; detailed route follows. |
|
Fillers are needed for clean DRC. |
|
Inputs for post-route STA and for LVS. |
Margins (environment variables)¶
Variable |
Default |
Effect |
|---|---|---|
|
|
|
|
|
|
|
|
|
Example: HOLD_MARGIN=0 ANT_MARGIN=0 scripts/run_flow.sh --tag ablate ...
reproduces the behaviour without either margin.
DRC¶
make drc runs scripts/drc.sh: the PDK’s KLayout deck
(sky130A_mr.drc, variable klayout_drc in the tech file) on
<design>.gds with feol, beol and offgrid enabled, and
summarises the result with scripts/rdb_summary.py into
signoff/<design>_drc_summary.txt (the .lyrdb opens in KLayout).
Reference result: 3 violations, all rule li.5, on three
library cells (clkbuf_4, nor3b_1, o221a_2). Each was
confirmed on the bare library cell by running the same deck on the
library GDS, so they are intrinsic to the PDK cell layouts, not
introduced by this flow:
lib=$PDK_ROOT/$PDK/libs.ref/sky130_fd_sc_hd/gds/sky130_fd_sc_hd.gds
c=o221a_2
klayout -b -r $PDK_ROOT/$PDK/libs.tech/klayout/drc/${PDK}_mr.drc \
-rd input=$lib -rd top_cell=sky130_fd_sc_hd__$c -rd report=/tmp/lib_$c.lyrdb \
-rd feol=true -rd beol=true -rd offgrid=true -rd thr=4
python3 scripts/rdb_summary.py /tmp/lib_$c.lyrdb
Two cell families are excluded from synthesis and resizing
(dont_use_cells): probe_p_8 / probec_p_8 (large probe
buffers whose met5/li shapes violate DRC next to the PDN) and
lpflow_* (low-power isolation cells with special power
connections). None of them appeared in the final netlist of the signoff
run.
LVS¶
make lvs runs scripts/lvs.sh (KLayout sky130.lvs,
run_mode=deep) of the GDS against a schematic built from the
routed design:
write_cdl -masterswrites the design netlist.scripts/lvs_prep.pyadds the cell subcircuits from the PDK SPICE (the CDL does not match the deck, which expects X-style transistors withscaleandconvert_subckts) and rewrites the tie cell.scripts/drop_cells.pyremoves the antenna diode cells.The deck runs with
scale=true,convert_subckts=trueandschematic_simplify=true(merges multi-finger devices).
Reference result: LVS_RESULT MATCH.
What LVS does not verify¶
The layout extraction cannot see two kinds of cell: tie cells
(conb_1: no recognised device, outputs are just rail connections)
and antenna diodes (diode_2: no recognised device). They are
rewritten to rail connections / removed from the schematic, so their
connections are correct by construction, not checked by LVS. State
this in any LVS claim. The same applies to fillers, which are excluded
from the CDL.
Antenna¶
Antenna repair runs on the global-route estimate: 11 violating nets
before repair, 0 after (custom_myfir_antenna_groute*.rpt). After
detailed route, check_antennas re-checks the real wires
(custom_myfir_antenna.rpt): 7 nets remain over the limit in the
reference run. Every one is the met1 side-area limit (400); the
ratios in the first signoff run ranged from 432 to 681.
The count depends on exactly how each run routes: with
ANT_MARGIN=40 the earlier run had 4 violating nets, the reference run
7, and the nets themselves differ between runs. A larger margin trades
extra diodes for a different set of violators, not for zero. Treat the
count as a rough indicator, report it as residual antenna violations,
and note that zero would need routing changes (jumpers, layer
constraints) that are outside this flow’s scope.
Multi-corner timing¶
All flow numbers are at the typical corner (tt_025C_1v80) with
OpenRCX parasitics from the single nominal rules file. The routed design
can be re-timed at other Liberty corners without re-routing:
cd runs/<tag>
for c in ss_100C_1v60 ff_n40C_1v95; do
CORNER=$c openroad -no_init -exit scripts/multicorner.tcl
done
Corner |
Worst setup slack (ns) |
Worst hold slack (ns) |
|---|---|---|
|
+13.29 |
+0.39 |
|
−2.26 (TNS −4.32) |
+0.36 |
|
+19.28 |
+0.36 |
At 28 ns the design closes at tt and ff but not at ss: it
would need about 30.5 ns there. Hold at ss was −0.27 ns before
HOLD_MARGIN (all failing paths were input-port-to-flop). Keep the
PPA comparison at one corner, and report ss/ff as a separate
signoff check. Parasitics are the nominal-corner SPEF in every case.
Reproducible runs and sweeps¶
scripts/run_flow.sh --tag NAME --period NS --abc auto|none|PS --steps "..."
copies the design into runs/NAME/ and runs the steps there, so a run
never disturbs another one. It refuses to reuse an existing tag. Each
run writes logs/steps.tsv (step, seconds, exit code) and
metrics/NAME.json (from scripts/collect_metrics.py).
# reference run, including signoff
nohup scripts/run_flow.sh --tag sign_p28_hm35 --period 28 --abc auto \
--steps "check sim synth ppa gls pnr sdf power drc lvs" > sign_p28_hm35.log 2>&1 &
# period sweep, two runs at a time
nohup scripts/sweep.sh 2 16 18 20 24 > sweep_fin.log 2>&1 &
python3 scripts/sweep_table.py "metrics/fin_p*.json" metrics/sign_p28_hm35.json
Detailed route takes about 30 to 45 minutes per run on the reference machine (8 cores, 7 GB memory); two parallel runs fit in memory.
Reference results (custom_myfir, --abc auto)¶
Period (ns) |
Setup (ns) |
Hold (ns) |
Area (µm²) |
Power (mW) |
SDF sim |
|---|---|---|---|---|---|
28 |
+13.29 |
+0.39 |
76596 |
2.75 |
PASS |
24 |
+9.29 |
+0.39 |
76596 |
3.20 |
PASS |
20 |
+5.29 |
+0.39 |
76596 |
3.84 |
PASS |
18 |
+3.29 |
+0.39 |
76596 |
4.27 |
PASS |
16 |
+1.35 |
+0.37 |
76578 |
4.79 |
PASS |
Post-route values, power from the real-activity VCD flow. The netlist is identical for 18 to 28 ns (10402 cells), so area is flat and power scales with frequency (about 0.077 mW/MHz). The estimated fmax is about 68 MHz (14.7 ns). Utilisation after CTS is 42 %, global-route usage 22 %, detailed route finishes with 0 violations.
Limitations¶
PPA numbers are at the typical corner only;
sssetup fails at 28 ns (above).Parasitics come from one nominal OpenRCX rules file for every corner.
LVS does not verify tie cells or antenna diodes (see above).
7 residual
met1side-area antenna violations (4 to 7 between runs).3 DRC findings (
li.5), all inherited from library cells.Power is activity-based (VCD) for the testbench vectors of this design only; the vectorless
report_powerfigures are not for reporting (the metrics files label them that way).examples/r4_mbe_mul(combinational) predates these scripts and has not been re-run with them.