Record 8427744607a0
Recorded by the maintainers from the proved reference matmul_g1: its design and its proof matmul_g1.lean (653 code lines). N × N parallel multiply-accumulate units and one step counter: A and B are captured into shift registers at start, and done rises N steps later (latency N). The record shows that reference passing the whole checker. It is par, the bar to beatpar, the bar to beat, not a player's entry, and it is not ranked among the submissions (proved references).
Area
87 173.61µm²
87 173.6064 µm² exactly
Beats 50% · matches 0%
1 of 2 other players beaten, 0 equal.
Performance · time
42 784ps
4 cycles × 10 696 ps; the period is the latest arrival at an output, or at a flip-flop's D plus setup, ideal clock
Beats 0% · matches 0%
0 of 2 other players beaten, 0 equal.
Score · area × time
3 729 636µm²·ns
87 173.6064 µm² × 42 784 ps
Beats 0% · matches 0%
0 of 2 other players beaten, 0 equal.
- vs par
- this design is par
- on this page
- 5th of 5 designs
- board
- par, the bar to beat: never ranked
- Pareto
- dominated on this page by vacuity-check and vacuity-check
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.
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 matmul's other players at this revision (2 players, 4 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 5 designs of the area-time plot (1 reference and 4 submissions): not a ranking.
Area
1 334.726µm²
1 334.72610 µm² exactly
Beats 50% · matches 0%
1 of 2 other players beaten, 0 equal.
- vs par
- this design is par
- on this page
- 3rd of 5 designs
- cells
- 14 383
- flops
- 802
- repair
- 6 buffers, 0 tie cells
- GE
- 20 783.15 extra
Performance · time
6 156ps
4 cycles × 1 539 ps; the period is the latest arrival at an output, or at a flip-flop's D plus setup, ideal clock
Beats 0% · matches 0%
0 of 2 other players beaten, 0 equal.
- vs par
- this design is par
- on this page
- 5th of 5 designs
- Fmax-eq.
- 162.4 MHz derived
- unit depth
- 61 extra
Score · area × time
8217µm²·ns
1 334.72610 µm² × 6 156 ps
Beats 0% · matches 0%
0 of 2 other players beaten, 0 equal.
- vs par
- this design is par
- on this page
- 5th of 5 designs
- board
- par, the bar to beat: never ranked
- Pareto
- dominated on this page by vacuity-check and vacuity-check
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. 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 matmul's other players at this revision (2 players, 4 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 5 designs of the area-time plot (1 reference and 4 submissions): not a ranking.
The sources
// matmul G1 reference: N*N parallel multiply-accumulate units and one step counter k.
// After `start`, cycle k (k = 0 .. N-1) adds A[i][k]*B[k][j] (signed, wrapping at AccW bits) to
// C[i][j]; `done` rises after the N-th step and stays 1 (C held) until the next `start`.
// Flip-flops only (the FF-only profile): every register is written by nonblocking assignments in
// one always_ff, at a static window. `start` captures A and B into shift registers; each step
// shifts A by one entry and B by one row, so every select is static (no run-time part-select).
module matmul #(parameter int N = 4, parameter int W = 8, parameter int AccW = 32) (
input logic clk, rst,
input logic start,
input logic [N*N*W-1:0] A, B,
output logic done,
output logic [N*N*AccW-1:0] C
);
logic [31:0] k;
logic busy;
logic [N*N*W-1:0] a_sh, b_sh; // A >> k entries, B >> k rows: A[i][k] at row i's entry 0, B[k][j] at entry j
always_ff @(posedge clk)
if (rst) begin
busy <= 1'b0;
done <= 1'b0;
k <= 32'd0;
end else if (start) begin
busy <= 1'b1;
done <= 1'b0;
k <= 32'd0;
a_sh <= A;
b_sh <= B;
end else if (busy) begin
k <= k + 32'd1;
a_sh <= a_sh >> W;
b_sh <= b_sh >> (N*W);
if (k == N - 1) begin
busy <= 1'b0;
done <= 1'b1;
end
end
for (genvar i = 0; i < N; i++) begin : g_row
for (genvar j = 0; j < N; j++) begin : g_col
always_ff @(posedge clk)
if (start)
C[(i*N+j)*AccW +: AccW] <= '0;
else if (busy)
// Each operand sign-extended by AccW copies of its sign bit; the sum is reduced mod 2^AccW
// by the assignment (the operands' extra high bits never reach C).
C[(i*N+j)*AccW +: AccW] <= C[(i*N+j)*AccW +: AccW]
+ {{AccW{a_sh[i*N*W + W - 1]}}, a_sh[i*N*W +: W]}
* {{AccW{b_sh[j*W + W - 1]}}, b_sh[j*W +: W]};
end
end
endmodule
matmul_g1.lean (653 code lines)
-- svgolf-latency: 4
import Submission.Design
import LeanModels.Sv.Gen2
import LeanModels.Sv.Hier2Gen
import LeanModels.Sv.SeqProfile
import LeanModels.Sv.SeqFamily
import LeanModels.Sv.Known2
import LeanModels.Sv.Lib.BitVecArith
import LeanModels.Sv.CombViewArith
import LeanModels.Sv.Mono2
import SvGolf.SeqV2
/-!
# Reference solution for task `matmul`, G1: `N²` multiply-accumulate units and one step counter
On `start`, `reference/matmul_g1.sv` captures `A`, `B` into shift registers and clears `C`; for `N`
busy cycles PE `(i, j)` adds `A[i][k]·B[k][j]` (sign-extended, mod `2^AccW`); `done` shows in the
snapshot `N` cycles after the start's (`lat = N`). Proved at every in-range `(N, W, AccW)`:
1. **Agreement** (`mm_sem_eq`): the loaded family `design.sem N W AccW` is the hand-built `mmD`.
2. **G0's clock step** (`mm_profile`, `mm_cycle`): one cycle of the loaded design, from any state
of its shape, any schedule, is the evaluated commit `mmNext`.
3. **The invariant on `k`** (`pe_step`, `next_step`): busy cycle `t` turns every accumulator's
`Σ_{k' < t} A[i][k']·B[k'][j]` into the sum to `t + 1`, mod `2^AccW` (`accC`).
4. **The spec's arithmetic** (`product_eq`, `accC_N`): `product` is `accC` at `t = N`.
5. **The protocol** (`model`): the phases of `TxTask.satisfiedAt_of_phases`.
6. **txtask-v2** (section 8): rule 2 (`mm_powerUp`: every power-up state has the model's shape, so
the claim and termination hold from every power-up), rule 1 (`mm_moore`: every process an
`always_ff`, so no combinational dependency) and rule 3 (`mm_xsafe`), then `solution`.
The design-independent lemmas are in the library: the packed view and its commit (`packBV`,
`LVec.insertEntries_ofBitVec`) in `LeanModels.Sv.SeqFamily`, the clock step (`ffDisjB_of_pairwise`,
`flatMap_ffUpd`) in `LeanModels.Sv.SeqProfile`, the window arithmetic and the datapath's `BitVec` facts in
`LeanModels.Sv.Lib.NatArith` and `LeanModels.Sv.Lib.BitVecArith`.
-/
namespace Submission
open LeanModels.Sv SvGolf
/-- The latency: `done` shows `N` snapshots after the start's. -/
def lat (N _W _AccW : Nat) : Nat := N
/-! ## 1. Agreement: the stages of `instantiate2` -/
/-- A 32-bit constant. -/
abbrev c32 (v : Int) : Expr2 := .lit (LVec.ofInt 32 v)
/-- `{{AccW{x[lo+W-1]}}, x[lo +: W]}`: entry `x[lo +: W]` sign-extended by `AccW` bits. -/
def sext (x : String) (AccW lo W : Nat) : Expr2 :=
.concat #[.repl AccW (.concat #[.slice (.ident x) (lo + W - 1) 1]), .slice (.ident x) lo W]
/-- The PE at `(i, j)`: clear on `start`, else accumulate `a_sh[i][0] * b_sh[0][j]` while `busy`. -/
def mmPE (N W AccW i j : Nat) : Process2 :=
.alwaysFF "clk" (.ifStmt (.ident "start")
(.nbaAssign (.slice "C" ((i * N + j) * AccW) AccW) (.lit (LVec.replicate AccW .l0)))
(some (.ifStmt (.ident "busy")
(.nbaAssign (.slice "C" ((i * N + j) * AccW) AccW)
(.resize AccW (.binary .add (.resize (AccW + W) (.slice (.ident "C") ((i * N + j) * AccW) AccW))
(.binary .mul (sext "a_sh" AccW (i * N * W) W) (sext "b_sh" AccW (j * W) W)))))
none)))
/-- A nonblocking write of a constant. -/
abbrev nb (n : String) (v : LVec) : Stmt2 := .nbaAssign (.ident n) (.lit v)
/-- The control: `busy`, `done`, the step counter `k`, the operand shift registers. -/
def mmCtrl (N W : Nat) : Process2 :=
.alwaysFF "clk" (.ifStmt (.ident "rst")
(.block #[nb "busy" ⟨#[.l0]⟩, nb "done" ⟨#[.l0]⟩, nb "k" (LVec.replicate 32 .l0)])
(some (.ifStmt (.ident "start")
(.block #[nb "busy" ⟨#[.l1]⟩, nb "done" ⟨#[.l0]⟩, nb "k" (LVec.replicate 32 .l0),
.nbaAssign (.ident "a_sh") (.ident "A"), .nbaAssign (.ident "b_sh") (.ident "B")])
(some (.ifStmt (.ident "busy")
(.block #[.nbaAssign (.ident "k") (.binary .add (.ident "k") (c32 1)),
.nbaAssign (.ident "a_sh") (.binary (.shr false) (.ident "a_sh") (c32 W)),
.nbaAssign (.ident "b_sh") (.binary (.shr false) (.ident "b_sh") (.binary .mul (c32 N) (c32 W))),
.ifStmt (.binary .eq (.ident "k") (.binary .sub (c32 N) (c32 1)))
(.block #[nb "busy" ⟨#[.l0]⟩, nb "done" ⟨#[.l1]⟩]) none])
none)))))
/-- The declarations, `A`/`B`/`a_sh`/`b_sh` at `wa` bits and `C` at `wc`. -/
def mmDecls (wa wc : Nat) : List Decl :=
[{ name := "clk", width := 1, isInput := true }, { name := "rst", width := 1, isInput := true },
{ name := "start", width := 1, isInput := true }, { name := "A", width := wa, isInput := true },
{ name := "B", width := wa, isInput := true }, { name := "done", width := 1, isOutput := true },
{ name := "C", width := wc, isOutput := true }, { name := "k", width := 32 },
{ name := "busy", width := 1 }, { name := "a_sh", width := wa }, { name := "b_sh", width := wa }]
/-- The PEs, row-major. -/
def mmPEs (N W AccW : Nat) : List Process2 :=
(List.range N).flatMap fun i => (List.range N).map fun j => mmPE N W AccW i j
def mmD (N W AccW : Nat) : Design2 where
name := "matmul"
decls := (mmDecls (N * N * W) (N * N * AccW)).toArray
processes := (mmCtrl N W :: mmPEs N W AccW).toArray
/-! ### The stages of `instantiate2` -/
def mmScope (N W AccW : Int) : IEnv :=
{ vals := [("N", N), ("W", W), ("AccW", AccW)], pwidths := [("N", 32), ("W", 32), ("AccW", 32)] }
def mmEnv (N W AccW : Int) : IEnv :=
{ mmScope N W AccW with dims := dimTable (mmScope N W AccW) design.pdesign.decls }
theorem mm_paramEnv (N W AccW : Int) :
design.pdesign.paramEnv2 [N, W, AccW] = .ok (mmEnv N W AccW) := rfl
def mmElems : List (String × Nat) :=
["clk", "rst", "start", "A", "B", "done", "C", "k", "busy", "a_sh", "b_sh"].map (·, 1)
theorem mm_decls (N W AccW : Int) :
buildDecls (mmEnv N W AccW) design.pdesign.decls =
.ok (mmDecls ((N * N * W - 1).natAbs + 1) ((N * N * AccW - 1).natAbs + 1)) := by
simp [design.pdesign, buildDecls, instDecl, PType.width_packed_cons, PDim.width, evalInt,
mmEnv, mmScope, List.lookup, mmDecls, Gen2.except_ok_bind]
theorem mm_elems (N W AccW : Int) : buildElems (mmEnv N W AccW) design.pdesign.decls = .ok mmElems := by
simp [design.pdesign, buildElems, PType.width_packed_cons, PDim.width, evalInt, mmEnv,
mmScope, List.lookup, mmElems, Gen2.except_ok_bind]
/-- The instantiation environment. -/
abbrev mmE (N W AccW : Nat) : IEnv :=
(mmEnv N W AccW).withDecls (mmDecls (N * N * W) (N * N * AccW))
theorem mm_ctrl (N W AccW : Nat) :
design.pdesign.processes.map (instProcess2 (mmE N W AccW) mmElems) = [mmCtrl N W] := by
sv2_inst_simp [instStmt2, instStmts2, design.pdesign, mmEnv, mmScope, mmDecls, mmElems, mmCtrl,
dimTable, dimTableStep, PType.width_packed_cons, PDim.width, ctxOpWidth2, rigidMax2,
PExpr.ctxPropagates, PExpr.constLeaf, PExpr.negGenvarWidened, LVec.width, divOperandWidth2,
instDivOperand2, fitDivOperand2]
try and_intros
all_goals first | rfl | decide
/-- The PE template (the process inside `g_row`/`g_col`). -/
def peT : PProcess :=
match design.pdesign.generates with
| [.genFor _ _ _ _ _ _ [.genFor _ _ _ _ _ _ [.process p]]] => p
| _ => .alwaysFF "" (.block [])
/-- The `sv2_inst_simp` set of the PE template (with the window facts in scope). `instExpr2` tries
to constant-fold the `*` of the two sign-extending concatenations, and the fold must reduce to its
error; `sv2_inst_simp`'s default set carries the constant folder (`ctype`/`evalAt` and their
workers), so of the evaluators only `evalSized`, which is outside that set, is named here. -/
macro "pe_simp" : tactic => `(tactic| sv2_inst_simp [peT, evalSized, Gen2.except_pure,
Lib.min_add_sub_one_self, Lib.max_add_sub_one_self, Lib.add_sub_one_sub_add_one, instStmt2, design.pdesign,
mmEnv, mmScope, mmDecls, mmElems, mmPE, sext,
dimTable, dimTableStep, PType.width_packed_cons, PDim.width, ctxOpWidth2, rigidMax2,
PExpr.constLeaf, PExpr.negGenvarWidened, LVec.width, *])
theorem pe_inst (N W AccW i j : Nat) (hN : 1 ≤ N) (hW : 1 ≤ W) (hA : 1 ≤ AccW) (hi : i < N)
(hj : j < N) :
instProcess2 (((mmE N W AccW).bindVal "i" i).bindVal "j" j) mmElems peT =
mmPE N W AccW i j := by
have pW := Nat.mul_pos (Nat.mul_pos hN hN) hW
have pA := Nat.mul_pos (Nat.mul_pos hN hN) hA
have hW0 : (0 : Int) < W := by omega
have hA0 : (0 : Int) < AccW := by omega
have hNW : (1 : Int) ≤ N * N * W := by rw [← Int.natCast_mul, ← Int.natCast_mul]; omega
have hNA : (1 : Int) ≤ N * N * AccW := by rw [← Int.natCast_mul, ← Int.natCast_mul]; omega
have hcA : ((N : Int) * N * W - 1).natAbs + 1 = N * N * W := by
rw [← Int.natCast_mul, ← Int.natCast_mul]; exact Lib.natAbs_sub_one_add_one pW
have hcC : ((N : Int) * N * AccW - 1).natAbs + 1 = N * N * AccW := by
rw [← Int.natCast_mul, ← Int.natCast_mul]; exact Lib.natAbs_sub_one_add_one pA
have hC := Lib.window_bounds_of_natCast (((i : Int) * N + j) * AccW) (N * N * AccW)
(Lib.rowMajor_window_le (w := AccW) hi hj) hA (by simp) (by simp)
have ha := Lib.window_bounds_of_natCast ((i : Int) * N * W) (N * N * W)
(Lib.rowMajor_window_le (N := N) (w := W) (j := 0) hi (by omega)) hW (by simp) (by simp)
have hb := Lib.window_bounds_of_natCast ((j : Int) * W) (N * N * W)
(Lib.rowMajor_window_le (M := N) (w := W) (i := 0) (by omega) hj) hW (by simp) (by simp)
have hA1 : 0 < AccW := hA
have hW1 : 0 < W := hW
have hWne : ¬ W = 0 := by omega
have hmode : (PartMode.up == PartMode.range) = false := rfl
have hlt1 : ¬ AccW + W < AccW := by omega
have hle1 : ¬ AccW + W ≤ AccW := by omega
pe_simp
-- `simp` cannot use `instExpr2`'s `resize` equation (measured): unfold it by hand.
repeat (unfold instExpr2; pe_simp)
/-- `for (g = 0; g < N; g++) body`. -/
def genN (lbl g : String) (body : List GenItem) : GenItem :=
.genFor (some lbl) g (.int 0) (.binary .slt (.genvarRef g) (.paramRef "N" none))
(.unary .inc (.genvarRef g)) (some 4) body
theorem mm_gens_src : design.pdesign.generates = [genN "g_row" "i" [genN "g_col" "j" ([peT].map .process)]] :=
rfl
/-- **The nested generate expands to the row-major PEs** (`mul2d_pp_expand`'s route). -/
theorem mm_gens (N W AccW F : Nat) (hN : 1 ≤ N) (hW : 1 ≤ W) (hA : 1 ≤ AccW) (hF : N < F) :
expandList2 (mmE N W AccW) mmElems F design.pdesign.generates = .ok (mmPEs N W AccW) := by
have hNv : (mmE N W AccW).vals.lookup "N" = some (N : Int) := rfl
rw [mm_gens_src]
apply expandList2_singleton_ok
rw [genN, GenItem.expand2_genFor_ascending_flatMap (N := (N : Int))
(f := fun v => (intRange 0 N).flatMap fun w =>
[peT].map (instProcess2 (((mmE N W AccW).bindVal "i" v).bindVal "j" w) mmElems))
(evalInt_int 0) (fun _ => isInt_paramRef_of (by rw [vals_lookup_bindVal_ne _ (by decide), hNv]))
(fun _ => evalInt_paramRef_of (by rw [vals_lookup_bindVal_ne _ (by decide), hNv])) (by simpa using hF)]
· simp only [intRange_zero, List.flatMap_map, Int.sub_zero, Int.toNat_natCast, List.map_cons,
List.map_nil, Gen2.flatMap_singleton_map, mmPEs]
congr 1
simp only [List.flatMap_def]
congr 1
refine List.map_congr_left fun i hi => List.map_congr_left fun j hj => ?_
exact pe_inst N W AccW i j hN hW hA (List.mem_range.mp hi) (List.mem_range.mp hj)
· intro v _ _
apply expandList2_singleton_ok
have hv : ((mmE N W AccW).bindVal "i" v).vals.lookup "N" = some (N : Int) := by
simp [IEnv.bindVal, List.lookup, hNv]
rw [genN, GenItem.expand2_genFor_ascending_processes (N := (N : Int)) (evalInt_int 0)
(fun _ => isInt_paramRef_of (by rw [vals_lookup_bindVal_ne _ (by decide), hv]))
(fun _ => evalInt_paramRef_of (by rw [vals_lookup_bindVal_ne _ (by decide), hv]))
(by simpa using hF)]
simp
theorem mm_instantiate2 (N W AccW : Nat) (hN : 1 ≤ N) (hW : 1 ≤ W) (hA : 1 ≤ AccW) :
design.pdesign.instantiate2 [(N : Int), W, AccW] (PDesign.surfaceFuel [(N : Int), W, AccW]) =
.ok (design.pdesign.assemble2 (mmEnv N W AccW) (mmDecls (N * N * W) (N * N * AccW)) mmElems
(mmPEs N W AccW)) := by
have hd := mm_decls N W AccW
rw [← Int.natCast_mul N N, ← Int.natCast_mul (N * N) W,
Lib.natAbs_sub_one_add_one (Nat.mul_pos (Nat.mul_pos hN hN) hW), ← Int.natCast_mul (N * N) AccW,
Lib.natAbs_sub_one_add_one (Nat.mul_pos (Nat.mul_pos hN hN) hA)] at hd
refine PDesign.instantiate2_of_ok rfl (mm_paramEnv _ _ _) hd (mm_elems _ _ _)
(mm_gens N W AccW _ hN hW hA ?_)
simp only [PDesign.surfaceFuel, List.map, List.sum_cons, List.sum_nil, Int.natAbs_natCast]
exact Nat.lt_of_lt_of_le (Nat.lt_of_lt_of_le (by omega) (Nat.le_mul_self _)) (Nat.le_add_right _ _)
theorem mm_processes (N W AccW : Nat) (hN : 1 ≤ N) (hW : 1 ≤ W) (hA : 1 ≤ AccW) :
(design.pdesign.instantiateD2 [(N : Int), W, AccW]).processes.toList =
mmCtrl N W :: mmPEs N W AccW := by
rw [PDesign.instantiateD2_of_ok (mm_instantiate2 N W AccW hN hW hA), PDesign.assemble2_processes,
mm_ctrl]
simp [design.pdesign]
/-- Two PEs write disjoint windows of `C`. -/
theorem pe_disj {N W AccW i j i' j' : Nat} (h : i * N + j < i' * N + j') :
∀ a ∈ (mmPE N W AccW i j).procWins, ∀ b ∈ (mmPE N W AccW i' j').procWins,
a.overlaps b = false := by
have := rowMajor_le (K := AccW) h
simp [mmPE, Process2.procWins, Stmt2.drvWins, Stmt2.drvWinsOpt, Lhs2.drvWins]
omega
theorem mm_pe_mem {N W AccW : Nat} {p : Process2} (hp : p ∈ mmPEs N W AccW) :
∃ i j, p = mmPE N W AccW i j := by
simp only [mmPEs, List.mem_flatMap, List.mem_map, List.mem_range] at hp
obtain ⟨i, -, j, -, rfl⟩ := hp
exact ⟨i, j, rfl⟩
/-- The control and the PEs drive pairwise disjoint windows. -/
theorem mm_pairwise (N W AccW : Nat) : (mmCtrl N W :: mmPEs N W AccW).Pairwise
fun p q => ∀ a ∈ p.procWins, ∀ b ∈ q.procWins, a.overlaps b = false := by
refine List.Pairwise.cons (fun p hp => ?_) (pairwise_flatMap_range (fun i _ => ?_) ?_)
· obtain ⟨i, j, rfl⟩ := mm_pe_mem hp
simp [mmCtrl, mmPE, Process2.procWins, Stmt2.drvWins, Stmt2.drvWinsOpt, Stmt2.drvWinsList,
Lhs2.drvWins]
· exact List.pairwise_map.mpr (List.pairwise_lt_range.imp_of_mem fun _ _ h => pe_disj (by omega))
· intro i i' hii _ a ha b hb
simp only [List.mem_map, List.mem_range] at ha hb
obtain ⟨j, hj, rfl⟩ := ha
obtain ⟨j', -, rfl⟩ := hb
exact pe_disj (by have := rowMajor_le (K := N) hii; omega)
theorem mm_driversOk (N W AccW : Nat) :
driversOk design.pdesign.declNames design.pdesign.inputNames (mmCtrl N W :: mmPEs N W AccW) = true := by
apply driversOk_of_ff ?_ ?_ (mm_pairwise N W AccW)
· intro p hp
rcases List.mem_cons.mp hp with rfl | hp
· exact ⟨_, _, rfl⟩
· obtain ⟨i, j, rfl⟩ := mm_pe_mem hp; exact ⟨_, _, rfl⟩
· simp only [List.mem_flatMap, List.mem_cons]
rintro n ⟨p, hp | hp, hn⟩
all_goals first | subst hp | obtain ⟨i, j, rfl⟩ := mm_pe_mem hp
all_goals revert n; simp [mmCtrl, mmPE, Process2.procWrites, Process2.procWins, Stmt2.drvWins,
Stmt2.drvWinsOpt, Stmt2.drvWinsList, Lhs2.drvWins]; all_goals decide
/-- **The loaded family is `mmD`**, at every `(N, W, AccW)` with positive parameters. -/
theorem mm_sem_eq (N W AccW : Nat) (hN : 1 ≤ N) (hW : 1 ≤ W) (hA : 1 ≤ AccW) :
design.sem N W AccW = mmD N W AccW := by
have h1 : design.sem N W AccW = design.pdesign.instantiateD2 [(N : Int), W, AccW] := by
show design.pdesign.instantiateGD2 [(N : Int), W, AccW] = _
exact PDesign.instantiateGD2_of_driversOk (by rw [mm_processes N W AccW hN hW hA]; exact mm_driversOk N W AccW)
rw [h1, PDesign.instantiateD2_of_ok (mm_instantiate2 N W AccW hN hW hA)]
simp only [PDesign.assemble2, mm_ctrl, mmD]
simp [design.pdesign]
/-! ## 2. G0's clock step for the loaded family, at every `(N, W, AccW)` -/
/-- **`mmD` is in G0's decidable FF-only profile at every `(N, W, AccW)`** (symbolic, not `rfl`). -/
theorem mm_profile (N W AccW : Nat) : (mmD N W AccW).ffOnlyProfileB = true := by
simp only [Design2.ffOnlyProfileB, Bool.and_eq_true]
refine ⟨?_, ffDisjB_of_pairwise (by simpa [mmD] using mm_pairwise N W AccW)⟩
simp [mmD, mmCtrl, mmPEs, mmPE, nb, List.all_flatMap, List.all_map, Stmt2.nbaOnly, Stmt2.optNbaOnly,
Stmt2.listNbaOnly, Stmt2.nbaStaticB, Stmt2.optNbaStaticB, Stmt2.listNbaStaticB, Lhs2.window?,
Design2.declWidth, Design2.inputNames, Stmt2.drvWins, Stmt2.drvWinsOpt, Stmt2.drvWinsList,
Lhs2.drvWins, mmDecls]
/-- The 11 signals of `mmD`, in declaration order. -/
def MS (ck r s a b dn c k bz ash bsh : LVec) : SvState :=
[("clk", ck), ("rst", r), ("start", s), ("A", a), ("B", b), ("done", dn), ("C", c), ("k", k),
("busy", bz), ("a_sh", ash), ("b_sh", bsh)]
theorem apply_mm (N W AccW : Nat) (e : SvState) (ck r s a b dn c k bz ash bsh : LVec) :
applyInputs2 (mmD N W AccW) e (MS ck r s a b dn c k bz ash bsh) =
MS (appIn e "clk" ck) (appIn e "rst" r) (appIn e "start" s) (appIn e "A" a) (appIn e "B" b)
dn c k bz ash bsh := by
unfold applyInputs2
rw [← Array.foldl_toList]
cases h1 : SvState.lookup e "clk" <;> cases h2 : SvState.lookup e "rst" <;>
cases h3 : SvState.lookup e "start" <;> cases h4 : SvState.lookup e "A" <;>
cases h5 : SvState.lookup e "B" <;>
simp [mmD, mmDecls, MS, appIn, h1, h2, h3, h4, h5, SvState.set]
/-- A nonblocking write of `v` to the whole of `n` (old value `old`). -/
abbrev wr (n : String) (old v : LVec) : String × Nat × LVec := (n, 0, v.resizeU old.width)
/-- The control's updates, from the pre-edge values. -/
def ctrlQ (N W : Nat) (r s a b dn k bz ash bsh : LVec) : NbaQueue2 :=
if r.condTrue then [wr "busy" bz ⟨#[.l0]⟩, wr "done" dn ⟨#[.l0]⟩, wr "k" k (LVec.replicate 32 .l0)]
else if s.condTrue then [wr "busy" bz ⟨#[.l1]⟩, wr "done" dn ⟨#[.l0]⟩,
wr "k" k (LVec.replicate 32 .l0), wr "a_sh" ash a, wr "b_sh" bsh b]
else if bz.condTrue then
if (LVec.ofLogic (k.eqLogical ((LVec.ofInt 32 (N : Int)).sub (LVec.ofInt 32 1)))).condTrue then
[wr "k" k (k.add (LVec.ofInt 32 1)), wr "a_sh" ash (ash.shr (LVec.ofInt 32 (W : Int))),
wr "b_sh" bsh (bsh.shr ((LVec.ofInt 32 (N : Int)).mul (LVec.ofInt 32 (W : Int)))),
wr "busy" bz ⟨#[.l0]⟩, wr "done" dn ⟨#[.l1]⟩]
else [wr "k" k (k.add (LVec.ofInt 32 1)), wr "a_sh" ash (ash.shr (LVec.ofInt 32 (W : Int))),
wr "b_sh" bsh (bsh.shr ((LVec.ofInt 32 (N : Int)).mul (LVec.ofInt 32 (W : Int))))]
else []
/-- `{{AccW{v[lo+W-1]}}, v[lo +: W]}`, evaluated. -/
def sxv (v : LVec) (AccW lo W : Nat) : LVec :=
LVec.concatMany #[LVec.replVec AccW (LVec.concatMany #[v.extract (lo + W - 1) 1]), v.extract lo W]
/-- The new accumulator `(i, j)` of a busy cycle, evaluated. -/
def peV (N W AccW i j : Nat) (c ash bsh : LVec) : LVec :=
(((c.extract ((i * N + j) * AccW) AccW).resizeU (AccW + W)).add
((sxv ash AccW (i * N * W) W).mul (sxv bsh AccW (j * W) W))).resizeU AccW
/-- The PE's update, from the pre-edge values. -/
def peQ (N W AccW : Nat) (s bz c ash bsh : LVec) (i j : Nat) : NbaQueue2 :=
if s.condTrue then [("C", (i * N + j) * AccW, (LVec.replicate AccW .l0).resizeU AccW)]
else if bz.condTrue then [("C", (i * N + j) * AccW, (peV N W AccW i j c ash bsh).resizeU AccW)]
else []
theorem ctrl_run (N W F : Nat) (hF : 30 ≤ F) (ck r s a b dn c k bz ash bsh : LVec) :
runEdgeProcess2 F (MS ck r s a b dn c k bz ash bsh) [] (mmCtrl N W) =
.ok (MS ck r s a b dn c k bz ash bsh, ctrlQ N W r s a b dn k bz ash bsh) := by
obtain ⟨f, rfl⟩ : ∃ f, F = f + 30 := ⟨F - 30, by omega⟩
unfold ctrlQ
by_cases h1 : r.condTrue = true <;> by_cases h2 : s.condTrue = true <;>
by_cases h3 : bz.condTrue = true <;>
by_cases h4 : (LVec.ofLogic (k.eqLogical ((LVec.ofInt 32 (N : Int)).sub (LVec.ofInt 32 1)))).condTrue = true <;>
simp [mmCtrl, nb, c32, runEdgeProcess2, execStmt2, execStmts2, evalExpr2, resolveLhs2, readSignal, MS,
SvState.lookup, evalBinOp, wr, h1, h2, h3, h4]
theorem pe_run (N W AccW i j F : Nat) (hF : 30 ≤ F) (ck r s a b dn c k bz ash bsh : LVec) :
runEdgeProcess2 F (MS ck r s a b dn c k bz ash bsh) [] (mmPE N W AccW i j) =
.ok (MS ck r s a b dn c k bz ash bsh, peQ N W AccW s bz c ash bsh i j) := by
obtain ⟨f, rfl⟩ : ∃ f, F = f + 30 := ⟨F - 30, by omega⟩
unfold peQ
by_cases h1 : s.condTrue = true <;> by_cases h2 : bz.condTrue = true <;>
simp [mmPE, sext, runEdgeProcess2, execStmt2, evalExpr2, evalExprs2, resolveLhs2, readSignal, MS,
SvState.lookup, evalBinOp, peV, sxv, h1, h2]
/-- One cycle's next state, from the pre-edge (inputs applied) values. -/
def mmNext (N W AccW : Nat) (ck r s a b dn c k bz ash bsh : LVec) : SvState :=
commitNba2 (MS ck r s a b dn c k bz ash bsh) (ctrlQ N W r s a b dn k bz ash bsh ++
(List.range N).flatMap fun i => (List.range N).flatMap (peQ N W AccW s bz c ash bsh i))
/-- **One cycle of the loaded design is G0's clock step**, evaluated (any schedule, any stimulus
entry, any state of the design's shape, every fuel from 30). -/
theorem mm_cycle (N W AccW : Nat) (hN : 1 ≤ N) (hW : 1 ≤ W) (hA : 1 ≤ AccW) (σ : ScheduleOracle)
(e : SvState) (ck r s a b dn c k bz ash bsh : LVec) (kk F : Nat) (hF : 30 ≤ F) :
cycleStep2 (design.sem N W AccW) σ F e (MS ck r s a b dn c k bz ash bsh) kk =
.ok (mmNext N W AccW (appIn e "clk" ck) (appIn e "rst" r) (appIn e "start" s) (appIn e "A" a)
(appIn e "B" b) dn c k bz ash bsh, kk + 3) := by
have hedge : ∀ p ∈ (mmD N W AccW).processes.toList, p.isEdgePhase = true := by
intro p hp
have hm : p ∈ mmCtrl N W :: mmPEs N W AccW := by simpa [mmD] using hp
rcases List.mem_cons.mp hm with rfl | hm
· rfl
· obtain ⟨i, j, rfl⟩ := mm_pe_mem hm; rfl
rw [mm_sem_eq N W AccW hN hW hA, cycleStep2_profile (mm_profile N W AccW) σ e _ kk ?_ ?_ (by omega),
apply_mm, clockStep, flatMap_ffUpd F _ hedge]
· simp only [mmD, List.toList_toArray, List.flatMap_cons, ctrl_run N W F hF, mmPEs, List.flatMap_assoc,
List.flatMap_map, pe_run N W AccW _ _ F hF, mmNext]
· intro i p hp
rw [apply_mm]
have hm : p ∈ mmCtrl N W :: mmPEs N W AccW :=
List.mem_of_getElem? (by simpa [mmD] using hp : (mmCtrl N W :: mmPEs N W AccW)[i]? = some p)
rcases List.mem_cons.mp hm with rfl | hm
· exact ⟨_, ctrl_run N W F hF _ _ _ _ _ _ _ _ _ _ _⟩
· obtain ⟨i, j, rfl⟩ := mm_pe_mem hm
exact ⟨_, pe_run N W AccW i j F hF _ _ _ _ _ _ _ _ _ _ _⟩
· rw [apply_mm]; simp [MS]
/-! ## 3. The busy cycle on known values: the invariant on `k` -/
attribute [local simp] condTrue_knownBit
/-- `Σ_{k' < t} A[i][k']·B[k'][j]` in the integers (`Tasks.Matmul.dot` at `t = N`). -/
def psum (N W : Nat) {n : Nat} (A B : BitVec n) (t i j : Nat) : Int :=
(List.range t).foldl (fun acc k =>
acc + Tasks.Matmul.entry W A (i * N + k) * Tasks.Matmul.entry W B (k * N + j)) 0
/-- The accumulators after `t` busy cycles: entry `(i, j)` is `psum t i j` mod `2^AccW`. -/
def accC (N W AccW : Nat) {n : Nat} (A B : BitVec n) (t : Nat) : LVec :=
LVec.ofBitVec (packBV AccW (N * N) fun r => BitVec.ofInt AccW (psum N W A B t (r / N) (r % N)))
theorem psum_succ (N W : Nat) {n : Nat} (A B : BitVec n) (t i j : Nat) : psum N W A B (t + 1) i j =
psum N W A B t i j + Tasks.Matmul.entry W A (i * N + t) * Tasks.Matmul.entry W B (t * N + j) := by
simp [psum, List.range_succ, List.foldl_append]
/-- **PE `(i, j)` in a busy cycle** adds `A[i][t]·B[t][j]` to its accumulator. -/
theorem pe_step {N W AccW : Nat} (hW : 1 ≤ W) (A B : BitVec (N * N * W)) {t i j : Nat} (hi : i < N)
(hj : j < N) : peQ N W AccW (knownBit false) (knownBit true) (accC N W AccW A B t) (.ofBitVec (A >>> (t * W)))
(.ofBitVec (B >>> (t * (N * W)))) i j = [("C", (i * N + j) * AccW,
.ofBitVec (BitVec.ofInt AccW (psum N W A B (t + 1) ((i * N + j) / N) ((i * N + j) % N))))] := by
have hd : (i * N + j) / N = i := by
rw [Nat.add_comm, Nat.add_mul_div_right _ _ (by omega), Nat.div_eq_of_lt hj, Nat.zero_add]
have hm : (i * N + j) % N = j := by rw [Nat.add_comm, Nat.add_mul_mod_self_right, Nat.mod_eq_of_lt hj]
have hr : i * N + j < N * N := by have := Lib.rowMajor_window_le (w := 1) hi hj; simp at this; omega
have ha : i * N * W + W ≤ N * N * W := by
have := Lib.rowMajor_window_le (N := N) (w := W) (j := 0) hi (by omega); simpa using this
have hb : j * W + W ≤ N * N * W := by
have := Lib.rowMajor_window_le (M := N) (w := W) (i := 0) (by omega) hj; simpa using this
have hc := Lib.rowMajor_window_le (w := AccW) hi hj
have e1 : t * W + i * N * W = (i * N + t) * W := by rw [Nat.add_mul, Nat.add_comm]
have e2 : t * (N * W) + j * W = (t * N + j) * W := by rw [Nat.add_mul, Nat.mul_assoc]
simp (disch := omega) only [peQ, condTrue_knownBit, Bool.false_eq_true, ↓reduceIte, peV, sxv, accC,
LVec.extract_ofBitVec, extractLsb'_packBV, hd, hm, LVec.concatMany_singleton, LVec.concatMany_cons_cons,
LVec.replVec_ofBitVec, LVec.concat_ofBitVec, LVec.ofBitVec_replicate_append_eq_signExtend hW,
LVec.mul_ofBitVec, LVec.resizeU_ofBitVec, LVec.add_ofBitVec, BitVec.setWidth_eq, Lib.setWidth_add_mul_signExtend,
Lib.extractLsb'_ushiftRight, e1, e2, psum_succ, BitVec.ofInt_add, Tasks.Matmul.entry]
/-! ## 4. The spec's arithmetic: `product` is the packed vector of the wrapped dot products -/
theorem product_eq (N W AccW : Nat) {n : Nat} (a b : BitVec n) : Tasks.Matmul.product N W AccW a b =
packBV AccW (N * N) fun r => BitVec.ofInt AccW (Tasks.Matmul.dot N W a b (r / N) (r % N)) :=
(packBV_eq_ofNat ..).symm
/-- After `N` busy cycles the accumulators hold the product (`psum N` is `dot`). -/
theorem accC_N (N W AccW : Nat) {n : Nat} (A B : BitVec n) :
accC N W AccW A B N = LVec.ofBitVec (Tasks.Matmul.product N W AccW A B) := by
rw [product_eq]; rfl
/-! ## 5. The registers through a cycle: the commit, then the invariant on `k` -/
section commit
variable {ck r s a b dn c k bz ash bsh : LVec} {lo : Nat} {v : LVec}
theorem nb_busy : nbaStep (MS ck r s a b dn c k bz ash bsh) ("busy", lo, v) =
MS ck r s a b dn c k (bz.insertAt lo v) ash bsh := by simp [nbaStep, MS, SvState.lookup, SvState.set]
theorem nb_done : nbaStep (MS ck r s a b dn c k bz ash bsh) ("done", lo, v) =
MS ck r s a b (dn.insertAt lo v) c k bz ash bsh := by simp [nbaStep, MS, SvState.lookup, SvState.set]
theorem nb_k : nbaStep (MS ck r s a b dn c k bz ash bsh) ("k", lo, v) =
MS ck r s a b dn c (k.insertAt lo v) bz ash bsh := by simp [nbaStep, MS, SvState.lookup, SvState.set]
theorem nb_ash : nbaStep (MS ck r s a b dn c k bz ash bsh) ("a_sh", lo, v) =
MS ck r s a b dn c k bz (ash.insertAt lo v) bsh := by simp [nbaStep, MS, SvState.lookup, SvState.set]
theorem nb_bsh : nbaStep (MS ck r s a b dn c k bz ash bsh) ("b_sh", lo, v) =
MS ck r s a b dn c k bz ash (bsh.insertAt lo v) := by simp [nbaStep, MS, SvState.lookup, SvState.set]
end commit
/-- Committing writes of `C` only. -/
theorem commit_C {ck r s a b dn k bz ash bsh : LVec} : ∀ {q : NbaQueue2}, (∀ u ∈ q, u.1 = "C") → ∀ c,
commitNba2 (MS ck r s a b dn c k bz ash bsh) q =
MS ck r s a b dn (q.foldl (fun acc u => acc.insertAt u.2.1 u.2.2) c) k bz ash bsh
| [], _, _ => rfl
| ⟨n, lo, v⟩ :: q, h, c => by
obtain rfl : n = "C" := h _ List.mem_cons_self
rw [commitNba2_cons, List.foldl_cons, ← commit_C (fun u hu => h u (List.mem_cons_of_mem _ hu))]
congr 1
/-- The PEs' updates, row-major, when PE `(i, j)` writes window `i·N + j` with `g (i·N + j)`. -/
theorem rowMajor_map {N AccW : Nat} {q : Nat → Nat → NbaQueue2} (g : Nat → LVec)
(h : ∀ i j, i < N → j < N → q i j = [("C", (i * N + j) * AccW, g (i * N + j))]) :
((List.range N).flatMap fun i => (List.range N).flatMap (q i)) =
(List.range (N * N)).map fun r => ("C", r * AccW, g r) := by
suffices ∀ M, M ≤ N → ((List.range M).flatMap fun i => (List.range N).flatMap (q i)) =
(List.range (M * N)).map (fun r => ("C", r * AccW, g r)) from this N (Nat.le_refl _)
intro M; induction M with
| zero => intro _; simp
| succ M ih =>
intro hM
rw [List.range_succ, List.flatMap_append, ih (by omega), Nat.succ_mul, List.range_add,
List.map_append, List.map_map]
simp only [List.flatMap_cons, List.flatMap_nil, List.append_nil]
rw [CombView.flatMap_congr' fun j hj => h M j (by omega) (List.mem_range.mp hj)]
congr 1
induction (List.range N) with
| nil => rfl
| cons a l ih' => rw [List.flatMap_cons, ih']; rfl
theorem commit_map {ck r s a b dn k bz ash bsh : LVec} (AccW : Nat) (g : Nat → LVec) (n : Nat) (c : LVec) :
commitNba2 (MS ck r s a b dn c k bz ash bsh) ((List.range n).map fun r => ("C", r * AccW, g r)) =
MS ck r s a b dn (LVec.insertEntries AccW g n c) k bz ash bsh := by
rw [commit_C (by simp), List.foldl_map]; rfl
/-- The control in a busy cycle `t < N`: `k` and the shift registers step; the last one ends. -/
theorem ctrl_busy {N W : Nat} (hN : 1 ≤ N) (hW : 1 ≤ W) (hr : N * N * W < 2 ^ 31) (A B : BitVec (N * N * W))
{t : Nat} (ht : t < N) (ck a b c : LVec) :
commitNba2 (MS ck (knownBit false) (knownBit false) a b (knownBit false) c (.ofBitVec (BitVec.ofNat 32 t))
(knownBit true) (.ofBitVec (A >>> (t * W))) (.ofBitVec (B >>> (t * (N * W)))))
(ctrlQ N W (knownBit false) (knownBit false) a b (knownBit false) (.ofBitVec (BitVec.ofNat 32 t)) (knownBit true)
(.ofBitVec (A >>> (t * W))) (.ofBitVec (B >>> (t * (N * W))))) =
MS ck (knownBit false) (knownBit false) a b (knownBit (decide (t + 1 = N))) c (.ofBitVec (BitVec.ofNat 32 (t + 1)))
(knownBit (decide (t + 1 ≠ N))) (.ofBitVec (A >>> (t * W + W))) (.ofBitVec (B >>> (t * (N * W) + N * W))) := by
have h1 : N * W ≤ N * N * W := by rw [Nat.mul_assoc N N W]; exact Nat.le_mul_of_pos_left _ hN
have hNW : N * W < 2 ^ 32 := by simp only [Nat.reducePow] at hr ⊢; omega
have hW' : W < 2 ^ 32 := Nat.lt_of_le_of_lt (Nat.le_mul_of_pos_left W hN) hNW
have hN' : N < 2 ^ 32 := Nat.lt_of_le_of_lt (Nat.le_mul_of_pos_right N hW) hNW
have hyW : (BitVec.ofNat 32 W).toNat = W := by rw [BitVec.toNat_ofNat, Nat.mod_eq_of_lt hW']
have hyNW : (BitVec.ofNat 32 N * BitVec.ofNat 32 W).toNat = N * W := by
rw [BitVec.toNat_mul, BitVec.toNat_ofNat, BitVec.toNat_ofNat, Nat.mod_eq_of_lt hW', Nat.mod_eq_of_lt hN',
Nat.mod_eq_of_lt hNW]
have h1' : LVec.ofInt 32 1 = LVec.ofBitVec 1#32 := by decide
simp only [ctrlQ, wr, condTrue_knownBit, Bool.false_eq_true, ↓reduceIte, LVec.ofInt_eq_ofBitVec, BitVec.ofInt_natCast,
BitVec.ofInt_ofNat, h1', LVec.mul_ofBitVec, LVec.sub_ofBitVec, LVec.eqLogical_ofBitVec, LVec.ofLogic_ofBool,
decide_eq_true_eq, Lib.ofNat_eq_ofNat_sub_one_iff ht (Nat.le_of_lt hN'), LVec.width_ofBitVec, LVec.shr_ofBitVec,
LVec.resizeU_ofBitVec, BitVec.setWidth_eq, hyW, hyNW, ← BitVec.shiftRight_add]
by_cases hf : t + 1 = N <;>
simp [hf, commitNba2_cons, commitNba2_nil, nb_k, nb_ash, nb_bsh, nb_busy, nb_done,
LVec.insertAt_zero_of_width_eq, LVec.resizeU_one_l0, LVec.resizeU_one_l1, BitVec.ofNat_add_ofNat]
/-- **The invariant on `k`, one step**: from the state `t < N` busy cycles after a start with
operands `A`, `B` (`acc[i][j] = Σ_{k' < t} A[i][k']·B[k'][j] mod 2^AccW`, `k = t`, the operands
shifted by `t` entries), one cycle without `start` reaches the state at `t + 1`; `done` rises (and
`busy` falls) exactly when `t + 1 = N`. -/
theorem next_step {N W AccW : Nat} (hN : 1 ≤ N) (hW : 1 ≤ W) (hr : N * N * W < 2 ^ 31)
(A B : BitVec (N * N * W)) {t : Nat} (ht : t < N) (ck a b : LVec) :
mmNext N W AccW ck (knownBit false) (knownBit false) a b (knownBit false) (accC N W AccW A B t)
(.ofBitVec (BitVec.ofNat 32 t)) (knownBit true) (.ofBitVec (A >>> (t * W)))
(.ofBitVec (B >>> (t * (N * W)))) =
MS ck (knownBit false) (knownBit false) a b (knownBit (decide (t + 1 = N))) (accC N W AccW A B (t + 1))
(.ofBitVec (BitVec.ofNat 32 (t + 1))) (knownBit (decide (t + 1 ≠ N))) (.ofBitVec (A >>> ((t + 1) * W)))
(.ofBitVec (B >>> ((t + 1) * (N * W)))) := by
have hq := rowMajor_map (N := N) (AccW := AccW) (q := peQ N W AccW (knownBit false) (knownBit true)
(accC N W AccW A B t) (.ofBitVec (A >>> (t * W))) (.ofBitVec (B >>> (t * (N * W)))))
(fun r => LVec.ofBitVec (BitVec.ofInt AccW (psum N W A B (t + 1) (r / N) (r % N))))
(fun i j hi hj => pe_step hW A B hi hj)
rw [mmNext, commitNba2_append, ctrl_busy hN hW hr A B ht, hq, commit_map,
LVec.insertEntries_ofBitVec _ _ (by simp [accC]), Nat.succ_mul, Nat.succ_mul]
rfl
/-! ## 6. Shape and termination -/
/-- The register widths are the declared ones. -/
def Wd (N W AccW : Nat) (dn c k bz ash bsh : LVec) : Prop :=
dn.width = 1 ∧ c.width = N * N * AccW ∧ k.width = 32 ∧ bz.width = 1 ∧ ash.width = N * N * W ∧
bsh.width = N * N * W
/-- A snapshot of the design's shape. -/
def I (N W AccW : Nat) (st : SvState) : Prop :=
∃ ck r s a b dn c k bz ash bsh, st = MS ck r s a b dn c k bz ash bsh ∧ Wd N W AccW dn c k bz ash bsh
theorem peQ_C (N W AccW : Nat) (s bz c ash bsh : LVec) :
∀ u ∈ (List.range N).flatMap fun i => (List.range N).flatMap (peQ N W AccW s bz c ash bsh i), u.1 = "C" := by
intro u hu
simp only [List.mem_flatMap] at hu
obtain ⟨_, _, _, _, hu⟩ := hu
unfold peQ at hu
by_cases h1 : s.condTrue = true <;> by_cases h2 : bz.condTrue = true <;> simp_all
/-- **Every cycle keeps the design's shape** (any values, `x` included). -/
theorem mmNext_I (N W AccW : Nat) (ck r s a b dn c k bz ash bsh : LVec) (hw : Wd N W AccW dn c k bz ash bsh) :
I N W AccW (mmNext N W AccW ck r s a b dn c k bz ash bsh) := by
obtain ⟨h1, h2, h3, h4, h5, h6⟩ := hw
simp only [mmNext, commitNba2_append, ctrlQ]
by_cases c1 : r.condTrue = true <;> by_cases c2 : s.condTrue = true <;> by_cases c3 : bz.condTrue = true <;>
by_cases c4 : (LVec.ofLogic (k.eqLogical ((LVec.ofInt 32 (N : Int)).sub (LVec.ofInt 32 1)))).condTrue = true <;>
simp only [c1, c2, c3, c4, Bool.false_eq_true, ↓reduceIte, commitNba2_cons, commitNba2_nil, nb_busy, nb_done, nb_k,
nb_ash, nb_bsh, wr] <;>
rw [commit_C (peQ_C N W AccW _ _ _ _ _)] <;>
exact ⟨_, _, _, _, _, _, _, _, _, _, _, rfl, by simp [Wd, width_foldl_insertAt, *]⟩
theorem mm_noq (N W AccW : Nat) : (mmD N W AccW).processes.toList.any Process2.hasQualifiedCase = false := by
simp [mmD, mmCtrl, mmPEs, mmPE, nb, Process2.hasQualifiedCase, Stmt2.hasQualifiedCase,
Stmt2.listHasQualifiedCase]
theorem mm_init (N W AccW : Nat) (hN : 1 ≤ N) (hW : 1 ≤ W) (hA : 1 ≤ AccW) :
I N W AccW (initState2 (design.sem N W AccW)) := by
rw [mm_sem_eq N W AccW hN hW hA]
refine ⟨.xVec 1, .xVec 1, .xVec 1, .xVec (N * N * W), .xVec (N * N * W), .xVec 1, .xVec (N * N * AccW),
.xVec 32, .xVec 1, .xVec (N * N * W), .xVec (N * N * W), ?_, by simp [Wd]⟩
simp [initState2, mmD, mmDecls, MS]
/-- **Termination**: every schedule and every stimulus completes. -/
theorem mm_terminates (N W AccW : Nat) (hN : 1 ≤ N) (hW : 1 ≤ W) (hA : 1 ≤ AccW) :
Terminates2 (design.sem N W AccW) := by
refine Terminates2.of_invariant (I N W AccW) (mm_init N W AccW hN hW hA) (fun σ => ⟨30, ?_⟩)
(by rw [mm_sem_eq N W AccW hN hW hA]; exact mm_noq N W AccW)
rintro e _ kk ⟨ck, r, s, a, b, dn, c, k, bz, ash, bsh, rfl, hw⟩
exact ⟨_, _, mm_cycle N W AccW hN hW hA σ e ck r s a b dn c k bz ash bsh kk 30 (Nat.le_refl _),
mmNext_I N W AccW _ _ _ _ _ dn c k bz ash bsh hw⟩
/-! ## 7. The protocol: the phases of `TxTask.satisfiedAt_of_phases`, and the statement -/
theorem MS_inj {ck r s a b dn c k bz ash bsh ck' r' s' a' b' dn' c' k' bz' ash' bsh' : LVec}
(h : MS ck r s a b dn c k bz ash bsh = MS ck' r' s' a' b' dn' c' k' bz' ash' bsh') :
dn = dn' ∧ c = c' ∧ k = k' ∧ bz = bz' ∧ ash = ash' ∧ bsh = bsh' := by
simp only [MS, List.cons.injEq, Prod.mk.injEq, true_and, and_true] at h
exact h.2.2.2.2.2
/-- A cycle without `rst` and `start` from an idle state (`busy` = 0) changes no register. -/
theorem next_idle (N W AccW : Nat) (ck a b dn c k ash bsh : LVec) :
mmNext N W AccW ck (knownBit false) (knownBit false) a b dn c k (knownBit false) ash bsh =
MS ck (knownBit false) (knownBit false) a b dn c k (knownBit false) ash bsh := by
have h : ((List.range N).flatMap fun i => (List.range N).flatMap
(peQ N W AccW (knownBit false) (knownBit false) c ash bsh i)) = [] := by simp [List.flatMap_eq_nil_iff, peQ]
rw [mmNext, h]; simp [ctrlQ]; rfl
/-- A reset cycle clears `busy` and `done`. -/
theorem next_reset {N W AccW : Nat} (ck s a b dn c k bz ash bsh : LVec) (hw : Wd N W AccW dn c k bz ash bsh) :
∃ c' k', mmNext N W AccW ck (knownBit true) s a b dn c k bz ash bsh =
MS ck (knownBit true) s a b (knownBit false) c' k' (knownBit false) ash bsh := by
obtain ⟨h1, -, -, h4, -, -⟩ := hw
simp only [mmNext, commitNba2_append, ctrlQ, condTrue_knownBit, ↓reduceIte, commitNba2_cons, commitNba2_nil, nb_busy,
nb_done, nb_k, wr, h1, h4, LVec.resizeU_one_l0]
rw [commit_C (peQ_C N W AccW _ _ _ _ _)]
exact ⟨_, _, by rw [LVec.insertAt_zero_of_width_eq (v := dn) (p := knownBit false) (by simp [h1]),
LVec.insertAt_zero_of_width_eq (v := bz) (p := knownBit false) (by simp [h4])]⟩
/-- A start cycle captures the operands and clears the accumulators: the state at `t = 0`. -/
theorem next_start {N W AccW : Nat} (A B : BitVec (N * N * W)) (ck dn c k bz ash bsh : LVec)
(hw : Wd N W AccW dn c k bz ash bsh) :
mmNext N W AccW ck (knownBit false) (knownBit true) (.ofBitVec A) (.ofBitVec B) dn c k bz ash bsh =
MS ck (knownBit false) (knownBit true) (.ofBitVec A) (.ofBitVec B) (knownBit false) (accC N W AccW A B 0)
(.ofBitVec (BitVec.ofNat 32 0)) (knownBit true) (.ofBitVec (A >>> (0 * W)))
(.ofBitVec (B >>> (0 * (N * W)))) := by
obtain ⟨h1, h2, h3, h4, h5, h6⟩ := hw
have hq := rowMajor_map (N := N) (AccW := AccW) (q := peQ N W AccW (knownBit true) bz c ash bsh)
(fun r => LVec.ofBitVec (BitVec.ofInt AccW (psum N W A B 0 (r / N) (r % N))))
(fun i j _ _ => by simp [peQ, psum])
rw [mmNext, commitNba2_append, hq]
simp only [ctrlQ, condTrue_knownBit, Bool.false_eq_true, ↓reduceIte, commitNba2_cons, commitNba2_nil, nb_busy,
nb_done, nb_k, nb_ash, nb_bsh, wr, h1, h3, h4, h5, h6, LVec.resizeU_one_l0, LVec.resizeU_one_l1, LVec.replicate_l0,
LVec.resizeU_ofBitVec, BitVec.setWidth_eq]
rw [commit_map, LVec.insertEntries_ofBitVec _ _ h2, LVec.insertAt_zero_of_width_eq (by simp [h1]),
LVec.insertAt_zero_of_width_eq (by simp [h3]), LVec.insertAt_zero_of_width_eq (by simp [h4]),
LVec.insertAt_zero_of_width_eq (by simp [h5]), LVec.insertAt_zero_of_width_eq (by simp [h6])]
simp [accC]
/-- The state `t` busy cycles after a start with operands `A`, `B` (the invariant on `k`). -/
def BusyAt (N W AccW : Nat) (A B : BitVec (N * N * W)) (t : Nat) (st : SvState) : Prop :=
∃ ck r s a b, st = MS ck r s a b (knownBit false) (accC N W AccW A B t) (.ofBitVec (BitVec.ofNat 32 t))
(knownBit true) (.ofBitVec (A >>> (t * W))) (.ofBitVec (B >>> (t * (N * W))))
/-- Finished with `A`, `B`: `done` = 1 and `C` the product. -/
def FinAt (N W AccW : Nat) (A B : BitVec (N * N * W)) (st : SvState) : Prop :=
∃ ck r s a b k ash bsh, st = MS ck r s a b (knownBit true) (.ofBitVec (Tasks.Matmul.product N W AccW A B)) k
(knownBit false) ash bsh
/-- One cycle from a state of the shape, evaluated (`mm_cycle`). -/
def Stp (N W AccW : Nat) (e prev st : SvState) : Prop :=
∃ ck r s a b dn c k bz ash bsh, prev = MS ck r s a b dn c k bz ash bsh ∧ Wd N W AccW dn c k bz ash bsh ∧
st = mmNext N W AccW (appIn e "clk" ck) (appIn e "rst" r) (appIn e "start" s) (appIn e "A" a)
(appIn e "B" b) dn c k bz ash bsh
theorem ops_appIn {N W AccW : Nat} {e : SvState} {x : Tasks.Matmul.task.Ops (N, W, AccW)}
(h : HoldAll e (Tasks.Matmul.task.operands (N, W, AccW)) x) (a b : LVec) :
appIn e "A" a = .ofBitVec x.1 ∧ appIn e "B" b = .ofBitVec x.2.1 := by
simp only [Tasks.Matmul.task, HoldAll] at h; simp [appIn, h.1, h.2.1]
/-- **The phase model** at `(N, W, AccW)`, latency `N`: `Ready` after a reset, `Busy x m` the state
`t = N - 1 - m` busy cycles after a start, `Fin x` the product shown. -/
def model (N W AccW : Nat) (hN : 1 ≤ N) (hW : 1 ≤ W) (hA : 1 ≤ AccW) (hNW : N * N * W < 2 ^ 31) :
Tasks.Matmul.task.PhaseModel (N, W, AccW) N (design.sem N W AccW) where
Shape := I N W AccW
Step := Stp N W AccW
init := mm_init N W AccW hN hW hA
cycle := by
rintro σ F e prev k st k' ⟨ck, r, s, a, b, dn, c, kk, bz, ash, bsh, rfl, hw⟩ hc
exact ⟨ck, r, s, a, b, dn, c, kk, bz, ash, bsh, rfl, hw, (Prod.mk.inj (cycleStep2_det hc
(mm_cycle N W AccW hN hW hA σ e ck r s a b dn c kk bz ash bsh k 30 (Nat.le_refl _)))).1⟩
shape := by
rintro e prev st - ⟨ck, r, s, a, b, dn, c, k, bz, ash, bsh, -, hw, rfl⟩
exact mmNext_I N W AccW _ _ _ _ _ dn c k bz ash bsh hw
Ready st := ∃ ck r s a b c k ash bsh, st = MS ck r s a b (knownBit false) c k (knownBit false) ash bsh
Busy x m st := ∃ t, t + m + 1 = N ∧ BusyAt N W AccW x.1 x.2.1 t st
Fin x st := FinAt N W AccW x.1 x.2.1 st
ready_done := by
rintro st ⟨ck, r, s, a, b, c, k, ash, bsh, rfl⟩
simp [BitIs, Tasks.Matmul.task, MS, SvState.lookup]
busy_done := by
rintro x m st ⟨t, -, ck, r, s, a, b, rfl⟩
simp [BitIs, Tasks.Matmul.task, MS, SvState.lookup]
fin_done := by
rintro ⟨A, B, ⟨⟩⟩ st ⟨ck, r, s, a, b, k, ash, bsh, rfl⟩
simp [BitIs, HoldAll, Tasks.Matmul.task, MS, SvState.lookup]
reset := by
rintro e prev st - ⟨ck, r, s, a, b, dn, c, k, bz, ash, bsh, -, hw, rfl⟩ hr
rw [show appIn e "rst" r = knownBit true from BitIs.appIn_eq hr r]
obtain ⟨c', k', h⟩ := next_reset (appIn e "clk" ck) (appIn e "start" s) (appIn e "A" a) (appIn e "B" b)
dn c k bz ash bsh hw
rw [h]; exact ⟨_, _, _, _, _, _, _, _, _, rfl⟩
start := by
rintro e prev st x - ⟨ck, r, s, a, b, dn, c, k, bz, ash, bsh, -, hw, rfl⟩ - hr hs hx
obtain ⟨ha, hb⟩ := ops_appIn hx a b
rw [show appIn e "rst" r = knownBit false from BitIs.appIn_eq hr r,
show appIn e "start" s = knownBit true from BitIs.appIn_eq hs s, ha, hb, next_start _ _ _ dn c k bz ash bsh hw]
exact .inr ⟨N - 1, by omega, 0, by omega, _, _, _, _, _, rfl⟩
busy := by
rintro e prev st x m - ⟨ck, r, s, a, b, dn, c, k, bz, ash, bsh, rfl, -, rfl⟩ ⟨t, ht, ck', r', s', a', b', hb⟩
hr hs -
obtain ⟨rfl, rfl, rfl, rfl, rfl, rfl⟩ := MS_inj hb
rw [show appIn e "rst" r = knownBit false from BitIs.appIn_eq hr r,
show appIn e "start" s = knownBit false from BitIs.appIn_eq hs s, next_step hN hW hNW _ _ (by omega)]
by_cases hf : t + 1 = N
· left
simp only [hf, accC_N, decide_true, ne_eq, not_true_eq_false, decide_false]
exact ⟨_, _, _, _, _, _, _, _, rfl⟩
· right
simp only [hf, ne_eq, not_false_eq_true, decide_true, decide_false]
exact ⟨m - 1, by omega, t + 1, by omega, _, _, _, _, _, rfl⟩
hold_ready := by
rintro e prev st - ⟨ck, r, s, a, b, dn, c, k, bz, ash, bsh, rfl, -, rfl⟩ ⟨ck', r', s', a', b', c', k', ash',
bsh', hb⟩ hr hs
obtain ⟨rfl, rfl, rfl, rfl, rfl, rfl⟩ := MS_inj hb
rw [show appIn e "rst" r = knownBit false from BitIs.appIn_eq hr r,
show appIn e "start" s = knownBit false from BitIs.appIn_eq hs s, next_idle]
exact ⟨_, _, _, _, _, _, _, _, _, rfl⟩
hold_fin := by
rintro e prev st x - ⟨ck, r, s, a, b, dn, c, k, bz, ash, bsh, rfl, -, rfl⟩ ⟨ck', r', s', a', b', k', ash',
bsh', hb⟩ hr hs
obtain ⟨rfl, rfl, rfl, rfl, rfl, rfl⟩ := MS_inj hb
rw [show appIn e "rst" r = knownBit false from BitIs.appIn_eq hr r,
show appIn e "start" s = knownBit false from BitIs.appIn_eq hs s, next_idle]
exact ⟨_, _, _, _, _, _, _, _, rfl⟩
theorem mm_interface (N W AccW : Nat) (hN : 1 ≤ N) (hW : 1 ≤ W) (hA : 1 ≤ AccW) :
Tasks.Matmul.task.Interface (N, W, AccW) (design.sem N W AccW) := by
rw [mm_sem_eq N W AccW hN hW hA]
exact ⟨rfl, rfl, by
show (["clk", "rst", "start", "A", "B", "done", "C", "k", "busy", "a_sh", "b_sh"] : List String).Nodup
decide⟩
theorem mm_disciplined (N W AccW : Nat) (hN : 1 ≤ N) (hW : 1 ≤ W) (hA : 1 ≤ AccW) :
Tasks.Matmul.task.Disciplined (design.sem N W AccW) := by
rw [mm_sem_eq N W AccW hN hW hA]
refine ⟨by simp [Tasks.Matmul.task, mmD, mmDecls, Design2.inputNames], fun p hp => ?_⟩
have hm : p ∈ mmCtrl N W :: mmPEs N W AccW := by simpa [mmD] using hp
rcases List.mem_cons.mp hm with rfl | hm
· simp [Tasks.Matmul.task, mmCtrl, nb, c32, edgeClock, asyncReset, processDataNames, stmtNames,
stmtOptNames, stmtListNames, lhsNames, exprNames]
· obtain ⟨i, j, rfl⟩ := mm_pe_mem hm
simp [Tasks.Matmul.task, mmPE, sext, edgeClock, asyncReset, processDataNames, stmtNames, stmtOptNames,
lhsNames, exprNames, exprListNames]
/-! ## 8. txtask-v2: every power-up (rule 2), Moore outputs (rule 1), no x-creating construct (rule 3)
Rule 2: the phase model's shape `I` (the register widths, any values) holds at every power-up state
(`mm_powerUp`), and every obligation of the model holds from any state of the shape, so the claim holds
from every power-up (`TxTask.satisfiedAtV2_of_phases`, `SvGolf.SeqV2`); the same cycle gives
termination from every power-up (`mm_terminatesPU`). Rule 1: every process is an `always_ff`
(`mm_profile`), so the design has no combinational dependency at all (`combDeps_of_ffOnly`). Rule 3
(`mm_xsafe`): every expression is a known literal, a declared signal, an in-range constant slice
(`C`'s windows, the operand entries), a concatenation, a replication, a width conversion or `+`, `-`,
`*`, `>>`, `==` on operands of matching widths. -/
/-- **Rule 1.** -/
theorem mm_moore (N W AccW : Nat) (hN : 1 ≤ N) (hW : 1 ≤ W) (hA : 1 ≤ AccW) :
MooreOutDep (design.sem N W AccW) [] := by
rw [mm_sem_eq N W AccW hN hW hA]
exact MooreOutDep.of_noComb (combDeps_of_ffOnly (FFOnly.ofProfile (mm_profile N W AccW)))
(by simp [mmD, mmDecls, Design2.outputNames, Design2.inputNames])
/-- Every power-up state of the design has its shape. -/
theorem mm_powerUp (N W AccW : Nat) (hN : 1 ≤ N) (hW : 1 ≤ W) (hA : 1 ≤ AccW) (st : SvState)
(h : PowerUp (design.sem N W AccW) st) : I N W AccW st := by
rw [mm_sem_eq N W AccW hN hW hA] at h
obtain ⟨ck, r1, rfl, -, -, h1⟩ := powerUp_cons.mp h
obtain ⟨r, r2, rfl, -, -, h2⟩ := powerUp_cons.mp h1
obtain ⟨s, r3, rfl, -, -, h3⟩ := powerUp_cons.mp h2
obtain ⟨a, r4, rfl, -, -, h4⟩ := powerUp_cons.mp h3
obtain ⟨b, r5, rfl, -, -, h5⟩ := powerUp_cons.mp h4
obtain ⟨dn, r6, rfl, w6, -, h6⟩ := powerUp_cons.mp h5
obtain ⟨c, r7, rfl, w7, -, h7⟩ := powerUp_cons.mp h6
obtain ⟨k, r8, rfl, w8, -, h8⟩ := powerUp_cons.mp h7
obtain ⟨bz, r9, rfl, w9, -, h9⟩ := powerUp_cons.mp h8
obtain ⟨ash, r10, rfl, w10, -, h10⟩ := powerUp_cons.mp h9
obtain ⟨bsh, r11, rfl, w11, -, h11⟩ := powerUp_cons.mp h10
obtain rfl := powerUp_nil.mp h11
exact ⟨ck, r, s, a, b, dn, c, k, bz, ash, bsh, rfl, w6, w7, w8, w9, w10, w11⟩
/-- **Termination from every power-up** (the cycle of `mm_terminates`). -/
theorem mm_terminatesPU (N W AccW : Nat) (hN : 1 ≤ N) (hW : 1 ≤ W) (hA : 1 ≤ AccW) :
TerminatesPU (design.sem N W AccW) := by
refine TerminatesPU.of_invariant (I N W AccW) (mm_powerUp N W AccW hN hW hA) (fun σ => ⟨30, ?_⟩)
(by rw [mm_sem_eq N W AccW hN hW hA]; exact mm_noq N W AccW)
rintro e _ kk ⟨ck, r, s, a, b, dn, c, k, bz, ash, bsh, rfl, hw⟩
exact ⟨_, _, mm_cycle N W AccW hN hW hA σ e ck r s a b dn c k bz ash bsh kk 30 (Nat.le_refl _),
mmNext_I N W AccW _ _ _ _ _ dn c k bz ash bsh hw⟩
theorem lvec_l0_known : (⟨#[.l0]⟩ : LVec).allKnown = true := by
show (LVec.ofLogic .l0).allKnown = true
rw [LVec.ofLogic_l0]; exact LVec.allKnown_ofBitVec _
theorem lvec_l1_known : (⟨#[.l1]⟩ : LVec).allKnown = true := by
show (LVec.ofLogic .l1).allKnown = true
rw [LVec.ofLogic_l1]; exact LVec.allKnown_ofBitVec _
/-- The control passes rule 3. -/
theorem ctrl_xsafe (N W AccW : Nat) : xsProc (mmD N W AccW) (mmCtrl N W) = true := by
simp [xsProc, xsStmt, xsStmts, xsOpt, xsLhs, xsWidth, binW, sigWidth, mmCtrl, nb, c32, mmD, mmDecls,
lvec_l0_known, lvec_l1_known, LVec.replicate_l0, LVec.ofInt]
/-- A PE passes rule 3: its windows of `C` and of the operand registers are in range. -/
theorem pe_xsafe {N W AccW i j : Nat} (hW : 1 ≤ W) (hi : i < N) (hj : j < N) :
xsProc (mmD N W AccW) (mmPE N W AccW i j) = true := by
have hN : 0 < N := by omega
have hC := Lib.rowMajor_window_le (M := N) (N := N) (w := AccW) hi hj
have hA : i * N * W + W ≤ N * N * W := by
have := Lib.rowMajor_window_le (M := N) (N := N) (w := W) hi hN
simpa [Nat.add_zero] using this
have hB : j * W + W ≤ N * N * W := by
have := Lib.rowMajor_window_le (M := N) (N := N) (w := W) hN hj
simpa using this
have hA1 : i * N * W + W - 1 + 1 ≤ N * N * W := by omega
have hB1 : j * W + W - 1 + 1 ≤ N * N * W := by omega
simp [xsProc, xsStmt, xsOpt, xsLhs, xsWidth, xsWidthList, binW, sigWidth, mmPE, sext, mmD, mmDecls,
LVec.replicate_l0, hC, hA, hB, hA1, hB1]
theorem mm_pe_mem' {N W AccW : Nat} {p : Process2} (hp : p ∈ mmPEs N W AccW) :
∃ i j, i < N ∧ j < N ∧ p = mmPE N W AccW i j := by
simp only [mmPEs, List.mem_flatMap, List.mem_map, List.mem_range] at hp
obtain ⟨i, hi, j, hj, rfl⟩ := hp
exact ⟨i, j, hi, hj, rfl⟩
/-- **Rule 3.** -/
theorem mm_xsafe (N W AccW : Nat) (hN : 1 ≤ N) (hW : 1 ≤ W) (hA : 1 ≤ AccW) :
XSafe (design.sem N W AccW) := by
rw [mm_sem_eq N W AccW hN hW hA]
refine List.all_eq_true.mpr fun p hp => ?_
have hm : p ∈ mmCtrl N W :: mmPEs N W AccW := by simpa [mmD] using hp
rcases List.mem_cons.mp hm with rfl | hm
· exact ctrl_xsafe N W AccW
· obtain ⟨i, j, hi, hj, rfl⟩ := mm_pe_mem' hm
exact pe_xsafe hW hi hj
/-- **The reference satisfies the matmul statement (txtask-v2) at latency `N`** (`4` at the scoring
point). -/
theorem solution : Goal lat := by
refine ⟨fun p hp => ?_, rfl⟩
obtain ⟨N, W, AccW⟩ := p
obtain ⟨hN, hW, hA, hNW, -⟩ := hp
exact Tasks.Matmul.task.satisfiedAtV2_of_phases (mm_interface N W AccW hN hW hA)
(mm_disciplined N W AccW hN hW hA) (mm_terminatesPU N W AccW hN hW hA) hN
(model N W AccW hN hW hA hNW) (mm_powerUp N W AccW hN hW hA) (mm_moore N W AccW hN hW hA)
(mm_xsafe N W AccW hN hW hA)
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 = 2, W = 2, AccW = 4
Drawn at N = 2, W = 2, AccW = 4, a display instance; scored at N = 4, W = 8, AccW = 32. 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: 473 cells here, 12 574 at the scoring point. The register-to-register critical path at N = 2, W = 2, AccW = 4 (13 cells, ending at the flip-flop input _116_ falling; a period of 2 332 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 = 2, W = 2, AccW = 4: 473 cells, 153 and2_1, 78 nand2_1, 66 dfxtp_1, 42 or2_1, 35 nor2_1, 32 nor2b_1, 24 mux2_1, 21 xor2_1, 12 nand2b_1, 10 xnor2_1. There (measured, not ranked): 3 782.38 µm², 2 332 ps. Open the drawing on its own (SVG, 318.7 KB) to pan and zoom it with the browser's own controls.
Critical path
at N = 4, W = 8, AccW = 32 · scored
The path the ranked register-to-register period ends on, at the scoring point N = 4, W = 8, AccW = 32: 61 nets, 60 cells, to the flip-flop input _00900_ falling; the period, 10 696 ps, is the last arrival plus that flip-flop's setup time (ideal clock). 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 a flip-flop's Q: its arrival is the flop's clock-to-Q delay after the clock edge. 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 = 2, W = 2, AccW = 4, the critical path is 13 cells to the flip-flop input _116_ falling (a period of 2 332 ps there); each cell is a link to it in the drawing:
\_0_[15]_802_and2_1_803_and2_1_804_and2_1_805_and2_1_806_and2_1_820_and2_1_845_and2_1_848_and2_1_852_and2_1_856_and2_1_858_or2_1_859_and2_1_861_and2_1_116_flip-flop D
every stage (61 rows)
| # | net | edge | driven by | cell | arrival (ps) | transition (ps) | stage (ps) |
|---|---|---|---|---|---|---|---|
| 0 | \_100_[24] | rise | flop Q | – | 674.8 | 569.0 | |
| 1 | _03703_ | rise | _22951_ | and2_1 | 909.5 | 89.8 | 234.7 |
| 2 | _03706_ | rise | _22954_ | xor2_1 | 1 111.0 | 190.5 | 201.5 |
| 3 | _03708_ | rise | _22956_ | xor2_1 | 1 327.8 | 190.5 | 216.8 |
| 4 | _03710_ | rise | _22958_ | xor2_1 | 1 546.5 | 193.2 | 218.7 |
| 5 | _03712_ | fall | _22960_ | xnor2_1 | 1 654.4 | 74.6 | 107.9 |
| 6 | _03713_ | fall | _22961_ | or2_1 | 1 855.9 | 41.1 | 201.5 |
| 7 | _03717_ | fall | _22966_ | and2_1 | 2 017.8 | 48.1 | 161.9 |
| 8 | _03734_ | fall | _22983_ | or2_1 | 2 228.1 | 41.1 | 210.3 |
| 9 | _03738_ | fall | _22988_ | and2_1 | 2 390.0 | 48.1 | 161.9 |
| 10 | _03766_ | fall | _23016_ | or2_1 | 2 600.3 | 41.1 | 210.3 |
| 11 | _03770_ | fall | _23021_ | and2_1 | 2 762.2 | 48.1 | 161.9 |
| 12 | _03801_ | fall | _23052_ | or2_1 | 2 972.5 | 41.1 | 210.3 |
| 13 | _03805_ | fall | _23057_ | and2_1 | 3 134.4 | 48.1 | 161.9 |
| 14 | _03845_ | fall | _23097_ | or2_1 | 3 344.7 | 41.1 | 210.3 |
| 15 | _03849_ | fall | _23102_ | and2_1 | 3 506.7 | 48.1 | 162.0 |
| 16 | _03895_ | fall | _23148_ | or2_1 | 3 716.9 | 41.1 | 210.2 |
| 17 | _03899_ | fall | _23153_ | and2_1 | 3 878.9 | 48.1 | 162.0 |
| 18 | _03944_ | fall | _23198_ | or2_1 | 4 089.2 | 41.1 | 210.3 |
| 19 | _03948_ | fall | _23203_ | and2_1 | 4 251.1 | 48.1 | 161.9 |
| 20 | _03995_ | fall | _23250_ | or2_1 | 4 461.4 | 41.1 | 210.3 |
| 21 | _03999_ | fall | _23255_ | and2_1 | 4 623.3 | 48.1 | 161.9 |
| 22 | _04041_ | fall | _23297_ | or2_1 | 4 833.6 | 41.1 | 210.3 |
| 23 | _04045_ | fall | _23302_ | and2_1 | 4 995.5 | 48.1 | 161.9 |
| 24 | _04084_ | fall | _23341_ | or2_1 | 5 205.8 | 41.1 | 210.3 |
| 25 | _04088_ | fall | _23346_ | and2_1 | 5 367.8 | 48.1 | 162.0 |
| 26 | _04122_ | fall | _23380_ | or2_1 | 5 578.0 | 41.1 | 210.2 |
| 27 | _04126_ | fall | _23385_ | and2_1 | 5 740.0 | 48.1 | 162.0 |
| 28 | _04156_ | fall | _23415_ | or2_1 | 5 950.3 | 41.1 | 210.3 |
| 29 | _04160_ | fall | _23420_ | and2_1 | 6 112.2 | 48.1 | 161.9 |
| 30 | _04181_ | fall | _23441_ | or2_1 | 6 322.5 | 41.1 | 210.3 |
| 31 | _04185_ | fall | _23446_ | and2_1 | 6 484.4 | 48.1 | 161.9 |
| 32 | _04196_ | fall | _23457_ | or2_1 | 6 694.7 | 41.2 | 210.3 |
| 33 | _04200_ | fall | _23462_ | and2_1 | 6 856.7 | 48.1 | 162.0 |
| 34 | _04203_ | fall | _23465_ | or2_1 | 7 073.2 | 45.7 | 216.5 |
| 35 | _04206_ | rise | _23469_ | nand2_1 | 7 156.4 | 76.4 | 83.2 |
| 36 | _04219_ | fall | _23483_ | nand2_1 | 7 199.9 | 29.4 | 43.5 |
| 37 | _04220_ | fall | _23484_ | and2_1 | 7 356.5 | 47.8 | 156.6 |
| 38 | _04221_ | fall | _23485_ | or2_1 | 7 554.0 | 45.9 | 197.5 |
| 39 | _04224_ | rise | _23489_ | nand2_1 | 7 637.3 | 76.4 | 83.3 |
| 40 | _04237_ | fall | _23503_ | nand2_1 | 7 680.8 | 29.4 | 43.5 |
| 41 | _04238_ | fall | _23504_ | and2_1 | 7 837.4 | 47.8 | 156.6 |
| 42 | _04239_ | fall | _23505_ | or2_1 | 8 045.7 | 53.9 | 208.3 |
| 43 | _04252_ | fall | _23520_ | or2_1 | 8 268.7 | 49.0 | 223.0 |
| 44 | _04271_ | fall | _23541_ | nand2b_1 | 8 395.9 | 34.7 | 127.2 |
| 45 | _04275_ | fall | _23545_ | and2_1 | 8 536.9 | 48.1 | 141.0 |
| 46 | _04278_ | fall | _23548_ | or2_1 | 8 764.2 | 53.6 | 227.3 |
| 47 | _04291_ | fall | _23563_ | or2_1 | 8 987.1 | 49.0 | 222.9 |
| 48 | _04310_ | fall | _23584_ | nand2b_1 | 9 114.3 | 34.7 | 127.2 |
| 49 | _04314_ | fall | _23588_ | and2_1 | 9 255.3 | 48.1 | 141.0 |
| 50 | _04317_ | fall | _23591_ | or2_1 | 9 465.6 | 41.2 | 210.3 |
| 51 | _04321_ | fall | _23596_ | and2_1 | 9 625.1 | 45.9 | 159.5 |
| 52 | _04323_ | fall | _23598_ | and2_1 | 9 765.6 | 43.2 | 140.5 |
| 53 | _04324_ | rise | _23599_ | inv_1 | 9 811.4 | 32.0 | 45.8 |
| 54 | _04333_ | fall | _23609_ | nand2_1 | 9 870.5 | 52.7 | 59.1 |
| 55 | _04339_ | fall | _23616_ | or2_1 | 10 089.0 | 45.9 | 218.5 |
| 56 | _04341_ | rise | _23618_ | nand2_1 | 10 135.1 | 33.8 | 46.1 |
| 57 | _04342_ | rise | _23619_ | and2_1 | 10 248.8 | 70.7 | 113.7 |
| 58 | _04343_ | rise | _23620_ | and2_1 | 10 368.5 | 45.7 | 119.7 |
| 59 | _04345_ | fall | _23622_ | nor2_1 | 10 399.0 | 154.8 | 30.5 |
| 60 | _00900_ | fall | _23623_ | and2_1 | 10 593.0 | 31.2 | 194.0 |
Cells
at N = 4, W = 8, AccW = 32 · scored
| cell | count | µm² each | µm² in all | share |
|---|---|---|---|---|
xor2_1 | 2 127 | 8.7584 | 18629.1168 | 21.4% |
nand2_1 | 4 831 | 3.7536 | 18133.6416 | 20.8% |
dfxtp_1 | 802 | 20.0192 | 16055.3984 | 18.4% |
and2_1 | 1 963 | 6.2560 | 12280.5280 | 14.1% |
xnor2_1 | 851 | 8.7584 | 7453.3984 | 8.6% |
mux2_1 | 514 | 11.2608 | 5788.0512 | 6.6% |
or2_1 | 788 | 6.2560 | 4929.7280 | 5.7% |
nor2b_1 | 313 | 6.2560 | 1958.1280 | 2.2% |
nand2b_1 | 180 | 6.2560 | 1126.0800 | 1.3% |
nor2_1 | 189 | 3.7536 | 709.4304 | 0.8% |
inv_1 | 10 | 3.7536 | 37.5360 | 0.0% |
buf_8 | 2 | 15.0144 | 30.0288 | 0.0% |
buf_4 | 3 | 7.5072 | 22.5216 | 0.0% |
buf_12 | 1 | 20.0192 | 20.0192 | 0.0% |
| total | 12 574 | 87173.6064 |
Repair (buffer_trees_v1): 6 buffers on 5 nets, 0 tie cells, 2 weighing rounds; nothing is resized.
Legality: worst load 0.875 × max_capacitance, worst transition 0.905 × max_transition; 0 timing-table reads outside the characterized grid.
the gate mix of the extra GE column (the pinned GE table, score/ge_seq.json)
| cell | count | GE each | GE total |
|---|---|---|---|
NAND | 4831 | 1.000 | 4831.000 |
XOR | 2127 | 2.000 | 4254.000 |
AND | 1963 | 1.333 | 2616.679 |
XNOR | 851 | 2.000 | 1702.000 |
DFF_P | 802 | 5.333 | 4277.066 |
OR | 788 | 1.333 | 1050.404 |
MUX | 514 | 2.333 | 1199.162 |
ANDNOT | 313 | 1.333 | 417.229 |
NOR | 189 | 1.000 | 189.000 |
ORNOT | 180 | 1.333 | 239.940 |
NOT | 10 | 0.667 | 6.670 |
| total | 12568 | 20783.150 |
The record
- task
matmul- player
- svgolf-reference
- assisted by
- Claude (Anthropic)
- submitted
- 2026-11-09
- notes
- reference solution
- scoring point
- AccW = 32, N = 4, W = 8
- design
matmul_g1.svsha2568dbb8b29b4ba583cf4b0660c3b5cd14d497a4ec400a3956a6aa98a44e6514ca7- proof
matmul_g1.leansha256a6eb0c66f152549632c8346756ea11ad8ba1907470de4c26a0ba9afa59909b7e: the proof of the referencematmul_g1(matmul_g1.lean, 653 code lines)- canonical text
- sha256
fdc791068af82bac74d5068c6bed0b88627e5621538a20f929ea2c01234b4fdc - record hash
8427744607a01efb23cbbd4c14f323ce72f14582315f260ebaf26bbabdddf05e- downloads
8427744607a0.sv8427744607a0.lean8427744607a0.json
The proof
- code lines
- 653
- build time
- 7.88 s
- axioms
Classical.choice,Quot.sound,propext- tactics seen
decide,induction,omega,simp- imports
Submission.Design,LeanModels.Sv.Gen2,LeanModels.Sv.Hier2Gen,LeanModels.Sv.SeqProfile,LeanModels.Sv.SeqFamily,LeanModels.Sv.Known2,LeanModels.Sv.Lib.BitVecArith,LeanModels.Sv.CombViewArith,LeanModels.Sv.Mono2,SvGolf.SeqV2
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
7fb4c3d844e0911b67cb6657e9dfdac8741e72d3- lean-surfaces revision
7fb4c3d844e0911b67cb6657e9dfdac8741e72d3- canonicalization script
bbd11227d45dc1365f27597fd45c565fe721ba2648450588fc4fd983d00b367b- scoring script
c3874dbfc85921a87df6eacdfc6b2c9a0e2b34edd5738951e0d8bdb51f4558b2- GE table
0a1aa1147b6916efa4a41e03aa3c8c70e68c1e3fd7a0e9c9a3cbc177edefd1f6- extractor
4930696021c588c75dc890034c534c95560ad9a28a4955b87d1f8d68b5edbc0c- canonical RTLIL
3f301fadb89d4763df48c726521f4cfe32657c4789bda082c5b46fccfebbe604
the checker's files (sha256)
| file | sha256 |
|---|---|
check.py | b0c939438a765c2ffeb679721c83ced6213c0a96bfef84759948d3857db9f431 |
check/ | 936f97636d6256374eaa4435d03321eeff29e22f808a72c7781375ab648600e8 |
check/ | fb51d72510b515460866c2798aad3c6f55295536e627b92854c3580af215bb91 |
check/ | 73856a45905724544247ad29042bdc2f1778271ec07b15cdb3a5f214685165c1 |
check/ | 076db01a9791bcc25ee84e9b0033604a4b675f00b212eab0c864078e6a4748b7 |
check/ | dd89805f9e8335ab15d6bb753eea76865bce47e84ccffa7e7280c91da8131d23 |
lakefile.toml | f35a88404367d22906effc14d644aac9a9cad47dfd3b704ea8fec88acfaf9c4d |
lean-toolchain | 62c2d9c0fc1ec4c67e151c11eff41ca004ef38e179cf9476c230406e6defedef |
score/ | f18c14c23a14d55db63e202f42cc424dc84c1dd1c337f132ad60859dc9d5d33a |
score/ | bbd11227d45dc1365f27597fd45c565fe721ba2648450588fc4fd983d00b367b |
score/ | 95332451ae3754226fb87b46027385b46a91121e7eb9ae25b302bce756d90475 |
score/ | 6a29196a3f4b58241f4c9af650c9141e4632b737214d563a11ba142a725c4bea |
score/ | 0a1aa1147b6916efa4a41e03aa3c8c70e68c1e3fd7a0e9c9a3cbc177edefd1f6 |
score/ | 912031912c3e3f6790725161753e95415df34846bd8c27dde3eb707747ddbcbe |
score/ | 93547b343dbac135737e98516b1004a780e1cf2f579e1bd7fed82c908c6d4125 |
score/ | 4b8688ba3f4d67681658e5944afe878592adf588cd416a8b47597ebe06deb045 |
score/ | d66a6086a37a97f5985266057051979fb6a8a3c557d3ef9c42acc1b5e41502d8 |
score/ | 71d2e3b82f8813ec64a58f0a46909ca23dfab377db44eda6f294b713f45ae781 |
score/ | 11952f61058051c9e8de91a801b672d07e66a29308e978156c04ffd9f0dbef45 |
score/ | ea58219d8ef14c3bad265218ee266ccdb709ac9d4dc5258edc34d51ee83a0445 |
score/ | c3874dbfc85921a87df6eacdfc6b2c9a0e2b34edd5738951e0d8bdb51f4558b2 |
Post-synthesis check
At each point below, the check ran the scored netlist and ls-sv's reading of the design 8 times, once per seed, each run from a power-up state and through a seeded stimulus of 24 transactions. Each point ran against its own latency, the solution's lat at that point (the table's latency column): every transaction had to finish within it. The check found no mismatch. The stimulus is a sample of the inputs: a guard against the two tools disagreeing, not a proof.
- mode
- full
- points
- AccW = 8, N = 2, W = 4, AccW = 32, N = 4, W = 8 (the scoring point)
- seeds
- 8 seeds per point: seed 0 from the all-zeros power-up, every other one from a random power-up drawn from its seed (below)
- stimulus
- 24 transactions per seed
- clock cycles
- 2 085 in all
- mismatches
- 0
- salted
- no (the default empty salt: anyone can run this exact stimulus again)
- scored netlist
- sha256
aa392e15c1cacfb2f17500a0e769d8825ef7b545ba61f69da959fb9fa5935bb8
| point | latency (cycles) | seeds | clock cycles | transactions | mismatches | netlist sha256 |
|---|---|---|---|---|---|---|
| AccW = 8, N = 2, W = 4 | 2 | 8 | 828 | 192 | 0 | 7231759fb4f6 |
| AccW = 32, N = 4, W = 8 | 4 | 8 | 1 257 | 192 | 0 | aa392e15c1ca |
every seed's run (16)
A seed's draw is sha256(salt, task, point, seed) (its first 12 digits below; check/postsynth.py seed_draw). Everything random in the seed's run comes from it: the stimulus, ls-sv's power-up (zeros for seed 0, else seed:<k>) and the netlist's Verilator randSeed, which the all-zeros power-up of seed 0 does not use.
| point | seed | ls-sv power-up | netlist randSeed | clock cycles | transactions | draw |
|---|---|---|---|---|---|---|
| AccW = 8, N = 2, W = 4 | 0 | zeros | 303973935 | 109 | 24 | 4ed5c6f8253a |
| AccW = 8, N = 2, W = 4 | 1 | seed:6761245804292904090 | 1195438325 | 109 | 24 | 82e6921a1035 |
| AccW = 8, N = 2, W = 4 | 2 | seed:845291531553475423 | 391528801 | 112 | 24 | 2b060ddd6dc0 |
| AccW = 8, N = 2, W = 4 | 3 | seed:5147191549726746004 | 1397868753 | 97 | 24 | 37b381d742d3 |
| AccW = 8, N = 2, W = 4 | 4 | seed:3814886047666414678 | 1168828466 | 96 | 24 | 93e4b866922d |
| AccW = 8, N = 2, W = 4 | 5 | seed:3507955749274142316 | 192587499 | 102 | 24 | 8c2d8181b840 |
| AccW = 8, N = 2, W = 4 | 6 | seed:9665391063568079335 | 688604025 | 101 | 24 | 7c2b8adaae80 |
| AccW = 8, N = 2, W = 4 | 7 | seed:11035912927779587285 | 825733514 | 102 | 24 | 1db8b9c4c23c |
| AccW = 32, N = 4, W = 8 | 0 | zeros | 1946689348 | 151 | 24 | e3ef9e3638b5 |
| AccW = 32, N = 4, W = 8 | 1 | seed:9734188592010554240 | 1063280839 | 168 | 24 | 2132be692d56 |
| AccW = 32, N = 4, W = 8 | 2 | seed:6594409222167094000 | 88936619 | 160 | 24 | 0371e783230d |
| AccW = 32, N = 4, W = 8 | 3 | seed:15091031972466303227 | 590535838 | 151 | 24 | ed721e3e3e18 |
| AccW = 32, N = 4, W = 8 | 4 | seed:17087008212923660376 | 707745727 | 162 | 24 | fcdbab7c9305 |
| AccW = 32, N = 4, W = 8 | 5 | seed:13568177433463839254 | 834142182 | 152 | 24 | ccccf631a1d2 |
| AccW = 32, N = 4, W = 8 | 6 | seed:2839497944125262158 | 1252134952 | 162 | 24 | f1dc560f7767 |
| AccW = 32, N = 4, W = 8 | 7 | seed:8076188500603944899 | 547904091 | 151 | 24 | 831e3fb7b1cd |
Area against time
- submission
- proved reference
- the Pareto front of the entries
- this design
- par's score: area × time = 3 729 636 µm²·ns
the designs as numbers, by score
| design | kind | area (µm²) | time (ps) | score (µm²·ns) |
|---|---|---|---|---|
| fanout-ferret | submission | 241 920.77 | 9 410 | 2 276 474 |
| vacuity-check | submission | 85 260.52 | 27 880 | 2 377 063 |
| fanout-ferret | submission | 137 103.99 | 18 318 | 2 511 471 |
| vacuity-check | submission | 79 700.19 | 42 756 | 3 407 661 |
matmul_g1 · this design | proved reference · par | 87 173.61 | 42 784 | 3 729 636 |
- submission
- proved reference
- the Pareto front of the entries
- this design
- par's score: area × time = 8217 µm²·ns
the designs as numbers, by score
| design | kind | area (µm²) | time (ps) | score (µm²·ns) |
|---|---|---|---|---|
| fanout-ferret | submission | 3 661.548 | 1 621 | 5935 |
| vacuity-check | submission | 1 315.583 | 4 905 | 6453 |
| fanout-ferret | submission | 2 085.873 | 3 126 | 6520 |
| vacuity-check | submission | 1 216.745 | 5 992 | 7291 |
matmul_g1 · this design | proved reference · par | 1 334.726 | 6 156 | 8217 |
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)
# a clocked task: the clocked flow, --seq (the time is the register-to-register period)
python3.12 score/ppa.py tasks/matmul/reference/matmul_g1.sv --task tasks/matmul --seq --path \
--json out.json --netlist scored.v --repaired-netlist repaired.v
Expect, in out.json:
- score.rtlil_sha256
3f301fadb89d4763df48c726521f4cfe32657c4789bda082c5b46fccfebbe604- ppa.netlist_sha256
aa392e15c1cacfb2f17500a0e769d8825ef7b545ba61f69da959fb9fa5935bb8- ppa.repair.netlist_sha256
01ee4c6ad0e7bfb4ac08306ce94adf476d222881106f6682c666c0b697beed6c- ppa.area_e4um2_sky130
871736064- ppa.delay_ps_sky130
10696
The schematic and the critical-path data are site/schematics/matmul/8dbb8b29b4ba583c.json and its .svg, written by site/gen_schematics.py (the display instance is ppa.py ... --param AccW=4 --param N=2 --param W=2 --repaired-netlist, drawn by netlistsvg 1.0.2; repaired netlist sha256 7988aa71bc0ba774…).
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 tasks/matmul/reference/matmul_g1.sv --task tasks/matmul --seq --pdk asap7 --no-mapped \
--json out.json
Expect, in out.json:
- ppa.netlist_sha256
aa392e15c1cacfb2f17500a0e769d8825ef7b545ba61f69da959fb9fa5935bb8- ppa.repair.netlist_sha256
c727a8839baa17040141dc449a47d67dd7af9be85a72b314c2cee7058d855d6d- ppa.area_e5um2_asap7
133472610- ppa.delay_ps_asap7
1539
leaderboard/asap7/matmul.json keeps these numbers; python3.12 leaderboard/leaderboard.py asap7 --rescore --task matmul 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=8427744607a01efb23cbbd4c14f323ce72f14582315f260ebaf26bbabdddf05e
mkdir -p rec/matmul && cp "$H.json" "$H.sv" "$H.lean" rec/matmul/
python3.12 leaderboard/leaderboard.py verify --data-dir rec --task matmul --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.