Skip to main content

logicaffeine_compile/spatial/decode_engine/rtl/
act_quant.rs

1//! The activation quantizer ([`crate::spatial::decode_engine::numerics::quantize`]) as Verilog: a block of 32 f32
2//! values to its 4-bit-significand scale and its codes.
3//!
4//! The scale has a closed form in the block's largest magnitude `S·2^(E−23)` (`S` its 24-bit significand): with
5//! `m₁ = ⌈⌈S/2¹³⌉/127⌉`, the scale is `m₁·2^(E−10)` when `m₁ ≤ 15`, else `⌈⌈S/2¹⁴⌉/127⌉·2^(E−9)`, a significand of 8
6//! or 9 ([`closed_form_scale`], held to `block_scale` over every significand). A code is the exact quotient of its
7//! value by the scale, carried with 32 fraction bits and a remainder, rounded to nearest with ties to even.
8
9use crate::spatial::decode_engine::numerics::ActScale;
10
11/// The per-block scale's closed form ([`crate::spatial::decode_engine::numerics::wide_scale`], an 8-bit significand),
12/// for a normal `amax` with significand `s` (24 bits) and unbiased exponent `e`: with `m₁ = ⌈⌈S/2⁹⌉/127⌉`, the scale is
13/// `m₁·2^(e−14)` when `m₁ ≤ 255`, else `⌈⌈S/2¹⁰⌉/127⌉·2^(e−13)`, a significand of 128 to 130.
14pub fn closed_form_wide_scale(s: u32, e: i32) -> ActScale {
15    let m1 = ((s + 511) >> 9).div_ceil(127) as i64;
16    if m1 <= 255 {
17        ActScale { m: m1, e: e - 14 }
18    } else {
19        ActScale { m: ((s + 1023) >> 10).div_ceil(127) as i64, e: e - 13 }
20    }
21}
22
23/// Clocks from `logos_wide_scale_p`'s operands to their scale.
24pub const WIDE_SCALE_P_LATENCY: usize = 3;
25
26/// The block scale's closed form, for a normal `amax` with significand `s` (24 bits) and unbiased exponent `e`.
27pub fn closed_form_scale(s: u32, e: i32) -> ActScale {
28    let m1 = ((s + 8191) >> 13).div_ceil(127) as i64;
29    if m1 <= 15 {
30        ActScale { m: m1, e: e - 10 }
31    } else {
32        ActScale { m: ((s + 16383) >> 14).div_ceil(127) as i64, e: e - 9 }
33    }
34}
35
36/// `logos_act_block(x, two) -> (m, e, codes)`: 32 f32 values packed little-end first in `x`; `two` selects the two-word
37/// codes; `codes` packs 32 signed 16-bit codes; `e` is the scale's exponent, signed. `logos_act_wide #(LANES)` is the
38/// per-block quantizer over a block of `LANES` values: the same ports, its `m` eight bits. `logos_act_stream #(LANES)`
39/// is `logos_act_wide` over a block arriving sixteen values a beat, with sixteen code units: one quantizer for every
40/// block length up to `LANES`, its beats held in block RAM. `logos_wide_scale_p` is `logos_wide_scale` for the user
41/// clock, its scale [`WIDE_SCALE_P_LATENCY`] clocks after its operands.
42pub fn act_quant_verilog() -> String {
43    format!("{}\n{}", super::ram::ram_verilog(&[512]), ACT_QUANT_VERILOG)
44}
45
46const ACT_QUANT_VERILOG: &str = r#"// One activation block: its 4-bit-significand scale and its exact codes.
47module logos_act_code(input [31:0] x, input [7:0] m, input signed [11:0] e, input two, output reg signed [15:0] code);
48  wire        neg = x[31];
49  wire [7:0]  ex = x[30:23];
50  wire [23:0] sx = (ex == 8'd0) ? 24'd0 : {1'b1, x[22:0]};
51  // The value's place against the scale's: x / (m·2^e) = sx·2^k / m, k = (ex − 150) − e; carried 32 places finer.
52  wire signed [12:0] k = $signed({5'd0, ex}) - 13'sd150 - e;
53  wire signed [12:0] up32 = k + 13'sd32;
54  wire [63:0] num = (up32 < 13'sd0) ? 64'd0 : (up32 > 13'sd39) ? 64'hffff_ffff_ffff_ffff : ({40'd0, sx} << up32[5:0]);
55  wire [63:0] q = num / {56'd0, m};
56  wire [63:0] rem = num % {56'd0, m};
57  wire [31:0] whole = q[63:32];
58  wire [31:0] frac = q[31:0];
59  wire        up = (frac > 32'h8000_0000) || (frac == 32'h8000_0000 && (rem != 64'd0 || whole[0]));
60  wire [31:0] n = whole + {31'd0, up};
61  wire [31:0] lim = two ? 32'd32767 : 32'd127;
62  wire [31:0] c = (n > lim) ? lim : n;
63  always @* code = neg ? -$signed({1'b0, c[14:0]}) : $signed({1'b0, c[14:0]});
64endmodule
65
66module logos_act_block(input [1023:0] x, input two, output reg [3:0] m, output reg signed [11:0] e, output [511:0] codes);
67  // The largest magnitude, a denormal read as zero.
68  reg [30:0] amax;
69  integer i;
70  always @* begin
71    amax = 31'd0;
72    for (i = 0; i < 32; i = i + 1)
73      if (x[32*i+30 -: 8] != 8'd0 && x[32*i+30 -: 31] > amax) amax = x[32*i+30 -: 31];
74  end
75  wire [23:0] s = {1'b1, amax[22:0]};
76  wire signed [11:0] eu = $signed({4'd0, amax[30:23]}) - 12'sd127;
77  wire [24:0] r13 = ({1'b0, s} + 25'd8191) >> 13;
78  wire [11:0] c13 = r13[11:0];
79  wire [11:0] m1 = (c13 + 12'd126) / 12'd127;
80  wire [24:0] r14 = ({1'b0, s} + 25'd16383) >> 14;
81  wire [11:0] c14 = r14[11:0];
82  wire [11:0] m2 = (c14 + 12'd126) / 12'd127;
83  always @* begin
84    if (amax == 31'd0) begin m = 4'd8; e = 12'sd0; end
85    else if (m1 <= 12'd15) begin m = m1[3:0]; e = eu - 12'sd10 - (two ? 12'sd8 : 12'sd0); end
86    else begin m = m2[3:0]; e = eu - 12'sd9 - (two ? 12'sd8 : 12'sd0); end
87  end
88  genvar g;
89  generate
90    for (g = 0; g < 32; g = g + 1) begin : lane
91      wire signed [15:0] c;
92      logos_act_code u(.x(x[32*g +: 32]), .m({4'd0, m}), .e(e), .two(two), .code(c));
93      assign codes[16*g +: 16] = (amax == 31'd0) ? 16'd0 : c;
94    end
95  endgenerate
96endmodule
97
98// The per-block scale of a block whose largest magnitude is `amax` (the bits below the sign, a denormal read as zero):
99// wide_scale's 8-bit significand, its exponent eight lower for two-word codes.
100module logos_wide_scale(input [30:0] amax, input two, output reg [7:0] m, output reg signed [11:0] e);
101  wire [23:0] s = {1'b1, amax[22:0]};
102  wire signed [11:0] eu = $signed({4'd0, amax[30:23]}) - 12'sd127;
103  wire [24:0] r9 = ({1'b0, s} + 25'd511) >> 9;
104  wire [15:0] c9 = r9[15:0];
105  wire [24:0] r10 = ({1'b0, s} + 25'd1023) >> 10;
106  wire [15:0] c10 = r10[15:0];
107  // Each ceiling over 127 as a product and a shift, no divider: (x·33027) >> 22 is ⌊x/127⌋ for every x = c + 126, c at
108  // most 2^15.
109  wire [31:0] p1 = ({16'd0, c9} + 32'd126) * 32'd33027;
110  wire [31:0] p2 = ({16'd0, c10} + 32'd126) * 32'd33027;
111  wire [15:0] m1 = {6'd0, p1[31:22]};
112  wire [15:0] m2 = {6'd0, p2[31:22]};
113  always @* begin
114    if (amax == 31'd0) begin m = 8'd128; e = 12'sd0; end
115    else if (m1 <= 16'd255) begin m = m1[7:0]; e = eu - 12'sd14 - (two ? 12'sd8 : 12'sd0); end
116    else begin m = m2[7:0]; e = eu - 12'sd13 - (two ? 12'sd8 : 12'sd0); end
117  end
118endmodule
119
120// logos_wide_scale for the user clock, an operand a clock: m and e are logos_wide_scale's of the amax and two three
121// clocks before. Each ceiling's operand ⌈S/2^k⌉ + 126 by one addition (126·2^k added before the shift); its product by
122// 33027, whose top ten bits are the quotient by 127; the choice between the two.
123module logos_wide_scale_p(input clk, input [30:0] amax, input two, output reg [7:0] m, output reg signed [11:0] e);
124  wire [24:0] s = {2'b01, amax[22:0]};
125  wire [24:0] r9 = (s + 25'd65023) >> 9;
126  wire [24:0] r10 = (s + 25'd130047) >> 10;
127  reg [15:0] x1, x2;
128  reg signed [11:0] eu1, eu2;
129  reg z1, z2;
130  always @(posedge clk) begin
131    x1 <= r9[15:0];
132    x2 <= r10[15:0];
133    eu1 <= $signed({4'd0, amax[30:23]}) - (two ? 12'sd135 : 12'sd127);
134    z1 <= amax == 31'd0;
135  end
136  reg [31:0] p1, p2;
137  always @(posedge clk) begin
138    p1 <= {16'd0, x1} * 32'd33027;
139    p2 <= {16'd0, x2} * 32'd33027;
140    eu2 <= eu1;
141    z2 <= z1;
142  end
143  always @(posedge clk) begin
144    if (z2) begin m <= 8'd128; e <= 12'sd0; end
145    else if (p1[31:30] == 2'd0) begin m <= p1[29:22]; e <= eu2 - 12'sd14; end
146    else begin m <= p2[29:22]; e <= eu2 - 12'sd13; end
147  end
148endmodule
149
150// One weight block's activations under one 8-bit-significand scale.
151module logos_act_wide #(parameter LANES = 32)
152  (input [32*LANES-1:0] x, input two, output [7:0] m, output signed [11:0] e, output [16*LANES-1:0] codes);
153  reg [30:0] amax;
154  integer i;
155  always @* begin
156    amax = 31'd0;
157    for (i = 0; i < LANES; i = i + 1)
158      if (x[32*i+30 -: 8] != 8'd0 && x[32*i+30 -: 31] > amax) amax = x[32*i+30 -: 31];
159  end
160  logos_wide_scale sc(.amax(amax), .two(two), .m(m), .e(e));
161  genvar g;
162  generate
163    for (g = 0; g < LANES; g = g + 1) begin : lane
164      wire signed [15:0] c;
165      logos_act_code u(.x(x[32*g +: 32]), .m(m), .e(e), .two(two), .code(c));
166      assign codes[16*g +: 16] = (amax == 31'd0) ? 16'd0 : c;
167    end
168  endgenerate
169endmodule
170
171// logos_act_wide over a block that arrives sixteen values a beat, LANES a multiple of sixteen. `in_v` comes with each
172// beat and `clear` with a block's first: the block's largest magnitude is kept as its beats arrive and the beats are
173// held in block RAM. `go`, a cycle after the last beat, latches the scale; the sixteen code units then make a beat's
174// codes a cycle into `codes`, `busy` until the last beat's. A block of fewer beats leaves the codes above its own as
175// they were; the next block's beats come once `busy` falls.
176module logos_act_stream #(parameter LANES = 256)
177  (input clk, input rst, input clear, input in_v, input [511:0] in_beat, input go, input two,
178   output reg busy, output reg [7:0] m, output reg signed [11:0] e, output [16*LANES-1:0] codes);
179  localparam BEATS = LANES / 16;
180  localparam BA = (BEATS > 1) ? $clog2(BEATS) : 1;
181  // The beat's largest magnitude, a denormal read as zero, by a balanced tree.
182  wire [30:0] mag [0:15];
183  wire [30:0] t1 [0:7];
184  wire [30:0] t2 [0:3];
185  wire [30:0] t3 [0:1];
186  genvar g;
187  generate
188    for (g = 0; g < 16; g = g + 1) begin : magnitude
189      assign mag[g] = (in_beat[32*g+30 -: 8] == 8'd0) ? 31'd0 : in_beat[32*g+30 -: 31];
190    end
191    for (g = 0; g < 8; g = g + 1) begin : max8
192      assign t1[g] = (mag[2*g] > mag[2*g+1]) ? mag[2*g] : mag[2*g+1];
193    end
194    for (g = 0; g < 4; g = g + 1) begin : max4
195      assign t2[g] = (t1[2*g] > t1[2*g+1]) ? t1[2*g] : t1[2*g+1];
196    end
197    for (g = 0; g < 2; g = g + 1) begin : max2
198      assign t3[g] = (t2[2*g] > t2[2*g+1]) ? t2[2*g] : t2[2*g+1];
199    end
200  endgenerate
201  wire [30:0] beat_max = (t3[0] > t3[1]) ? t3[0] : t3[1];
202  reg [30:0] amax;
203  wire [7:0] m_c;
204  wire signed [11:0] e_c;
205  logos_wide_scale sc(.amax(amax), .two(two), .m(m_c), .e(e_c));
206  reg [BA:0] n, c;
207  reg two_r;
208  wire [511:0] xb;
209  logos_ram_w512 #(.D(BEATS), .A(BA)) beats(.clk(clk), .ra(c[BA-1:0]), .rd(xb), .we(in_v),
210    .wa(clear ? {BA{1'b0}} : n[BA-1:0]), .wd(in_beat), .wm({64{1'b1}}));
211  reg [256*BEATS-1:0] cr;
212  assign codes = cr[16*LANES-1:0];
213  wire [255:0] cb;
214  generate
215    for (g = 0; g < 16; g = g + 1) begin : lane
216      wire signed [15:0] cg;
217      logos_act_code u(.x(xb[32*g +: 32]), .m(m), .e(e), .two(two_r), .code(cg));
218      assign cb[16*g +: 16] = cg;
219    end
220  endgenerate
221  always @(posedge clk) begin
222    if (rst) begin
223      busy <= 1'b0; n <= {(BA+1){1'b0}};
224    end else begin
225      if (in_v) begin
226        n <= (clear ? {(BA+1){1'b0}} : n) + 1'b1;
227        amax <= (clear || beat_max > amax) ? beat_max : amax;
228      end
229      if (go) begin
230        busy <= n != {(BA+1){1'b0}}; c <= {(BA+1){1'b0}}; m <= m_c; e <= e_c; two_r <= two;
231      end else if (busy) begin
232        cr[256*c +: 256] <= cb;
233        if (c + 1'b1 == n) busy <= 1'b0; else c <= c + 1'b1;
234      end
235    end
236  end
237endmodule
238"#;