Reproduce a score
From svgolf/score/PPA.md of the repository. The ranked score, the sky130 A0′ area and time, is a pure function of the design's text, the pinned flow and tool build, the pins and the task's scoring point and input drive; these steps give every recorded number again. Scoring in general: the rules.
A ranked number is a pure function of: the design's text; the pinned flow (score/srcgate.py, score/canon.ys,
score/svgolf.ys, score/ge.json; for a clocked task score/svgolf_seq.ys and score/ge_seq.json), run by the pinned
Yosys BUILD and its ABC (the build, not only the version: "Determinism (measured)", and the reference build below);
score/ppa.py and score/ppa_pins.json; the pinned liberty; and the task's scoring point and input
drive. A check result (and so a record) carries the hash of each: score.yosys_version, score.canon_script_sha256,
score.script_sha256, score.ge_table_sha256, score.rtlil_sha256, and ppa.liberty_sha256, ppa.ppa_pins_sha256,
ppa.ppa_script_sha256, ppa.netlist_sha256, ppa.repair.netlist_sha256, ppa.setup.
Tools and pinned files (the versions the goldens and tests/expected.json were recorded with; tests/run.py fails when
the Yosys version or a script hash differs from tests/expected.json's flow):
| what | version or sha256 | role |
|---|---|---|
| Python | 3.12 (3.12.8 here), with pyslang (11.0.0 here) | score/*.py; pyslang parses the source for the source gate |
| Yosys | Yosys 0.53 (git sha1 53c22ab7c0ced80861c7536c5dae682c30fb5834, clang++ 17.0.0 -fPIC -O3); the binary of the rating build (Homebrew's yosys 0.53, macOS arm64) has sha256 011e094340da48932dd0af17da0846519af24cee7de9ae84c11cc78753ab7732 | the pinned flow: two processes, below |
| ABC | the yosys-abc beside that Yosys: UC Berkeley, ABC 1.01 (compiled May 6 2025 05:45:16); the binary here (Homebrew's yosys 0.53, macOS arm64) has sha256 769348bb154a26d5d97cd877d086893b3c6a1c8242343a505025bad62c7fa653 (another build has another hash and can give other numbers on some designs, measured 2026-11-05; the version line a record carries names the compiler) | run only by Yosys's synth and abc -g passes; the ranked weighing and its repair are Python (ppa.py), no ABC |
| liberty | sky130_fd_sc_hd__tt_025C_1v80.lib, 12,800,135 bytes, ef34f999d582044cad9c1f4d5422504ae99702ad42baa432d75215476e5d23d5 (OpenROAD-flow-scripts 6c6fc7e2, URL in ppa_pins.json) | fetched, never vendored: python3.12 score/ppa.py --fetch-liberty (size and sha256 checked) or SVGOLF_SKY130_LIB=/path/to/copy |
score/canon.ys | bbd11227d45dc1365f27597fd45c565fe721ba2648450588fc4fd983d00b367b | Yosys process 1 |
score/svgolf.ys | ea58219d8ef14c3bad265218ee266ccdb709ac9d4dc5258edc34d51ee83a0445 | Yosys process 2 (combinational) |
score/svgolf_seq.ys | c3874dbfc85921a87df6eacdfc6b2c9a0e2b34edd5738951e0d8bdb51f4558b2 | Yosys process 2 (clocked) |
score/ge.json | 6a29196a3f4b58241f4c9af650c9141e4632b737214d563a11ba142a725c4bea | the GE prices (the extra GE column) |
score/ge_seq.json | 0a1aa1147b6916efa4a41e03aa3c8c70e68c1e3fd7a0e9c9a3cbc177edefd1f6 | the GE prices of a clocked task (ge.json's plus the flop's; its _derivation names the pins file it was derived under, so it changed with sky130_a0_v3's pins) |
score/ppa_pins.json | d66a6086a37a97f5985266057051979fb6a8a3c557d3ef9c42acc1b5e41502d8 (sky130_a0_v3), recorded in every block as ppa_pins_sha256 | the gate-to-cell table, the timing setup and input drives, the repair, the tie cells |
The reference build (the build ratings use, decided 2026-11-05; README.md, "Scoring", and score/reference_build.json): Homebrew's Yosys 0.53 for macOS arm64, compiled by clang++ 17 with -O3, with its yosys-abc (both sha256 above); every published number was made with it, the flow is deterministic on it, and another build can give other numbers on some designs ("Determinism (measured)"). It is not pinned to a slower portable build.
The pinned flow, step by step (what score.py does):
The source gate,
srcgate.py, refuses preprocessor directives, attributes, synthesis pragmas andunique/priority, and writes the CANONICAL text (comments stripped). Yosys reads that text, never the submitted file.Yosys process 1,
yosys -q -l canon.log -s canon.ys:score/canon.yswithout its comment lines, its placeholders filled (@SOURCES@: oneread_verilog -sv "<canonical file>"per source;@TOP@: the task's top;@CHPARAM@:-chparam NAME VALUEper scoring parameter;@RTLIL@: the output). For theaddreference atN = 16:read_verilog -sv "add_n.sv" hierarchy -check -top add_n -chparam N 16 proc flatten -noscopeinfo hierarchy -top add_n setattr -unset src setattr -mod -unset src setattr -unset hdlname setattr -mod -unset hdlname rename -hide rename -enumerate write_rtlil "canon.il"Yosys process 2 (a fresh process: Yosys's results depend on names a process has interned),
score/svgolf.ys:read_rtlil "canon.il" synth -flatten -top add_n abc -g AND,NAND,OR,NOR,XOR,XNOR,ANDNOT,ORNOT,MUX opt_clean tee -q -o "stat.json" stat -json tee -q -o "ltp.txt" ltp -noff write_verilog -noattr -noexpr "netlist.v"ABC runs twice, each time with Yosys 0.53's own default script (the log,
grep "ABC: + " synth.log, prints both): insidesynth,abc -fast,strash; dretime; map; then theabc -gpass, onto a generated gate library of the listed gates,strash; &get -n; &fraig -x; &put; scorr; dc2; dretime; strash; &get -n; &dch -f; &nf; &put.netlist.vis the SCORED netlist (the onepostsynthsimulates andcheck.pyweighs); GE is thestatcount priced byge.json, the depthltp -noff. A clocked task runsscore/svgolf_seq.ysinstead: the same withsynth -nofsm(no FSM re-encoding, decided 2026-11-06),dfflegalize -cell $_DFF_P_ xafter it andopt_clean -purgeas the last clean-up (decided 2026-11-07: "Known gap" below), priced byge_seq.json.The ranked weighing,
ppa.pyonnetlist.v(Python only): each gate becomes ONE_1cell (ppa_pins.jsongates: NOTinv_1, NANDnand2_1, NORnor2_1, ANDand2_1, ORor2_1, XORxor2_1, XNORxnor2_1, MUXmux2_1, ANDNOTnor2b_1, ORNOTnand2b_1; each checked against the liberty's own function), a flopdfxtp_1, cut out of the timing graph; the netlist is REPAIRED by buffering only ("The repair and the legality check"); then the STA: every primary input that is read is the output of the task's input drive (ppa.setup.input_drive, defaultsky130_fd_sc_hd__buf_8) whose own input is an ideal 50 ps ramp at 0, every output bit a 10 fF load, no wires, every table read by bilinear interpolation inside its characterized grid only (a read outside it rejects the netlist), both edges, the worst arrival and the largest transition carried;delay_ps_sky130is the latest output arrival, rounded to the ps (clocked: the latest output or flop D arrival plus the flop's setup, the clock ideal with a 50 ps transition);area_um2_sky130the sum of the cell areas (exact:area_e4um2_sky130); the legality audit last.
Reproduce a recorded number. From svgolf/ of a checkout at the record's commit, with the tools above on PATH
(tested 11/04 by running the two blocks below literally, in a scratch copy of svgolf/: both references and the hand run
matched the goldens bit for bit; again after the per-edge loads):
python3.12 score/ppa.py --fetch-liberty
python3.12 score/ppa.py tasks/add/reference/add_n.sv --task tasks/add --no-mapped --json add_n.json --netlist add_n.scored.v --repaired-netlist add_n.repaired.v
python3.12 score/ppa.py tasks/aes_sbox/reference/sbox_bp.sv --task tasks/aes_sbox --no-mapped --json sbox_bp.json --netlist sbox_bp.scored.v --repaired-netlist sbox_bp.repaired.v
add_n.json then has "area_e4um2_sky130": 4604416, "delay_ps_sky130": 2611, "netlist_sha256": "3b7da549a07e…",
"repair": {..., "netlist_sha256": "ac214a9f04d7…"} and "setup": {..., "input_drive": "sky130_fd_sc_hd__buf_8", ...}, and
its score block "rtlil_sha256": "0699af350c19…": the golden add_ripple of score/tests/ppa/expected_ppa.json.
sbox_bp.json has 8921056, 4297, 22493fba30d8…, b27773491834…: the golden sbox_bp. A clocked task needs --seq
(python3.12 score/ppa.py tasks/matmul/reference/matmul_g1.sv --task tasks/matmul --seq: 87173.6064 um^2, period 10696 ps,
6 buffers on 5 nets, tests/expected.json). A record's own numbers come from check.py ... --ppa require, which weighs the very netlist its
postsynth stage verified, by the same function; leaderboard.py verify weighs it again.
The same add_n numbers by hand, each step one tool and none of score.py:
mkdir hand && cd hand
python3.12 ../score/srcgate.py ../tasks/add/reference/add_n.sv --write add_n.sv
grep -v '^[[:space:]]*#' ../score/canon.ys | sed -e 's|@SOURCES@|read_verilog -sv "add_n.sv"|' -e 's|@TOP@|add_n|g' -e 's|@CHPARAM@| -chparam N 16|' -e 's|@RTLIL@|"canon.il"|' > canon.ys
grep -v '^[[:space:]]*#' ../score/svgolf.ys | sed -e 's|@RTLIL@|"canon.il"|' -e 's|@TOP@|add_n|g' -e 's|@STAT@|"stat.json"|' -e 's|@LTP@|"ltp.txt"|' -e 's|@NETLIST@|"netlist.v"|' > synth.ys
yosys -q -l canon.log -s canon.ys
yosys -q -l synth.log -s synth.ys
python3.12 ../score/ppa.py --netlist-in netlist.v --task ../tasks/add --repaired-netlist repaired.v --json hand.json
shasum -a 256 canon.il netlist.v repaired.v
The three hashes are the record's score.rtlil_sha256, ppa.netlist_sha256 and ppa.repair.netlist_sha256 (0699af35…,
3b7da549…, ac214a9f…), and hand.json's columns are add_n.json's. For another design: its canonical file(s), its
top and its scoring parameters in the two sed lines, and its task in --task.
With OpenSTA. Time the REPAIRED netlist (--repaired-netlist) with one explicit instance of the task's input drive in
front of every input (a buffer per input, whose own input gets set_input_transition 0.05), set_load 0.010 on every
output and set_load 0 on every net (no wires), the clock ideal with a 0.05 ns transition: OpenSTA (openroad/orfs 26Q4)
then gives ppa.py's arrivals to 1 fs per arc; set_driving_cell instead would make every input path earlier by the
drive's zero-load delay ("The input drive"). score/test_ppa_opensta.py holds the script (OPENSTA_TCL) and OpenSTA's
recorded numbers.
The ASAP7 view's numbers (decided 2026-11-09: a reader may see the golf scores on ASAP7; sky130 stays the default).
The same command with --pdk asap7 weighs the same scored netlist on the five pinned ASAP7 liberty files (flow A0',
asap7_a0_v1, "ASAP7: an alternative library" below); leaderboard/asap7/<task>.json keeps each design's numbers, and
leaderboard.py asap7 --rescore weighs them all again and compares, bit for bit:
python3.12 score/ppa.py --pdk asap7 --fetch-liberty # once: the five files into score/lib/asap7/ (git-ignored)
python3.12 score/ppa.py tasks/add/reference/add_n.sv --task tasks/add --pdk asap7 --no-mapped --json add_n_asap7.json
python3.12 leaderboard/leaderboard.py asap7 --rescore --task add
add_n_asap7.json has ppa.netlist_sha256 3b7da549… (the sky130 run's: the scored netlist does not depend on the
library), area_e5um2_asap7 699840 (6.99840 um^2) and delay_ps_asap7 563, so area x time is 3.940 um2*ns.