Golf scores on ASAP7, a predictive 7 nm library: never fabricated, and like sky130's score it counts no wires. sky130, a real 130 nm process, is the default. How ASAP7 is scored, and its quirks.

Tasks / mul / reference

Reference mul_tree

proved referenceproved reference scored at N = 16

A proved reference of mul, written or adapted by the maintainers and shipped with the task: its design and its proof mul_tree.lean (322 code lines). A log-depth carry-save tree of 4:2 compressors over the partial-product rows, with a ripple final adder. It is shown with parshown with par, never a player's entry (proved references).

Area

8 603.25µm²

8 603.2512 µm² exactly

Beats 0% · matches 0%

0 of 2 other players beaten, 0 equal.

vs par
+0.5% largermul_tree_h · 8 563.21 µm²
on this page
7th of 10 designs
cells
1 456
flops
0
repair
0 buffers, 0 tie cells
GE
2 034.904 extra

Performance · time

6 129ps

the delay: the latest output arrival, p[30] rising

Beats 50% · matches 0%

1 of 2 other players beaten, 0 equal.

vs par
+2.3% slowermul_tree_h · 5 992 ps
on this page
6th of 10 designs
Fmax-eq.
163.2 MHz derived
critical path
58 cells
unit depth
63 extra

Score · area × time

52 729µm²·ns

8 603.2512 µm² × 6 129 ps

Beats 0% · matches 0%

0 of 2 other players beaten, 0 equal.

vs par
+2.8% worsemul_tree_h · 51 311 µm²·ns
on this page
6th of 10 designs
board
a proved reference, shown with par: never ranked
Pareto
dominated on this page by late-carry, dadda_rc_16 and mul_tree_h

On the board, of 2 other players: 0 beaten, 0 level, 1 trade off, 1 ahead.

how these numbers are measured and compared

Area: sky130 A0′, the repaired netlist. Time: NLDM, inputs driven by buf_8, 10 fF per output bit, no wires.

Area, time and score are ranked (the leaderboard orders by the score); a number tagged extra, derived or research is shown, never ranked. Fmax-equivalent = 10⁶ / time in ps, for comparison with sites that quote Fmax.

The field is the ranked records of mul's other players at this revision (2 players, 2 records), never the references, the baselines or a rejected design; each player counts once, at their best, and ties are exact at the recorded unit; advisory, recomputed at every build, never stored in a record and never used by the ranking.

On the board, another player is beaten when this design dominates every record of theirs, level when none of theirs is better and one is identical (the rest dominated), ahead when a record of theirs dominates this design, and a trade off otherwise (one of theirs better on one axis, worse on the other).

“On this page” and “Pareto” compare with the 10 designs of the area-time plot (5 references, 2 submissions and 3 baselines): not a ranking.

Area

128.552µm²

128.55186 µm² exactly

Beats 0% · matches 0%

0 of 2 other players beaten, 0 equal.

vs par
+0.5% largermul_tree_h · 127.954 µm²
on this page
7th of 10 designs
cells
1 458
flops
0
repair
0 buffers, 0 tie cells
GE
2 034.904 extra

Performance · time

1 191ps

the delay: the latest output arrival

Beats 50% · matches 0%

1 of 2 other players beaten, 0 equal.

vs par
+2.7% slowermul_tree_h · 1 160 ps
on this page
7th of 10 designs
Fmax-eq.
839.6 MHz derived
unit depth
63 extra

Score · area × time

153.1µm²·ns

128.55186 µm² × 1 191 ps

Beats 50% · matches 0%

1 of 2 other players beaten, 0 equal.

vs par
+3.2% worsemul_tree_h · 148.4 µm²·ns
on this page
6th of 10 designs
board
a proved reference, shown with par: never ranked
Pareto
dominated on this page by late-carry, dadda_rc_16 and mul_tree_h

On the board, of 2 other players: 0 beaten, 0 level, 1 trade off, 1 ahead.

how these numbers are measured and compared

Area: ASAP7 A0′ (asap7_a0_v1), the same scored netlist, each gate its least-area ASAP7 build, repaired by buffering. Time: NLDM, inputs driven by BUFx8, 2.5 fF per output bit, no wires; a table read below its grid is read at its floor. ASAP7: a predictive 7 nm library, never fabricated; no wires, like sky130's score (how ASAP7 is scored, and its quirks).

Area, time and score order this view's board (by the score); the records' ranked score is sky130's, the default view; a number tagged extra, derived or research is shown, never ranked. Fmax-equivalent = 10⁶ / time in ps, for comparison with sites that quote Fmax.

The field is the ranked records of mul's other players at this revision (2 players, 2 records), never the references, the baselines or a rejected design; each player counts once, at their best, and ties are exact at the recorded unit; advisory, recomputed at every build, never stored in a record and never used by the ranking.

On the board, another player is beaten when this design dominates every record of theirs, level when none of theirs is better and one is identical (the rest dominated), ahead when a record of theirs dominates this design, and a trade off otherwise (one of theirs better on one axis, worse on the other).

“On this page” and “Pareto” compare with the 10 designs of the area-time plot (5 references, 2 submissions and 3 baselines): not a ranking.

The sources

mul_tree.svdownload
// svGolf task `mul` -- reference solution 3: a log-depth carry-save TREE multiplier, parametric in N.
//
// The N partial-product rows pp[j] are the N leaves (a single row each: sum row S = pp[j], carry
// row C = 0) of one balanced binary tree in a binary-HEAP numbering: the root is node 0, node k has
// the children 2k+1 and 2k+2, the N-1 internal nodes are 0 .. N-2 and the N leaves N-1 .. 2N-2.
// An internal node whose children are both leaves (2k+1 >= N-1) just pairs them, S = S[2k+1],
// C = S[2k+2]: two rows are already a carry-save pair. Every other internal node is a 4:2
// compressor on its two children's pairs: two stacked rows of 3:2 counters (full adders), the
// first row X/Y, the second S/C. So the carry-save part has ceil(log2 N) - 1 levels of two full
// adders, and the two rows of the root go through a ripple-carry final adder.
//
// Which nodes pair and which compress is decided by the constant vectors `th` (th[x] = 1 iff
// x >= N-1) and `nth` (its complement), read at the affine index 2k+1: a node-level multiplexer
// whose select is a constant, which synthesis removes. The heap numbering needs no stage counter,
// no localparam, no conditional generate and no division: every index and bound is affine in N and
// the genvars. A shifted partial product is a GATHER read of `ae`, the zero-padded copy of `a`, so no
// write ever leaves its vector. All sums are taken modulo 2^(2N), exactly the width of the product.
module mul_n #(parameter int N = 16) (
  input  logic [N-1:0]   a, b,
  output logic [2*N-1:0] p
);
  logic [4*N-2:0]          ae;      // ae[x] = a[x-(2N-1)] for 2N-1 <= x < 3N-1, else 0
  logic [2*N-2:0]          th, nth; // th[x] = (x >= N-1), nth = its complement
  logic [2*N-2:0][2*N-1:0] S, C;    // sum / carry row of each heap node
  logic [N-1:0][2*N-1:0]   X, Y;    // first-row sum / carry of node k (row N-1 unused)
  logic [2*N:0]            R;       // final-adder carries

  assign ae[2*N-2:0]       = '0;
  assign ae[3*N-2:2*N-1]   = a;
  assign ae[4*N-2:3*N-1]   = '0;
  assign X[N-1]            = '0;
  assign Y[N-1]            = '0;
  assign R[0]              = 1'b0;

  genvar j, k, c, x;
  generate
    for (x = 0; x < N-1; x++) begin : g_thlo
      assign th[x]  = 1'b0;
      assign nth[x] = 1'b1;
    end
    for (x = 0; x < N; x++) begin : g_thhi
      assign th[N-1+x]  = 1'b1;
      assign nth[N-1+x] = 1'b0;
    end
    // leaves: node N-1+j is the partial-product row j, shifted left by j (a gather read of ae)
    for (j = 0; j < N; j++) begin : g_leaf
      for (c = 0; c < 2*N; c++) begin : g_lc
        assign S[N-1+j][c] = ae[c + 2*N - 1 - j] & b[j];
        assign C[N-1+j][c] = 1'b0;
      end
    end
    // internal nodes: pair two leaves (th), or a 4:2 compressor (nth) on the children 2k+1, 2k+2
    for (k = 0; k < N-1; k++) begin : g_node
      assign Y[k][0] = 1'b0;
      assign C[k][0] = th[2*k+1] & S[2*k+2][0];
      for (c = 0; c < 2*N; c++) begin : g_col
        assign X[k][c] = S[2*k+1][c] ^ C[2*k+1][c] ^ S[2*k+2][c];
        assign S[k][c] = (th[2*k+1] & S[2*k+1][c]) | (nth[2*k+1] & (X[k][c] ^ Y[k][c] ^ C[2*k+2][c]));
      end
      for (c = 0; c < 2*N-1; c++) begin : g_car
        assign Y[k][c+1] = (S[2*k+1][c] & C[2*k+1][c]) | (S[2*k+2][c] & (S[2*k+1][c] ^ C[2*k+1][c]));
        assign C[k][c+1] = (th[2*k+1] & S[2*k+2][c+1])
                         | (nth[2*k+1] & ((X[k][c] & Y[k][c]) | (C[2*k+2][c] & (X[k][c] ^ Y[k][c]))));
      end
    end
    // final carry-propagate (ripple) adder of the root's two rows
    for (c = 0; c < 2*N; c++) begin : g_fin
      assign p[c]   = S[0][c] ^ C[0][c] ^ R[c];
      assign R[c+1] = (S[0][c] & C[0][c]) | (R[c] & (S[0][c] ^ C[0][c]));
    end
  endgenerate
endmodule
mul_tree.lean (322 code lines)
import Submission.Design
import SvGolf.Comb
import LeanModels.Sv.CombEqTactic
import LeanModels.Sv.CombLib

/-!
# Reference solution 3 for task `mul`: a carry-save tree multiplier, proved with the `sv_comb` tactics

This is a submission file like any player's `Solution.lean`. The checker generates the module
`Submission.Design` (`Submission.design`, `Submission.Goal`); this file proves

    theorem Submission.solution : Submission.Goal

for EVERY `N ≥ 1`. The design is `reference/mul_tree.sv`: the `N` partial-product rows are the `N`
leaves (nodes `N-1 .. 2N-2`) of a binary HEAP (node `k` has the children `2k+1`, `2k+2`; the root is
node 0), each leaf a sum row `S = a·b_j·2^j` with a zero carry row `C`. An internal node whose two
children are leaves (`2k+1 ≥ N-1`, selected by the constant vector `th`) just pairs them,
`(S, C) = (S[2k+1], S[2k+2])`; every other internal node is a 4:2 compressor, two stacked rows of
full adders, on its children's pairs. The root's two rows go through a ripple-carry adder. The
carry-save part has depth `⌈log₂ N⌉ - 1` compressors; there is no stage loop, no localparam, no
conditional generate and no division in the design, so the existing fragment of `sv_structure` /
`sv_equations` reads it as it is.

## Route (the file is in this order)

1. **Arithmetic** (`TreeArith`, plain Lean, no ls-sv): `csa_row` (a row of 3:2 counters with the
   carry row shifted by one column preserves the sum, up to the dropped top carry), `node_sum` (a
   4:2 node preserves the value of its two pairs, up to `2^(2N)`), `lane_gated` (a leaf row is the
   shifted `a`, gated by a bit of `b`), and `tree_mul`: the sums over the CUT `{t, …, 2t}` of the
   heap, `csum V t (t+1)`, are invariant modulo `2^(2N)` while `t` runs from `N-1` (the leaves:
   `a · b` exactly) down to `0` (the root), because node `t` has exactly the children `2t+1` and
   `2t+2`, which are the two elements by which the cut `{t+1, …, 2t+2}` exceeds `{t+1, …, 2t}`; a
   pairing node preserves the value exactly (its children have zero carry rows), a compressor
   up to `2^(2N)`. The final adder is `ripple_row` of the library.
2. **Structure** (`tree_struct`): `sv_structure` (`CombDesign ∧ Acyclic`); the goals compare 2-D windows and
   close by `intros; sv_rowmajor; omega`. The rank is a lexicographic key `(stage, node order,
   phase)` of each site identity (`treeKey`): inputs and constants, the leaves, the internal nodes
   from the highest heap index down to the root (two phases each: the first row of counters, then
   the second), the ripple adder column by column. `tree_struct` needs `maxHeartbeats 1000000`.
   The loops over `2*N` columns need the generate-fuel bound `4 · N + 60` of the tactic library
   (`AutoObl`; it was `N + 60`).
3. **Ports** (`tree_interface`): `sv_interface`.
4. **Equations, bridge and coverage** (`solution`): the printed equations of `sv_equations` are bridged
   to the clean facts of `tree_mul` one `sv_bridge eq_x` per fact (the `Int` index forms of the netlist
   against the `Nat` indices of the arithmetic: congruence and `omega`).
5. **Coverage** (`solution`, the same theorem): the coverage goals of `sv_equations` are the heap reads
   (`S`, `C`, `X`, `Y` at a row and a column, the constant selects `th` / `nth`, and the carry
   chain `R`); the closing `all_goals … sv_cover` proves them, solving the writer (leaf rows `≥ N-1`,
   node rows below, the column-0 assigns) from the read.
-/

/-! ## 1. The arithmetic (plain Lean, `CombLib` only)

The heap-indexed 4:2 carry-save tree multiplier: `tree_mul` proves `p = a * b` from the netlist
equations as clean Boolean facts about bit functions; nothing here mentions a design or a state. -/
namespace TreeArith
open LeanModels.Sv.CombLib

theorem bfVal_false {f : Nat → Bool} {n : Nat} (h : ∀ j < n, f j = false) : bfVal f n = 0 := by
  induction n with
  | zero => rfl
  | succ n ih =>
    simp only [bfVal]
    rw [ih (fun j hj => h j (by omega)), h n (by omega)]
    simp

/-- A row of 3:2 counters, the carry row shifted one column: column `c` of `s`/`cy`, with
`sh (c+1) = cy c`. The top carry `cy (W-1)` is dropped from `sh`. -/
theorem csa_row (n : Nat) (x y z s cy sh : Nat → Bool)
    (hs : ∀ c < n + 1, s c = (x c ^^ y c ^^ z c))
    (hcy : ∀ c < n + 1, cy c = ((x c && y c) || (z c && (x c ^^ y c))))
    (hsh0 : sh 0 = false) (hsh : ∀ c, c + 1 < n + 1 → sh (c + 1) = cy c) :
    bfVal s (n + 1) + bfVal sh (n + 1) + 2 ^ (n + 1) * (cy n).toNat =
      bfVal x (n + 1) + bfVal y (n + 1) + bfVal z (n + 1) := by
  have key : ∀ m, m ≤ n + 1 →
      bfVal s m + 2 * bfVal cy m = bfVal x m + bfVal y m + bfVal z m := by
    intro m
    induction m with
    | zero => intro _; simp [bfVal]
    | succ m ih =>
      intro hm
      have ih := ih (by omega)
      have hfa := fullAdder_arith (x m) (y m) (z m)
      rw [← hs m (by omega), ← hcy m (by omega)] at hfa
      simp only [bfVal]
      have : 2 ^ m * ((s m).toNat + 2 * (cy m).toNat) =
          2 ^ m * ((x m).toNat + (y m).toNat + (z m).toNat) := by rw [hfa]
      generalize (s m).toNat = S0 at *
      generalize (cy m).toNat = C0 at *
      generalize (x m).toNat = X0 at *
      generalize (y m).toNat = Y0 at *
      generalize (z m).toNat = Z0 at *
      generalize 2 ^ m = P at *
      grind
  have hsh' : bfVal sh (n + 1) = 2 * bfVal cy n := by
    rw [bfVal_shift, hsh0]
    have : bfVal (fun j => sh (j + 1)) n = bfVal cy n :=
      bfVal_congr (fun j hj => hsh j (by omega))
    rw [this]; simp
  have hk := key (n + 1) (Nat.le_refl _)
  rw [hsh']
  simp only [bfVal, Nat.pow_succ] at hk ⊢
  generalize 2 ^ n = P at *
  generalize (cy n).toNat = C0 at *
  grind

/-- A 4:2 compressor node: two stacked rows of counters, on a window of `n+1` columns, preserves
the sum exactly up to the two dropped top carries. -/
theorem node_sum (n : Nat) (SL CL SR CR S C X Y : Nat → Bool)
    (hX : ∀ c < n + 1, X c = (SL c ^^ CL c ^^ SR c))
    (hY0 : Y 0 = false)
    (hY : ∀ c, c + 1 < n + 1 → Y (c + 1) = ((SL c && CL c) || (SR c && (SL c ^^ CL c))))
    (hS : ∀ c < n + 1, S c = (X c ^^ Y c ^^ CR c))
    (hC0 : C 0 = false)
    (hC : ∀ c, c + 1 < n + 1 → C (c + 1) = ((X c && Y c) || (CR c && (X c ^^ Y c)))) :
    ∃ q, bfVal S (n + 1) + bfVal C (n + 1) + 2 ^ (n + 1) * q =
      (bfVal SL (n + 1) + bfVal CL (n + 1)) + (bfVal SR (n + 1) + bfVal CR (n + 1)) := by
  have h1 := csa_row n SL CL SR X (fun c => (SL c && CL c) || (SR c && (SL c ^^ CL c))) Y
    hX (fun _ _ => rfl) hY0 hY
  have h2 := csa_row n X Y CR S (fun c => (X c && Y c) || (CR c && (X c ^^ Y c))) C
    hS (fun _ _ => rfl) hC0 hC
  refine ⟨(((SL n && CL n) || (SR n && (SL n ^^ CL n))).toNat) +
    ((X n && Y n) || (CR n && (X n ^^ Y n))).toNat, ?_⟩
  simp only [Nat.mul_add] at h1 h2 ⊢
  generalize 2 ^ (n + 1) = P at *
  omega


/-! ### The leaves: a gated, shifted copy of `a` -/

/-- The zero-padded copy `ae` of `a`, read at offset `2N-1-r`, is `a` shifted left by `r`
(for `r ≤ N`: nothing is cut off). -/
theorem lane_val {N : Nat} (hN : 1 ≤ N) (a : BitVec N) (ae : Nat → Bool)
    (h1 : ∀ k, k < 2 * N - 1 → ae k = false)
    (h2 : ∀ k, k < N → ae (2 * N - 1 + k) = a.getLsbD k)
    (h3 : ∀ k, k < N → ae (3 * N - 1 + k) = false)
    (r : Nat) (hr : r ≤ N) :
    bfVal (fun c => ae (c + (2 * N - 1 - r))) (2 * N) = a.toNat * 2 ^ r := by
  have hs1 := bfVal_split (fun c => ae (c + (2 * N - 1 - r))) r (2 * N - r)
  rw [show r + (2 * N - r) = 2 * N by omega] at hs1
  rw [hs1]
  have hlow : bfVal (fun c => ae (c + (2 * N - 1 - r))) r = 0 :=
    bfVal_false (fun j hj => h1 _ (by omega))
  rw [hlow]
  have hs2 := bfVal_split (fun j => ae (r + j + (2 * N - 1 - r))) N (N - r)
  rw [show N + (N - r) = 2 * N - r by omega] at hs2
  rw [hs2]
  have hmid : bfVal (fun j => ae (r + j + (2 * N - 1 - r))) N = a.toNat := by
    rw [← bfVal_bv a]
    exact bfVal_congr (fun j hj => by
      have : r + j + (2 * N - 1 - r) = 2 * N - 1 + j := by omega
      rw [this]; exact h2 j hj)
  have hhigh : bfVal (fun j => (fun j => ae (r + j + (2 * N - 1 - r))) (N + j)) (N - r) = 0 :=
    bfVal_false (fun j hj => by
      show ae (r + (N + j) + (2 * N - 1 - r)) = false
      have : r + (N + j) + (2 * N - 1 - r) = 3 * N - 1 + j := by omega
      rw [this]; exact h3 j (by omega))
  rw [hmid, hhigh]
  simp only [Nat.zero_add, Nat.mul_zero, Nat.add_zero]
  rw [Nat.mul_comm]

/-- The value of a gated lane. -/
theorem lane_gated {N : Nat} (hN : 1 ≤ N) (a : BitVec N) (ae : Nat → Bool)
    (h1 : ∀ k, k < 2 * N - 1 → ae k = false)
    (h2 : ∀ k, k < N → ae (2 * N - 1 + k) = a.getLsbD k)
    (h3 : ∀ k, k < N → ae (3 * N - 1 + k) = false)
    (r : Nat) (g : Bool) (hg : g = true → r ≤ N) :
    bfVal (fun c => ae (c + (2 * N - 1 - r)) && g) (2 * N) = g.toNat * (a.toNat * 2 ^ r) := by
  rw [bfVal_and]
  cases g with
  | false => simp
  | true => simp only [Bool.toNat_true]; rw [lane_val hN a ae h1 h2 h3 r (hg rfl)]

/-! ### Sums over a stretch of heap nodes -/

def csum (V : Nat → Nat) : Nat → Nat → Nat
  | _, 0 => 0
  | s, n + 1 => csum V s n + V (s + n)

theorem csum_front (V : Nat → Nat) (n : Nat) : ∀ s, csum V s (n + 1) = V s + csum V (s + 1) n := by
  induction n with
  | zero => intro s; simp [csum]
  | succ n ih =>
    intro s
    have e : s + 1 + n = s + (n + 1) := by omega
    have e2 : csum V (s + 1) (n + 1) = csum V (s + 1) n + V (s + 1 + n) := rfl
    rw [csum, ih s, e2, e]
    omega

/-- Leaves: `csum` of the leaf values is `a · b`, one row per bit of `b`. -/
theorem csum_leaves (N : Nat) (a : Nat) (g : Nat → Bool) (V : Nat → Nat)
    (hV : ∀ j, j < N → V (N - 1 + j) = (g j).toNat * (a * 2 ^ j)) :
    ∀ n, n ≤ N → csum V (N - 1) n = a * bfVal g n := by
  intro n
  induction n with
  | zero => intro _; simp [csum, bfVal]
  | succ n ih =>
    intro hn
    have ih := ih (by omega)
    rw [csum, ih, hV n (by omega)]
    simp only [bfVal]
    generalize 2 ^ n = P
    generalize (g n).toNat = B0
    generalize bfVal g n = F
    grind

/-- **The whole multiplier**, from the equations of the netlist as clean Boolean facts. -/
theorem tree_mul {N : Nat} (hN : 1 ≤ N) (a b : BitVec N) (p : BitVec (2 * N))
    (ae th nth : Nat → Bool) (S C X Y : Nat → Nat → Bool) (R : Nat → Bool)
    (hae1 : ∀ k, k < 2 * N - 1 → ae k = false)
    (hae2 : ∀ k, k < N → ae (2 * N - 1 + k) = a.getLsbD k)
    (hae3 : ∀ k, k < N → ae (3 * N - 1 + k) = false)
    (hthlo : ∀ x, x < N - 1 → th x = false)
    (hnthlo : ∀ x, x < N - 1 → nth x = true)
    (hthhi : ∀ x, x < N → th (N - 1 + x) = true)
    (hnthhi : ∀ x, x < N → nth (N - 1 + x) = false)
    (hleafS : ∀ j, j < N → ∀ c, c < 2 * N →
      S (N - 1 + j) c = (ae (c + (2 * N - 1 - j)) && b.getLsbD j))
    (hleafC : ∀ j, j < N → ∀ c, c < 2 * N → C (N - 1 + j) c = false)
    (hY0 : ∀ k, k < N - 1 → Y k 0 = false)
    (hC0 : ∀ k, k < N - 1 → C k 0 = (th (2 * k + 1) && S (2 * k + 2) 0))
    (hX : ∀ k, k < N - 1 → ∀ c, c < 2 * N →
      X k c = (S (2 * k + 1) c ^^ C (2 * k + 1) c ^^ S (2 * k + 2) c))
    (hS : ∀ k, k < N - 1 → ∀ c, c < 2 * N →
      S k c = ((th (2 * k + 1) && S (2 * k + 1) c) ||
        (nth (2 * k + 1) && (X k c ^^ Y k c ^^ C (2 * k + 2) c))))
    (hY : ∀ k, k < N - 1 → ∀ c, c < 2 * N - 1 →
      Y k (c + 1) = ((S (2 * k + 1) c && C (2 * k + 1) c) ||
        (S (2 * k + 2) c && (S (2 * k + 1) c ^^ C (2 * k + 1) c))))
    (hC : ∀ k, k < N - 1 → ∀ c, c < 2 * N - 1 →
      C k (c + 1) = ((th (2 * k + 1) && S (2 * k + 2) (c + 1)) ||
        (nth (2 * k + 1) && ((X k c && Y k c) || (C (2 * k + 2) c && (X k c ^^ Y k c))))))
    (hR0 : R 0 = false)
    (hp : ∀ c, c < 2 * N → p.getLsbD c = (S 0 c ^^ C 0 c ^^ R c))
    (hR : ∀ c, c < 2 * N → R (c + 1) =
      ((S 0 c && C 0 c) || (R c && (S 0 c ^^ C 0 c)))) :
    p = BitVec.ofNat (2 * N) (a.toNat * b.toNat) := by
  obtain ⟨n, hn⟩ : ∃ n, 2 * N = n + 1 := ⟨2 * N - 1, by omega⟩
  let V : Nat → Nat := fun m => bfVal (S m) (2 * N) + bfVal (C m) (2 * N)
  -- the leaves: one shifted copy of `a` per bit of `b`
  have hleaf : ∀ j, j < N → V (N - 1 + j) = (b.getLsbD j).toNat * (a.toNat * 2 ^ j) := by
    intro j hj
    have e1 : bfVal (S (N - 1 + j)) (2 * N) =
        bfVal (fun c => ae (c + (2 * N - 1 - j)) && b.getLsbD j) (2 * N) :=
      bfVal_congr (fun c hc => hleafS j hj c hc)
    have e2 : bfVal (C (N - 1 + j)) (2 * N) = 0 :=
      bfVal_false (fun c hc => hleafC j hj c hc)
    show bfVal (S (N - 1 + j)) (2 * N) + bfVal (C (N - 1 + j)) (2 * N) = _
    rw [e1, e2, lane_gated hN a ae hae1 hae2 hae3 j (b.getLsbD j) (fun _ => by omega)]
    omega
  have hleaves : csum V (N - 1) N = a.toNat * b.toNat := by
    rw [csum_leaves N a.toNat b.getLsbD V hleaf N (Nat.le_refl N), bfVal_bv]
  -- a leaf has no carry row
  have hCleaf : ∀ m, N - 1 ≤ m → m ≤ 2 * N - 2 → ∀ c, c < 2 * N → C m c = false := by
    intro m h1 h2 c hc
    have := hleafC (m - (N - 1)) (by omega) c hc
    rwa [show N - 1 + (m - (N - 1)) = m by omega] at this
  -- the nodes
  have hnode : ∀ k, k < N - 1 → ∃ q, V k + 2 ^ (2 * N) * q = V (2 * k + 1) + V (2 * k + 2) := by
    intro k hk
    by_cases hb : 2 * k + 1 < N - 1
    · -- a 4:2 compressor
      have ht := hthlo (2 * k + 1) hb
      have hnt := hnthlo (2 * k + 1) hb
      obtain ⟨q, hq⟩ := node_sum n (S (2 * k + 1)) (C (2 * k + 1)) (S (2 * k + 2)) (C (2 * k + 2))
        (S k) (C k) (X k) (Y k)
        (fun c hc => hX k hk c (by omega)) (hY0 k hk)
        (fun c hc => hY k hk c (by omega))
        (fun c hc => by rw [hS k hk c (by omega), ht, hnt]; simp)
        (by rw [hC0 k hk, ht]; simp)
        (fun c hc => by rw [hC k hk c (by omega), ht, hnt]; simp)
      rw [← hn] at hq
      exact ⟨q, by simp only [V]; omega⟩
    · -- two leaves are paired
      have ht : th (2 * k + 1) = true := by
        have := hthhi (2 * k + 1 - (N - 1)) (by omega)
        rwa [show N - 1 + (2 * k + 1 - (N - 1)) = 2 * k + 1 by omega] at this
      have hnt : nth (2 * k + 1) = false := by
        have := hnthhi (2 * k + 1 - (N - 1)) (by omega)
        rwa [show N - 1 + (2 * k + 1 - (N - 1)) = 2 * k + 1 by omega] at this
      have hSk : ∀ c, c < 2 * N → S k c = S (2 * k + 1) c := by
        intro c hc; rw [hS k hk c hc, ht, hnt]; simp
      have hCk : ∀ c, c < 2 * N → C k c = S (2 * k + 2) c := by
        intro c hc
        cases c with
        | zero => rw [hC0 k hk, ht]; simp
        | succ c => rw [hC k hk c (by omega), ht, hnt]; simp
      have e1 : bfVal (S k) (2 * N) = bfVal (S (2 * k + 1)) (2 * N) := bfVal_congr hSk
      have e2 : bfVal (C k) (2 * N) = bfVal (S (2 * k + 2)) (2 * N) := bfVal_congr hCk
      have e3 : bfVal (C (2 * k + 1)) (2 * N) = 0 :=
        bfVal_false (fun c hc => hCleaf (2 * k + 1) (by omega) (by omega) c hc)
      have e4 : bfVal (C (2 * k + 2)) (2 * N) = 0 :=
        bfVal_false (fun c hc => hCleaf (2 * k + 2) (by omega) (by omega) c hc)
      exact ⟨0, by simp only [V]; omega⟩
  -- the cut sums
  have hcut : ∀ t, t < N - 1 → ∃ q, csum V t (t + 1) + 2 ^ (2 * N) * q = csum V (t + 1) (t + 2) := by
    intro t ht
    obtain ⟨q, hq⟩ := hnode t ht
    refine ⟨q, ?_⟩
    have f1 : csum V t (t + 1) = V t + csum V (t + 1) t := csum_front V t t
    have f2 : csum V (t + 1) (t + 2) = csum V (t + 1) t + V (t + 1 + t) + V (t + 1 + (t + 1)) := rfl
    rw [f1, f2, show t + 1 + t = 2 * t + 1 by omega, show t + 1 + (t + 1) = 2 * t + 2 by omega]
    omega
  have hdown : ∀ d, d ≤ N - 1 → csum V (N - 1 - d) (N - 1 - d + 1) % 2 ^ (2 * N) =
      (a.toNat * b.toNat) % 2 ^ (2 * N) := by
    intro d
    induction d with
    | zero =>
      intro _
      rw [show N - 1 - 0 + 1 = N by omega, Nat.sub_zero, hleaves]
    | succ d ih =>
      intro hd
      have ih := ih (by omega)
      obtain ⟨q, hq⟩ := hcut (N - 1 - (d + 1)) (by omega)
      rw [show N - 1 - (d + 1) + 1 = N - 1 - d by omega,
        show N - 1 - (d + 1) + 2 = N - 1 - d + 1 by omega] at hq
      rw [← ih, ← hq, Nat.add_mul_mod_self_left, show N - 1 - (d + 1) + 1 = N - 1 - d by omega]
  have hroot := hdown (N - 1) (Nat.le_refl _)
  rw [Nat.sub_self, show (0 : Nat) + 1 = 1 from rfl] at hroot
  have hV0 : csum V 0 1 = V 0 := by simp [csum]
  rw [hV0] at hroot
  -- the final adder
  have hrip := ripple_row (2 * N) (S 0) (C 0) (fun c => S 0 c ^^ C 0 c) p.getLsbD R
    (fun _ _ => rfl) (fun c hc => hp c hc) (fun c hc => hR c hc)
  rw [hR0, bfVal_bv] at hrip
  simp only [Bool.toNat_false, Nat.add_zero] at hrip
  have hlt : p.toNat < 2 ^ (2 * N) := p.isLt
  have hpv : p.toNat = V 0 % 2 ^ (2 * N) := by
    have := sum_split hlt (t := V 0) (q := (R (2 * N)).toNat) (by simp only [V]; omega)
    exact this.1
  apply BitVec.eq_of_toNat_eq
  rw [BitVec.toNat_ofNat, hpv, hroot]

end TreeArith

namespace Submission

open LeanModels.Sv LeanModels.Sv.CombView LeanModels.Sv.CombLib LeanModels.Sv.CombEq SvGolf


/-! ## 2. Structure: `sv_structure` -/

/-- The rank: `(stage, node order, phase)`. Inputs and constants (the thermometers `th`, `nth`
included) first, then the leaves, then the internal nodes from the last (deepest, highest heap
index) to the root, in two phases (the first row of counters, then the second), then the ripple
final adder column by column. -/
def treeKey (N : Nat) : SiteId → Key
  | .plain _ => (0, 0, 0)
  | .fam "g_thlo" _ _ => (0, 0, 0)
  | .fam "g_thhi" _ _ => (0, 0, 0)
  | .fam "g_lc" _ _ => (1, 0, 0)
  | .fam "g_node" _ [k] => (2, N - 1 - k.toNat, 0)
  | .fam "g_col" 0 [k, _] => (2, N - 1 - k.toNat, 1)
  | .fam "g_col" _ [k, _] => (2, N - 1 - k.toNat, 2)
  | .fam "g_car" 0 [k, _] => (2, N - 1 - k.toNat, 1)
  | .fam "g_car" _ [k, _] => (2, N - 1 - k.toNat, 2)
  | .fam "g_fin" 0 [c] => (3, c.toNat, 1)
  | .fam "g_fin" _ [c] => (3, c.toNat, 0)
  | .fam _ _ _ => (3, 0, 0)

set_option maxHeartbeats 1000000 in
/-- `CombDesign` and `Acyclic` for every `N ≥ 1`, from the lexicographic key: ONE application of the
soundness theorem and one round of obligations for both. -/
theorem tree_struct (N : Nat) (hN : 1 ≤ N) :
    CombDesign (design.sem N) ∧ Acyclic (design.sem N) ∧ SharedObl design.pdesign [N] := by
  sv_structure (lexRank := treeKey N)
  all_goals (intros; sv_rowmajor; omega)

/-! ## 3. The ports -/

theorem tree_interface (N : Nat) (hN : 1 ≤ N) (hc : CombDesign (design.sem N)) :
    SvGolf.Tasks.Mul.task.Interface N (design.sem N) := by
  sv_interface


/-! ## 4. Bridge, equations and coverage of the heap reads

Every cell the tree reads must have a writer. The heap rows `[0, N-1)` are written by the node loop,
the rows `[N-1, 2N-1)` by the leaf loop; a carry row is also written in column 0 by its own assign.
`sv_cover` closes these coverage goals itself (it solves the writer's loop indices from the read's row
and column and walks the disjunction of writers, `CombCover.lean`), so `solution` only lists the
equations it bridges. -/

set_option maxHeartbeats 600000 in
theorem solution : Goal := by
  intro N hN
  have hN' : 1 ≤ N := hN.1
  obtain ⟨hc, ha, hso⟩ := tree_struct N hN'
  refine CombTask.satisfiedAt_of_comb (tree_interface N hN' hc) hc ha ?_
  intro st heqs hs v hv
  obtain ⟨x, y, ⟨⟩⟩ := v
  simp only [SvGolf.Tasks.Mul.task, HoldAll_cons, HoldAll_nil, and_true] at hv ⊢
  obtain ⟨hA, hB⟩ := hv
  sv_equations
  · obtain ⟨⟨vp, hvp, hvpw⟩, -⟩ := known_p_g_fin_0 0 (by omega)
    have ba : ∀ i, i < N → st.boolAt "a" i = x.getLsbD i := fun i hi => SvState.boolAt_ofBitVec hA hi
    have bb : ∀ i, i < N → st.boolAt "b" i = y.getLsbD i := fun i hi => SvState.boolAt_ofBitVec hB hi
    rw [hvp, SvState.lookup_eq_ofBitVec_bv (W := 2 * N) hvp (by omega)
      (fun k hk => (known_p_g_fin_0 k (by omega)).2)]
    congr 2
    refine TreeArith.tree_mul hN' x y (XF.bv (2 * N) (st.boolAt "p")) (st.boolAt "ae") (st.boolAt "th")
      (st.boolAt "nth") (st.cell "S" (2 * N)) (st.cell "C" (2 * N)) (st.cell "X" (2 * N))
      (st.cell "Y" (2 * N)) (st.boolAt "R")
      ?_ ?_ ?_ ?_ ?_ ?_ ?_ ?_ ?_ ?_ ?_ ?_ ?_ ?_ ?_ ?_ ?_ ?_
    · sv_bridge eq_ae_plain_0
    · sv_bridge eq_ae_plain_1 with ba
    · sv_bridge eq_ae_plain_2
    · sv_bridge eq_th_g_thlo_0
    · sv_bridge eq_nth_g_thlo_1
    · sv_bridge eq_th_g_thhi_0
    · sv_bridge eq_nth_g_thhi_1
    · sv_bridge eq_S_g_lc_0 with bb
    · sv_bridge eq_C_g_lc_1
    · sv_bridge eq_Y_g_node_0
    · sv_bridge eq_C_g_node_1
    · sv_bridge eq_X_g_col_0
    · sv_bridge eq_S_g_col_1
    · sv_bridge eq_Y_g_car_0
    · sv_bridge eq_C_g_car_1
    · sv_bridge eq_R_plain_5
    · sv_bridge eq_p_g_fin_0 with XF.getLsbD_bv
    · sv_bridge eq_R_g_fin_1
  case inputsKnown_1 => exact inKnown_ofBitVec hA (by omega)
  case inputsKnown_2 => exact inKnown_ofBitVec hB (by omega)
  all_goals first | (intros; omega) | (intros; rfl) | sv_cover

/--
info: 'Submission.solution' depends on axioms: [propext, Classical.choice, Quot.sound]
-/
#guard_msgs (whitespace := lax) in
#print axioms solution

end Submission

The netlist

The netlist card shows the sky130 weighing (its cells, its critical path): the ASAP7 view changes the overview above, not this card.

Schematic

drawn at N = 4

Drawn at N = 4, a display instance; scored at N = 16. Only this drawing and the notes under it are at the smaller size: the critical path, the cells and every other number on this page are the scored netlist's.

_540_ · sky130_fd_sc_hd__nand2_1 nand2_1 _541_ · sky130_fd_sc_hd__and2_1 and2_1 _542_ · sky130_fd_sc_hd__and2_1 and2_1 _543_ · sky130_fd_sc_hd__and2_1 and2_1 _544_ · sky130_fd_sc_hd__and2_1 and2_1 _545_ · sky130_fd_sc_hd__xnor2_1 xnor2_1 _546_ · sky130_fd_sc_hd__and2_1 and2_1 _547_ · sky130_fd_sc_hd__and2_1 and2_1 _548_ · sky130_fd_sc_hd__and2_1 and2_1 _549_ · sky130_fd_sc_hd__nand2_1 nand2_1 _550_ · sky130_fd_sc_hd__nand2_1 nand2_1 _551_ · sky130_fd_sc_hd__xor2_1 xor2_1 _552_ · sky130_fd_sc_hd__nand2_1 nand2_1 _553_ · sky130_fd_sc_hd__xor2_1 xor2_1 _554_ · sky130_fd_sc_hd__and2_1 and2_1 _555_ · sky130_fd_sc_hd__xor2_1 xor2_1 _556_ · sky130_fd_sc_hd__and2_1 and2_1 _557_ · sky130_fd_sc_hd__nand2_1 nand2_1 _558_ · sky130_fd_sc_hd__and2_1 and2_1 _559_ · sky130_fd_sc_hd__and2_1 and2_1 _560_ · sky130_fd_sc_hd__and2_1 and2_1 _561_ · sky130_fd_sc_hd__nand2_1 nand2_1 _562_ · sky130_fd_sc_hd__xnor2_1 xnor2_1 _563_ · sky130_fd_sc_hd__nand2_1 nand2_1 _564_ · sky130_fd_sc_hd__xor2_1 xor2_1 _565_ · sky130_fd_sc_hd__nand2_1 nand2_1 _566_ · sky130_fd_sc_hd__xor2_1 xor2_1 _567_ · sky130_fd_sc_hd__nand2_1 nand2_1 _568_ · sky130_fd_sc_hd__xor2_1 xor2_1 _569_ · sky130_fd_sc_hd__and2_1 and2_1 _570_ · sky130_fd_sc_hd__xor2_1 xor2_1 _571_ · sky130_fd_sc_hd__nand2_1 nand2_1 _572_ · sky130_fd_sc_hd__and2_1 and2_1 _573_ · sky130_fd_sc_hd__nand2_1 nand2_1 _574_ · sky130_fd_sc_hd__nand2_1 nand2_1 _575_ · sky130_fd_sc_hd__and2_1 and2_1 _576_ · sky130_fd_sc_hd__nand2_1 nand2_1 _577_ · sky130_fd_sc_hd__xnor2_1 xnor2_1 _578_ · sky130_fd_sc_hd__nand2_1 nand2_1 _579_ · sky130_fd_sc_hd__xor2_1 xor2_1 _580_ · sky130_fd_sc_hd__nand2_1 nand2_1 _581_ · sky130_fd_sc_hd__xor2_1 xor2_1 _582_ · sky130_fd_sc_hd__nand2_1 nand2_1 _583_ · sky130_fd_sc_hd__xor2_1 xor2_1 _584_ · sky130_fd_sc_hd__nand2_1 nand2_1 _585_ · sky130_fd_sc_hd__xor2_1 xor2_1 _586_ · sky130_fd_sc_hd__nand2_1 nand2_1 _587_ · sky130_fd_sc_hd__nand2_1 nand2_1 _588_ · sky130_fd_sc_hd__and2_1 and2_1 _589_ · sky130_fd_sc_hd__and2_1 and2_1 _590_ · sky130_fd_sc_hd__nand2_1 nand2_1 _591_ · sky130_fd_sc_hd__xor2_1 xor2_1 _592_ · sky130_fd_sc_hd__nand2_1 nand2_1 _593_ · sky130_fd_sc_hd__xor2_1 xor2_1 _594_ · sky130_fd_sc_hd__nand2_1 nand2_1 _595_ · sky130_fd_sc_hd__xor2_1 xor2_1 _596_ · sky130_fd_sc_hd__nand2_1 nand2_1 _597_ · sky130_fd_sc_hd__nand2_1 nand2_1 _598_ · sky130_fd_sc_hd__and2_1 and2_1 _599_ · sky130_fd_sc_hd__nand2_1 nand2_1 _600_ · sky130_fd_sc_hd__xor2_1 xor2_1 _601_ · sky130_fd_sc_hd__nand2_1 nand2_1 _602_ · sky130_fd_sc_hd__xor2_1 xor2_1 _603_ · sky130_fd_sc_hd__nand2_1 nand2_1 a b p 0 1 2 3 4 5 6 7 1 0 2 3 0 1 2 3

It is the same design through the same pinned flow at a smaller size (ppa.py --param), so that what it does can be seen: 64 cells here, 1 456 at the scoring point. The critical path at N = 4 (11 cells, ending at p[6] rising, 1 777 ps there) is in the accent colour. Drawn by netlistsvg with svGolf's sky130 skin (derived from netlistsvg's default skin, as the drawing is: MIT licence, Copyright (c) 2016 Neil Turley, the notice): one symbol per cell function, the cell above it, the instance on hover; nor2b and nand2b are drawn with the bubble on their inverted input. The cells of the path are listed under Critical path, each a link to the cell here.

The netlist at N = 4: 64 cells, 27 nand2_1, 18 and2_1, 16 xor2_1, 3 xnor2_1. There (measured, not ranked): 380.36 µm², 1 777 ps. Open the drawing on its own (SVG, 40.2 KB) to pan and zoom it with the browser's own controls.

Critical path

at N = 16 · scored

The path the ranked delay ends on, at the scoring point N = 16: 59 nets, 58 cells, to p[30] rising at 6 129 ps. Each row is a net of the repaired netlist with the arrival and the transition the STA computed there (NLDM tables, the larger transition carried; no wires). The first row is an input: its arrival is the input driver's own delay (buf_8 fed by a 50 ps ramp). From ppa.py --path (critical_path_timing); the instance names are the scored netlist's, not the schematic's (drawn at a smaller size).

In the schematic, drawn at N = 4, the critical path is 11 cells to p[6] rising (1 777 ps there); each cell is a link to it in the drawing:

  1. a[3]
  2. _559_ and2_1
  3. _562_ xnor2_1
  4. _564_ xor2_1
  5. _566_ xor2_1
  6. _568_ xor2_1
  7. _569_ and2_1
  8. _584_ nand2_1
  9. _586_ nand2_1
  10. _594_ nand2_1
  11. _596_ nand2_1
  12. _602_ xor2_1
  13. p[6]
every stage (59 rows)
#netedgedriven bycellarrival (ps)transition (ps)stage (ps)
0a[7]fallinput port–128.133.5
1_09795_fall_10753_and2_1290.651.7162.5
2_09841_rise_10800_xor2_1491.3190.5200.7
3_09843_rise_10802_xor2_1708.0190.5216.7
4_09845_rise_10804_xor2_1924.8190.5216.8
5_09847_rise_10806_xor2_11 141.6190.5216.8
6_09849_rise_10808_xor2_11 358.4190.5216.8
7_09851_rise_10810_xor2_11 575.1190.5216.7
8_09853_rise_10812_xor2_11 791.9190.5216.8
9_09855_rise_10814_xor2_12 008.7190.5216.8
10_09857_rise_10816_xor2_12 225.4190.5216.7
11_09859_rise_10818_xor2_12 430.5174.4205.1
12_09861_rise_10820_xor2_12 645.3190.5214.8
13_09863_rise_10822_xor2_12 862.0190.5216.7
14_09864_fall_10823_nand2_12 933.845.971.8
15_09865_rise_10825_nand2_13 021.080.187.2
16_09918_fall_10878_nand2_13 070.645.949.6
17_09919_rise_10880_nand2_13 157.880.187.2
18_09976_fall_10937_nand2_13 207.445.949.6
19_09977_rise_10939_nand2_13 294.680.187.2
20_10040_fall_11002_nand2_13 344.145.949.5
21_10041_rise_11004_nand2_13 431.380.187.2
22_10110_fall_11073_nand2_13 480.945.949.6
23_10111_rise_11075_nand2_13 568.180.187.2
24_10187_fall_11151_nand2_13 617.745.949.6
25_10188_rise_11153_nand2_13 704.980.187.2
26_10273_fall_11235_nand2_13 754.545.949.6
27_10274_rise_11237_nand2_13 841.780.187.2
28_10369_fall_11324_nand2_13 891.245.949.5
29_10370_rise_11326_nand2_13 978.480.187.2
30_10465_fall_11412_nand2_14 028.045.949.6
31_10466_rise_11414_nand2_14 115.280.187.2
32_10554_fall_11494_nand2_14 164.845.949.6
33_10555_rise_11496_nand2_14 252.080.187.2
34_09217_fall_11574_nand2_14 301.645.949.6
35_09218_rise_11576_nand2_14 388.880.187.2
36_09297_fall_11647_nand2_14 438.345.949.5
37_09298_rise_11649_nand2_14 525.580.187.2
38_09368_fall_11714_nand2_14 575.145.949.6
39_09369_rise_11716_nand2_14 662.380.187.2
40_09433_fall_11774_nand2_14 711.945.949.6
41_09434_rise_11776_nand2_14 799.180.187.2
42_09496_fall_11832_nand2_14 848.745.949.6
43_09497_rise_11834_nand2_14 935.980.187.2
44_09551_fall_11883_nand2_14 985.545.949.6
45_09552_rise_11885_nand2_15 072.680.187.1
46_09600_fall_11928_nand2_15 122.245.949.6
47_09601_rise_11930_nand2_15 209.480.187.2
48_09639_fall_11965_nand2_15 259.045.949.6
49_09641_rise_11967_nand2_15 346.280.187.2
50_09676_fall_11999_nand2_15 395.845.949.6
51_09677_rise_12001_nand2_15 483.080.187.2
52_09704_fall_12026_nand2_15 532.645.949.6
53_09706_rise_12028_nand2_15 619.880.187.2
54_09727_fall_12047_nand2_15 669.345.949.5
55_09728_rise_12049_nand2_15 756.580.187.2
56_09741_fall_12061_nand2_15 806.145.949.6
57_09742_rise_12063_nand2_15 893.380.187.2
58_9191_rise_12073_xor2_16 128.5247.2235.2

Cells

at N = 16 · scored

the cells of the repaired netlist, by area6 bars; the table beside the chart lists the same numbers.xor2_1: 4063.9 µm² (µm² · 464 cells)xor2_14063.9 µm² · 464 cellsnand2_1: 2567.46 µm² (µm² · 684 cells)nand2_12567.46 µm² · 684 cellsand2_1: 1789.22 µm² (µm² · 286 cells)and2_11789.22 µm² · 286 cellsxnor2_1: 166.41 µm² (µm² · 19 cells)xnor2_1166.41 µm² · 19 cellsnand2b_1: 12.51 µm² (µm² · 2 cells)nand2b_112.51 µm² · 2 cellsnor2_1: 3.75 µm² (µm² · 1 cell)nor2_13.75 µm² · 1 cell
The area of each cell type in the repaired A0′ netlist, at the scoring point: the count times the cell's area in the pinned liberty. Their sum is the ranked area.
cellcountµm² eachµm² in allshare
xor2_14648.75844063.897647.2%
nand2_16843.75362567.462429.8%
and2_12866.25601789.216020.8%
xnor2_1198.7584166.40961.9%
nand2b_126.256012.51200.1%
nor2_113.75363.75360.0%
total1 4568603.2512

Repair (buffer_trees_v1): 0 buffers on 0 nets, 0 tie cells, 1 weighing round; nothing is resized.

Legality: worst load 0.144 × max_capacitance, worst transition 0.178 × max_transition; 0 timing-table reads outside the characterized grid.

the gate mix of the extra GE column (the pinned GE table, score/ge.json)
gate mix: cells of each type in the scored netlist6 bars; the table beside the chart lists the same numbers.NAND: 684 cells (684.000 GE)NAND684 684.000 GEXOR: 464 cells (928.000 GE)XOR464 928.000 GEAND: 286 cells (381.238 GE)AND286 381.238 GEXNOR: 19 cells (38.000 GE)XNOR19 38.000 GEORNOT: 2 cells (2.666 GE)ORNOT2 2.666 GENOR: 1 cell (1.000 GE)NOR1 1.000 GE
Cells by type; area is the count weighted by the pinned GE table.
cellcountGE eachGE total
NAND6841.000684.000
XOR4642.000928.000
AND2861.333381.238
XNOR192.00038.000
ORNOT21.3332.666
NOR11.0001.000
total14562034.904

The reference

task
mul
design
mul_tree.sv sha256 dca1d7da6838f8f7eaaa2ee3094aa6148f5881d99fb5693c546da161d9056d37
proof
mul_tree.lean, 322 code lines
tactics seen
grind, induction, omega, simp, sv_cover, sv_equations, sv_interface, sv_rowmajor, sv_structure
same design as
–
recorded
no: the checker's run details (build time, axioms, the post-synthesis vectors) are kept for recorded designs only
downloads
mul_tree.sv mul_tree.lean

Area against time

µm² × ps · lower left is better · ASAP7
mul: this design's sky130 area against time, among the task's designs10 designs by sky130 area (µm²) (x) and time (ps) (y), this one ringed; lower left is better; 2 beyond the view, counted at its edges. The table under the chart lists the same numbers.500055006000650070007500800082008400860088009000sky130 area (µm²)time (ps) · lower is betterpar's score: area × time = 51 311 µm²·nsthe Pareto front of the entriesdadda_ks_16 · baseline, not proved · Score: 46 009 µm²·ns · Area: 8937.3 µm² · Delay: 5148 psdadda_rc_16 · baseline, not proved · Score: 44 946 µm²·ns · Area: 8230.4 µm² · Delay: 5461 psyosys_mul · baseline, not proved · banned operator · Score: 55 259 µm²·ns · Area: 8826.0 µm² · Delay: 6261 psmul_wallace · proved reference · 2026-10-30T05:26:59Z · Score: 53 635 µm²·ns · Area: 8966.1 µm² · Delay: 5982 psmul_tree_h · proved reference · par · 2026-10-30T12:20:05Z · Score: 51 311 µm²·ns · Area: 8563.2 µm² · Delay: 5992 psplus-minus-two · submission · 2026-11-05 · Score: 50 498 µm²·ns · Area: 7978.9 µm² · Delay: 6329 pslate-carry · submission · 2026-11-05 · Score: 42 530 µm²·ns · Area: 7961.4 µm² · Delay: 5342 psthis design · mul_tree · proved reference · 2026-10-30T03:37:11Z · Score: 52 729 µm²·ns · Area: 8603.3 µm² · Delay: 6129 psparthis design2 designs beyond the view (above)▴ 2
mul: this design's sky130 area against time, among the task's designs10 designs by sky130 area (µm²) (x) and time (ps) (y), this one ringed; lower left is better; 2 beyond the view, counted at its edges. The table under the chart lists the same numbers.500060007000800085009000sky130 area (µm²)time (ps) · lower is betterpar's score: area × time = 51 311 µm²·nsthe Pareto front of the entriesdadda_ks_16 · baseline, not proved · Score: 46 009 µm²·ns · Area: 8937.3 µm² · Delay: 5148 psdadda_rc_16 · baseline, not proved · Score: 44 946 µm²·ns · Area: 8230.4 µm² · Delay: 5461 psyosys_mul · baseline, not proved · banned operator · Score: 55 259 µm²·ns · Area: 8826.0 µm² · Delay: 6261 psmul_wallace · proved reference · 2026-10-30T05:26:59Z · Score: 53 635 µm²·ns · Area: 8966.1 µm² · Delay: 5982 psmul_tree_h · proved reference · par · 2026-10-30T12:20:05Z · Score: 51 311 µm²·ns · Area: 8563.2 µm² · Delay: 5992 psplus-minus-two · submission · 2026-11-05 · Score: 50 498 µm²·ns · Area: 7978.9 µm² · Delay: 6329 pslate-carry · submission · 2026-11-05 · Score: 42 530 µm²·ns · Area: 7961.4 µm² · Delay: 5342 psthis design · mul_tree · proved reference · 2026-10-30T03:37:11Z · Score: 52 729 µm²·ns · Area: 8603.3 µm² · Delay: 6129 psparthis design2 designs beyond the view (above)▴ 2
  • submission
  • proved reference
  • baseline (not proved)
  • baseline with a banned operator
  • the Pareto front of the entries
  • this design
  • par's score: area × time = 51 311 µm²·ns
sky130 area (x) against the ranked time (y); lower left is better on both. The step line is the Pareto front of the entries (the proved references and the players' ranked records); a design under par's score curve has a better score than par. Baselines are drawn for scale: scored, never proved. Fitted to the cluster: 2 designs beyond the view, counted at its edge (◂ ▸ ▴ ▾); “All designs” shows them.
the designs as numbers, by score
designkindarea (µm²)time (ps)score (µm²·ns)
late-carry submission7 961.395 34242 530
dadda_rc_16 baseline, not proved8 230.395 46144 946
dadda_ks_16 baseline, not proved8 937.325 14846 009
plus-minus-two submission7 978.906 32950 498
mul_tree_h proved reference · par8 563.215 99251 311
mul_tree · this designproved reference8 603.256 12952 729
mul_wallace proved reference8 966.105 98253 635
yosys_mul baseline, not proved · banned operator8 825.966 26155 259
mul_n proved reference8 290.4510 90790 424
mul_n_byhand proved reference8 290.4510 90790 424
mul: this design's ASAP7 area against time, among the task's designs10 designs by ASAP7 area (µm²) (x) and time (ps) (y), this one ringed; lower left is better; 2 beyond the view, counted at its edges. The table under the chart lists the same numbers.100012001400120125130ASAP7 area (µm²)time (ps) · lower is betterpar's score: area × time = 148.4 µm²·nsthe Pareto front of the entriesdadda_ks_16 · baseline, not proved · Score: 120.1 µm²·ns · Area: 133.174 µm² · Delay: 902 psdadda_rc_16 · baseline, not proved · Score: 135.7 µm²·ns · Area: 123.391 µm² · Delay: 1100 psyosys_mul · baseline, not proved · banned operator · Score: 150.1 µm²·ns · Area: 131.789 µm² · Delay: 1139 psmul_tree_h · proved reference · par · 2026-10-30T12:20:05Z · Score: 148.4 µm²·ns · Area: 127.954 µm² · Delay: 1160 psmul_wallace · proved reference · 2026-10-30T05:26:59Z · Score: 158.8 µm²·ns · Area: 133.917 µm² · Delay: 1186 pslate-carry · submission · 2026-11-05 · Score: 131.5 µm²·ns · Area: 120.854 µm² · Delay: 1088 psplus-minus-two · submission · 2026-11-05 · Score: 156.5 µm²·ns · Area: 119.746 µm² · Delay: 1307 psthis design · mul_tree · proved reference · 2026-10-30T03:37:11Z · Score: 153.1 µm²·ns · Area: 128.552 µm² · Delay: 1191 psparthis design2 designs beyond the view (above)▴ 2
mul: this design's ASAP7 area against time, among the task's designs10 designs by ASAP7 area (µm²) (x) and time (ps) (y), this one ringed; lower left is better; 2 beyond the view, counted at its edges. The table under the chart lists the same numbers.100012001400120125130ASAP7 area (µm²)time (ps) · lower is betterpar's score: area × time = 148.4 µm²·nsthe Pareto front of the entriesdadda_ks_16 · baseline, not proved · Score: 120.1 µm²·ns · Area: 133.174 µm² · Delay: 902 psdadda_rc_16 · baseline, not proved · Score: 135.7 µm²·ns · Area: 123.391 µm² · Delay: 1100 psyosys_mul · baseline, not proved · banned operator · Score: 150.1 µm²·ns · Area: 131.789 µm² · Delay: 1139 psmul_tree_h · proved reference · par · 2026-10-30T12:20:05Z · Score: 148.4 µm²·ns · Area: 127.954 µm² · Delay: 1160 psmul_wallace · proved reference · 2026-10-30T05:26:59Z · Score: 158.8 µm²·ns · Area: 133.917 µm² · Delay: 1186 pslate-carry · submission · 2026-11-05 · Score: 131.5 µm²·ns · Area: 120.854 µm² · Delay: 1088 psplus-minus-two · submission · 2026-11-05 · Score: 156.5 µm²·ns · Area: 119.746 µm² · Delay: 1307 psthis design · mul_tree · proved reference · 2026-10-30T03:37:11Z · Score: 153.1 µm²·ns · Area: 128.552 µm² · Delay: 1191 psparthis design2 designs beyond the view (above)▴ 2
  • submission
  • proved reference
  • baseline (not proved)
  • baseline with a banned operator
  • the Pareto front of the entries
  • this design
  • par's score: area × time = 148.4 µm²·ns
ASAP7 area (x) against the ASAP7 time (y); lower left is better on both. The step line is the Pareto front of the entries (the proved references and the players' ranked records); a design under par's score curve has a better score than par. Baselines are drawn for scale: scored, never proved. Fitted to the cluster: 2 designs beyond the view, counted at its edge (◂ ▸ ▴ ▾); “All designs” shows them.
the designs as numbers, by score
designkindarea (µm²)time (ps)score (µm²·ns)
dadda_ks_16 baseline, not proved133.174902120.1
late-carry submission120.8541 088131.5
dadda_rc_16 baseline, not proved123.3911 100135.7
mul_tree_h proved reference · par127.9541 160148.4
yosys_mul baseline, not proved · banned operator131.7891 139150.1
mul_tree · this designproved reference128.5521 191153.1
plus-minus-two submission119.7461 307156.5
mul_wallace proved reference133.9171 186158.8
mul_n proved reference123.9301 981245.5
mul_n_byhand proved reference123.9301 981245.5

Reproduce

From svgolf/, with the pinned tools (reproduce a score lists them):

shell
# the ranked numbers, at the scoring point (from svgolf/, with the pinned Yosys and liberty)
python3.12 score/ppa.py tasks/mul/reference/mul_tree.sv --task tasks/mul --path \
    --json out.json --netlist scored.v --repaired-netlist repaired.v

Expect, in out.json:

ppa.netlist_sha256
e3cd41f4226ce33d1990d9b5ec3297d9e8d4ca7b7b7c596491c02b6379678ea2
ppa.repair.netlist_sha256
8b0ed1dbc11ad9b8301feb7d1cb4069cb46494cc00ab2e86721371a93bb7db41
ppa.area_e4um2_sky130
86032512
ppa.delay_ps_sky130
6129

The schematic and the critical-path data are site/schematics/mul/dca1d7da6838f8f7.json and its .svg, written by site/gen_schematics.py (the display instance is ppa.py ... --param N=4 --repaired-netlist, drawn by netlistsvg 1.0.2; repaired netlist sha256 a70d73d9a220079d…).

From svgolf/, with the pinned tools (reproduce a score lists them):

shell
# the ASAP7 view's numbers, at the scoring point (from svgolf/, with the pinned Yosys; once:
# python3.12 score/ppa.py --pdk asap7 --fetch-liberty, the five pinned ASAP7 files into score/lib/asap7/)
python3.12 score/ppa.py tasks/mul/reference/mul_tree.sv --task tasks/mul --pdk asap7 --no-mapped \
    --json out.json

Expect, in out.json:

ppa.netlist_sha256
e3cd41f4226ce33d1990d9b5ec3297d9e8d4ca7b7b7c596491c02b6379678ea2
ppa.repair.netlist_sha256
b26b9b997eca012dda81d81d65e9e2d186deedf45ce841cb4ad5892b0e9acbcb
ppa.area_e5um2_asap7
12855186
ppa.delay_ps_asap7
1191

leaderboard/asap7/mul.json keeps these numbers; python3.12 leaderboard/leaderboard.py asap7 --rescore --task mul weighs every design of the task again and compares, bit for bit.