1use super::fabric_rtl::{AluOp, FabricOp, Src};
11
12#[derive(
14 Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, serde::Serialize, serde::Deserialize, Ord,
15)]
16pub enum LaneTy {
17 I8x8,
18 I16x4,
19 I32x2,
20}
21
22#[derive(
26 Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, serde::Serialize, serde::Deserialize, Ord,
27)]
28pub enum VecOp {
29 Add,
30 And,
31 Or,
32 Xor,
33}
34
35impl VecOp {
36 pub fn eval(self, ty: LaneTy, a: u64, b: u64) -> u64 {
38 match self {
39 VecOp::Add => eval_lanewise_add(ty, a, b),
40 VecOp::And => a & b,
41 VecOp::Or => a | b,
42 VecOp::Xor => a ^ b,
43 }
44 }
45
46 pub fn verilog(self, ty: LaneTy, a: &str, b: &str) -> String {
49 match self {
50 VecOp::Add => {
51 let m = swar_lo_mask(ty);
52 let hm = !m;
53 format!("((({a}) & 64'h{m:016x}) + (({b}) & 64'h{m:016x})) ^ ((({a}) ^ ({b})) & 64'h{hm:016x})")
54 }
55 VecOp::And => format!("(({a}) & ({b}))"),
56 VecOp::Or => format!("(({a}) | ({b}))"),
57 VecOp::Xor => format!("(({a}) ^ ({b}))"),
58 }
59 }
60}
61
62impl LaneTy {
63 pub fn lanes(self) -> usize {
64 match self {
65 LaneTy::I8x8 => 8,
66 LaneTy::I16x4 => 4,
67 LaneTy::I32x2 => 2,
68 }
69 }
70 pub fn width(self) -> usize {
71 64 / self.lanes()
72 }
73 pub const ALL: &'static [LaneTy] = &[LaneTy::I8x8, LaneTy::I16x4, LaneTy::I32x2];
74}
75
76fn lane_full_mask(ty: LaneTy) -> u64 {
78 let w = ty.width();
79 let lane = if w == 64 { u64::MAX } else { (1u64 << w) - 1 };
80 (0..ty.lanes()).fold(0u64, |m, j| m | (lane << (j * w)))
81}
82
83fn swar_lo_mask(ty: LaneTy) -> u64 {
85 let w = ty.width();
86 let lane = (1u64 << (w - 1)) - 1;
87 (0..ty.lanes()).fold(0u64, |m, j| m | (lane << (j * w)))
88}
89
90pub fn lanewise_add_body(ty: LaneTy) -> (Vec<FabricOp>, usize) {
94 let (w, lanes) = (ty.width(), ty.lanes());
95 let full = lane_full_mask(ty) & ((1u64 << w) - 1); let mut cells: Vec<FabricOp> = Vec::new();
97 let mut lane_vals: Vec<Src> = Vec::new();
98 for j in 0..lanes {
99 let sh = (j * w) as i64;
100 let (aj, bj) = if j == 0 {
101 (Src::In(0), Src::In(1))
102 } else {
103 cells.push(FabricOp {
104 op: AluOp::UShr,
105 a: Src::In(0),
106 b: Src::Const(sh),
107 });
108 let a = Src::Val(cells.len() - 1);
109 cells.push(FabricOp {
110 op: AluOp::UShr,
111 a: Src::In(1),
112 b: Src::Const(sh),
113 });
114 let b = Src::Val(cells.len() - 1);
115 (a, b)
116 };
117 cells.push(FabricOp {
118 op: AluOp::Add,
119 a: aj,
120 b: bj,
121 });
122 let sum = Src::Val(cells.len() - 1);
123 cells.push(FabricOp {
124 op: AluOp::And,
125 a: sum,
126 b: Src::Const(full as i64),
127 });
128 let masked = Src::Val(cells.len() - 1);
129 let laneval = if j == 0 {
130 masked
131 } else {
132 cells.push(FabricOp {
133 op: AluOp::Shl,
134 a: masked,
135 b: Src::Const(sh),
136 });
137 Src::Val(cells.len() - 1)
138 };
139 lane_vals.push(laneval);
140 }
141 let mut acc = lane_vals[0];
143 for &lv in &lane_vals[1..] {
144 cells.push(FabricOp {
145 op: AluOp::Or,
146 a: acc,
147 b: lv,
148 });
149 acc = Src::Val(cells.len() - 1);
150 }
151 let result = match acc {
152 Src::Val(k) => k,
153 _ => cells.len() - 1,
154 };
155 (cells, result)
156}
157
158pub fn swar_add_body(ty: LaneTy) -> (Vec<FabricOp>, usize) {
162 let m = swar_lo_mask(ty) as i64;
163 let hm = !swar_lo_mask(ty) as i64;
164 let cells = vec![
165 FabricOp {
166 op: AluOp::And,
167 a: Src::In(0),
168 b: Src::Const(m),
169 }, FabricOp {
171 op: AluOp::And,
172 a: Src::In(1),
173 b: Src::Const(m),
174 }, FabricOp {
176 op: AluOp::Add,
177 a: Src::Val(0),
178 b: Src::Val(1),
179 }, FabricOp {
181 op: AluOp::Xor,
182 a: Src::In(0),
183 b: Src::In(1),
184 }, FabricOp {
186 op: AluOp::And,
187 a: Src::Val(3),
188 b: Src::Const(hm),
189 }, FabricOp {
191 op: AluOp::Xor,
192 a: Src::Val(2),
193 b: Src::Val(4),
194 }, ];
196 (cells, 5)
197}
198
199pub fn naive_add_body() -> (Vec<FabricOp>, usize) {
202 (
203 vec![FabricOp {
204 op: AluOp::Add,
205 a: Src::In(0),
206 b: Src::In(1),
207 }],
208 0,
209 )
210}
211
212pub fn eval_lanewise_fadd_f32x2(a: u64, b: u64) -> u64 {
218 let lo = f32::from_bits(a as u32) + f32::from_bits(b as u32);
219 let hi = f32::from_bits((a >> 32) as u32) + f32::from_bits((b >> 32) as u32);
220 ((hi.to_bits() as u64) << 32) | lo.to_bits() as u64
221}
222
223pub fn eval_lanewise_fmul_f32x2(a: u64, b: u64) -> u64 {
225 let lo = f32::from_bits(a as u32) * f32::from_bits(b as u32);
226 let hi = f32::from_bits((a >> 32) as u32) * f32::from_bits((b >> 32) as u32);
227 ((hi.to_bits() as u64) << 32) | lo.to_bits() as u64
228}
229
230pub fn pack_f32x2(lane0: f32, lane1: f32) -> u64 {
232 ((lane1.to_bits() as u64) << 32) | lane0.to_bits() as u64
233}
234
235pub const FP32_ADD_V: &str = r#"module fp32_add(input [31:0] a, input [31:0] b_in, input sub, output reg [31:0] y);
242 localparam [31:0] QNAN = 32'h7FC00000;
243 reg [31:0] b;
244 reg sa, sb;
245 reg [7:0] ea, eb;
246 reg [22:0] ma, mb;
247 reg azero,bzero,ainf,binf,anan,bnan,asub,bsub;
248 reg [8:0] xa, xb;
249 reg ha, hb;
250 reg [23:0] siga, sigb;
251 reg [11:0] exp_l, shamt, lead;
252 reg [23:0] sig_big, sig_small;
253 reg sgn_big, sgn_small, res_sign, op_is_sub;
254 reg [127:0] big_ext, small_ext, sum_ext, norm_sig;
255 integer i;
256 reg signed [11:0] norm_exp;
257 reg [24:0] rounded;
258 reg guard, roundb, sticky, lsb, round_up;
259 reg [23:0] final_sig;
260
261 function [31:0] pack_sub32(input sgn, input [127:0] wsum, input [11:0] wlead, input signed [11:0] bexp);
262 reg [11:0] rshift; reg [127:0] win; reg g2,r2,s2,l2,up2; reg [24:0] rr;
263 begin
264 rshift = 12'd1 - bexp;
265 if (wlead >= 12'd26) begin
266 win = wsum >> (wlead - 12'd26);
267 if (|(wsum & ((128'd1 << (wlead - 12'd26)) - 128'd1))) win[0] = 1'b1;
268 end else begin
269 win = wsum << (12'd26 - wlead);
270 end
271 if (rshift >= 12'd128) begin s2 = |win; win = 128'd0; win[0] = s2; end
272 else if (rshift != 12'd0) begin
273 if (|(win & ((128'd1 << rshift) - 128'd1))) begin win = win >> rshift; win[0] = 1'b1; end
274 else win = win >> rshift;
275 end
276 g2 = win[2]; r2 = win[1]; s2 = win[0]; l2 = win[3];
277 up2 = g2 & (r2 | s2 | l2);
278 rr = {1'b0, win[26:3]} + (up2 ? 25'd1 : 25'd0);
279 if (rr[23]) pack_sub32 = {sgn, 8'd1, rr[22:0]};
280 else pack_sub32 = {sgn, 8'd0, rr[22:0]};
281 end
282 endfunction
283
284 always @(*) begin
285 b = sub ? {~b_in[31], b_in[30:0]} : b_in;
286 sa = a[31]; sb = b[31];
287 ea = a[30:23]; eb = b[30:23];
288 ma = a[22:0]; mb = b[22:0];
289 azero = (ea==8'd0) && (ma==23'd0); bzero = (eb==8'd0) && (mb==23'd0);
290 ainf = (ea==8'hFF) && (ma==23'd0); binf = (eb==8'hFF) && (mb==23'd0);
291 anan = (ea==8'hFF) && (ma!=23'd0); bnan = (eb==8'hFF) && (mb!=23'd0);
292 asub = (ea==8'd0) && (ma!=23'd0); bsub = (eb==8'd0) && (mb!=23'd0);
293 xa = asub ? 9'd1 : {1'b0, ea};
294 xb = bsub ? 9'd1 : {1'b0, eb};
295 ha = (ea!=8'd0); hb = (eb!=8'd0);
296 siga = {ha, ma}; sigb = {hb, mb};
297 y = 32'd0;
298 // pre-set datapath regs (avoid latches in the combinational always)
299 exp_l=12'd0; shamt=12'd0; sig_big=24'd0; sig_small=24'd0; sgn_big=1'b0; sgn_small=1'b0;
300 op_is_sub=1'b0; res_sign=1'b0; big_ext=128'd0; small_ext=128'd0; sum_ext=128'd0; lead=12'd0;
301 norm_exp=12'sd0; norm_sig=128'd0; rounded=25'd0; final_sig=24'd0;
302 guard=1'b0; roundb=1'b0; sticky=1'b0; lsb=1'b0; round_up=1'b0;
303
304 if (anan || bnan) y = QNAN;
305 else if (ainf && binf) y = (sa==sb) ? {sa,8'hFF,23'd0} : QNAN;
306 else if (ainf) y = {sa,8'hFF,23'd0};
307 else if (binf) y = {sb,8'hFF,23'd0};
308 else if (azero && bzero) y = (sa==sb) ? {sa,31'd0} : 32'd0;
309 else if (azero) y = b;
310 else if (bzero) y = a;
311 else begin
312 if (xa > xb || (xa == xb && siga >= sigb)) begin
313 exp_l={3'd0,xa}; sig_big=siga; sgn_big=sa; sig_small=sigb; sgn_small=sb; shamt={3'd0,(xa-xb)};
314 end else begin
315 exp_l={3'd0,xb}; sig_big=sigb; sgn_big=sb; sig_small=siga; sgn_small=sa; shamt={3'd0,(xb-xa)};
316 end
317 op_is_sub = (sgn_big != sgn_small);
318 res_sign = sgn_big;
319 big_ext = {40'd0, sig_big, 64'd0};
320 if (shamt >= 12'd128) begin small_ext = 128'd0; small_ext[0] = |sig_small; end
321 else begin
322 small_ext = ({40'd0, sig_small, 64'd0}) >> shamt;
323 if (shamt != 12'd0 && |(({40'd0, sig_small, 64'd0}) & ((128'd1 << shamt) - 128'd1))) small_ext[0] = 1'b1;
324 end
325 if (op_is_sub) sum_ext = big_ext - small_ext; else sum_ext = big_ext + small_ext;
326 if (sum_ext == 128'd0) y = 32'd0;
327 else begin
328 lead = 12'd0;
329 for (i = 127; i >= 0; i = i - 1) if (sum_ext[i] && lead == 12'd0) lead = i[11:0];
330 norm_exp = $signed({3'd0, exp_l[8:0]}) + $signed({3'd0, lead[8:0]}) - 12'sd87;
331 if (lead >= 12'd26) begin
332 norm_sig = sum_ext >> (lead - 12'd26);
333 if (|(sum_ext & ((128'd1 << (lead - 12'd26)) - 128'd1))) norm_sig[0] = 1'b1;
334 end else norm_sig = sum_ext << (12'd26 - lead);
335 guard=norm_sig[2]; roundb=norm_sig[1]; sticky=norm_sig[0]; lsb=norm_sig[3];
336 round_up = guard & (roundb | sticky | lsb);
337 rounded = {1'b0, norm_sig[26:3]} + (round_up ? 25'd1 : 25'd0);
338 if (rounded[24]) begin final_sig = rounded[24:1]; norm_exp = norm_exp + 12'sd1; end
339 else final_sig = rounded[23:0];
340 if (norm_exp >= 12'sd255) y = {res_sign, 8'hFF, 23'd0};
341 else if (norm_exp <= 12'sd0) y = pack_sub32(res_sign, sum_ext, lead, norm_exp);
342 else y = {res_sign, norm_exp[7:0], final_sig[22:0]};
343 end
344 end
345 end
346endmodule
347"#;
348
349pub const FP32_MUL_V: &str = r#"module fp32_mul(input [31:0] a, input [31:0] b, output reg [31:0] y);
354 localparam [31:0] QNAN = 32'h7FC00000;
355 reg sa, sb, rs;
356 reg [7:0] ea, eb;
357 reg [22:0] ma, mb;
358 reg azero,bzero,ainf,binf,anan,bnan,ha,hb;
359 reg [23:0] siga, sigb, sa_sh, sb_sh;
360 reg [4:0] lza, lzb;
361 reg [47:0] prod, norm;
362 reg signed [11:0] exp_sum, e, fe, bexp;
363 reg [26:0] mant;
364 reg g, r, s, l, up;
365 reg [24:0] rnd, rr;
366 reg [23:0] fsig;
367 reg [11:0] rsh;
368 reg [63:0] wide;
369 reg g2, r2, s2, l2, up2;
370
371 function [4:0] clz24(input [23:0] v);
372 integer j; reg dn;
373 begin clz24 = 5'd24; dn = 1'b0;
374 for (j = 23; j >= 0; j = j - 1) if (v[j] && !dn) begin clz24 = 5'd23 - j[4:0]; dn = 1'b1; end
375 end
376 endfunction
377
378 always @(*) begin
379 sa = a[31]; sb = b[31]; rs = sa ^ sb;
380 ea = a[30:23]; eb = b[30:23];
381 ma = a[22:0]; mb = b[22:0];
382 azero=(ea==8'd0)&&(ma==23'd0); bzero=(eb==8'd0)&&(mb==23'd0);
383 ainf =(ea==8'hFF)&&(ma==23'd0); binf =(eb==8'hFF)&&(mb==23'd0);
384 anan =(ea==8'hFF)&&(ma!=23'd0); bnan =(eb==8'hFF)&&(mb!=23'd0);
385 ha=(ea!=8'd0); hb=(eb!=8'd0);
386 siga={ha,ma}; sigb={hb,mb};
387 y=32'd0;
388 lza=5'd0; lzb=5'd0; sa_sh=24'd0; sb_sh=24'd0; prod=48'd0; norm=48'd0;
389 exp_sum=12'sd0; e=12'sd0; fe=12'sd0; bexp=12'sd0; mant=27'd0; rnd=25'd0; rr=25'd0;
390 fsig=24'd0; rsh=12'd0; wide=64'd0; g=0;r=0;s=0;l=0;up=0;g2=0;r2=0;s2=0;l2=0;up2=0;
391
392 if (anan || bnan) y = QNAN;
393 else if ((ainf && bzero) || (binf && azero)) y = QNAN;
394 else if (ainf || binf) y = {rs, 8'hFF, 23'd0};
395 else if (azero || bzero) y = {rs, 31'd0};
396 else begin
397 lza = clz24(siga); lzb = clz24(sigb);
398 exp_sum = (ea!=8'd0 ? $signed({4'd0,ea})-12'sd127 : -12'sd126)
399 + (eb!=8'd0 ? $signed({4'd0,eb})-12'sd127 : -12'sd126)
400 - $signed({7'd0,lza}) - $signed({7'd0,lzb});
401 sa_sh = (siga << lza); sb_sh = (sigb << lzb);
402 prod = sa_sh * sb_sh;
403 if (prod[47]) begin norm = prod; e = exp_sum + 12'sd1; end
404 else begin norm = prod << 1; e = exp_sum; end
405
406 if (e < -12'sd126) begin
407 wide = ({16'd0, norm}) >> 12'd21;
408 if (|(({16'd0, norm}) & ((64'd1 << 12'd21) - 64'd1))) wide[0] = 1'b1;
409 rsh = (-12'sd126) - e;
410 if (rsh >= 12'd64) begin s2 = |wide; wide = 64'd0; wide[0] = s2; end
411 else if (rsh != 12'd0) begin
412 if (|(wide & ((64'd1 << rsh) - 64'd1))) begin wide = wide >> rsh; wide[0] = 1'b1; end
413 else wide = wide >> rsh;
414 end
415 g2=wide[2]; r2=wide[1]; s2=wide[0]; l2=wide[3];
416 up2 = g2 & (r2 | s2 | l2);
417 rr = {1'b0, wide[26:3]} + (up2 ? 25'd1 : 25'd0);
418 if (rr[23]) y = {rs, 8'd1, rr[22:0]};
419 else y = {rs, 8'd0, rr[22:0]};
420 end else begin
421 mant = {norm[47:24], norm[23], norm[22], (|norm[21:0])};
422 g=mant[2]; r=mant[1]; s=mant[0]; l=mant[3];
423 up = g & (r | s | l);
424 rnd = {1'b0, mant[26:3]} + (up ? 25'd1 : 25'd0);
425 if (rnd[24]) begin fsig = rnd[24:1]; fe = e + 12'sd1; end
426 else begin fsig = rnd[23:0]; fe = e; end
427 if (fe > 12'sd127) y = {rs, 8'hFF, 23'd0};
428 else if (fe < -12'sd126) y = {rs, 8'd1, fsig[22:0]};
429 else begin bexp = fe + 12'sd127; y = {rs, bexp[7:0], fsig[22:0]}; end
430 end
431 end
432 end
433endmodule
434"#;
435
436pub fn emit_fp32_scalar_mul_testbench(n: usize) -> String {
439 format!(
440 r#"`timescale 1ns/1ps
441module tb_fp32_mul;
442 reg [31:0] a, b; wire [31:0] y;
443 reg [31:0] ia [0:{last}]; reg [31:0] ib [0:{last}]; reg [31:0] oo [0:{last}];
444 integer i, errors = 0;
445 fp32_mul dut(.a(a), .b(b), .y(y));
446 initial begin
447 $readmemh("fp32_mul_a.hex", ia);
448 $readmemh("fp32_mul_b.hex", ib);
449 $readmemh("fp32_mul_o.hex", oo);
450 for (i = 0; i < {n}; i = i + 1) begin
451 a = ia[i]; b = ib[i]; #1;
452 if (y !== oo[i]) begin errors = errors + 1; if (errors <= 20) $display("FAIL %0d: a=%h b=%h got=%h exp=%h", i, a, b, y, oo[i]); end
453 end
454 if (errors == 0) $display("PASS: {n} fp32 muls bit-exact to Rust f32");
455 else $display("FAIL: %0d fp32 mul mismatches", errors);
456 $finish;
457 end
458endmodule
459"#,
460 last = n - 1,
461 )
462}
463
464pub const FP16_ADD_V: &str = r#"module fp16_add(input [15:0] a, input [15:0] b_in, input sub, output reg [15:0] y);
468 localparam [15:0] QNAN = 16'h7E00;
469 reg [15:0] b;
470 reg sa, sb;
471 reg [4:0] ea, eb;
472 reg [9:0] ma, mb;
473 reg azero,bzero,ainf,binf,anan,bnan,asub,bsub;
474 reg [5:0] xa, xb;
475 reg ha, hb;
476 reg [10:0] siga, sigb, sig_big, sig_small, final_sig;
477 reg [11:0] exp_l, shamt, lead;
478 reg sgn_big, sgn_small, res_sign, op_is_sub;
479 reg [127:0] big_ext, small_ext, sum_ext, norm_sig;
480 integer i;
481 reg signed [11:0] norm_exp;
482 reg [11:0] rounded;
483 reg guard, roundb, sticky, lsb, round_up;
484
485 function [15:0] pack_sub16(input sgn, input [127:0] wsum, input [11:0] wlead, input signed [11:0] bexp);
486 reg [11:0] rshift; reg [127:0] win; reg g2,r2,s2,l2,up2; reg [11:0] rr;
487 begin
488 rshift = 12'd1 - bexp;
489 if (wlead >= 12'd13) begin
490 win = wsum >> (wlead - 12'd13);
491 if (|(wsum & ((128'd1 << (wlead - 12'd13)) - 128'd1))) win[0] = 1'b1;
492 end else begin
493 win = wsum << (12'd13 - wlead);
494 end
495 if (rshift >= 12'd128) begin s2 = |win; win = 128'd0; win[0] = s2; end
496 else if (rshift != 12'd0) begin
497 if (|(win & ((128'd1 << rshift) - 128'd1))) begin win = win >> rshift; win[0] = 1'b1; end
498 else win = win >> rshift;
499 end
500 g2 = win[2]; r2 = win[1]; s2 = win[0]; l2 = win[3];
501 up2 = g2 & (r2 | s2 | l2);
502 rr = {1'b0, win[13:3]} + (up2 ? 12'd1 : 12'd0);
503 if (rr[10]) pack_sub16 = {sgn, 5'd1, rr[9:0]};
504 else pack_sub16 = {sgn, 5'd0, rr[9:0]};
505 end
506 endfunction
507
508 always @(*) begin
509 b = sub ? {~b_in[15], b_in[14:0]} : b_in;
510 sa = a[15]; sb = b[15];
511 ea = a[14:10]; eb = b[14:10];
512 ma = a[9:0]; mb = b[9:0];
513 azero = (ea==5'd0) && (ma==10'd0); bzero = (eb==5'd0) && (mb==10'd0);
514 ainf = (ea==5'h1F) && (ma==10'd0); binf = (eb==5'h1F) && (mb==10'd0);
515 anan = (ea==5'h1F) && (ma!=10'd0); bnan = (eb==5'h1F) && (mb!=10'd0);
516 asub = (ea==5'd0) && (ma!=10'd0); bsub = (eb==5'd0) && (mb!=10'd0);
517 xa = asub ? 6'd1 : {1'b0, ea};
518 xb = bsub ? 6'd1 : {1'b0, eb};
519 ha = (ea!=5'd0); hb = (eb!=5'd0);
520 siga = {ha, ma}; sigb = {hb, mb};
521 y = 16'd0;
522 exp_l=12'd0; shamt=12'd0; sig_big=11'd0; sig_small=11'd0; sgn_big=1'b0; sgn_small=1'b0;
523 op_is_sub=1'b0; res_sign=1'b0; big_ext=128'd0; small_ext=128'd0; sum_ext=128'd0; lead=12'd0;
524 norm_exp=12'sd0; norm_sig=128'd0; rounded=12'd0; final_sig=11'd0;
525 guard=1'b0; roundb=1'b0; sticky=1'b0; lsb=1'b0; round_up=1'b0;
526
527 if (anan || bnan) y = QNAN;
528 else if (ainf && binf) y = (sa==sb) ? {sa,5'h1F,10'd0} : QNAN;
529 else if (ainf) y = {sa,5'h1F,10'd0};
530 else if (binf) y = {sb,5'h1F,10'd0};
531 else if (azero && bzero) y = (sa==sb) ? {sa,15'd0} : 16'd0;
532 else if (azero) y = b;
533 else if (bzero) y = a;
534 else begin
535 if (xa > xb || (xa == xb && siga >= sigb)) begin
536 exp_l={6'd0,xa}; sig_big=siga; sgn_big=sa; sig_small=sigb; sgn_small=sb; shamt={6'd0,(xa-xb)};
537 end else begin
538 exp_l={6'd0,xb}; sig_big=sigb; sgn_big=sb; sig_small=siga; sgn_small=sa; shamt={6'd0,(xb-xa)};
539 end
540 op_is_sub = (sgn_big != sgn_small);
541 res_sign = sgn_big;
542 big_ext = {53'd0, sig_big, 64'd0};
543 if (shamt >= 12'd128) begin small_ext = 128'd0; small_ext[0] = |sig_small; end
544 else begin
545 small_ext = ({53'd0, sig_small, 64'd0}) >> shamt;
546 if (shamt != 12'd0 && |(({53'd0, sig_small, 64'd0}) & ((128'd1 << shamt) - 128'd1))) small_ext[0] = 1'b1;
547 end
548 if (op_is_sub) sum_ext = big_ext - small_ext; else sum_ext = big_ext + small_ext;
549 if (sum_ext == 128'd0) y = 16'd0;
550 else begin
551 lead = 12'd0;
552 for (i = 127; i >= 0; i = i - 1) if (sum_ext[i] && lead == 12'd0) lead = i[11:0];
553 norm_exp = $signed({6'd0, exp_l[5:0]}) + $signed({3'd0, lead[8:0]}) - 12'sd74;
554 if (lead >= 12'd13) begin
555 norm_sig = sum_ext >> (lead - 12'd13);
556 if (|(sum_ext & ((128'd1 << (lead - 12'd13)) - 128'd1))) norm_sig[0] = 1'b1;
557 end else norm_sig = sum_ext << (12'd13 - lead);
558 guard=norm_sig[2]; roundb=norm_sig[1]; sticky=norm_sig[0]; lsb=norm_sig[3];
559 round_up = guard & (roundb | sticky | lsb);
560 rounded = {1'b0, norm_sig[13:3]} + (round_up ? 12'd1 : 12'd0);
561 if (rounded[11]) begin final_sig = rounded[11:1]; norm_exp = norm_exp + 12'sd1; end
562 else final_sig = rounded[10:0];
563 if (norm_exp >= 12'sd31) y = {res_sign, 5'h1F, 10'd0};
564 else if (norm_exp <= 12'sd0) y = pack_sub16(res_sign, sum_ext, lead, norm_exp);
565 else y = {res_sign, norm_exp[4:0], final_sig[9:0]};
566 end
567 end
568 end
569endmodule
570"#;
571
572pub const FP16_MUL_V: &str = r#"module fp16_mul(input [15:0] a, input [15:0] b, output reg [15:0] y);
575 localparam [15:0] QNAN = 16'h7E00;
576 reg sa, sb, rs;
577 reg [4:0] ea, eb;
578 reg [9:0] ma, mb;
579 reg azero,bzero,ainf,binf,anan,bnan,ha,hb;
580 reg [10:0] siga, sigb, sa_sh, sb_sh, fsig;
581 reg [4:0] lza, lzb;
582 reg [21:0] prod, norm;
583 reg signed [11:0] exp_sum, e, fe, bexp;
584 reg [13:0] mant;
585 reg g, r, s, l, up;
586 reg [11:0] rnd, rr, rsh;
587 reg [63:0] wide;
588 reg g2, r2, s2, l2, up2;
589
590 function [4:0] clz11(input [10:0] v);
591 integer j; reg dn;
592 begin clz11 = 5'd11; dn = 1'b0;
593 for (j = 10; j >= 0; j = j - 1) if (v[j] && !dn) begin clz11 = 5'd10 - j[4:0]; dn = 1'b1; end
594 end
595 endfunction
596
597 always @(*) begin
598 sa = a[15]; sb = b[15]; rs = sa ^ sb;
599 ea = a[14:10]; eb = b[14:10];
600 ma = a[9:0]; mb = b[9:0];
601 azero=(ea==5'd0)&&(ma==10'd0); bzero=(eb==5'd0)&&(mb==10'd0);
602 ainf =(ea==5'h1F)&&(ma==10'd0); binf =(eb==5'h1F)&&(mb==10'd0);
603 anan =(ea==5'h1F)&&(ma!=10'd0); bnan =(eb==5'h1F)&&(mb!=10'd0);
604 ha=(ea!=5'd0); hb=(eb!=5'd0);
605 siga={ha,ma}; sigb={hb,mb};
606 y=16'd0;
607 lza=5'd0; lzb=5'd0; sa_sh=11'd0; sb_sh=11'd0; prod=22'd0; norm=22'd0;
608 exp_sum=12'sd0; e=12'sd0; fe=12'sd0; bexp=12'sd0; mant=14'd0; rnd=12'd0; rr=12'd0;
609 fsig=11'd0; rsh=12'd0; wide=64'd0; g=0;r=0;s=0;l=0;up=0;g2=0;r2=0;s2=0;l2=0;up2=0;
610
611 if (anan || bnan) y = QNAN;
612 else if ((ainf && bzero) || (binf && azero)) y = QNAN;
613 else if (ainf || binf) y = {rs, 5'h1F, 10'd0};
614 else if (azero || bzero) y = {rs, 15'd0};
615 else begin
616 lza = clz11(siga); lzb = clz11(sigb);
617 exp_sum = (ea!=5'd0 ? $signed({7'd0,ea})-12'sd15 : -12'sd14)
618 + (eb!=5'd0 ? $signed({7'd0,eb})-12'sd15 : -12'sd14)
619 - $signed({7'd0,lza}) - $signed({7'd0,lzb});
620 sa_sh = (siga << lza); sb_sh = (sigb << lzb);
621 prod = sa_sh * sb_sh;
622 if (prod[21]) begin norm = prod; e = exp_sum + 12'sd1; end
623 else begin norm = prod << 1; e = exp_sum; end
624
625 if (e < -12'sd14) begin
626 wide = ({42'd0, norm}) >> 12'd8;
627 if (|(({42'd0, norm}) & ((64'd1 << 12'd8) - 64'd1))) wide[0] = 1'b1;
628 rsh = (-12'sd14) - e;
629 if (rsh >= 12'd64) begin s2 = |wide; wide = 64'd0; wide[0] = s2; end
630 else if (rsh != 12'd0) begin
631 if (|(wide & ((64'd1 << rsh) - 64'd1))) begin wide = wide >> rsh; wide[0] = 1'b1; end
632 else wide = wide >> rsh;
633 end
634 g2=wide[2]; r2=wide[1]; s2=wide[0]; l2=wide[3];
635 up2 = g2 & (r2 | s2 | l2);
636 rr = {1'b0, wide[13:3]} + (up2 ? 12'd1 : 12'd0);
637 if (rr[10]) y = {rs, 5'd1, rr[9:0]};
638 else y = {rs, 5'd0, rr[9:0]};
639 end else begin
640 mant = {norm[21:11], norm[10], norm[9], (|norm[8:0])};
641 g=mant[2]; r=mant[1]; s=mant[0]; l=mant[3];
642 up = g & (r | s | l);
643 rnd = {1'b0, mant[13:3]} + (up ? 12'd1 : 12'd0);
644 if (rnd[11]) begin fsig = rnd[11:1]; fe = e + 12'sd1; end
645 else begin fsig = rnd[10:0]; fe = e; end
646 if (fe > 12'sd15) y = {rs, 5'h1F, 10'd0};
647 else if (fe < -12'sd14) y = {rs, 5'd1, fsig[9:0]};
648 else begin bexp = fe + 12'sd15; y = {rs, bexp[4:0], fsig[9:0]}; end
649 end
650 end
651 end
652endmodule
653"#;
654
655pub fn emit_simd_fadd_f16x4_verilog() -> String {
658 let mut m =
659 String::from("module simd_fadd_f16x4(input [63:0] a, input [63:0] b, output [63:0] r);\n");
660 for k in 0..4 {
661 let (lo, hi) = (k * 16, k * 16 + 15);
662 m.push_str(&format!(
663 " wire [15:0] y{k};\n fp16_add u{k}(.a(a[{hi}:{lo}]), .b_in(b[{hi}:{lo}]), .sub(1'b0), .y(y{k}));\n"
664 ));
665 }
666 m.push_str(" assign r = {y3, y2, y1, y0};\nendmodule\n");
667 m
668}
669
670pub fn emit_simd_fmul_f16x4_verilog() -> String {
673 let mut m =
674 String::from("module simd_fmul_f16x4(input [63:0] a, input [63:0] b, output [63:0] r);\n");
675 for k in 0..4 {
676 let (lo, hi) = (k * 16, k * 16 + 15);
677 m.push_str(&format!(
678 " wire [15:0] y{k};\n fp16_mul u{k}(.a(a[{hi}:{lo}]), .b(b[{hi}:{lo}]), .y(y{k}));\n"
679 ));
680 }
681 m.push_str(" assign r = {y3, y2, y1, y0};\nendmodule\n");
682 m
683}
684
685pub fn emit_fp32_scalar_add_testbench(n: usize) -> String {
688 format!(
689 r#"`timescale 1ns/1ps
690module tb_fp32_add;
691 reg [31:0] a, b; wire [31:0] y;
692 reg [31:0] ia [0:{last}]; reg [31:0] ib [0:{last}]; reg [31:0] oo [0:{last}];
693 integer i, errors = 0;
694 fp32_add dut(.a(a), .b_in(b), .sub(1'b0), .y(y));
695 initial begin
696 $readmemh("fp32_add_a.hex", ia);
697 $readmemh("fp32_add_b.hex", ib);
698 $readmemh("fp32_add_o.hex", oo);
699 for (i = 0; i < {n}; i = i + 1) begin
700 a = ia[i]; b = ib[i]; #1;
701 if (y !== oo[i]) begin errors = errors + 1; if (errors <= 20) $display("FAIL %0d: a=%h b=%h got=%h exp=%h", i, a, b, y, oo[i]); end
702 end
703 if (errors == 0) $display("PASS: {n} fp32 adds bit-exact to Rust f32");
704 else $display("FAIL: %0d fp32 add mismatches", errors);
705 $finish;
706 end
707endmodule
708"#,
709 last = n - 1,
710 )
711}
712
713pub fn emit_simd_add_verilog(name: &str, ty: LaneTy) -> String {
717 let m = swar_lo_mask(ty);
718 let hm = !m;
719 format!(
720 "// simd_{name} — packed {lanes}×{width}-bit SWAR add (carry-isolated, combinational, 1 clock).\n\
721 module simd_{name}(input [63:0] a, input [63:0] b, output [63:0] r);\n\
722 \x20 assign r = ((a & 64'h{m:016x}) + (b & 64'h{m:016x})) ^ ((a ^ b) & 64'h{hm:016x});\n\
723 endmodule\n",
724 lanes = ty.lanes(),
725 width = ty.width(),
726 )
727}
728
729pub fn emit_simd_add_testbench(name: &str, n: usize) -> String {
732 format!(
733 r#"`timescale 1ns/1ps
734module tb_simd_{name};
735 reg [63:0] a, b; wire [63:0] r;
736 reg [63:0] ia [0:{last}]; reg [63:0] ib [0:{last}]; reg [63:0] oo [0:{last}];
737 integer i, errors = 0;
738 simd_{name} dut(.a(a), .b(b), .r(r));
739 initial begin
740 $readmemh("simd_{name}_a.hex", ia);
741 $readmemh("simd_{name}_b.hex", ib);
742 $readmemh("simd_{name}_o.hex", oo);
743 for (i = 0; i < {n}; i = i + 1) begin
744 a = ia[i]; b = ib[i]; #1;
745 if (r !== oo[i]) begin errors = errors + 1; $display("FAIL vec %0d: got %h exp %h", i, r, oo[i]); end
746 end
747 if (errors == 0) $display("PASS: {n} SIMD adds match the lane-wise spec");
748 else $display("FAIL: %0d SIMD mismatches", errors);
749 $finish;
750 end
751endmodule
752"#,
753 last = n - 1,
754 )
755}
756
757pub fn emit_simd_fadd_f32x2_verilog() -> String {
761 "module simd_fadd_f32x2(input [63:0] a, input [63:0] b, output [63:0] r);\n\
762 \x20 wire [31:0] lo, hi;\n\
763 \x20 fp32_add u0(.a(a[31:0]), .b_in(b[31:0]), .sub(1'b0), .y(lo));\n\
764 \x20 fp32_add u1(.a(a[63:32]), .b_in(b[63:32]), .sub(1'b0), .y(hi));\n\
765 \x20 assign r = {hi, lo};\n\
766 endmodule\n"
767 .to_string()
768}
769
770pub fn emit_simd_fmul_f32x2_verilog() -> String {
773 "module simd_fmul_f32x2(input [63:0] a, input [63:0] b, output [63:0] r);\n\
774 \x20 wire [31:0] lo, hi;\n\
775 \x20 fp32_mul u0(.a(a[31:0]), .b(b[31:0]), .y(lo));\n\
776 \x20 fp32_mul u1(.a(a[63:32]), .b(b[63:32]), .y(hi));\n\
777 \x20 assign r = {hi, lo};\n\
778 endmodule\n"
779 .to_string()
780}
781
782pub fn f16_to_f32(bits: u16) -> f32 {
784 let sign = if bits & 0x8000 != 0 { -1.0f32 } else { 1.0 };
785 let exp = ((bits >> 10) & 0x1F) as i32;
786 let mant = (bits & 0x3FF) as f32;
787 if exp == 0x1F {
788 if mant == 0.0 {
789 return sign * f32::INFINITY;
790 }
791 return f32::from_bits(0x7FC0_0000); }
793 if exp == 0 {
794 return sign * mant * (-24f32).exp2();
796 }
797 sign * (1.0 + mant / 1024.0) * ((exp - 15) as f32).exp2()
798}
799
800pub fn f32_to_f16(x: f32) -> u16 {
804 let bits = x.to_bits();
805 let sign = ((bits >> 16) & 0x8000) as u16;
806 let exp = ((bits >> 23) & 0xFF) as i32;
807 let mant = bits & 0x7F_FFFF; if exp == 0xFF {
809 return if mant != 0 {
810 sign | 0x7E00
811 } else {
812 sign | 0x7C00
813 };
814 }
815 let e = exp - 127; if e > 15 {
817 return sign | 0x7C00; }
819 if e < -14 {
820 if e < -25 {
822 return sign; }
824 let full = (1u32 << 23) | mant; let shift = (-e - 1) as u32; let frac = full >> shift;
827 let rem = full & ((1u32 << shift) - 1);
828 let half = 1u32 << (shift - 1);
829 let mut f = frac;
830 if rem > half || (rem == half && (frac & 1) == 1) {
831 f += 1; }
833 if f >= 1024 {
834 return sign | 0x0400; }
836 return sign | f as u16;
837 }
838 let frac = mant >> 13;
840 let rem = mant & 0x1FFF;
841 let half = 0x1000;
842 let mut f = frac;
843 if rem > half || (rem == half && (frac & 1) == 1) {
844 f += 1;
845 }
846 let mut exp_out = (e + 15) as u16; if f >= 1024 {
848 f = 0;
849 exp_out += 1;
850 }
851 if exp_out >= 31 {
852 return sign | 0x7C00; }
854 sign | (exp_out << 10) | f as u16
855}
856
857pub fn f16_add(a: u16, b: u16) -> u16 {
860 f32_to_f16(f16_to_f32(a) + f16_to_f32(b))
861}
862
863pub fn f16_mul(a: u16, b: u16) -> u16 {
865 f32_to_f16(f16_to_f32(a) * f16_to_f32(b))
866}
867
868pub fn pack_f16x4(l0: u16, l1: u16, l2: u16, l3: u16) -> u64 {
870 (l0 as u64) | ((l1 as u64) << 16) | ((l2 as u64) << 32) | ((l3 as u64) << 48)
871}
872
873pub fn eval_lanewise_fadd_f16x4(a: u64, b: u64) -> u64 {
876 let lane = |sh: u32| f16_add((a >> sh) as u16, (b >> sh) as u16);
877 pack_f16x4(lane(0), lane(16), lane(32), lane(48))
878}
879
880pub fn eval_lanewise_fmul_f16x4(a: u64, b: u64) -> u64 {
882 let lane = |sh: u32| f16_mul((a >> sh) as u16, (b >> sh) as u16);
883 pack_f16x4(lane(0), lane(16), lane(32), lane(48))
884}
885
886pub fn eval_lanewise_add(ty: LaneTy, a: u64, b: u64) -> u64 {
888 let w = ty.width();
889 let full = if w == 64 { u64::MAX } else { (1u64 << w) - 1 };
890 let mut out = 0u64;
891 for j in 0..ty.lanes() {
892 let sh = j * w;
893 let aj = (a >> sh) & full;
894 let bj = (b >> sh) & full;
895 out |= ((aj.wrapping_add(bj)) & full) << sh;
896 }
897 out
898}