Record d20bd7d9a6e6
Area
474.20µm²
474.2048 µm² exactly
- vs par
- +3.0% largeradd_n · 460.44 µm²
- on this page
- 6th of 10 designs
- cells
- 83
- flops
- 0
- repair
- 0 buffers, 0 tie cells
- GE
- 114.665 extra
- A1′
- 447.93 µm² research
Performance · time
2 479ps
the delay: the latest output arrival, s[15] rising
- vs par
- −5.1% fasteradd_n · 2 611 ps
- on this page
- 4th of 10 designs
- Fmax-eq.
- 403.4 MHz derived
- critical path
- 30 cells
- unit depth
- 33 extra
- A1′
- 5 525 ps research
Score · area × time
1176µm²·ns
474.2048 µm² × 2 479 ps
- vs par
- −2.2% betteradd_n · 1202 µm²·ns
- on this page
- best of 10 designs
- board
- layer 1, rank 1 of 1 by score
- Pareto
- on the front
No other player has a ranked record on add yet: the beats and matches lines come with the first one.
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 add's other players at this revision (0 players, 0 records), never the references, the baselines or a rejected design; advisory, recomputed at every build, never stored in a record and never used by the ranking.
“On this page” and “Pareto” compare with the 10 designs of the area-time plot (5 references, 1 submission and 4 baselines): not a ranking.
Area
7.188µm²
7.18794 µm² exactly
- vs par
- +2.7% largeradd_n · 6.998 µm²
- on this page
- 6th of 10 designs
- cells
- 84
- flops
- 0
- repair
- 0 buffers, 0 tie cells
- GE
- 114.665 extra
Performance · time
561ps
the delay: the latest output arrival
- vs par
- −0.4% fasteradd_n · 563 ps
- on this page
- 4th of 10 designs
- Fmax-eq.
- 1782.5 MHz derived
- unit depth
- 33 extra
Score · area × time
4.032µm²·ns
7.18794 µm² × 561 ps
- vs par
- +2.3% worseadd_n · 3.94 µm²·ns
- on this page
- 7th of 10 designs
- board
- layer 1, rank 1 of 1 by score
- Pareto
- on the front
No other player has a ranked record on add yet: the beats and matches lines come with the first one.
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 add's other players at this revision (0 players, 0 records), never the references, the baselines or a rejected design; advisory, recomputed at every build, never stored in a record and never used by the ranking.
“On this page” and “Pareto” compare with the 10 designs of the area-time plot (5 references, 1 submission and 4 baselines): not a ranking.
The sources
// svGolf task `add`: a carry-select adder with 6-bit blocks, for every N >= 1.
// Q = N / 6 carry-select blocks: block j computes its carries twice, by two ripple chains in
// row j of c0 (block carry-in 0) and c1 (block carry-in 1); row j of cs picks one by the block
// carry cb[j] (the carry into each bit of the block), and the block carries ripple through one multiplexer per block. The R = N % 6 top
// bits ripple from cb[Q] through c (c[i] is the carry INTO bit i; cout = c[N]).
module add_n #(parameter int N = 16) (
input logic [N-1:0] a, b,
input logic cin,
output logic [N-1:0] s,
output logic cout
);
localparam integer Q = N / 6;
localparam integer R = N % 6;
logic [N-1:0] p, g;
logic [Q:0][6:0] c0, c1;
logic [Q:0][5:0] cs;
logic [Q:0] cb;
logic [N:0] c;
genvar i, j, t;
// the module-level assigns: block carry 0, the unused row Q of c0, c1, cs (every declared bit
// is driven), the carry into the tail, the carry out
assign cb[0] = cin;
assign c0[Q] = 7'b0;
assign c1[Q] = 7'b0;
assign cs[Q] = 6'b0;
assign c[6*Q] = cb[Q];
assign cout = c[N];
generate
for (i = 0; i < N; i++) begin : g_pg
assign p[i] = a[i] ^ b[i];
assign g[i] = a[i] & b[i];
end
for (j = 0; j < Q; j++) begin : g_blk
assign c0[j][0] = 1'b0;
assign c1[j][0] = 1'b1;
for (t = 0; t < 6; t++) begin : g_rip
assign c0[j][t+1] = g[6*j+t] | (p[6*j+t] & c0[j][t]);
assign c1[j][t+1] = g[6*j+t] | (p[6*j+t] & c1[j][t]);
assign cs[j][t] = cb[j] ? c1[j][t] : c0[j][t];
end
assign cb[j+1] = cb[j] ? c1[j][6] : c0[j][6];
end
for (t = 0; t < R; t++) begin : g_tail
assign c[6*Q+t+1] = g[6*Q+t] | (p[6*Q+t] & c[6*Q+t]);
end
for (j = 0; j < Q; j++) begin : g_sblk
for (t = 0; t < 6; t++) begin : g_s
assign s[6*j+t] = p[6*j+t] ^ cs[j][t];
end
end
for (t = 0; t < R; t++) begin : g_stail
assign s[6*Q+t] = p[6*Q+t] ^ c[6*Q+t];
end
endgenerate
endmodule
Solution.lean (332 code lines)
import Submission.Design
import SvGolf.Comb
import LeanModels.Sv.CombEqTactic
import LeanModels.Sv.CombLib
/-!
# Carry-select arithmetic, for every block size `K` and every width `N`
Plain Lean over `Nat`-indexed Boolean functions; nothing here mentions the design. A carry-select
adder with `K`-bit blocks is `Q = N / K` blocks, each with two ripple chains (`c0` from carry-in 0,
`c1` from 1, chain `j` stored at `W * j + t`, `W = K + 1`), a multiplexer row `c (K*j+t)` choosing
by the block carry `cb j`, block carries `cb (j+1)` chosen the same way, and a ripple tail of
`N % K` bits from `cb Q`. `csel_carry` says the selected carries are THE ripple carries
(`rip g p 0 ci`), so the adder is `CombLib.AddView` (`csel_view`).
-/
namespace CselMath
open LeanModels.Sv.CombLib
/-- The ripple carry into position `b + t` when the carry into position `b` is `x0`. -/
def rip (g p : Nat → Bool) (b : Nat) (x0 : Bool) : Nat → Bool
| 0 => x0
| t + 1 => g (b + t) || (p (b + t) && rip g p b x0 t)
/-- A chain stored at offset `o` that starts at `x0` and takes the ripple steps of positions
`b, b+1, …` is the ripple carry from `b`. -/
theorem chain_eq (g p d : Nat → Bool) (b o K : Nat) (x0 : Bool) (h0 : d o = x0)
(hs : ∀ t < K, d (o + t + 1) = (g (b + t) || (p (b + t) && d (o + t)))) :
∀ t ≤ K, d (o + t) = rip g p b x0 t := by
intro t
induction t with
| zero => intro _; simpa [rip] using h0
| succ t ih =>
intro ht
show d (o + t + 1) = _
rw [hs t (by omega), ih (by omega)]
try rfl
/-- Restarting the ripple at `b` from its own carry changes nothing. -/
theorem rip_add (g p : Nat → Bool) (ci : Bool) (b : Nat) :
∀ t, rip g p 0 ci (b + t) = rip g p b (rip g p 0 ci b) t := by
intro t
induction t with
| zero => rfl
| succ t ih =>
show rip g p 0 ci (b + t + 1) = _
simp only [rip, Nat.zero_add, ih]
/-- A multiplexer between the two chains is the chain from the select value. -/
theorem mux_rip (g p : Nat → Bool) (b : Nat) (x : Bool) (t : Nat) :
(if x = true then rip g p b true t else rip g p b false t) = rip g p b x t := by
cases x <;> rfl
/-- **The selected carries are the ripple carries.** -/
theorem csel_carry (K W N : Nat) (hK : 0 < K) (hW : W = K + 1)
(g p c0 c1 cb c : Nat → Bool) (ci : Bool)
(hcb0 : cb 0 = ci)
(h00 : ∀ j < N / K, c0 (W * j) = false)
(h10 : ∀ j < N / K, c1 (W * j) = true)
(h0s : ∀ j < N / K, ∀ t < K,
c0 (W * j + t + 1) = (g (K * j + t) || (p (K * j + t) && c0 (W * j + t))))
(h1s : ∀ j < N / K, ∀ t < K,
c1 (W * j + t + 1) = (g (K * j + t) || (p (K * j + t) && c1 (W * j + t))))
(hsel : ∀ j < N / K, ∀ t < K,
c (K * j + t) = (if cb j = true then c1 (W * j + t) else c0 (W * j + t)))
(hcbs : ∀ j < N / K,
cb (j + 1) = (if cb j = true then c1 (W * j + K) else c0 (W * j + K)))
(hcQ : c (K * (N / K)) = cb (N / K))
(htail : ∀ t < N % K, c (K * (N / K) + t + 1) =
(g (K * (N / K) + t) || (p (K * (N / K) + t) && c (K * (N / K) + t)))) :
∀ i ≤ N, c i = rip g p 0 ci i := by
-- the two chains of block j
have hc0 : ∀ j < N / K, ∀ t ≤ K, c0 (W * j + t) = rip g p (K * j) false t := fun j hj =>
chain_eq g p c0 (K * j) (W * j) K false (h00 j hj) (h0s j hj)
have hc1 : ∀ j < N / K, ∀ t ≤ K, c1 (W * j + t) = rip g p (K * j) true t := fun j hj =>
chain_eq g p c1 (K * j) (W * j) K true (h10 j hj) (h1s j hj)
-- the multiplexer between them
have hmux : ∀ j < N / K, ∀ t ≤ K,
(if cb j = true then c1 (W * j + t) else c0 (W * j + t)) = rip g p (K * j) (cb j) t := by
intro j hj t ht
rw [hc1 j hj t ht, hc0 j hj t ht, mux_rip]
-- the block carries are the ripple carries at the block boundaries
have hcb : ∀ j ≤ N / K, cb j = rip g p 0 ci (K * j) := by
intro j
induction j with
| zero => intro _; rw [hcb0]; try rfl
| succ j ih =>
intro hj
rw [hcbs j (by omega), hmux j (by omega) K (Nat.le_refl K), ih (by omega),
← rip_add, Nat.mul_add, Nat.mul_one]
intro i hi
by_cases hlo : i < K * (N / K)
· -- inside a block
have hj : i / K < N / K := by
first
| exact Nat.div_lt_of_lt_mul hlo
| exact (Nat.div_lt_iff_lt_mul hK).2 (by rw [Nat.mul_comm]; exact hlo)
have ht : i % K < K := Nat.mod_lt i hK
have hi' : K * (i / K) + i % K = i := Nat.div_add_mod i K
rw [← hi', hsel (i / K) hj (i % K) ht, hmux (i / K) hj (i % K) (by omega),
hcb (i / K) (by omega), ← rip_add]
· -- the tail
have hN : K * (N / K) + N % K = N := Nat.div_add_mod N K
obtain ⟨t, rfl⟩ : ∃ t, i = K * (N / K) + t := ⟨i - K * (N / K), by omega⟩
have htl : t ≤ N % K := by omega
clear hlo hi
induction t with
| zero => rw [Nat.add_zero, hcQ, hcb (N / K) (Nat.le_refl _)]
| succ t ih =>
rw [← Nat.add_assoc, htail t (by omega), ih (by omega)]
show _ = rip g p 0 ci (K * (N / K) + t + 1)
simp only [rip, Nat.zero_add]
/-- Bit `t` of block `j` lies below the tail. -/
theorem blk_lt {K N j t : Nat} (hj : j < N / K) (ht : t < K) : K * j + t < K * (N / K) := by
have h1 : K * (j + 1) ≤ K * (N / K) := by
first
| exact Nat.mul_le_mul_left K (show j + 1 ≤ N / K by omega)
| exact Nat.mul_le_mul (Nat.le_refl K) (show j + 1 ≤ N / K by omega)
rw [Nat.mul_add, Nat.mul_one] at h1
omega
/-- The carry into bit `i` of the carry-select adder: the block multiplexer row `cs` below
`K * (N / K)`, the tail carry `c` from there on. -/
def sel (K N : Nat) (cs c : Nat → Bool) (i : Nat) : Bool := if i < K * (N / K) then cs i else c i
theorem sel_lo {K N i : Nat} {cs c : Nat → Bool} (h : i < K * (N / K)) : sel K N cs c i = cs i :=
if_pos h
theorem sel_hi {K N i : Nat} {cs c : Nat → Bool} (h : ¬ i < K * (N / K)) : sel K N cs c i = c i :=
if_neg h
/-- **The carry-select adder is an adder**: its equations give `CombLib.AddView`, with `sel` (the
multiplexed block carries, then the tail carries) as the carry function. -/
theorem csel_view (K W N : Nat) (hK : 0 < K) (hW : W = K + 1) (x y : BitVec N) (ci : Bool)
(g p c0 c1 cb cs c s : Nat → Bool) (cout : Bool)
(hg : ∀ i < N, g i = (x.getLsbD i && y.getLsbD i))
(hp : ∀ i < N, p i = (x.getLsbD i ^^ y.getLsbD i))
(hcb0 : cb 0 = ci)
(h00 : ∀ j < N / K, c0 (W * j) = false)
(h10 : ∀ j < N / K, c1 (W * j) = true)
(h0s : ∀ j < N / K, ∀ t < K,
c0 (W * j + t + 1) = (g (K * j + t) || (p (K * j + t) && c0 (W * j + t))))
(h1s : ∀ j < N / K, ∀ t < K,
c1 (W * j + t + 1) = (g (K * j + t) || (p (K * j + t) && c1 (W * j + t))))
(hsel : ∀ j < N / K, ∀ t < K,
cs (K * j + t) = (if cb j = true then c1 (W * j + t) else c0 (W * j + t)))
(hcbs : ∀ j < N / K,
cb (j + 1) = (if cb j = true then c1 (W * j + K) else c0 (W * j + K)))
(hcQ : c (K * (N / K)) = cb (N / K))
(htail : ∀ t < N % K, c (K * (N / K) + t + 1) =
(g (K * (N / K) + t) || (p (K * (N / K) + t) && c (K * (N / K) + t))))
(hsb : ∀ j < N / K, ∀ t < K, s (K * j + t) = (p (K * j + t) ^^ cs (K * j + t)))
(hst : ∀ t < N % K, s (K * (N / K) + t) = (p (K * (N / K) + t) ^^ c (K * (N / K) + t)))
(hout : cout = c N) :
AddView N x y ci (sel K N cs c) s cout := by
have hN : K * (N / K) + N % K = N := Nat.div_add_mod N K
have hlt : ∀ j < N / K, ∀ t < K, K * j + t < K * (N / K) := fun _ hj _ ht => blk_lt hj ht
have hc := csel_carry K W N hK hW g p c0 c1 cb (sel K N cs c) ci hcb0 h00 h10 h0s h1s
(fun j hj t ht => by rw [sel_lo (hlt j hj t ht)]; exact hsel j hj t ht)
hcbs
(by rw [sel_hi (Nat.lt_irrefl _)]; exact hcQ)
(fun t ht => by
rw [sel_hi (i := K * (N / K) + t + 1) (by omega), sel_hi (i := K * (N / K) + t) (by omega)]
exact htail t ht)
refine ⟨?_, fun i hi => ?_, fun i hi => ?_, ?_⟩
· rw [hc 0 (Nat.zero_le _)]
try rfl
· rw [hc (i + 1) (by omega), hc i (by omega)]
show (g (0 + i) || (p (0 + i) && rip g p 0 ci i)) = _
rw [Nat.zero_add, hg i hi, hp i hi]
generalize rip g p 0 ci i = r
generalize x.getLsbD i = u
generalize y.getLsbD i = v
cases r <;> cases u <;> cases v <;> rfl
· by_cases hlo : i < K * (N / K)
· have hj : i / K < N / K := by
first
| exact Nat.div_lt_of_lt_mul hlo
| exact (Nat.div_lt_iff_lt_mul hK).2 (by rw [Nat.mul_comm]; exact hlo)
have hi' : K * (i / K) + i % K = i := Nat.div_add_mod i K
have h := hsb (i / K) hj (i % K) (Nat.mod_lt i hK)
rw [hi'] at h
rw [h, hp i hi, sel_lo hlo]
· obtain ⟨t, rfl⟩ : ∃ t, i = K * (N / K) + t := ⟨i - K * (N / K), by omega⟩
rw [hst t (by omega), hp _ hi, sel_hi hlo]
· rw [hout, sel_hi (i := N) (by omega)]
end CselMath
namespace Submission
open LeanModels.Sv LeanModels.Sv.CombView LeanModels.Sv.CombLib LeanModels.Sv.CombEq SvGolf
/-- The rank: each site's depth (K = 6). Plain sites: cb[0] = cin (0), the unused rows Q of c0,
c1, cs (1, 2, 3), c[6Q] = cb[Q] (4), cout (5). -/
def cselRank (N : Nat) : SiteId → Nat
| .plain p => if p = 4 then N + 9 else if p = 5 then 3 * N + 30 else 0
| .fam l p [i] =>
if l = "g_pg" then 1
else if l = "g_blk" then (if p ≤ 1 then 1 else 8 + i.toNat)
else if l = "g_tail" then N + 10 + i.toNat
else 3 * N + 30
| .fam l p [j, t] => if l = "g_s" then 3 * N + 30 else if p ≤ 1 then t.toNat + 2 else 9 + j.toNat
| .fam _ _ _ => 0
set_option maxHeartbeats 1000000 in
theorem csel_struct (N : Nat) (hN : 1 ≤ N) :
CombDesign (design.sem N) ∧ Acyclic (design.sem N) ∧ SharedObl design.pdesign [N] := by
sv_structure (rank := cselRank N)
all_goals first
| (intros; omega)
| (intros; sv_rowmajor; omega)
| fail "unclosed structure obligation"
theorem csel_interface (N : Nat) (hN : 1 ≤ N) (hc : CombDesign (design.sem N)) :
SvGolf.Tasks.Add.task.Interface N (design.sem N) := by
sv_interface
theorem bv1_eq_ofBool (v : BitVec 1) : v = BitVec.ofBool (v.getLsbD 0) := by
apply BitVec.eq_of_getLsbD_eq
intro i hi
obtain rfl : i = 0 := by omega
simp
theorem knownBit_congr {st : SvState} {n : String} {a b : Nat} (h : KnownBit st n a) (e : a = b) :
KnownBit st n b := e ▸ h
/-- **The player's arithmetic, at the ports.** From the netlist's equations over `Bool` (in `Nat`
index form, block size `K`, chain stride `W = K + 1`) and the knownness of `s` and `cout`: the task's
outputs. -/
theorem csel_final (K W : Nat) (hK : 0 < K) (hW : W = K + 1) {N : Nat} {st : SvState}
{x y : BitVec N} {c0 : Bool}
(hA : SvState.lookup st "a" = some (LVec.ofBitVec x))
(hB : SvState.lookup st "b" = some (LVec.ofBitVec y))
(hCi : SvState.lookup st "cin" = some (LVec.ofBitVec (BitVec.ofBool c0)))
(hg : ∀ i < N, st.boolAt "g" i = (st.boolAt "a" i && st.boolAt "b" i))
(hp : ∀ i < N, st.boolAt "p" i = (st.boolAt "a" i ^^ st.boolAt "b" i))
(hcb0 : st.boolAt "cb" 0 = st.boolAt "cin" 0)
(h00 : ∀ j < N / K, st.boolAt "c0" (W * j) = false)
(h10 : ∀ j < N / K, st.boolAt "c1" (W * j) = true)
(h0s : ∀ j < N / K, ∀ t < K, st.boolAt "c0" (W * j + t + 1) =
(st.boolAt "g" (K * j + t) || (st.boolAt "p" (K * j + t) && st.boolAt "c0" (W * j + t))))
(h1s : ∀ j < N / K, ∀ t < K, st.boolAt "c1" (W * j + t + 1) =
(st.boolAt "g" (K * j + t) || (st.boolAt "p" (K * j + t) && st.boolAt "c1" (W * j + t))))
(hsel : ∀ j < N / K, ∀ t < K, st.boolAt "cs" (K * j + t) =
(if st.boolAt "cb" j = true then st.boolAt "c1" (W * j + t) else st.boolAt "c0" (W * j + t)))
(hcbs : ∀ j < N / K, st.boolAt "cb" (j + 1) =
(if st.boolAt "cb" j = true then st.boolAt "c1" (W * j + K) else st.boolAt "c0" (W * j + K)))
(hcQ : st.boolAt "c" (K * (N / K)) = st.boolAt "cb" (N / K))
(htail : ∀ t < N % K, st.boolAt "c" (K * (N / K) + t + 1) =
(st.boolAt "g" (K * (N / K) + t) ||
(st.boolAt "p" (K * (N / K) + t) && st.boolAt "c" (K * (N / K) + t))))
(hsb : ∀ j < N / K, ∀ t < K,
st.boolAt "s" (K * j + t) = (st.boolAt "p" (K * j + t) ^^ st.boolAt "cs" (K * j + t)))
(hst : ∀ t < N % K, st.boolAt "s" (K * (N / K) + t) =
(st.boolAt "p" (K * (N / K) + t) ^^ st.boolAt "c" (K * (N / K) + t)))
(eout : st.boolAt "cout" 0 = st.boolAt "c" N)
{vs : LVec} (hs : SvState.lookup st "s" = some vs) (hsw : vs.width = N)
(hks : ∀ k, k < N → KnownBit st "s" k)
{vc : LVec} (hc : SvState.lookup st "cout" = some vc) (hcw : vc.width = 1)
(hkc : KnownBit st "cout" 0) :
SvState.lookup st "s" = some (LVec.ofBitVec
(BitVec.ofNat N (x.toNat + y.toNat + c0.toNat))) ∧
SvState.lookup st "cout" = some (LVec.ofBitVec
(BitVec.ofNat 1 ((x.toNat + y.toNat + c0.toNat) / 2 ^ N))) := by
have hN : K * (N / K) + N % K = N := Nat.div_add_mod N K
have hvs := SvState.lookup_eq_ofBitVec_bv hs hsw hks
have hvc := SvState.lookup_eq_ofBitVec_bv hc hcw (fun k hk => by
obtain rfl : k = 0 := by omega
exact hkc)
rw [hvs] at hs
rw [hvc] at hc
have hxb : XF.bv 1 (st.boolAt "cout") = BitVec.ofBool (st.boolAt "cout" 0) := by
apply BitVec.eq_of_getLsbD_eq
intro i hi
obtain rfl : i = 0 := by omega
rw [XF.getLsbD_bv hi]; simp
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
have bci : st.boolAt "cin" 0 = c0 := by
rw [SvState.boolAt_ofBitVec hCi (by omega)]; simp
have hv : AddView N x y c0 (CselMath.sel K N (st.boolAt "cs") (st.boolAt "c"))
(XF.bv N (st.boolAt "s")).getLsbD (st.boolAt "cout" 0) :=
CselMath.csel_view K W N hK hW x y c0 (st.boolAt "g") (st.boolAt "p") (st.boolAt "c0")
(st.boolAt "c1") (st.boolAt "cb") (st.boolAt "cs") (st.boolAt "c")
(XF.bv N (st.boolAt "s")).getLsbD (st.boolAt "cout" 0)
(fun i hi => by rw [hg i hi, ba i hi, bb i hi])
(fun i hi => by rw [hp i hi, ba i hi, bb i hi])
(by rw [hcb0, bci]) h00 h10 h0s h1s hsel hcbs hcQ htail
(fun j hj t ht => by
have hlt := CselMath.blk_lt hj ht
rw [XF.getLsbD_bv (show K * j + t < N by omega), hsb j hj t ht])
(fun t ht => by rw [XF.getLsbD_bv (show K * (N / K) + t < N by omega), hst t ht]) eout
obtain ⟨h1, h2⟩ := add_spec hv
rw [hs, h1, hc, hxb, h2]
exact ⟨rfl, rfl⟩
/-- A row-major cell at non-negative coordinates is the flat index `i * W + j` (`sv_bridge … with
rmN_flat` compares a 2-D read with a `Nat`-indexed one). -/
theorem rmN_flat {W i j : Int} (hW : 0 ≤ W) (hi : 0 ≤ i) (hj : 0 ≤ j) :
rmN W i j = (i * W + j).toNat := by
obtain ⟨w, rfl⟩ := Int.eq_ofNat_of_zero_le hW
obtain ⟨a, rfl⟩ := Int.eq_ofNat_of_zero_le hi
obtain ⟨b, rfl⟩ := Int.eq_ofNat_of_zero_le hj
unfold rmN
first
| rw [← Int.natCast_mul, ← Int.natCast_add, Int.toNat_natCast, Int.toNat_natCast,
Int.toNat_natCast, Int.toNat_natCast]
| simp only [Int.toNat_natCast, ← Int.natCast_mul, ← Int.natCast_add]
theorem toNatW : (7 : Int).toNat = 7 := rfl
theorem toNatK : (6 : Int).toNat = 6 := rfl
/-- The one-bit literals as `sv_equations` hands them over. -/
theorem lit0 : ({ bits := #[Logic.l0] } : LVec).boolAt 0 = false := by
first | rfl | decide | simp [LVec.boolAt]
theorem lit1 : ({ bits := #[Logic.l1] } : LVec).boolAt 0 = true := by
first | rfl | decide | simp [LVec.boolAt]
set_option maxHeartbeats 1000000 in
theorem solution : Goal := by
intro N hN
have hN' : 1 ≤ N := hN.1
obtain ⟨hc, ha, hso⟩ := csel_struct N hN'
refine CombTask.satisfiedAt_of_comb (csel_interface N hN' hc) hc ha ?_
intro st heqs hs v hv
obtain ⟨x, y, ci, ⟨⟩⟩ := v
obtain ⟨c0, rfl⟩ : ∃ c0, ci = BitVec.ofBool c0 := ⟨_, bv1_eq_ofBool ci⟩
simp only [SvGolf.Tasks.Add.task, HoldAll_cons, HoldAll_nil, and_true, BitVec.toNat_ofBool]
at hv ⊢
obtain ⟨hA, hB, hCi⟩ := hv
sv_equations
· have hknown : ∃ vs, SvState.lookup st "s" = some vs ∧ vs.width = N := by
by_cases hQ : 6 ≤ N
· obtain ⟨⟨vs, hvs, hvsw⟩, -⟩ := known_s_1 0 (by omega) 0 (by omega)
exact ⟨vs, hvs, by omega⟩
· obtain ⟨⟨vs, hvs, hvsw⟩, -⟩ := known_s_2 0 (by omega)
exact ⟨vs, hvs, by omega⟩
obtain ⟨vs, hvs, hvsw⟩ := hknown
have hks : ∀ k, k < N → KnownBit st "s" k := by
intro k hk
by_cases hkq : k < 6 * (N / 6)
· exact knownBit_congr (known_s_1 (k / 6) (by omega) (k % 6) (by omega)).2 (by omega)
· exact knownBit_congr (known_s_2 (k - 6 * (N / 6)) (by omega)).2 (by omega)
obtain ⟨⟨vc, hvc, hvcw⟩, hkc⟩ := known_cout_1
refine csel_final 6 7 (by decide) rfl hA hB hCi ?_ ?_ ?_ ?_ ?_ ?_ ?_ ?_ ?_ ?_ ?_ ?_ ?_ ?_ hvs
hvsw hks hvc hvcw hkc
· sv_bridge eq_g_1
· sv_bridge eq_p_1
· sv_bridge eq_cb_1
· first
| sv_bridge eq_c0_2 with rmN, toNatW, toNatK, Int.toNat_natCast
| sv_bridge eq_c0_2 with rmN_flat
· first
| sv_bridge eq_c1_2 with rmN, toNatW, toNatK, Int.toNat_natCast
| sv_bridge eq_c1_2 with rmN_flat
· first
| sv_bridge eq_c0_3 with rmN, toNatW, toNatK, Int.toNat_natCast
| sv_bridge eq_c0_3 with rmN_flat
· first
| sv_bridge eq_c1_3 with rmN, toNatW, toNatK, Int.toNat_natCast
| sv_bridge eq_c1_3 with rmN_flat
· first
| sv_bridge eq_cs_2 with rmN, toNatW, toNatK, Int.toNat_natCast
| sv_bridge eq_cs_2 with rmN_flat
· first
| sv_bridge eq_cb_2 with rmN, toNatW, toNatK, Int.toNat_natCast
| sv_bridge eq_cb_2 with rmN_flat
· sv_bridge eq_c_1
· sv_bridge eq_c_2
· first
| sv_bridge eq_s_1 with rmN, toNatW, toNatK, Int.toNat_natCast
| sv_bridge eq_s_1 with rmN_flat
· sv_bridge eq_s_2
· sv_bridge eq_cout_1
all_goals first
| (intros; omega)
| rfl
| (intros; rfl)
| decide
| sv_cover
| (refine inKnown_ofBitVec hA ?_; omega)
| (refine inKnown_ofBitVec hB ?_; omega)
| (refine inKnown_ofBitVec hCi ?_; omega)
| fail "unclosed obligation"
/--
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.
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: 20 cells here, 83 at the scoring point. The critical path at N = 4 (8 cells, ending at s[3] rising, 970 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: 20 cells, 12 nand2_1, 8 xor2_1. There (measured, not ranked): 115.11 µm², 970 ps. Open the drawing on its own (SVG, 15.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: 31 nets, 30 cells, to s[15] rising at 2 479 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 8 cells to s[3] rising (970 ps there); each cell is a link to it in the drawing:
b[0]_46_xor2_1_47_nand2_1_48_nand2_1_50_nand2_1_51_nand2_1_53_nand2_1_54_nand2_1_61_xor2_1s[3]
| # | net | edge | driven by | cell | arrival (ps) | transition (ps) | stage (ps) |
|---|---|---|---|---|---|---|---|
| 0 | b[1] | fall | input port | – | 114.4 | 22.5 | |
| 1 | _159_ | rise | _208_ | xor2_1 | 302.2 | 190.2 | 187.8 |
| 2 | _164_ | fall | _213_ | nand2_1 | 369.2 | 53.7 | 67.0 |
| 3 | _165_ | rise | _214_ | nand2_1 | 459.8 | 80.2 | 90.6 |
| 4 | _167_ | fall | _216_ | nand2_1 | 509.4 | 45.9 | 49.6 |
| 5 | _168_ | rise | _217_ | nand2_1 | 596.6 | 80.1 | 87.2 |
| 6 | _170_ | fall | _219_ | nand2_1 | 646.2 | 45.9 | 49.6 |
| 7 | _171_ | rise | _220_ | nand2_1 | 733.4 | 80.1 | 87.2 |
| 8 | _173_ | fall | _222_ | nand2_1 | 783.0 | 45.9 | 49.6 |
| 9 | _174_ | rise | _223_ | nand2_1 | 870.2 | 80.1 | 87.2 |
| 10 | _176_ | fall | _225_ | nand2_1 | 919.8 | 45.9 | 49.6 |
| 11 | _177_ | rise | _226_ | nand2_1 | 1 008.1 | 81.8 | 88.3 |
| 12 | _178_ | fall | _227_ | nand2_1 | 1 061.8 | 31.4 | 53.7 |
| 13 | _179_ | rise | _228_ | nand2_1 | 1 142.6 | 80.3 | 80.8 |
| 14 | _180_ | fall | _229_ | nand2_1 | 1 195.9 | 53.7 | 53.3 |
| 15 | _181_ | rise | _230_ | nand2_1 | 1 286.6 | 80.2 | 90.7 |
| 16 | _183_ | fall | _232_ | nand2_1 | 1 336.2 | 45.9 | 49.6 |
| 17 | _184_ | rise | _233_ | nand2_1 | 1 423.4 | 80.1 | 87.2 |
| 18 | _186_ | fall | _235_ | nand2_1 | 1 473.0 | 45.9 | 49.6 |
| 19 | _187_ | rise | _236_ | nand2_1 | 1 560.2 | 80.1 | 87.2 |
| 20 | _189_ | fall | _238_ | nand2_1 | 1 609.8 | 45.9 | 49.6 |
| 21 | _190_ | rise | _239_ | nand2_1 | 1 696.9 | 80.1 | 87.1 |
| 22 | _126_ | fall | _241_ | nand2_1 | 1 746.5 | 45.9 | 49.6 |
| 23 | _127_ | rise | _242_ | nand2_1 | 1 833.7 | 80.1 | 87.2 |
| 24 | _129_ | fall | _244_ | nand2_1 | 1 883.3 | 45.9 | 49.6 |
| 25 | _130_ | rise | _245_ | nand2_1 | 1 970.5 | 80.1 | 87.2 |
| 26 | _132_ | fall | _247_ | nand2_1 | 2 020.1 | 45.9 | 49.6 |
| 27 | _133_ | rise | _248_ | nand2_1 | 2 107.3 | 80.1 | 87.2 |
| 28 | _135_ | fall | _250_ | nand2_1 | 2 156.9 | 45.9 | 49.6 |
| 29 | _136_ | rise | _251_ | nand2_1 | 2 244.1 | 80.1 | 87.2 |
| 30 | _124_ | rise | _273_ | xor2_1 | 2 479.3 | 247.2 | 235.2 |
Cells
at N = 16 · scored
| cell | count | µm² each | µm² in all | share |
|---|---|---|---|---|
xor2_1 | 28 | 8.7584 | 245.2352 | 51.7% |
nand2_1 | 49 | 3.7536 | 183.9264 | 38.8% |
or2_1 | 3 | 6.2560 | 18.7680 | 4.0% |
mux2_1 | 1 | 11.2608 | 11.2608 | 2.4% |
xnor2_1 | 1 | 8.7584 | 8.7584 | 1.8% |
and2_1 | 1 | 6.2560 | 6.2560 | 1.3% |
| total | 83 | 474.2048 |
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)
| cell | count | GE each | GE total |
|---|---|---|---|
NAND | 49 | 1.000 | 49.000 |
XOR | 28 | 2.000 | 56.000 |
OR | 3 | 1.333 | 3.999 |
AND | 1 | 1.333 | 1.333 |
MUX | 1 | 2.333 | 2.333 |
XNOR | 1 | 2.000 | 2.000 |
| total | 83 | 114.665 |
The record
- task
add- player
- select-six
- assisted by
- Claude Opus 5.5 (Anthropic)
- submitted
- 2026-11-05
- notes
- Made by an AI agent (Claude Opus, Anthropic) in the maintainers' newcomer study on 2026-11-05, as a new design rather than a variation of a shipped one; the agent chose the player name. The design is a carry-select adder with 6-bit blocks, for every N >= 1: Q = N/6 blocks and an R = N%6 ripple tail, both integer localparams. Block j keeps two ripple chains in row j of the 2-D arrays c0 (block carry-in 0) and c1 (block carry-in 1); a multiplexer row cs picks the carry into each bit by the block carry cb[j], the block carries ripple through one multiplexer per block, and the sum bits are p ^ carry, written in two trailing loops. The block size and the placement of the sum assigns were chosen by sweeps scored with score/ppa.py. The proof takes the sv_structure / sv_interface / sv_equations / sv_bridge / sv_cover route with its own rank function over the site ids; its arithmetic is plain Lean, generic in the block size (a ripple carry restarted at any position, two chain lemmas and a multiplexer lemma), and shows that the selected carries are the ripple carries, so the design meets CombLib.AddView and add_spec finishes the proof. The comments in both files are the agent's. Proof ported by the maintainers for the 2026-11-06 range change; design unchanged.
- scoring point
- N = 16
- design
solution.svsha25652e1d40feb1823cf5a143674deb33d6d77ea611f898ee83b81fa3725abbebee9- proof
Solution.leansha256ecb1b45b3b17e9fb9a86083f6bc821e6ce8cbae22ae702df25e8b6cecb78765b- canonical text
- sha256
86215572ee2ce6b6f4e134debd96e9d4cd4b62cdfa579fcc96cf068ccd661c68 - record hash
d20bd7d9a6e6c181bf3ce92b55d7bb610fd7bd7dada03f3bab5ca0bb000dc315- downloads
d20bd7d9a6e6.svd20bd7d9a6e6.leand20bd7d9a6e6.json- previous frontier
- none: this is the task's first submission
The proof
- code lines
- 332
- build time
- 28.65 s
- axioms
Classical.choice,Quot.sound,propext- tactics seen
decide,induction,omega,simp,sv_cover,sv_equations,sv_interface,sv_rowmajor,sv_structure- imports
Submission.Design,SvGolf.Comb,LeanModels.Sv.CombEqTactic,LeanModels.Sv.CombLib
Checker and flow
Checked under the pinned flow of this site's boards.
- Yosys
Yosys 0.53 (git sha1 53c22ab7c0ced80861c7536c5dae682c30fb5834, clang++ 17.0.0 -fPIC -O3)- Verilator
Verilator 5.042 2025-11-02 rev UNKNOWN.REV- Lean toolchain
leanprover/lean4:v4.33.0-rc1- svGolf revision
88a13caa17ef6765f2c63bf22455ab39071df750- lean-surfaces revision
88a13caa17ef6765f2c63bf22455ab39071df750- canonicalization script
bbd11227d45dc1365f27597fd45c565fe721ba2648450588fc4fd983d00b367b- scoring script
ea58219d8ef14c3bad265218ee266ccdb709ac9d4dc5258edc34d51ee83a0445- GE table
6a29196a3f4b58241f4c9af650c9141e4632b737214d563a11ba142a725c4bea- extractor
4930696021c588c75dc890034c534c95560ad9a28a4955b87d1f8d68b5edbc0c- canonical RTLIL
8c7d4b7ea7ac23b7d36520b2c742dcd08655d29414d5fdd7440df5b095ab8180
the checker's files (sha256)
| file | sha256 |
|---|---|
check.py | e708084f72f955b0ae1aaabe469dba3eacc5a91484a1ed81bcc073b60f3c0b6d |
check/ | 3145eb578ca71e452eff05a398e8ad04673e6c72ca5343b234cb22e2cefe65a6 |
check/ | fb51d72510b515460866c2798aad3c6f55295536e627b92854c3580af215bb91 |
check/ | 73856a45905724544247ad29042bdc2f1778271ec07b15cdb3a5f214685165c1 |
check/ | 076db01a9791bcc25ee84e9b0033604a4b675f00b212eab0c864078e6a4748b7 |
check/ | 4e1ee2110c63f24e033dd689c929ea686ddf10f4db23b9057c6fb711394c7558 |
lakefile.toml | f35a88404367d22906effc14d644aac9a9cad47dfd3b704ea8fec88acfaf9c4d |
lean-toolchain | 62c2d9c0fc1ec4c67e151c11eff41ca004ef38e179cf9476c230406e6defedef |
score/ | f18c14c23a14d55db63e202f42cc424dc84c1dd1c337f132ad60859dc9d5d33a |
score/ | bbd11227d45dc1365f27597fd45c565fe721ba2648450588fc4fd983d00b367b |
score/ | 95332451ae3754226fb87b46027385b46a91121e7eb9ae25b302bce756d90475 |
score/ | 6a29196a3f4b58241f4c9af650c9141e4632b737214d563a11ba142a725c4bea |
score/ | 54b93ca63888009d0671c8978e8ab7bf4fb8ce303bc78d3b4e1f83d946cc3201 |
score/ | 912031912c3e3f6790725161753e95415df34846bd8c27dde3eb707747ddbcbe |
score/ | 93547b343dbac135737e98516b1004a780e1cf2f579e1bd7fed82c908c6d4125 |
score/ | 4b8688ba3f4d67681658e5944afe878592adf588cd416a8b47597ebe06deb045 |
score/ | d66a6086a37a97f5985266057051979fb6a8a3c557d3ef9c42acc1b5e41502d8 |
score/ | 71d2e3b82f8813ec64a58f0a46909ca23dfab377db44eda6f294b713f45ae781 |
score/ | 11952f61058051c9e8de91a801b672d07e66a29308e978156c04ffd9f0dbef45 |
score/ | ea58219d8ef14c3bad265218ee266ccdb709ac9d4dc5258edc34d51ee83a0445 |
score/ | c3874dbfc85921a87df6eacdfc6b2c9a0e2b34edd5738951e0d8bdb51f4558b2 |
Post-synthesis check
ls-sv's reading of the design and the scored netlist, simulated on the same vectors, agreed on every one. The vectors are a sample of the inputs: a guard against the two tools disagreeing, not a proof.
- mode
- full
- vectors
- 4132
- full set
- 4132
- mismatches
- 0
- point
- N=16
- seed
4348e13c54f2015c5f7ef8bd27a4f22ed7e1bcd4e984869b354d99d2b45f12e7- salted
- no
Area against time
- submission
- proved reference
- baseline (not proved)
- baseline with a banned operator
- the Pareto front of the entries
- this design
- par's score: area × time = 1202 µm²·ns
the designs as numbers, by score
| design | kind | area (µm²) | time (ps) | score (µm²·ns) |
|---|---|---|---|---|
| select-six · this design | submission | 474.20 | 2 479 | 1176 |
add_n | proved reference · par | 460.44 | 2 611 | 1202 |
add_n_byhand | proved reference | 460.44 | 2 611 | 1202 |
add_proc | proved reference | 460.44 | 2 611 | 1202 |
add_mux | proved reference | 460.44 | 2 611 | 1202 |
add_proc_m | proved reference | 460.44 | 2 611 | 1202 |
add_n_kogge_stone | baseline, not proved | 641.87 | 2 064 | 1325 |
add_n_sklansky | baseline, not proved | 541.77 | 2 451 | 1328 |
plus | baseline, not proved · banned operator | 497.98 | 3 143 | 1565 |
cmp_carry | baseline, not proved · banned operator | 857.07 | 1 965 | 1684 |
- submission
- proved reference
- baseline (not proved)
- baseline with a banned operator
- the Pareto front of the entries
- this design
- par's score: area × time = 3.94 µm²·ns
the designs as numbers, by score
| design | kind | area (µm²) | time (ps) | score (µm²·ns) |
|---|---|---|---|---|
add_n_sklansky | baseline, not proved | 8.048 | 487 | 3.919 |
add_n | proved reference · par | 6.998 | 563 | 3.94 |
add_n_byhand | proved reference | 6.998 | 563 | 3.94 |
add_proc | proved reference | 6.998 | 563 | 3.94 |
add_mux | proved reference | 6.998 | 563 | 3.94 |
add_proc_m | proved reference | 6.998 | 563 | 3.94 |
| select-six · this design | submission | 7.188 | 561 | 4.032 |
add_n_kogge_stone | baseline, not proved | 9.477 | 432 | 4.094 |
cmp_carry | baseline, not proved · banned operator | 12.553 | 336 | 4.218 |
plus | baseline, not proved · banned operator | 7.436 | 568 | 4.224 |
Reproduce and verify
From svgolf/, with the pinned tools (reproduce a score lists them):
# the ranked numbers, at the scoring point (from svgolf/, with the pinned Yosys and liberty)
python3.12 score/ppa.py d20bd7d9a6e6c181bf3ce92b55d7bb610fd7bd7dada03f3bab5ca0bb000dc315.sv --task tasks/add --path \
--json out.json --netlist scored.v --repaired-netlist repaired.v
Expect, in out.json:
- score.rtlil_sha256
8c7d4b7ea7ac23b7d36520b2c742dcd08655d29414d5fdd7440df5b095ab8180- ppa.netlist_sha256
8336e9565c6f0afb8d7124305394f022f1878b9b9c33f5fee277995dda94041c- ppa.repair.netlist_sha256
202476f3c9450be314ecab23dfe870ff3ad300ea4671a0e0647e20981380f779- ppa.area_e4um2_sky130
4742048- ppa.delay_ps_sky130
2479
The schematic and the critical-path data are site/schematics/add/52e1d40feb1823cf.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 f9c03540b33661ba…).
From svgolf/, with the pinned tools (reproduce a score lists them):
# 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 d20bd7d9a6e6c181bf3ce92b55d7bb610fd7bd7dada03f3bab5ca0bb000dc315.sv --task tasks/add --pdk asap7 --no-mapped \
--json out.json
Expect, in out.json:
- ppa.netlist_sha256
8336e9565c6f0afb8d7124305394f022f1878b9b9c33f5fee277995dda94041c- ppa.repair.netlist_sha256
28254366c960942c7454283d58f262a05a2339152c15aab707fbf403b8981ba0- ppa.area_e5um2_asap7
718794- ppa.delay_ps_asap7
561
leaderboard/asap7/add.json keeps these numbers; python3.12 leaderboard/leaderboard.py asap7 --rescore --task add weighs every design of the task again and compares, bit for bit.
Verify the record
Download the three files, then from svgolf/:
# re-run the whole check on this record's files (the cost of the original check)
H=d20bd7d9a6e6c181bf3ce92b55d7bb610fd7bd7dada03f3bab5ca0bb000dc315
mkdir -p rec/add && cp "$H.json" "$H.sv" "$H.lean" rec/add/
python3.12 leaderboard/leaderboard.py verify --data-dir rec --task add --full
verify --offline checks only the hashes and the arithmetic; plain verify also rescores the design (no Lean; seconds for most designs, a minute or more for the largest), on sky130 and on ASAP7.