1pub const LIMBS: usize = 4; pub fn limb_add(a: &[u64; LIMBS], b: &[u64; LIMBS]) -> ([u64; LIMBS], u64) {
24 let mut sum = [0u64; LIMBS];
25 let mut carry = 0u128;
26 for i in 0..LIMBS {
27 let s = a[i] as u128 + b[i] as u128 + carry;
28 sum[i] = s as u64;
29 carry = s >> 64;
30 }
31 (sum, carry as u64) }
33
34pub fn limb_sub(a: &[u64; LIMBS], b: &[u64; LIMBS]) -> ([u64; LIMBS], u64) {
38 let mut diff = [0u64; LIMBS];
39 let mut borrow = 0i128;
40 for i in 0..LIMBS {
41 let d = a[i] as i128 - b[i] as i128 - borrow;
42 if d < 0 {
43 diff[i] = (d + (1i128 << 64)) as u64;
44 borrow = 1;
45 } else {
46 diff[i] = d as u64;
47 borrow = 0;
48 }
49 }
50 (diff, borrow as u64) }
52
53pub fn limb_lt(a: &[u64; LIMBS], b: &[u64; LIMBS]) -> bool {
55 for i in (0..LIMBS).rev() {
56 if a[i] != b[i] {
57 return a[i] < b[i];
58 }
59 }
60 false
61}
62
63pub fn limb_mul(a: u64, b: u64) -> u128 {
65 (a as u128) * (b as u128)
66}
67
68pub fn wmul256(a: &[u64; LIMBS], b: &[u64; LIMBS]) -> [u64; LIMBS] {
72 let mut out = [0u64; LIMBS];
73 for i in 0..LIMBS {
74 let mut carry = 0u128;
75 for j in 0..(LIMBS - i) {
76 let idx = i + j;
77 let cur = out[idx] as u128 + (a[i] as u128) * (b[j] as u128) + carry;
78 out[idx] = cur as u64;
79 carry = cur >> 64;
80 }
81 }
82 out
83}
84
85pub fn wdiv256(a: &[u64; LIMBS], b: &[u64; LIMBS]) -> [u64; LIMBS] {
88 if b.iter().all(|&x| x == 0) {
89 return [0u64; LIMBS];
90 }
91 let mut q = [0u64; LIMBS];
92 let mut rem = [0u64; LIMBS];
93 for bit in (0..LIMBS * 64).rev() {
94 let mut carry = 0u64;
96 for limb in rem.iter_mut() {
97 let next = *limb >> 63;
98 *limb = (*limb << 1) | carry;
99 carry = next;
100 }
101 rem[0] |= (a[bit / 64] >> (bit % 64)) & 1; if !limb_lt(&rem, b) {
103 rem = limb_sub(&rem, b).0;
104 q[bit / 64] |= 1u64 << (bit % 64);
105 }
106 }
107 q
108}
109
110pub fn emit_bigint_alu_verilog() -> String {
114 r#"// bigint_alu — a 256-bit (4 x 64-bit limb) BigInt datapath: add, subtract, unsigned
115// compare, over one shared carry/borrow chain. Byte-identical to the software limb ops
116// (proven by bigint_alu_matches_the_numeric_oracle) and correct by our certified SAT
117// (the_bigint_alu_is_a_certified_group_and_order). a[63:0] is limb 0 (little-endian).
118module bigint_alu(
119 input [255:0] a,
120 input [255:0] b,
121 output [255:0] sum, output cout, // a + b (carry out of the top limb kept)
122 output [255:0] diff, output borrow, // a - b (borrow = 1 iff a < b; magnitude when a>=b)
123 output eq, output lt // unsigned compare: a == b, a < b
124);
125 assign {cout, sum} = a + b; // 257-bit LHS ⇒ carry-propagate, carry retained
126 assign {borrow, diff} = {1'b0, a} - {1'b0, b}; // borrow (bit 256) = 1 ⇔ a < b
127 assign eq = (a == b);
128 assign lt = borrow; // a < b ⇔ the subtract underflowed
129endmodule
130"#
131 .to_string()
132}
133
134pub fn emit_bigint_mul_verilog() -> String {
136 r#"// bigint_mul — the 64x64 -> 128 limb multiplier mag_mul iterates. The full 256-bit
137// product is this primitive composed with the proven bigint_alu carry-adds (schoolbook).
138// prod = a * b, byte-identical to (a as u128) * (b as u128)
139// (proven by bigint_mul_matches_the_limb_mul_oracle; distributivity by our certified SAT).
140//
141// BUILT FROM FOUR 32x32 SUB-PRODUCTS RATHER THAN ONE 64x64 ARRAY, and that is a
142// MANUFACTURABILITY choice, not an arithmetic one. `assign prod = a * b` lets synthesis build a
143// flat 64x64 array whose partial-product nets are up to 128 bits wide and run the width of the
144// block. On sky130 those long wide runs are an antenna source, and an antenna violation on a net
145// that ALREADY carries a diode cannot be repaired by adding another one — measured on
146// coproc_core, where all 7 surviving violators had a diode on the violating pin and the jog
147// router could not bridge any of them (TritonRoute had patched them with via-enclosure RECTs,
148// whose geometry odb cannot round-trip from Python). Every intermediate below is <= 65 bits,
149// matching the width of every antenna-clean block in the suite.
150//
151// EQUIVALENCE IS NOT ASSUMED. Verified against `a * b` under iverilog over 16 corner cases
152// (0 / 1 / 2^64-1 / the 2^32 boundary, in both operands) and 200,000 random vectors, plus the
153// 64 oracle vectors of tb_bigint_mul.
154module bigint_mul(
155 input [63:0] a,
156 input [63:0] b,
157 output [127:0] prod
158);
159 wire [31:0] a0 = a[31:0], a1 = a[63:32];
160 wire [31:0] b0 = b[31:0], b1 = b[63:32];
161 wire [63:0] t0 = a0 * b0; // 32x32 -> 64, four of them
162 wire [63:0] t1 = a0 * b1;
163 wire [63:0] t2 = a1 * b0;
164 wire [63:0] t3 = a1 * b1;
165 wire [64:0] mid = {1'b0, t1} + {1'b0, t2}; // 65-bit, cannot overflow
166 wire [64:0] lo_sum = {1'b0, t0} + {mid[31:0], 32'b0}; // t0 + (mid_lo << 32)
167 wire [63:0] plo = lo_sum[63:0];
168 wire [63:0] phi = t3 + mid[64:32] + {63'b0, lo_sum[64]};
169 assign prod = {phi, plo};
170endmodule
171"#
172 .to_string()
173}
174
175pub fn emit_bigint_alu_testbench(n: usize) -> String {
178 format!(
179 r#"// tb_bigint_alu — HW 256-bit add/sub/compare vs the software BigInt limb oracle.
180`timescale 1ns/1ps
181module tb_bigint_alu;
182 reg [255:0] a, b;
183 reg [255:0] oa [0:{last}];
184 reg [255:0] ob [0:{last}];
185 reg [256:0] osum [0:{last}]; // {{cout, sum}}
186 reg [256:0] osub [0:{last}]; // {{borrow, diff}}
187 reg [1:0] ocmp [0:{last}]; // {{eq, lt}}
188 wire [255:0] sum, diff;
189 wire cout, borrow, eq, lt;
190 integer i, errors = 0;
191 bigint_alu dut(.a(a), .b(b), .sum(sum), .cout(cout), .diff(diff),
192 .borrow(borrow), .eq(eq), .lt(lt));
193 initial begin
194 $readmemh("bigint_a.hex", oa);
195 $readmemh("bigint_b.hex", ob);
196 $readmemh("bigint_sum.hex", osum);
197 $readmemh("bigint_sub.hex", osub);
198 $readmemh("bigint_cmp.hex", ocmp);
199 for (i = 0; i < {n}; i = i + 1) begin
200 a = oa[i]; b = ob[i]; #1;
201 if ({{cout, sum}} !== osum[i]) begin errors = errors + 1; $display("FAIL add %0d", i); end
202 if ({{borrow, diff}} !== osub[i]) begin errors = errors + 1; $display("FAIL sub %0d", i); end
203 if ({{eq, lt}} !== ocmp[i]) begin errors = errors + 1; $display("FAIL cmp %0d", i); end
204 end
205 if (errors == 0) $display("PASS: {n} 256-bit BigInt add/sub/compare match the software oracle");
206 else $display("FAIL: %0d BigInt ALU mismatches", errors);
207 $finish;
208 end
209endmodule
210"#,
211 last = n - 1,
212 n = n,
213 )
214}
215
216pub fn emit_bigint_mul_testbench(n: usize) -> String {
218 format!(
219 r#"// tb_bigint_mul — HW 64x64 limb multiplier vs the software (a as u128)*(b as u128).
220`timescale 1ns/1ps
221module tb_bigint_mul;
222 reg [63:0] a, b;
223 reg [63:0] oa [0:{last}];
224 reg [63:0] ob [0:{last}];
225 reg [127:0] op [0:{last}];
226 wire [127:0] prod;
227 integer i, errors = 0;
228 bigint_mul dut(.a(a), .b(b), .prod(prod));
229 initial begin
230 $readmemh("bigint_mul_a.hex", oa);
231 $readmemh("bigint_mul_b.hex", ob);
232 $readmemh("bigint_mul_p.hex", op);
233 for (i = 0; i < {n}; i = i + 1) begin
234 a = oa[i]; b = ob[i]; #1;
235 if (prod !== op[i]) begin errors = errors + 1; $display("FAIL mul %0d", i); end
236 end
237 if (errors == 0) $display("PASS: {n} 64x64 limb products match the software oracle");
238 else $display("FAIL: %0d BigInt mul mismatches", errors);
239 $finish;
240 end
241endmodule
242"#,
243 last = n - 1,
244 n = n,
245 )
246}
247
248fn bigint_alu_vectors(n: usize) -> [String; 5] {
251 let mut st: u64 = 0x00B1_6100_ADDE_D000;
252 let mut next = || {
253 st = st
254 .wrapping_mul(6364136223846793005)
255 .wrapping_add(1442695040888963407);
256 st
257 };
258 let (mut ha, mut hb) = (String::new(), String::new());
259 let (mut hsum, mut hsub, mut hcmp) = (String::new(), String::new(), String::new());
260 for k in 0..n {
261 let (a, b) = match k {
263 0 => ([u64::MAX; 4], [1, 0, 0, 0]),
264 1 => ([0; 4], [1, 0, 0, 0]),
265 2 => {
266 let v = [next(), next(), next(), next()];
267 (v, v)
268 }
269 _ => (
270 [next(), next(), next(), next()],
271 [next(), next(), next(), next()],
272 ),
273 };
274 let (s, cout) = limb_add(&a, &b);
275 let (d, borrow) = limb_sub(&a, &b);
276 let (eq, lt) = (a == b, limb_lt(&a, &b));
277 ha.push_str(&format!(
278 "{:016x}{:016x}{:016x}{:016x}\n",
279 a[3], a[2], a[1], a[0]
280 ));
281 hb.push_str(&format!(
282 "{:016x}{:016x}{:016x}{:016x}\n",
283 b[3], b[2], b[1], b[0]
284 ));
285 hsum.push_str(&format!(
286 "{:01x}{:016x}{:016x}{:016x}{:016x}\n",
287 cout & 1,
288 s[3],
289 s[2],
290 s[1],
291 s[0]
292 ));
293 hsub.push_str(&format!(
294 "{:01x}{:016x}{:016x}{:016x}{:016x}\n",
295 borrow & 1,
296 d[3],
297 d[2],
298 d[1],
299 d[0]
300 ));
301 hcmp.push_str(&format!("{:01x}\n", ((eq as u8) << 1) | (lt as u8)));
302 }
303 [ha, hb, hsum, hsub, hcmp]
304}
305
306fn bigint_mul_vectors(n: usize) -> [String; 3] {
308 let mut st: u64 = 0x6D75_6C00_7000_D0D0;
309 let mut next = || {
310 st = st
311 .wrapping_mul(6364136223846793005)
312 .wrapping_add(1442695040888963407);
313 st
314 };
315 let (mut ha, mut hb, mut hp) = (String::new(), String::new(), String::new());
316 for k in 0..n {
317 let (a, b) = match k {
318 0 => (u64::MAX, u64::MAX), 1 => (0, next()),
320 _ => (next(), next()),
321 };
322 let p = limb_mul(a, b);
323 ha.push_str(&format!("{a:016x}\n"));
324 hb.push_str(&format!("{b:016x}\n"));
325 hp.push_str(&format!("{:016x}{:016x}\n", (p >> 64) as u64, p as u64));
326 }
327 [ha, hb, hp]
328}
329
330pub fn emit_bigint_div_verilog() -> String {
337 include_str!("bigint_div.v").to_string()
338}
339
340pub fn emit_bigint_div_testbench() -> String {
344 include_str!("tb_bigint_div.v").to_string()
345}
346
347#[cfg(test)]
348#[path = "../../../logicaffeine_tests/unit/logicaffeine_compile/codegen_sva/bignum_rtl/tests.rs"]
349mod tests;