1pub const ROUND88_P_LATENCY: usize = 5;
27
28pub const LANE_VALUE_LATENCY: usize = 5 + ROUND88_P_LATENCY;
30
31pub fn lane_verilog() -> String {
33 format!("{}\n{}", super::fp32::fp32_verilog(), LANE_VERILOG)
34}
35
36const LANE_VERILOG: &str = r#"// The block-scale lane: a block's integer sums to its f32 value, added into its row.
37
38// v·2^least rounded once to f32.
39module logos_round88(input signed [87:0] v, input signed [11:0] least, output reg [31:0] f);
40 reg [87:0] mag;
41 reg neg;
42 integer lead, i, s;
43 reg [87:0] kept, rem, half;
44 reg [24:0] m;
45 reg signed [13:0] unb;
46 always @* begin
47 neg = v[87];
48 mag = neg ? -v : v;
49 lead = -1;
50 for (i = 0; i < 88; i = i + 1) if (mag[i]) lead = i;
51 f = 32'd0;
52 if (lead >= 0) begin
53 if (lead <= 23) begin
54 m = mag[24:0] << (23 - lead);
55 unb = least + $signed(lead[13:0]);
56 end else begin
57 s = lead - 23;
58 kept = mag >> s;
59 rem = mag & ((88'd1 << s) - 88'd1);
60 half = 88'd1 << (s - 1);
61 m = kept[24:0] + ((rem > half || (rem == half && kept[0])) ? 25'd1 : 25'd0);
62 unb = least + $signed(lead[13:0]);
63 if (m[24]) begin m = m >> 1; unb = unb + 14'sd1; end
64 end
65 if (unb > 14'sd127) f = {neg, 8'hff, 23'd0};
66 else if (unb < -14'sd126) f = {neg, 31'd0};
67 else f = {neg, unb[7:0] + 8'd127, m[22:0]};
68 end
69 end
70endmodule
71
72// logos_round88 for the user clock, an operand a clock: f is logos_round88 of the v and least five clocks before. The
73// magnitude; each byte's leading one; the leading one, the top byte that has one at its own; the significand kept, the
74// first bit dropped (round) and whether any below it is set (sticky); the rounding to nearest even and the f32.
75module logos_round88_p(input clk, input signed [87:0] v, input signed [11:0] least, output reg [31:0] f);
76 function [2:0] lead8(input [7:0] x);
77 integer i;
78 begin
79 lead8 = 3'd0;
80 for (i = 0; i < 8; i = i + 1) if (x[i]) lead8 = i[2:0];
81 end
82 endfunction
83 // Stage 1: the sign and the magnitude.
84 reg [87:0] mag1;
85 reg neg1;
86 reg signed [11:0] l1;
87 always @(posedge clk) begin neg1 <= v[87]; mag1 <= v[87] ? -v : v; l1 <= least; end
88 // Stage 2: each byte's leading one, and whether it has one.
89 reg [10:0] any2;
90 reg [32:0] at2;
91 reg [87:0] mag2;
92 reg neg2;
93 reg signed [11:0] l2;
94 integer g;
95 always @(posedge clk) begin
96 for (g = 0; g < 11; g = g + 1) begin
97 any2[g] <= |mag1[8*g +: 8];
98 at2[3*g +: 3] <= lead8(mag1[8*g +: 8]);
99 end
100 mag2 <= mag1; neg2 <= neg1; l2 <= l1;
101 end
102 // Stage 3: the leading one: the top byte that has one, at its own.
103 reg [3:0] top;
104 integer t;
105 always @* begin
106 top = 4'd0;
107 for (t = 0; t < 11; t = t + 1) if (any2[t]) top = t[3:0];
108 end
109 reg [6:0] lead3;
110 reg none3;
111 reg [87:0] mag3;
112 reg neg3;
113 reg signed [11:0] l3;
114 always @(posedge clk) begin
115 lead3 <= {top, at2[3*top +: 3]};
116 none3 <= ~|any2;
117 mag3 <= mag2; neg3 <= neg2; l3 <= l2;
118 end
119 // Stage 4: the significand kept (its leading one at bit 23), the round and sticky bits past it, the exponent.
120 wire [6:0] s3 = lead3 - 7'd23;
121 wire [87:0] below = ~({88{1'b1}} << (s3 - 7'd1));
122 wire [87:0] kept3 = mag3 >> s3;
123 reg [24:0] m4;
124 reg rnd4, sticky4, none4, neg4;
125 reg signed [13:0] unb4;
126 always @(posedge clk) begin
127 if (lead3 <= 7'd23) begin
128 m4 <= mag3[24:0] << (7'd23 - lead3);
129 rnd4 <= 1'b0;
130 sticky4 <= 1'b0;
131 end else begin
132 m4 <= kept3[24:0];
133 rnd4 <= mag3[s3 - 7'd1];
134 sticky4 <= |(mag3 & below);
135 end
136 unb4 <= l3 + $signed({7'd0, lead3});
137 none4 <= none3; neg4 <= neg3;
138 end
139 // Stage 5: the rounding, nearest even, and the f32: past the top infinite, below the normals a zero of its sign.
140 reg [24:0] m;
141 reg signed [13:0] unb;
142 always @(posedge clk) begin
143 m = m4 + ((rnd4 && (sticky4 || m4[0])) ? 25'd1 : 25'd0);
144 unb = unb4;
145 if (m[24]) begin m = m >> 1; unb = unb + 14'sd1; end
146 if (none4) f <= 32'd0;
147 else if (unb > 14'sd127) f <= {neg4, 8'hff, 23'd0};
148 else if (unb < -14'sd126) f <= {neg4, 31'd0};
149 else f <= {neg4, unb[7:0] + 8'd127, m[22:0]};
150 end
151endmodule
152
153// One weight block of UNITS 16-element units.
154module logos_lane #(parameter UNITS = 16)
155 (input [24*UNITS-1:0] p_hi, input [12*UNITS-1:0] s_hi, input [24*UNITS-1:0] p_lo, input [12*UNITS-1:0] s_lo,
156 input [12*UNITS-1:0] q, input [16*UNITS-1:0] c, input [16*UNITS-1:0] n,
157 input [7:0] z, input [15:0] a, input [15:0] b, input [7:0] m, input signed [11:0] e, input two,
158 input [31:0] acc, output [31:0] out);
159 // The units' signed sums: the high word in excess form, the low byte as it is, weighted by 256 under two words.
160 reg signed [47:0] ia, ib;
161 reg signed [31:0] sp_hi, sa_hi, sp_lo, sa_lo, sp, sa;
162 integer u;
163 always @* begin
164 ia = 48'sd0;
165 ib = 48'sd0;
166 for (u = 0; u < UNITS; u = u + 1) begin
167 sp_hi = $signed({8'd0, p_hi[24*u +: 24]}) - ($signed({20'd0, q[12*u +: 12]}) <<< 7)
168 - $signed({24'd0, z}) * $signed({20'd0, s_hi[12*u +: 12]}) + ($signed({24'd0, z}) <<< 11);
169 sa_hi = $signed({20'd0, s_hi[12*u +: 12]}) - 32'sd2048;
170 sp_lo = $signed({8'd0, p_lo[24*u +: 24]}) - $signed({24'd0, z}) * $signed({20'd0, s_lo[12*u +: 12]});
171 sa_lo = $signed({20'd0, s_lo[12*u +: 12]});
172 sp = two ? (sp_hi <<< 8) + sp_lo : sp_hi;
173 sa = two ? (sa_hi <<< 8) + sa_lo : sa_hi;
174 ia = ia + $signed(c[16*u +: 16]) * sp;
175 ib = ib + $signed(n[16*u +: 16]) * sa;
176 end
177 end
178 // Each binary16 scale as a signed significand and its exponent: sig·2^(exp), a denormal's exp the least.
179 function signed [12:0] sig16(input [15:0] h);
180 sig16 = (h[14:10] == 5'd0) ? (h[15] ? -$signed({3'd0, h[9:0]}) : $signed({3'd0, h[9:0]}))
181 : (h[15] ? -$signed({2'd0, 1'b1, h[9:0]}) : $signed({2'd0, 1'b1, h[9:0]}));
182 endfunction
183 function signed [11:0] exp16(input [15:0] h);
184 exp16 = (h[14:10] == 5'd0) ? -12'sd24 : $signed({7'd0, h[14:10]}) - 12'sd25;
185 endfunction
186 wire signed [12:0] sa_sig = sig16(a), sb_sig = sig16(b);
187 wire signed [11:0] ea = exp16(a) + e, eb = exp16(b) + e;
188 wire signed [87:0] va = $signed(sa_sig) * $signed({1'b0, m}) * ia;
189 wire signed [87:0] vb = $signed(sb_sig) * $signed({1'b0, m}) * ib;
190 wire has_a = (sa_sig != 13'sd0) && (ia != 48'sd0), has_b = (sb_sig != 13'sd0) && (ib != 48'sd0);
191 wire signed [11:0] least = !has_b ? ea : !has_a ? eb : (ea < eb ? ea : eb);
192 wire signed [87:0] sum = (has_a ? (va <<< (ea - least)) : 88'sd0) + (has_b ? (vb <<< (eb - least)) : 88'sd0);
193 wire [31:0] value;
194 logos_round88 r(.v(sum), .least(least), .f(value));
195 logos_fadd add(.a(acc), .b(value), .y(out));
196endmodule
197
198// The lane up to its rounding, ten registered stages: value is the f32 of the block whose inputs came ten clocks
199// before, each stage logos_lane's arithmetic in its order, the rounding logos_round88_p's five. The unit multipliers and offset terms are signed bytes, a byte a
200// unit, and the offset zero or a power of two; built for one activation word (WORDS 1), `two` is never set, a unit's
201// signed sums take 19 and 13 bits, stage 1 sums a pair of units' products as one DSP block's two 18×19 multipliers, and
202// the terms' sums take 30 and 23.
203module logos_lane_value_p #(parameter UNITS = 16, WORDS = 2)
204 (input clk, input [24*UNITS-1:0] p_hi, input [12*UNITS-1:0] s_hi, input [24*UNITS-1:0] p_lo, input [12*UNITS-1:0] s_lo,
205 input [12*UNITS-1:0] q, input [8*UNITS-1:0] c, input [8*UNITS-1:0] n,
206 input [7:0] z, input [15:0] a, input [15:0] b, input [7:0] m, input signed [11:0] e, input two,
207 output reg [31:0] value);
208 localparam WIDE = WORDS > 1;
209 // A stage-1 term: a unit's products built for two words, a pair of units' summed built for one.
210 localparam TERMS = WIDE ? UNITS : (UNITS + 1) / 2;
211 // The terms' widths: two words' 48 bits; one word's 30 and 23, sixteen units' products within 16·128·260,096 and
212 // 16·128·2,032.
213 localparam AW = WIDE ? 48 : 30, BW = WIDE ? 48 : 23;
214 wire zero = z == 8'd0;
215 wire [2:0] zlog = z[7] ? 3'd7 : z[6] ? 3'd6 : z[5] ? 3'd5 : z[4] ? 3'd4 : z[3] ? 3'd3 : z[2] ? 3'd2 : z[1] ? 3'd1 : 3'd0;
216 // Each unit's signed sums.
217 wire signed [31:0] sp [0:UNITS-1];
218 wire signed [31:0] sa [0:UNITS-1];
219 genvar g;
220 generate
221 for (g = 0; g < UNITS; g = g + 1) begin : unit
222 wire signed [31:0] zs_hi = zero ? 32'sd0 : $signed({20'd0, s_hi[12*g +: 12]}) <<< zlog;
223 wire signed [31:0] zs_lo = zero ? 32'sd0 : $signed({20'd0, s_lo[12*g +: 12]}) <<< zlog;
224 wire signed [31:0] sp_hi = $signed({8'd0, p_hi[24*g +: 24]}) - ($signed({20'd0, q[12*g +: 12]}) <<< 7) - zs_hi + ($signed({24'd0, z}) <<< 11);
225 wire signed [31:0] sa_hi = $signed({20'd0, s_hi[12*g +: 12]}) - 32'sd2048;
226 wire signed [31:0] sp_lo = $signed({8'd0, p_lo[24*g +: 24]}) - zs_lo;
227 wire signed [31:0] sa_lo = $signed({20'd0, s_lo[12*g +: 12]});
228 assign sp[g] = (WIDE && two) ? (sp_hi <<< 8) + sp_lo : sp_hi;
229 assign sa[g] = (WIDE && two) ? (sa_hi <<< 8) + sa_lo : sa_hi;
230 end
231 endgenerate
232 // Stage 1: the terms, each unit's products with its multipliers.
233 wire [AW*TERMS-1:0] pa1;
234 wire [BW*TERMS-1:0] pb1;
235 generate
236 for (g = 0; g < TERMS; g = g + 1) begin : term
237 reg signed [AW-1:0] ta;
238 reg signed [BW-1:0] tb;
239 if (WIDE) begin : wide
240 always @(posedge clk) begin
241 ta <= $signed(c[8*g +: 8]) * sp[g];
242 tb <= $signed(n[8*g +: 8]) * sa[g];
243 end
244 end else begin : pair
245 wire signed [18:0] p0 = sp[2*g][18:0];
246 wire signed [12:0] s0 = sa[2*g][12:0];
247 wire signed [7:0] c0 = c[16*g +: 8], n0 = n[16*g +: 8];
248 wire signed [18:0] p1;
249 wire signed [12:0] s1;
250 wire signed [7:0] c1, n1;
251 if (2*g + 1 < UNITS) begin : second
252 assign p1 = sp[2*g + 1][18:0];
253 assign s1 = sa[2*g + 1][12:0];
254 assign c1 = c[16*g + 8 +: 8];
255 assign n1 = n[16*g + 8 +: 8];
256 end else begin : alone
257 assign p1 = 19'sd0;
258 assign s1 = 13'sd0;
259 assign c1 = 8'sd0;
260 assign n1 = 8'sd0;
261 end
262 always @(posedge clk) begin
263 ta <= c0 * p0 + c1 * p1;
264 tb <= n0 * s0 + n1 * s1;
265 end
266 end
267 assign pa1[AW*g +: AW] = ta;
268 assign pb1[BW*g +: BW] = tb;
269 end
270 endgenerate
271 reg [15:0] a1, b1;
272 reg [7:0] m1;
273 reg signed [11:0] e1;
274 always @(posedge clk) begin
275 a1 <= a; b1 <= b; m1 <= m; e1 <= e;
276 end
277 // Stages 2 and 3: the terms' sums in fours, then the fours' sum.
278 localparam G = (TERMS + 3) / 4;
279 reg signed [AW-1:0] fa2 [0:G-1];
280 reg signed [BW-1:0] fb2 [0:G-1];
281 reg signed [AW-1:0] ia2, ga, ta;
282 reg signed [BW-1:0] ib2, gb, tb;
283 reg [15:0] a2f, b2f, a2, b2;
284 reg [7:0] m2f, m2;
285 reg signed [11:0] e2f, e2;
286 integer v, w, x;
287 always @(posedge clk) begin
288 for (w = 0; w < G; w = w + 1) begin
289 ga = {AW{1'b0}};
290 gb = {BW{1'b0}};
291 for (v = 4 * w; v < 4 * w + 4; v = v + 1) if (v < TERMS) begin
292 ga = ga + $signed(pa1[AW*v +: AW]);
293 gb = gb + $signed(pb1[BW*v +: BW]);
294 end
295 fa2[w] <= ga; fb2[w] <= gb;
296 end
297 a2f <= a1; b2f <= b1; m2f <= m1; e2f <= e1;
298 end
299 always @(posedge clk) begin
300 ta = {AW{1'b0}};
301 tb = {BW{1'b0}};
302 for (x = 0; x < G; x = x + 1) begin
303 ta = ta + fa2[x];
304 tb = tb + fb2[x];
305 end
306 ia2 <= ta; ib2 <= tb; a2 <= a2f; b2 <= b2f; m2 <= m2f; e2 <= e2f;
307 end
308 // Stage 4: the two scaled terms and their exponents.
309 function signed [12:0] sig16(input [15:0] h);
310 sig16 = (h[14:10] == 5'd0) ? (h[15] ? -$signed({3'd0, h[9:0]}) : $signed({3'd0, h[9:0]}))
311 : (h[15] ? -$signed({2'd0, 1'b1, h[9:0]}) : $signed({2'd0, 1'b1, h[9:0]}));
312 endfunction
313 function signed [11:0] exp16(input [15:0] h);
314 exp16 = (h[14:10] == 5'd0) ? -12'sd24 : $signed({7'd0, h[14:10]}) - 12'sd25;
315 endfunction
316 wire signed [12:0] sa_sig = sig16(a2), sb_sig = sig16(b2);
317 // Which term stage 5 shifts: the one at the greater exponent (a's when only a's is present), by the exponents' gap
318 // (zero when only one is present), the lesser exponent the sum's.
319 wire signed [11:0] ea = exp16(a2) + e2, eb = exp16(b2) + e2;
320 wire has_a = (sa_sig != 13'sd0) && (|ia2), has_b = (sb_sig != 13'sd0) && (|ib2);
321 wire a_up = has_a && (!has_b || ea > eb);
322 reg signed [87:0] va3, vb3;
323 reg a_up3, up_on3, flat_on3;
324 reg [11:0] gap3;
325 reg signed [11:0] least3;
326 always @(posedge clk) begin
327 va3 <= $signed(sa_sig) * $signed({1'b0, m2}) * ia2;
328 vb3 <= $signed(sb_sig) * $signed({1'b0, m2}) * ib2;
329 a_up3 <= a_up;
330 up_on3 <= a_up || has_b;
331 flat_on3 <= a_up ? has_b : has_a;
332 gap3 <= (has_a && has_b) ? (a_up ? ea - eb : eb - ea) : 12'd0;
333 least3 <= !has_b ? ea : !has_a ? eb : (ea < eb ? ea : eb);
334 end
335 // Stage 5: the terms aligned to the lesser exponent and summed exactly, the term at the greater exponent shifted by
336 // the gap, the other as it is.
337 wire signed [87:0] up = a_up3 ? va3 : vb3, flat = a_up3 ? vb3 : va3;
338 reg signed [87:0] sum4;
339 reg signed [11:0] least4;
340 always @(posedge clk) begin
341 sum4 <= (up_on3 ? (up <<< gap3) : 88'sd0) + (flat_on3 ? flat : 88'sd0);
342 least4 <= least3;
343 end
344 // Stages 6 to 10: the one rounding.
345 wire [31:0] rounded;
346 logos_round88_p r(.clk(clk), .v(sum4), .least(least4), .f(rounded));
347 always @* value = rounded;
348endmodule
349
350// logos_lane_value_p for a block of one scale: its units' multipliers 1 and their offset multipliers 0, so the
351// block's value is its one scaled term, sig(a)·m·Σ(the units' signed sums) rounded once at exp(a) + e — Q8_0's and
352// Q4_0's 32-element blocks. Its stages are logos_lane_value_p's, value the f32 of the block whose inputs came ten
353// clocks before: no unit multiplies, one product a block.
354module logos_lane_slim_p #(parameter UNITS = 2, WORDS = 2)
355 (input clk, input [24*UNITS-1:0] p_hi, input [12*UNITS-1:0] s_hi, input [24*UNITS-1:0] p_lo, input [12*UNITS-1:0] s_lo,
356 input [12*UNITS-1:0] q, input [7:0] z, input [15:0] a, input [7:0] m, input signed [11:0] e, input two,
357 output reg [31:0] value);
358 // Stage 1: each unit's signed sum.
359 reg signed [47:0] pa1 [0:UNITS-1];
360 reg [15:0] a1;
361 reg [7:0] m1;
362 reg signed [11:0] e1;
363 reg signed [31:0] sp_hi, sp_lo, zs_hi, zs_lo;
364 localparam WIDE = WORDS > 1;
365 wire zero = z == 8'd0;
366 wire [2:0] zlog = z[7] ? 3'd7 : z[6] ? 3'd6 : z[5] ? 3'd5 : z[4] ? 3'd4 : z[3] ? 3'd3 : z[2] ? 3'd2 : z[1] ? 3'd1 : 3'd0;
367 integer u;
368 always @(posedge clk) begin
369 for (u = 0; u < UNITS; u = u + 1) begin
370 zs_hi = zero ? 32'sd0 : $signed({20'd0, s_hi[12*u +: 12]}) <<< zlog;
371 zs_lo = zero ? 32'sd0 : $signed({20'd0, s_lo[12*u +: 12]}) <<< zlog;
372 sp_hi = $signed({8'd0, p_hi[24*u +: 24]}) - ($signed({20'd0, q[12*u +: 12]}) <<< 7) - zs_hi + ($signed({24'd0, z}) <<< 11);
373 sp_lo = $signed({8'd0, p_lo[24*u +: 24]}) - zs_lo;
374 pa1[u] <= (WIDE && two) ? (sp_hi <<< 8) + sp_lo : sp_hi;
375 end
376 a1 <= a; m1 <= m; e1 <= e;
377 end
378 // Stages 2 and 3: the units' sum, carried a stage further as logos_lane_value_p's fours and their sum are.
379 reg signed [47:0] ia2f, ia2, ta;
380 reg [15:0] a2f, a2;
381 reg [7:0] m2f, m2;
382 reg signed [11:0] e2f, e2;
383 integer x;
384 always @(posedge clk) begin
385 ta = 48'sd0;
386 for (x = 0; x < UNITS; x = x + 1) ta = ta + pa1[x];
387 ia2f <= ta; a2f <= a1; m2f <= m1; e2f <= e1;
388 end
389 always @(posedge clk) begin ia2 <= ia2f; a2 <= a2f; m2 <= m2f; e2 <= e2f; end
390 // Stage 4: the scaled term and its exponent.
391 function signed [12:0] sig16(input [15:0] h);
392 sig16 = (h[14:10] == 5'd0) ? (h[15] ? -$signed({3'd0, h[9:0]}) : $signed({3'd0, h[9:0]}))
393 : (h[15] ? -$signed({2'd0, 1'b1, h[9:0]}) : $signed({2'd0, 1'b1, h[9:0]}));
394 endfunction
395 function signed [11:0] exp16(input [15:0] h);
396 exp16 = (h[14:10] == 5'd0) ? -12'sd24 : $signed({7'd0, h[14:10]}) - 12'sd25;
397 endfunction
398 wire signed [12:0] sa_sig = sig16(a2);
399 reg signed [87:0] va3;
400 reg signed [11:0] ea3;
401 reg has_a3;
402 always @(posedge clk) begin
403 va3 <= $signed(sa_sig) * $signed({1'b0, m2}) * ia2;
404 ea3 <= exp16(a2) + e2;
405 has_a3 <= (sa_sig != 13'sd0) && (ia2 != 48'sd0);
406 end
407 // Stage 5: the term, or zero.
408 reg signed [87:0] sum4;
409 reg signed [11:0] least4;
410 always @(posedge clk) begin
411 sum4 <= has_a3 ? va3 : 88'sd0;
412 least4 <= ea3;
413 end
414 // Stages 6 to 10: the one rounding.
415 wire [31:0] rounded;
416 logos_round88_p r(.clk(clk), .v(sum4), .least(least4), .f(rounded));
417 always @* value = rounded;
418endmodule
419"#;