Record 9fdf50f9ae02
Area
79 700.19µm²
79 700.1888 µm² exactly
Beats 100% · matches 0%
1 of 1 other player beaten, 0 equal.
Performance · time
42 756ps
4 cycles × 10 689 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 1 other player beaten, 0 equal.
Score · area × time
3 407 661µm²·ns
79 700.1888 µm² × 42 756 ps
Beats 0% · matches 0%
0 of 1 other player beaten, 0 equal.
- vs par
- −8.6% bettermatmul_g1 · 3 729 636 µm²·ns
- on this page
- 4th of 5 designs
- board
- layer 1, rank 4 of 4 by score
- Pareto
- on the front
On the board, of 1 other player: 0 beaten, 0 level, 1 trade off, 0 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 (1 player, 2 records), never the references, the baselines or a rejected design; each player counts once, at their best, and ties are exact at the recorded unit; advisory, recomputed at every build, never stored in a record and never used by the ranking.
On the board, another player is beaten when this design dominates every record of theirs, level when none of theirs is better and one is identical (the rest dominated), ahead when a record of theirs dominates this design, and a trade off otherwise (one of theirs better on one axis, worse on the other).
“On this page” and “Pareto” compare with the 5 designs of the area-time plot (1 reference and 4 submissions): not a ranking.
Area
1 216.745µm²
1 216.74474 µm² exactly
Beats 100% · matches 0%
1 of 1 other player beaten, 0 equal.
Performance · time
5 992ps
4 cycles × 1 498 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 1 other player beaten, 0 equal.
Score · area × time
7291µm²·ns
1 216.74474 µm² × 5 992 ps
Beats 0% · matches 0%
0 of 1 other player beaten, 0 equal.
- vs par
- −11.3% bettermatmul_g1 · 8217 µm²·ns
- on this page
- 4th of 5 designs
- board
- layer 1, rank 4 of 4 by score
- Pareto
- on the front
On the board, of 1 other player: 0 beaten, 0 level, 1 trade off, 0 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 (1 player, 2 records), never the references, the baselines or a rejected design; each player counts once, at their best, and ties are exact at the recorded unit; advisory, recomputed at every build, never stored in a record and never used by the ranking.
On the board, another player is beaten when this design dominates every record of theirs, level when none of theirs is better and one is identical (the rest dominated), ahead when a record of theirs dominates this design, and a trade off otherwise (one of theirs better on one axis, worse on the other).
“On this page” and “Pareto” compare with the 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
// operand shifters and the step counter free-running: loaded on start, stepped in every other cycle
always_ff @(posedge clk) begin
if (start) begin
a_sh <= A;
b_sh <= B;
k <= 32'd0;
end else begin
a_sh <= a_sh >> W;
b_sh <= b_sh >> (N*W);
k <= k + 32'd1;
end
if (rst) begin
busy <= 1'b0;
done <= 1'b0;
end else if (start) begin
busy <= 1'b1;
done <= 1'b0;
end else if (busy) begin
if (k == N - 1) begin
busy <= 1'b0;
done <= 1'b1;
end
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
// free-running accumulate: once the N steps are over b_sh has been shifted out (all zero),
// so the product is 0 and C holds without a hold multiplexer
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
S10.lean (722 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
/-!
# Variation of the `matmul` reference: free-running operand shifters, step counter and accumulators
`work/f10.sv` is the reference design (`N²` multiply-accumulate units, a step counter `k`, operand shift
registers `a_sh`, `b_sh`, latency `N`) with two architectural changes:
* the shift registers and `k` are not held while idle: they load on `start` and step in EVERY other cycle
(reset included), so they need no hold multiplexer and `k` needs no reset;
* the accumulators have no `busy` gate either: each PE adds `a_sh[i][0] * b_sh[0][j]` in every cycle that
has no `start`. After the `N` steps `b_sh` has been shifted out (all zero), so the product is 0 and `C`
holds with no hold multiplexer: the `Fin` phase is the busy run's state at `t ≥ N` (`accC_ge`).
Only `busy` and `done` are controlled. The proof is the reference's with the control process, the PE
process and the phases changed:
1. `mmCtrl`, `ctrlQ`, `mmPE`, `peQ`: the processes and their evaluated updates.
2. `entry_ge`, `psum_ge`, `accC_ge`, `ctrl_idle`, `next_run`: the run past the `N` steps (`C` unchanged).
3. `next_idle`, `next_reset`: before the first start `C` accumulates whatever the registers hold (not claimed).
-/
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]` in every cycle
(no `busy` gate: after the `N` steps `b_sh` is all zero, so the product is 0 and `C` holds). -/
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 (.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)))))))
/-- A nonblocking write of a constant. -/
abbrev nb (n : String) (v : LVec) : Stmt2 := .nbaAssign (.ident n) (.lit v)
/-- The control: the operand shift registers and the step counter run free (loaded on `start`, stepped
in every other cycle); `busy` and `done` hold the handshake. -/
def mmCtrl (N W : Nat) : Process2 :=
.alwaysFF "clk" (.block #[
.ifStmt (.ident "start")
(.block #[.nbaAssign (.ident "a_sh") (.ident "A"), .nbaAssign (.ident "b_sh") (.ident "B"),
nb "k" (LVec.replicate 32 .l0)])
(some (.block #[
.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))),
.nbaAssign (.ident "k") (.binary .add (.ident "k") (c32 1))])),
.ifStmt (.ident "rst")
(.block #[nb "busy" ⟨#[.l0]⟩, nb "done" ⟨#[.l0]⟩])
(some (.ifStmt (.ident "start")
(.block #[nb "busy" ⟨#[.l1]⟩, nb "done" ⟨#[.l0]⟩])
(some (.ifStmt (.ident "busy")
(.block #[.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: the free-running registers, then the handshake. -/
def ctrlQ (N W : Nat) (r s a b dn k bz ash bsh : LVec) : NbaQueue2 :=
(if s.condTrue then [wr "a_sh" ash a, wr "b_sh" bsh b, wr "k" k (LVec.replicate 32 .l0)]
else [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 "k" k (k.add (LVec.ofInt 32 1))]) ++
(if r.condTrue then [wr "busy" bz ⟨#[.l0]⟩, wr "done" dn ⟨#[.l0]⟩]
else if s.condTrue then [wr "busy" bz ⟨#[.l1]⟩, wr "done" dn ⟨#[.l0]⟩]
else if bz.condTrue then
if (LVec.ofLogic (k.eqLogical ((LVec.ofInt 32 (N : Int)).sub (LVec.ofInt 32 1)))).condTrue then
[wr "busy" bz ⟨#[.l0]⟩, wr "done" dn ⟨#[.l1]⟩]
else []
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 [("C", (i * N + j) * AccW, (peV N W AccW i j c ash bsh).resizeU AccW)]
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) (bz : LVec) : peQ N W AccW (knownBit false) bz (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
/-- Entries past the end of a flat vector are zero. -/
theorem entry_ge {W n : Nat} (x : BitVec n) {idx : Nat} (h : n ≤ idx * W) : Tasks.Matmul.entry W x idx = 0 := by
have h0 : x.extractLsb' (idx * W) W = 0#W := by
apply BitVec.eq_of_getLsbD_eq
intro i hi
simp only [BitVec.getLsbD_extractLsb', hi, decide_true, Bool.true_and, BitVec.getLsbD_zero]
first | exact BitVec.getLsbD_of_ge x _ (by omega) | exact BitVec.getLsbD_ge x _ (by omega)
simp [Tasks.Matmul.entry, h0]
/-- Past `N` steps the partial sums do not change: `B` has only `N` rows. -/
theorem psum_ge (N W : Nat) (A B : BitVec (N * N * W)) (i j : Nat) : ∀ t, N ≤ t →
psum N W A B t i j = psum N W A B N i j := by
intro t ht
obtain ⟨d, rfl⟩ := Nat.exists_eq_add_of_le ht
induction d with
| zero => rfl
| succ d ih =>
have h1 : N * N ≤ (N + d) * N + j :=
Nat.le_trans (Nat.mul_le_mul (Nat.le_add_right N d) (Nat.le_refl N)) (Nat.le_add_right _ _)
have h2 : N * N * W ≤ ((N + d) * N + j) * W := Nat.mul_le_mul_right W h1
show psum N W A B (N + d + 1) i j = _
rw [psum_succ, ih (Nat.le_add_right _ _), entry_ge B h2]
simp
theorem accC_ge (N W AccW : Nat) (A B : BitVec (N * N * W)) {t : Nat} (ht : N ≤ t) :
accC N W AccW A B t = accC N W AccW A B N := by
have h : ∀ i j, psum N W A B t i j = psum N W A B N i j := fun i j => psum_ge N W A B i j t ht
simp only [accC, h]
/-! ## 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, List.cons_append, List.nil_append,
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, List.cons_append, List.nil_append, List.append_nil, 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
/-- The free-running registers in an idle cycle (`busy` = 0), from the state `t` cycles after a start. -/
theorem ctrl_idle {N W : Nat} (hN : 1 ≤ N) (hW : 1 ≤ W) (hr : N * N * W < 2 ^ 31) (A B : BitVec (N * N * W))
(t : Nat) (ck a b dn c : LVec) :
commitNba2 (MS ck (knownBit false) (knownBit false) a b dn c (.ofBitVec (BitVec.ofNat 32 t))
(knownBit false) (.ofBitVec (A >>> (t * W))) (.ofBitVec (B >>> (t * (N * W)))))
(ctrlQ N W (knownBit false) (knownBit false) a b dn (.ofBitVec (BitVec.ofNat 32 t)) (knownBit false)
(.ofBitVec (A >>> (t * W))) (.ofBitVec (B >>> (t * (N * W))))) =
MS ck (knownBit false) (knownBit false) a b dn c (.ofBitVec (BitVec.ofNat 32 (t + 1)))
(knownBit false) (.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, List.cons_append, List.nil_append,
List.append_nil, LVec.ofInt_eq_ofBitVec, BitVec.ofInt_natCast, BitVec.ofInt_ofNat, h1', LVec.mul_ofBitVec,
LVec.add_ofBitVec, LVec.width_ofBitVec, LVec.shr_ofBitVec, LVec.resizeU_ofBitVec, BitVec.setWidth_eq, hyW, hyNW,
← BitVec.shiftRight_add]
simp [commitNba2_cons, commitNba2_nil, nb_k, nb_ash, nb_bsh, LVec.insertAt_zero_of_width_eq,
BitVec.ofNat_add_ofNat]
/-- **An idle step after the `N` steps**: the accumulators add `A[i][t]·B[t][j]` for `t ≥ N`, i.e. zero
(`b_sh` has been shifted out), so `C` holds; `k` and the shift registers keep running. -/
theorem next_run {N W AccW : Nat} (hN : 1 ≤ N) (hW : 1 ≤ W) (hr : N * N * W < 2 ^ 31)
(A B : BitVec (N * N * W)) (t : Nat) (ck a b dn : LVec) :
mmNext N W AccW ck (knownBit false) (knownBit false) a b dn (accC N W AccW A B t)
(.ofBitVec (BitVec.ofNat 32 t)) (knownBit false) (.ofBitVec (A >>> (t * W)))
(.ofBitVec (B >>> (t * (N * W)))) =
MS ck (knownBit false) (knownBit false) a b dn (accC N W AccW A B (t + 1))
(.ofBitVec (BitVec.ofNat 32 (t + 1))) (knownBit false) (.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 false)
(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_idle hN hW hr A B t, 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_append, 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 `Ready` (`busy` = 0, `done` = 0): `C` is not claimed (it accumulates
whatever the operand registers hold), and the free-running registers step. -/
theorem next_idle (N W AccW : Nat) (ck a b dn c k ash bsh : LVec) :
∃ c' k' ash' bsh', 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
simp only [mmNext, commitNba2_append, ctrlQ, condTrue_knownBit, Bool.false_eq_true, ↓reduceIte, List.append_nil,
wr, commitNba2_cons, commitNba2_nil, nb_k, nb_ash, nb_bsh]
rw [commit_C (peQ_C N W AccW _ _ _ _ _)]
exact ⟨_, _, _, _, rfl⟩
/-- A reset cycle clears `busy` and `done` (the free-running registers step or load, whatever `start` shows). -/
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' ash' bsh', 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
by_cases hs : s.condTrue = true <;>
simp only [mmNext, commitNba2_append, ctrlQ, hs, condTrue_knownBit, Bool.false_eq_true, ↓reduceIte, commitNba2_cons,
commitNba2_nil, nb_busy, nb_done, nb_k, nb_ash, nb_bsh, 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, List.cons_append, List.nil_append, 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 :=
∃ t, N ≤ t ∧ ∃ ck r s a b, st = MS ck r s a b (knownBit true) (accC N W AccW A B N)
(.ofBitVec (BitVec.ofNat 32 t)) (knownBit false) (.ofBitVec (A >>> (t * W))) (.ofBitVec (B >>> (t * (N * W))))
/-- 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 ⟨t, ht, ck, r, s, a, b, rfl⟩
simp [BitIs, HoldAll, Tasks.Matmul.task, MS, SvState.lookup, accC_N]
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', ash', bsh', 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, decide_true, ne_eq, not_true_eq_false, decide_false]
exact ⟨N, Nat.le_refl _, _, _, _, _, _, 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]
obtain ⟨c₁, k₁, a₁, b₁, h⟩ := next_idle N W AccW (appIn e "clk" ck) (appIn e "A" a) (appIn e "B" b)
(knownBit false) c k ash bsh
rw [h]
exact ⟨_, _, _, _, _, _, _, _, _, rfl⟩
hold_fin := by
rintro e prev st x - ⟨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,
← accC_ge N W AccW x.fst x.snd.fst ht, next_run hN hW hNW x.fst x.snd.fst t]
exact ⟨t + 1, by omega, _, _, _, _, _, by rw [accC_ge N W AccW x.fst x.snd.fst (by omega : N ≤ t + 1)]⟩
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: 379 cells here, 11 447 at the scoring point. The register-to-register critical path at N = 2, W = 2, AccW = 4 (32 cells, ending at the flip-flop input _084_ falling; a period of 5 527 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: 379 cells, 108 nor2_1, 88 and2_1, 66 dfxtp_1, 35 nor2b_1, 26 nand2_1, 17 xor2_1, 14 mux2_1, 9 or2_1, 8 nand2b_1, 8 xnor2_1. There (measured, not ranked): 3 076.70 µm², 5 527 ps. Open the drawing on its own (SVG, 276.9 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: 59 nets, 58 cells, to the flip-flop input _00399_ falling; the period, 10 689 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 32 cells to the flip-flop input _084_ falling (a period of 5 527 ps there); each cell is a link to it in the drawing:
\_0_[1]_493_and2_1_497_and2_1_501_and2_1_505_and2_1_509_and2_1_513_and2_1_517_and2_1_521_and2_1_525_and2_1_529_and2_1_533_and2_1_537_and2_1_541_and2_1_545_and2_1_549_and2_1_553_and2_1_557_and2_1_561_and2_1_565_and2_1_569_and2_1_573_and2_1_577_and2_1_581_and2_1_585_and2_1_589_and2_1_593_and2_1_597_and2_1_601_and2_1_605_and2_1_609_nand2_1_613_xor2_1_614_nor2_1_084_flip-flop D
every stage (59 rows)
| # | net | edge | driven by | cell | arrival (ps) | transition (ps) | stage (ps) |
|---|---|---|---|---|---|---|---|
| 0 | \_56_[0] | rise | flop Q | – | 739.4 | 661.3 | |
| 1 | _07980_ | rise | _15986_ | and2_1 | 984.6 | 90.8 | 245.2 |
| 2 | _07983_ | rise | _15989_ | xor2_1 | 1 186.3 | 190.5 | 201.7 |
| 3 | _07985_ | rise | _15991_ | xor2_1 | 1 403.1 | 190.5 | 216.8 |
| 4 | _07987_ | rise | _15993_ | xor2_1 | 1 621.8 | 193.2 | 218.7 |
| 5 | _07989_ | fall | _15995_ | xnor2_1 | 1 709.0 | 53.6 | 87.2 |
| 6 | _07990_ | fall | _15996_ | or2_1 | 1 913.4 | 49.5 | 204.4 |
| 7 | _07993_ | fall | _16000_ | and2_1 | 2 065.6 | 36.3 | 152.2 |
| 8 | _08010_ | fall | _16017_ | or2_1 | 2 283.0 | 49.5 | 217.4 |
| 9 | _08013_ | fall | _16021_ | and2_1 | 2 435.1 | 36.3 | 152.1 |
| 10 | _08041_ | fall | _16049_ | or2_1 | 2 652.5 | 49.5 | 217.4 |
| 11 | _08044_ | fall | _16053_ | and2_1 | 2 804.7 | 36.3 | 152.2 |
| 12 | _08077_ | fall | _16086_ | or2_1 | 3 022.1 | 49.5 | 217.4 |
| 13 | _08080_ | fall | _16090_ | and2_1 | 3 174.2 | 36.3 | 152.1 |
| 14 | _08117_ | fall | _16127_ | or2_1 | 3 391.6 | 49.5 | 217.4 |
| 15 | _08120_ | fall | _16131_ | and2_1 | 3 543.8 | 36.3 | 152.2 |
| 16 | _08164_ | fall | _16175_ | or2_1 | 3 761.2 | 49.5 | 217.4 |
| 17 | _08167_ | fall | _16179_ | and2_1 | 3 913.4 | 36.3 | 152.2 |
| 18 | _08210_ | fall | _16222_ | or2_1 | 4 130.8 | 49.5 | 217.4 |
| 19 | _08213_ | fall | _16226_ | and2_1 | 4 282.9 | 36.3 | 152.1 |
| 20 | _08259_ | fall | _16272_ | or2_1 | 4 500.3 | 49.5 | 217.4 |
| 21 | _08262_ | fall | _16276_ | and2_1 | 4 652.5 | 36.3 | 152.2 |
| 22 | _08302_ | fall | _16316_ | or2_1 | 4 869.9 | 49.5 | 217.4 |
| 23 | _08305_ | fall | _16320_ | and2_1 | 5 022.0 | 36.3 | 152.1 |
| 24 | _08345_ | fall | _16360_ | or2_1 | 5 239.4 | 49.5 | 217.4 |
| 25 | _08348_ | fall | _16364_ | and2_1 | 5 391.6 | 36.3 | 152.2 |
| 26 | _08382_ | fall | _16398_ | or2_1 | 5 609.0 | 49.5 | 217.4 |
| 27 | _08385_ | fall | _16402_ | and2_1 | 5 761.2 | 36.3 | 152.2 |
| 28 | _08415_ | fall | _16432_ | or2_1 | 5 978.6 | 49.5 | 217.4 |
| 29 | _08418_ | fall | _16436_ | and2_1 | 6 130.7 | 36.3 | 152.1 |
| 30 | _08437_ | fall | _16455_ | or2_1 | 6 348.1 | 49.5 | 217.4 |
| 31 | _08440_ | fall | _16459_ | and2_1 | 6 500.3 | 36.3 | 152.2 |
| 32 | _08452_ | fall | _16471_ | or2_1 | 6 717.7 | 49.2 | 217.4 |
| 33 | _08455_ | fall | _16475_ | and2_1 | 6 869.7 | 36.3 | 152.0 |
| 34 | _08458_ | fall | _16478_ | or2_1 | 7 087.1 | 49.2 | 217.4 |
| 35 | _08461_ | fall | _16482_ | and2_1 | 7 246.1 | 42.1 | 159.0 |
| 36 | _08470_ | fall | _16492_ | nand2b_1 | 7 370.4 | 33.9 | 124.3 |
| 37 | _08471_ | fall | _16493_ | and2_1 | 7 515.6 | 36.1 | 145.2 |
| 38 | _08472_ | fall | _16494_ | or2_1 | 7 720.6 | 54.1 | 205.0 |
| 39 | _08475_ | rise | _16498_ | nand2_1 | 7 812.2 | 83.3 | 91.6 |
| 40 | _08482_ | fall | _16506_ | nand2_1 | 7 857.1 | 30.7 | 44.9 |
| 41 | _08483_ | fall | _16507_ | and2_1 | 8 000.9 | 36.1 | 143.8 |
| 42 | _08484_ | fall | _16508_ | or2_1 | 8 209.5 | 56.8 | 208.6 |
| 43 | _08493_ | fall | _16519_ | or2_1 | 8 433.6 | 49.0 | 224.1 |
| 44 | _08508_ | fall | _16536_ | nand2b_1 | 8 560.8 | 35.0 | 127.2 |
| 45 | _08512_ | fall | _16540_ | and2_1 | 8 688.7 | 36.2 | 127.9 |
| 46 | _08515_ | fall | _16543_ | or2_1 | 8 915.4 | 56.4 | 226.7 |
| 47 | _08524_ | fall | _16554_ | or2_1 | 9 139.4 | 49.0 | 224.0 |
| 48 | _08539_ | fall | _16571_ | nand2b_1 | 9 266.6 | 35.0 | 127.2 |
| 49 | _08543_ | fall | _16575_ | and2_1 | 9 394.5 | 36.2 | 127.9 |
| 50 | _08546_ | fall | _16578_ | or2_1 | 9 617.7 | 53.5 | 223.2 |
| 51 | _08549_ | rise | _16582_ | nand2_1 | 9 709.1 | 83.3 | 91.4 |
| 52 | _08556_ | fall | _16590_ | nand2_1 | 9 754.0 | 30.7 | 44.9 |
| 53 | _08557_ | fall | _16591_ | and2_1 | 9 897.8 | 36.1 | 143.8 |
| 54 | _08558_ | fall | _16592_ | or2_1 | 10 096.9 | 49.7 | 199.1 |
| 55 | _08561_ | fall | _16596_ | and2_1 | 10 257.7 | 43.6 | 160.8 |
| 56 | _08564_ | rise | _16599_ | nor2_1 | 10 349.7 | 87.3 | 92.0 |
| 57 | _08565_ | fall | _16600_ | nor2_1 | 10 388.0 | 162.9 | 38.3 |
| 58 | _00399_ | fall | _16601_ | and2_1 | 10 585.9 | 31.2 | 197.9 |
Cells
at N = 4, W = 8, AccW = 32 · scored
| cell | count | µm² each | µm² in all | share |
|---|---|---|---|---|
and2_1 | 2 569 | 6.2560 | 16071.6640 | 20.2% |
dfxtp_1 | 802 | 20.0192 | 16055.3984 | 20.1% |
xor2_1 | 1 783 | 8.7584 | 15616.2272 | 19.6% |
nand2_1 | 3 592 | 3.7536 | 13482.9312 | 16.9% |
xnor2_1 | 811 | 8.7584 | 7103.0624 | 8.9% |
or2_1 | 512 | 6.2560 | 3203.0720 | 4.0% |
mux2_1 | 274 | 11.2608 | 3085.4592 | 3.9% |
nor2_1 | 721 | 3.7536 | 2706.3456 | 3.4% |
nor2b_1 | 205 | 6.2560 | 1282.4800 | 1.6% |
nand2b_1 | 164 | 6.2560 | 1025.9840 | 1.3% |
inv_1 | 10 | 3.7536 | 37.5360 | 0.0% |
buf_4 | 4 | 7.5072 | 30.0288 | 0.0% |
| total | 11 447 | 79700.1888 |
Repair (buffer_trees_v1): 4 buffers on 1 net, 0 tie cells, 2 weighing rounds; nothing is resized.
Legality: worst load 0.967 × max_capacitance, worst transition 0.974 × 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 | 3592 | 1.000 | 3592.000 |
AND | 2569 | 1.333 | 3424.477 |
XOR | 1783 | 2.000 | 3566.000 |
XNOR | 811 | 2.000 | 1622.000 |
DFF_P | 802 | 5.333 | 4277.066 |
NOR | 721 | 1.000 | 721.000 |
OR | 512 | 1.333 | 682.496 |
MUX | 274 | 2.333 | 639.242 |
ANDNOT | 205 | 1.333 | 273.265 |
ORNOT | 164 | 1.333 | 218.612 |
NOT | 10 | 0.667 | 6.670 |
| total | 11443 | 19022.828 |
The record
- task
matmul- player
- vacuity-check
- assisted by
- Claude Sonnet 5.5
- submitted
- 2026-11-10
- notes
- reference architecture with free-running operand shifters, step counter and accumulators (no hold mux; C holds because b_sh is shifted out); only busy and done are controlled; latency N
- scoring point
- AccW = 32, N = 4, W = 8
- design
f10.svsha25673385c65005d82ccb977d186be60dc1b7facf38d205672c688dbc4eef384cca4- proof
S10.leansha256fd011cd83585c5b32067fbe5f0f792324c6494ce5771f7e3cb84ea82aa1a98f8- canonical text
- sha256
e429dd71608c3cda7053f0affbed59afe39dd283e92e5e3406ba14615129bce4 - record hash
9fdf50f9ae02743512989dd85fb239f25cb535cbcfa873e9a6f01f678b3b5316- downloads
9fdf50f9ae02.sv9fdf50f9ae02.lean9fdf50f9ae02.json- previous frontier
- none: no submission of this task is dated before this one; 3 other submissions are dated the same day (2026-11-10), which does not order them
The proof
- code lines
- 722
- build time
- 8.05 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
892ba94b00e36c0f83e15b4acf3641cee47d43c8- lean-surfaces revision
892ba94b00e36c0f83e15b4acf3641cee47d43c8- canonicalization script
bbd11227d45dc1365f27597fd45c565fe721ba2648450588fc4fd983d00b367b- scoring script
c3874dbfc85921a87df6eacdfc6b2c9a0e2b34edd5738951e0d8bdb51f4558b2- GE table
0a1aa1147b6916efa4a41e03aa3c8c70e68c1e3fd7a0e9c9a3cbc177edefd1f6- extractor
4930696021c588c75dc890034c534c95560ad9a28a4955b87d1f8d68b5edbc0c- canonical RTLIL
8ef35b30d0f52e3159cc8c02622c0dcbb64f74a66a788c7a644290f222b2e066
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
162dd944a1d8897a4404380b5e64d49bc17c89ef7e0cbcb9ae2b4832f3f5044b
| point | latency (cycles) | seeds | clock cycles | transactions | mismatches | netlist sha256 |
|---|---|---|---|---|---|---|
| AccW = 8, N = 2, W = 4 | 2 | 8 | 828 | 192 | 0 | de575d31fdb1 |
| AccW = 32, N = 4, W = 8 | 4 | 8 | 1 257 | 192 | 0 | 162dd944a1d8 |
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 · this design | submission | 79 700.19 | 42 756 | 3 407 661 |
matmul_g1 | 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 · this design | submission | 1 216.745 | 5 992 | 7291 |
matmul_g1 | 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 9fdf50f9ae02743512989dd85fb239f25cb535cbcfa873e9a6f01f678b3b5316.sv --task tasks/matmul --seq --path \
--json out.json --netlist scored.v --repaired-netlist repaired.v
Expect, in out.json:
- score.rtlil_sha256
8ef35b30d0f52e3159cc8c02622c0dcbb64f74a66a788c7a644290f222b2e066- ppa.netlist_sha256
162dd944a1d8897a4404380b5e64d49bc17c89ef7e0cbcb9ae2b4832f3f5044b- ppa.repair.netlist_sha256
56933abd2f8ba210d2a582509cdc956dfa76a7d0ce85edef6ad1c27764d46336- ppa.area_e4um2_sky130
797001888- ppa.delay_ps_sky130
10689
The schematic and the critical-path data are site/schematics/matmul/73385c65005d82cc.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 65b518d638b937bc…).
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 9fdf50f9ae02743512989dd85fb239f25cb535cbcfa873e9a6f01f678b3b5316.sv --task tasks/matmul --seq --pdk asap7 --no-mapped \
--json out.json
Expect, in out.json:
- ppa.netlist_sha256
162dd944a1d8897a4404380b5e64d49bc17c89ef7e0cbcb9ae2b4832f3f5044b- ppa.repair.netlist_sha256
399ab1bed57c64fb3d4cae671312deb17e75832e0b8b18eb81d1f9c0eb518793- ppa.area_e5um2_asap7
121674474- ppa.delay_ps_asap7
1498
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=9fdf50f9ae02743512989dd85fb239f25cb535cbcfa873e9a6f01f678b3b5316
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.