Skip to main content

logicaffeine_compile/spatial/decode_engine/rtl/
lane.rs

1//! The block-scale lane as Verilog ([`crate::spatial::decode_engine::lane::lane`]): a weight block's excess partial
2//! sums to its signed sums, the two scaled terms, their exact sum rounded once to f32, and the f32 add into the row.
3//!
4//! `logos_lane #(UNITS)` takes, per 16-element unit, the high word's `Σ q·a′` and `Σ a′` (the only word of a one-word
5//! code), the low byte's alike, the weight image's `Σ q`, and the unit's multipliers `c` and `n`; per block the
6//! format's offset `z`, the two binary16 scales, the activation scale `m·2^e`, whether the code has two words, and the
7//! row's running f32. `logos_round88` is the one rounding: an exact signed integer times a power of two to f32,
8//! nearest even, a result below the normals flushed to a zero of its sign, past the top infinite.
9//!
10//! `logos_lane_value_p #(UNITS, WORDS)` is the lane up to its rounding, pipelined for the streaming engine: a block's
11//! inputs a clock, and [`LANE_VALUE_LATENCY`] clocks later the block's f32 value, the f32 the lane adds into its row
12//! (the lane from −0, which every value leaves as it is), its stages each unit's signed sums and their products, the
13//! units' sums in fours, their sum, the two scaled terms, their exact sum, and the rounding in [`ROUND88_P_LATENCY`] stages of its own
14//! (`logos_round88_p`: the magnitude; each byte's leading one; the leading one; the significand kept with its round and
15//! sticky bits; the rounding and the f32). Its multipliers are the bytes every decoder's
16//! `c` and `n` are, its offset term a shift (every format's offset is zero or a power of two). Built for one activation
17//! word (`WORDS` 1), a unit's signed sum `Σ (q − z)·a` over its 16 elements is within 19 bits (every format's offset
18//! code within ±128, every one-word activation code within ±127: at most 16·128·127 = 260,096), so a pair of units'
19//! products is one DSP block's two 18×19 multipliers summed; built for two, each unit's product is one wide multiplier.
20//!
21//! `logos_lane_slim_p #(UNITS, WORDS)` is `logos_lane_value_p` for a block of one scale — every unit's multiplier 1
22//! and its offset multiplier 0, Q8_0's and Q4_0's blocks — at the same latency: the units' signed sums summed, the one
23//! scaled term and the rounding, no unit multiplies and one product a block.
24
25/// Clocks from `logos_round88_p`'s operands to their f32.
26pub const ROUND88_P_LATENCY: usize = 5;
27
28/// Clocks from a block's inputs to its value in `logos_lane_value_p`: four to the exact sum, then the rounding.
29pub const LANE_VALUE_LATENCY: usize = 5 + ROUND88_P_LATENCY;
30
31/// Every module of the lane, with the f32 adder it accumulates through.
32pub 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"#;