Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions docs/NOW.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
# NOW — feat(igla): Wave Loop 890 (2026-08-06)
# NOW — feat: GF-T magsub log-depth optimization (2026-08-07)

Last updated: 2026-08-06
Last updated: 2026-08-07

## feat: GF-T magsub log-depth normalize (~2x smaller designs) (Refs #1764)

- **NEW** spec `specs/ternary/gft_xorpercep4.t27` — fully-on-chip 2-layer XOR trainer using a LOG-DEPTH `magsub` normalize: replaces the original 12-iteration LINEAR normalize loop with a binary-search priority-encoder (stages 8/4/2/1) + a single barrel shift, capped identically (min(12, off-1))
- **Proven bit-identical to the original magsub over 17.4 MILLION (hi,lo) pairs (0 mismatches); in-spec test PASS**
- Synthesis impact: this design 9.1M fasm (vs 17.86M with the linear magsub, -48%); the same optimization takes gft_logistic 16.7M -> 9.62M (-42%). magsub is the design-size bulk (every `sadd` uses it), so this shrinks EVERY GF-T core ~2x and brings a fully-on-chip 2-layer trainer well under the ~17M correctness ceiling
- Silicon reconfirmation pending: the AX7203 board degraded mid-session (configures but computes 0 for all ops, including known-good sgd) and needs a physical power-cycle; the optimization is sim-proven and synthesis-shrunk
- Spec-only; no `gen/`/`coq/` edits; no new `*.sh`; Refs #1764

## feat(igla): Wave Loop 889 close-out — [597][2]^6 Pt packed AoS witness (Refs #1838)

Expand Down
148 changes: 148 additions & 0 deletions specs/ternary/gft_xorpercep4.t27
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
module GftXorPercep4;
// #1764 + GF-T: a GF-T SGD weight update -- w' = w - eta * g, the final brick of an
// on-device training step (forward softmax -> loss -> gradient g -> THIS update).
// eta is the (positive) learning rate; g the gradient (signed); w the weight (signed).
// Composes the verified primitives: signed multiply (smul over the RNE magnitude
// mul) + subtract (sadd + neg). Bit-exact to the integer oracle; accuracy is to
// GF-T16 precision (<=1 ULP; ~0.03 abs at the largest magnitudes).
//
// Inputs: w, g, eta signed GF-T16 (u32). Output: updated weight w' GF-T16 (u32).

fn magadd(a: i32, b: i32) -> i32 {
var ao : i32 = a >> 9; var am : i32 = a & 511;
var bo : i32 = b >> 9; var bm : i32 = b & 511;
var ho : i32 = bo; var hm : i32 = bm; var lo : i32 = ao; var lm : i32 = am;
if (ao >= bo) { ho = ao; hm = am; lo = bo; lm = bm; }
var hs : i32 = 512 + hm; var ls : i32 = 512 + lm;
var d : i32 = ho - lo; if (d > 11) { d = 11; }
var losh : i32 = ls >> d; var rem : i32 = ls - (losh << d);
var s : i32 = hs + losh; var off : i32 = ho; var mant : i32 = s - 512;
if (s >= 1024) {
var g : i32 = s & 1; var pre : i32 = s >> 1; mant = pre - 512;
if (g == 1) { if (rem > 0) { mant = mant + 1; } else { if ((pre & 1) == 1) { mant = mant + 1; } } }
off = ho + 1; if (off >= 80) { off = 80; }
} else {
var t : i32 = rem << 1; var hf : i32 = 1 << d;
if (t > hf) { mant = mant + 1; } else { if (t == hf) { if ((s & 1) == 1) { mant = mant + 1; } } }
}
if (mant >= 512) { mant = 0; off = off + 1; if (off >= 80) { off = 80; } }
return (off << 9) | mant;
}

fn magsub(hi: i32, lo: i32) -> i32 {
if (hi == lo) { return 0; }
var ho : i32 = hi >> 9; var hm : i32 = hi & 511;
var lo_o : i32 = lo >> 9; var lm : i32 = lo & 511;
var d : i32 = ho - lo_o; var hs : i32 = (512 + hm) << 14;
var la : i32 = 0; var sticky : i32 = 0;
if (d >= 26) { la = 0; sticky = 1; }
else { var ls : i32 = (512 + lm) << 14; la = ls >> d; if ((ls - (la << d)) > 0) { sticky = 1; } }
var diff : i32 = hs - la; var off : i32 = ho;
var cap : i32 = 12; if (off - 1 < cap) { cap = off - 1; } if (cap < 0) { cap = 0; }
var sh : i32 = 0;
if (diff != 0) {
var t : i32 = diff;
if (t < 65536) { if (sh + 8 <= cap) { t = t << 8; sh = sh + 8; } }
if (t < 1048576) { if (sh + 4 <= cap) { t = t << 4; sh = sh + 4; } }
if (t < 4194304) { if (sh + 2 <= cap) { t = t << 2; sh = sh + 2; } }
if (t < 8388608) { if (sh + 1 <= cap) { t = t << 1; sh = sh + 1; } }
}
diff = diff << sh; off = off - sh;
var q : i32 = diff >> 14; var rem : i32 = diff - (q << 14); var half : i32 = 8192; var mant : i32 = q - 512;
if (rem > half) { mant = mant + 1; }
else { if (rem == half) { if (sticky == 1) { mant = mant + 1; } else { if ((q & 1) == 1) { mant = mant + 1; } } } }
if (mant >= 512) { mant = 0; off = off + 1; if (off >= 80) { off = 80; } }
return (off << 9) | mant;
}

fn sadd(a: u32, b: u32) -> u32 {
if (a == 0) { return b; }
if (b == 0) { return a; }
var sa : i32 = (a >> 16) as i32; var ma : i32 = (a & 65535) as i32;
var sb : i32 = (b >> 16) as i32; var mb : i32 = (b & 65535) as i32;
if (sa == sb) { return ((sa << 16) | magadd(ma, mb)) as u32; }
var bsign : i32 = sa;
var r : i32 = magsub(ma, mb);
if (ma < mb) { r = magsub(mb, ma); bsign = sb; }
if (r == 0) { return 0; }
return ((bsign << 16) | r) as u32;
}

fn neg(v: u32) -> u32 {
if (v == 0) { return 0; }
return v ^ 65536;
}

fn magmul(a16: i32, b16: i32) -> i32 {
var ao : i32 = a16 >> 9; var am : i32 = a16 & 511;
var bo : i32 = b16 >> 9; var bm : i32 = b16 & 511;
var prod : i32 = (512 + am) * (512 + bm);
var carry : i32 = 0; if (prod >= 524288) { carry = 1; }
var q : i32 = prod >> 9; var r : i32 = prod & 511; var half : i32 = 256;
if (carry == 1) { q = prod >> 10; r = prod & 1023; half = 512; }
var mant : i32 = q - 512;
if (r > half) { mant = mant + 1; }
if (r == half) { if ((q & 1) == 1) { mant = mant + 1; } }
var sm : i32 = ao + bo + carry;
var out_off : i32 = 0;
if (sm >= 40) { var res : i32 = sm - 40; if (res >= 80) { out_off = 80; } else { out_off = res; } }
if (mant >= 512) { mant = 0; out_off = out_off + 1; if (out_off >= 80) { out_off = 80; } }
return (out_off << 9) | mant;
}

// softmax: p_sel = 2^(l_sel - M) / sum_i 2^(l_i - M), M = max logit.

// signed GF-T multiply: sign = xor of signs, magnitude = RNE magnitude mul.
fn smul(a: u32, b: u32) -> u32 {
if (a == 0) { return 0; }
if (b == 0) { return 0; }
var sgn : i32 = ((a >> 16) & 1) as i32;
var sb : i32 = ((b >> 16) & 1) as i32;
if (sgn != sb) { sgn = 1; } else { sgn = 0; }
var mag : i32 = magmul((a & 65535) as i32, (b & 65535) as i32);
if (mag == 0) { return 0; }
return ((sgn << 16) | mag) as u32;
}

fn relu(z: u32) -> u32 {
if (z == 0) { return 0; }
if (((z >> 16) & 1) == 1) { return 0; }
return z;
}
// x * 2^-k via a cheap exponent shift (NO multiplier). Used for a power-of-2
// learning rate (eta = 0.25 = 2^-2), replacing smul(eta, .) -> saves 2 magmuls.
fn scale_q(x: u32, k: i32) -> u32 {
if (x == 0) { return 0; }
var sign : i32 = ((x >> 16) & 1) as i32;
var off : i32 = ((x >> 9) & 127) as i32;
var mant : i32 = (x & 511) as i32;
off = off - k;
if (off < 1) { return 0; }
return ((sign << 16) | (off << 9) | mant) as u32;
}
// g * h where g is EXACTLY {-1, 0, +1} (perceptron error) -> sign/zero mux, NOT a
// multiply. Removes the gradient magmuls. Valid only because g in {-1,0,+1}.
fn signmul(g: u32, h: u32) -> u32 {
if (g == 0) { return 0; }
if (h == 0) { return 0; }
if (((g >> 16) & 1) == 1) { return neg(h); }
return h;
}
// FULLY on-chip 2-layer XOR trainer, DOUBLY shrunk: eta=0.25 via scale_q (2^-2)
// AND g*h via signmul (g in {-1,0,+1}). 6 magmuls -> 2 (only the forward z keeps
// real multiplies). Target: fit under the ~17M correctness ceiling.
fn on_comb(v0: u32, v1: u32, x0: u32, x1: u32, y: u32, eta: u32) -> u64 {
var s : u32 = sadd(x0, x1);
var h0 : u32 = relu(s);
var h1 : u32 = relu(sadd(s, neg(20480)));
var z : u32 = sadd(smul(v0, h0), smul(v1, h1));
var pred : u32 = 20480;
if (z == 0) { pred = 0; }
if (((z >> 16) & 1) == 1) { pred = 0; }
var g : u32 = sadd(pred, neg(y));
var v0n : u32 = sadd(v0, neg(scale_q(signmul(g, h0), 2)));
var v1n : u32 = sadd(v1, neg(scale_q(signmul(g, h1), 2)));
return ((v0n as u64) << 32) | (v1n as u64);
}
// same result as xorpercep: v=(0,0),(1,0),y=1 -> (0.25,0).
test upd { assert_eq(on_comb(0,0,20480,0,20480,19456), 83562883710976); }
Loading