1use std::fmt::Write as _;
13
14use super::fp32::{FADD_LATENCY, FMUL_LATENCY};
15
16use crate::spatial::decode_engine::numerics::{
17 EXP2_64, EXP_BOTTOM, EXP_TOP, INV_C, INV_LN2_64, LN2_64_HI, LN2_64_LO, LN2_HI, LN2_LO, LN_C, ONE_SIXTH, ONE_THIRD,
18 RECIP_SEED, RSQRT_SEED, SOFTPLUS_TOP,
19};
20
21pub fn vector_verilog() -> String {
23 let mut v = String::new();
24 v.push_str(super::fp32::fp32_verilog());
25 v.push('\n');
26 for (name, table) in [("exp2_64", &EXP2_64[..]), ("recip_seed", &RECIP_SEED[..]), ("rsqrt_seed", &RSQRT_SEED[..]), ("ln_c", &LN_C[..]), ("inv_c", &INV_C[..])] {
27 writeln!(v, "module logos_rom_{name}(input [6:0] i, output reg [31:0] y);").unwrap();
28 writeln!(v, " always @* case (i)").unwrap();
29 for (j, &bits) in table.iter().enumerate() {
30 writeln!(v, " 7'd{j}: y = 32'h{bits:08x};").unwrap();
31 }
32 writeln!(v, " default: y = 32'h00000000;\n endcase\nendmodule\n").unwrap();
33 }
34 let constants = [
35 ("INV_LN2_64", INV_LN2_64),
36 ("LN2_64_HI", LN2_64_HI),
37 ("LN2_64_LO", LN2_64_LO),
38 ("LN2_HI", LN2_HI),
39 ("LN2_LO", LN2_LO),
40 ("ONE_SIXTH", ONE_SIXTH),
41 ("ONE_THIRD", ONE_THIRD),
42 ("EXP_TOP", EXP_TOP),
43 ("EXP_BOTTOM", EXP_BOTTOM),
44 ("SOFTPLUS_TOP", SOFTPLUS_TOP),
45 ];
46 let mut defines = String::new();
47 for (name, value) in constants {
48 writeln!(defines, "`define LOGOS_{name} 32'h{:08x}", value.to_bits()).unwrap();
49 }
50 v.push_str(&defines);
51 v.push_str(VECTOR_VERILOG);
52 v.push_str(super::ram::delay_verilog());
53 v.push_str(&exp_p_verilog());
54 v.push_str(&functions_p_verilog());
55 v
56}
57
58pub const I2F_LATENCY: usize = 2;
61
62pub const RNE_P_LATENCY: usize = 2;
65
66pub const EXP_LATENCY: usize =
70 2 * FMUL_LATENCY + RNE_P_LATENCY + I2F_LATENCY + 2 * FADD_LATENCY + 3 * (FMUL_LATENCY + FADD_LATENCY) + FMUL_LATENCY + 1;
71
72fn exp_p_verilog() -> String {
74 let integer = FMUL_LATENCY + RNE_P_LATENCY + I2F_LATENCY;
75 let at_s1 = integer + FMUL_LATENCY;
76 let polynomial = at_s1 + 2 * FADD_LATENCY + 3 * (FMUL_LATENCY + FADD_LATENCY);
77 [
78 ("@I2F@", I2F_LATENCY),
79 ("@X_AT_S1@", at_s1),
80 ("@FADD@", FADD_LATENCY),
81 ("@ROUND@", FMUL_LATENCY + FADD_LATENCY),
82 ("@TABLE@", polynomial - integer),
83 ("@POWER@", polynomial - integer + FMUL_LATENCY),
84 ("@END@", EXP_LATENCY - 1),
85 ("@LATENCY@", EXP_LATENCY),
86 ]
87 .iter()
88 .fold(EXP_P_VERILOG.to_string(), |t, (k, v)| t.replace(k, &v.to_string()))
89}
90
91pub const RECIP_LATENCY: usize = 2 * (2 * FMUL_LATENCY + FADD_LATENCY) + 2;
94
95pub const RSQRT_LATENCY: usize = 2 * (4 * FMUL_LATENCY + FADD_LATENCY) + 2;
98
99pub const LN_LATENCY: usize = 2 + I2F_LATENCY + 4 * FMUL_LATENCY + 4 * FADD_LATENCY + 3 * FADD_LATENCY + 1;
104
105fn functions_p_verilog() -> String {
107 let (recip_newton, rsqrt_newton) = (2 * FMUL_LATENCY + FADD_LATENCY, 4 * FMUL_LATENCY + FADD_LATENCY);
108 let poly = 4 * FMUL_LATENCY + 4 * FADD_LATENCY;
109 [
110 ("@RECIP_STEP@", FMUL_LATENCY + FADD_LATENCY),
111 ("@RECIP_NEWTONS@", 2 * recip_newton),
112 ("@RECIP_NEWTON@", recip_newton),
113 ("@RECIP@", RECIP_LATENCY),
114 ("@RSQRT_TO_E@", 2 * FMUL_LATENCY + FADD_LATENCY),
115 ("@RSQRT_NEWTONS@", 2 * rsqrt_newton),
116 ("@RSQRT_NEWTON@", rsqrt_newton),
117 ("@RSQRT@", RSQRT_LATENCY),
118 ("@FMUL@", FMUL_LATENCY),
119 ("@D_T1@", FMUL_LATENCY + FADD_LATENCY),
120 ("@D2_T3@", 2 * FADD_LATENCY + FMUL_LATENCY),
121 ("@D_T4@", 2 * FMUL_LATENCY + FADD_LATENCY),
122 ("@POLY@", poly),
123 ("@A0@", poly + FADD_LATENCY - FMUL_LATENCY),
124 ("@A3@", poly + 2 * FADD_LATENCY - FMUL_LATENCY),
125 ("@LN_END@", poly + 3 * FADD_LATENCY),
126 ("@LN@", LN_LATENCY),
127 ("@FADD@", FADD_LATENCY),
128 ("@I2F@", I2F_LATENCY),
129 ]
130 .iter()
131 .fold(FUNCTIONS_P_VERILOG.to_string(), |t, (k, v)| t.replace(k, &v.to_string()))
132}
133
134const FUNCTIONS_P_VERILOG: &str = r#"
135// recip on the pipelined units, an operand a clock: y is logos_recip of the operand @RECIP@ clocks before, its two
136// Newton steps' f32 operations in their order, the seed taken from the table and scaled and registered with the
137// operand, the special cases held to the end.
138module logos_recip_p(input clk, input [31:0] x_in, output reg [31:0] y);
139 wire [31:0] xc = `LOGOS_FTZ(x_in);
140 wire signed [15:0] ec = $signed({8'd0, xc[30:23]}) - 16'sd127;
141 wire [31:0] seed_t, seed_m;
142 logos_rom_recip_seed rom(.i(xc[22:16]), .y(seed_t));
143 logos_ldexp ld(.x(seed_t), .k(-ec), .y(seed_m));
144 reg [31:0] x, seed;
145 always @(posedge clk) begin x <= xc; seed <= {xc[31], seed_m[30:0]}; end
146 wire nan = (x[30:23] == 8'hff) && (x[22:0] != 23'd0);
147 wire inf = (x[30:0] == 31'h7f80_0000);
148 wire zero = (x[30:0] == 31'd0);
149 wire [31:0] xy0, d0, y1, xy1, d1, y2, seed_d, x_d, y1_d;
150 logos_fmul_p a0(.clk(clk), .a(x), .b(seed), .y(xy0));
151 logos_fadd_p b0(.clk(clk), .a(32'h4000_0000), .b({~xy0[31], xy0[30:0]}), .y(d0));
152 logos_delay #(.W(32), .D(@RECIP_STEP@)) ds(.clk(clk), .d(seed), .q(seed_d));
153 logos_fmul_p c0(.clk(clk), .a(seed_d), .b(d0), .y(y1));
154 logos_delay #(.W(32), .D(@RECIP_NEWTON@)) dx(.clk(clk), .d(x), .q(x_d));
155 logos_fmul_p a1(.clk(clk), .a(x_d), .b(y1), .y(xy1));
156 logos_fadd_p b1(.clk(clk), .a(32'h4000_0000), .b({~xy1[31], xy1[30:0]}), .y(d1));
157 logos_delay #(.W(32), .D(@RECIP_STEP@)) dy(.clk(clk), .d(y1), .q(y1_d));
158 logos_fmul_p c1(.clk(clk), .a(y1_d), .b(d1), .y(y2));
159 wire [34:0] flags;
160 logos_delay #(.W(35), .D(@RECIP_NEWTONS@)) df(.clk(clk), .d({nan, zero, inf, x}), .q(flags));
161 always @(posedge clk) begin
162 if (flags[34]) y <= flags[31:0];
163 else if (flags[33]) y <= {flags[31], 8'hff, 23'd0};
164 else if (flags[32]) y <= {flags[31], 31'd0};
165 else y <= y2;
166 end
167endmodule
168
169// rsqrt on the pipelined units, an operand a clock: y is logos_rsqrt of the operand @RSQRT@ clocks before, its two
170// Newton steps' f32 operations in their order, the seed taken from the table and scaled and registered with the
171// operand, the special cases held to the end.
172module logos_rsqrt_p(input clk, input [31:0] x_in, output reg [31:0] y);
173 wire [31:0] xc = `LOGOS_FTZ(x_in);
174 wire signed [15:0] ec = $signed({8'd0, xc[30:23]}) - 16'sd127;
175 wire parity = ec[0];
176 wire signed [15:0] half = -((ec - $signed({15'd0, parity})) >>> 1);
177 wire [31:0] seed_t, seed_c;
178 logos_rom_rsqrt_seed rom(.i({parity, xc[22:17]}), .y(seed_t));
179 logos_ldexp ld(.x(seed_t), .k(half), .y(seed_c));
180 reg [31:0] x, seed;
181 always @(posedge clk) begin x <= xc; seed <= seed_c; end
182 wire nan = (x[30:23] == 8'hff) && (x[22:0] != 23'd0);
183 wire inf = (x[30:0] == 31'h7f80_0000);
184 wire zero = (x[30:0] == 31'd0);
185 wire [31:0] xy0, xyy0, h0, d0, y1, xy1, xyy1, h1, d1, y2, s_a, s_e, x_d, y1_a, y1_e;
186 logos_fmul_p a0(.clk(clk), .a(x), .b(seed), .y(xy0));
187 logos_delay #(.W(32), .D(@FMUL@)) dsa(.clk(clk), .d(seed), .q(s_a));
188 logos_fmul_p b0(.clk(clk), .a(xy0), .b(s_a), .y(xyy0));
189 logos_fmul_p c0(.clk(clk), .a(32'h3f00_0000), .b(xyy0), .y(h0));
190 logos_fadd_p d0u(.clk(clk), .a(32'h3fc0_0000), .b({~h0[31], h0[30:0]}), .y(d0));
191 logos_delay #(.W(32), .D(@RSQRT_TO_E@)) dse(.clk(clk), .d(s_a), .q(s_e));
192 logos_fmul_p e0(.clk(clk), .a(s_e), .b(d0), .y(y1));
193 logos_delay #(.W(32), .D(@RSQRT_NEWTON@)) dx(.clk(clk), .d(x), .q(x_d));
194 logos_fmul_p a1(.clk(clk), .a(x_d), .b(y1), .y(xy1));
195 logos_delay #(.W(32), .D(@FMUL@)) dya(.clk(clk), .d(y1), .q(y1_a));
196 logos_fmul_p b1(.clk(clk), .a(xy1), .b(y1_a), .y(xyy1));
197 logos_fmul_p c1(.clk(clk), .a(32'h3f00_0000), .b(xyy1), .y(h1));
198 logos_fadd_p d1u(.clk(clk), .a(32'h3fc0_0000), .b({~h1[31], h1[30:0]}), .y(d1));
199 logos_delay #(.W(32), .D(@RSQRT_TO_E@)) dye(.clk(clk), .d(y1_a), .q(y1_e));
200 logos_fmul_p e1(.clk(clk), .a(y1_e), .b(d1), .y(y2));
201 wire [34:0] flags;
202 logos_delay #(.W(35), .D(@RSQRT_NEWTONS@)) df(.clk(clk), .d({nan || (x[31] && !zero), zero, inf, x}), .q(flags));
203 always @(posedge clk) begin
204 if (flags[34]) y <= 32'h7fc0_0000;
205 else if (flags[33]) y <= 32'h7f80_0000;
206 else if (flags[32]) y <= 32'd0;
207 else y <= y2;
208 end
209endmodule
210
211// ln on the pipelined units, an operand a clock: y is logos_ln of the operand @LN@ clocks before, its f32 operations
212// in their order, the significand's table index registered, then the table's entries, the centre and the exponent as
213// an f32 registered, each operand held to the operation that takes it, the special cases held to the end.
214module logos_ln_p(input clk, input [31:0] x_in, output reg [31:0] y);
215 wire [31:0] xc = `LOGOS_FTZ(x_in);
216 wire bigc = xc[22];
217 wire [31:0] fixed = bigc ? ({8'd0, 1'b1, xc[22:0]} << 5) : ({8'd0, 1'b1, xc[22:0]} << 6);
218 wire [31:0] off = fixed - (32'd48 << 23);
219 wire [31:0] jw = (off >> 23) + (((off[22:0] > 23'h40_0000) || (off[22:0] == 23'h40_0000 && off[23])) ? 32'd1 : 32'd0);
220 reg [31:0] x1;
221 reg [6:0] j;
222 reg signed [31:0] be, cj;
223 always @(posedge clk) begin
224 x1 <= xc; j <= jw[6:0]; cj <= $signed({25'd0, jw[6:0]}) + 32'sd48;
225 be <= $signed({24'd0, xc[30:23]}) - 32'sd127 + (bigc ? 32'sd1 : 32'sd0);
226 end
227 // The centre's numerator and the exponent as f32s on logos_i2f_p, the operand and the table index held beside them;
228 // the centre the numerator over 64, an exact exponent's step (the numerator is 48 to 175).
229 wire [31:0] cnum, ef_c, x1d;
230 wire [6:0] jd;
231 logos_i2f_p ci(.clk(clk), .n(cj), .y(cnum));
232 logos_i2f_p ei(.clk(clk), .n(be), .y(ef_c));
233 logos_delay #(.W(39), .D(@I2F@)) dj(.clk(clk), .d({x1, j}), .q({x1d, jd}));
234 wire [31:0] c_c = {cnum[31], cnum[30:23] - 8'd6, cnum[22:0]};
235 wire [31:0] invc_c, lnc_c;
236 logos_rom_inv_c r0(.i(jd), .y(invc_c));
237 logos_rom_ln_c r1(.i(jd), .y(lnc_c));
238 reg [31:0] x, m, c, invc, lnc, ef;
239 always @(posedge clk) begin
240 x <= x1d; m <= {1'b0, x1d[22] ? 8'd126 : 8'd127, x1d[22:0]};
241 c <= c_c; invc <= invc_c; lnc <= lnc_c; ef <= ef_c;
242 end
243 wire nan = (x[30:23] == 8'hff) && (x[22:0] != 23'd0);
244 wire inf = (x[30:0] == 31'h7f80_0000);
245 wire zero = (x[30:0] == 31'd0);
246 wire [31:0] dm, d, d2, t0, t1, t2, t3, t4, poly, a0, a1, a2, a3, z;
247 wire [31:0] invc_d, d_t1, d2_t3, d_t4, lnc_d, a0_d, a3_d;
248 logos_fadd_p s0(.clk(clk), .a(m), .b({~c[31], c[30:0]}), .y(dm));
249 logos_delay #(.W(32), .D(@FADD@)) di(.clk(clk), .d(invc), .q(invc_d));
250 logos_fmul_p f0(.clk(clk), .a(dm), .b(invc_d), .y(d));
251 logos_fmul_p f1(.clk(clk), .a(d), .b(d), .y(d2));
252 logos_fmul_p f2(.clk(clk), .a(d), .b(32'h3e80_0000), .y(t0));
253 logos_fadd_p s1(.clk(clk), .a(`LOGOS_ONE_THIRD), .b({~t0[31], t0[30:0]}), .y(t1));
254 logos_delay #(.W(32), .D(@D_T1@)) dd1(.clk(clk), .d(d), .q(d_t1));
255 logos_fmul_p f3(.clk(clk), .a(d_t1), .b(t1), .y(t2));
256 logos_fadd_p s2(.clk(clk), .a(32'h3f00_0000), .b({~t2[31], t2[30:0]}), .y(t3));
257 logos_delay #(.W(32), .D(@D2_T3@)) dd2(.clk(clk), .d(d2), .q(d2_t3));
258 logos_fmul_p f4(.clk(clk), .a(d2_t3), .b(t3), .y(t4));
259 logos_delay #(.W(32), .D(@D_T4@)) dd4(.clk(clk), .d(d_t1), .q(d_t4));
260 logos_fadd_p s3(.clk(clk), .a(d_t4), .b({~t4[31], t4[30:0]}), .y(poly));
261 logos_fmul_p f5(.clk(clk), .a(ef), .b(`LOGOS_LN2_HI), .y(a0));
262 logos_delay #(.W(32), .D(@POLY@)) dl(.clk(clk), .d(lnc), .q(lnc_d));
263 logos_fadd_p g0(.clk(clk), .a(lnc_d), .b(poly), .y(a1));
264 logos_delay #(.W(32), .D(@A0@)) da0(.clk(clk), .d(a0), .q(a0_d));
265 logos_fadd_p g1(.clk(clk), .a(a0_d), .b(a1), .y(a2));
266 logos_fmul_p f6(.clk(clk), .a(ef), .b(`LOGOS_LN2_LO), .y(a3));
267 logos_delay #(.W(32), .D(@A3@)) da3(.clk(clk), .d(a3), .q(a3_d));
268 logos_fadd_p g2(.clk(clk), .a(a2), .b(a3_d), .y(z));
269 wire [34:0] flags;
270 logos_delay #(.W(35), .D(@LN_END@)) df(.clk(clk), .d({nan || (x[31] && !zero), zero, inf, x}), .q(flags));
271 always @(posedge clk) begin
272 if (flags[34]) y <= 32'h7fc0_0000;
273 else if (flags[33]) y <= 32'hff80_0000;
274 else if (flags[32]) y <= flags[31:0];
275 else y <= z;
276 end
277endmodule
278"#;
279
280const EXP_P_VERILOG: &str = r#"
281// logos_i2f for the user clock, an integer a clock: y is logos_i2f of the integer two clocks before, the integer's
282// magnitude and its leading one registered, then the shift and the f32.
283module logos_i2f_p(input clk, input signed [31:0] n, output reg [31:0] y);
284 // The highest set bit of m, -1 for none.
285 function signed [31:0] lead_of(input [31:0] m);
286 integer i;
287 begin
288 lead_of = -1;
289 for (i = 0; i < 32; i = i + 1) if (m[i]) lead_of = i;
290 end
291 endfunction
292 wire [31:0] mag_c = n[31] ? -n : n;
293 wire signed [31:0] lead_c = lead_of(mag_c);
294 reg [31:0] mag;
295 reg signed [31:0] lead;
296 reg s;
297 always @(posedge clk) begin mag <= mag_c; lead <= lead_c; s <= n[31]; end
298 wire [31:0] mant = mag << (23 - lead);
299 always @(posedge clk)
300 if (lead < 0) y <= 32'd0;
301 else y <= {s, 8'd127 + lead[7:0], mant[22:0]};
302endmodule
303
304// exp on the pipelined units, an operand a clock: y is logos_exp of the operand @LATENCY@ clocks before, its f32
305// operations in their order, the nearest integer on logos_rne_p and its f32 on logos_i2f_p, the integer held beside it, the
306// table's entry and the power of two taken from the integer and held to the polynomial, the special cases held to the end.
307module logos_exp_p(input clk, input [31:0] x_in, output reg [31:0] y);
308 wire [31:0] x = `LOGOS_FTZ(x_in);
309 wire nan = (x[30:23] == 8'hff) && (x[22:0] != 23'd0);
310 wire above, below;
311 logos_flt top(.a(`LOGOS_EXP_TOP), .b(x), .y(above));
312 logos_flt bottom(.a(x), .b(`LOGOS_EXP_BOTTOM), .y(below));
313 wire [31:0] t;
314 logos_fmul_p m0(.clk(clk), .a(x), .b(`LOGOS_INV_LN2_64), .y(t));
315 wire signed [31:0] n1;
316 logos_rne_p r(.clk(clk), .x(t), .n(n1));
317 wire [31:0] nf;
318 logos_i2f_p c(.clk(clk), .n(n1), .y(nf));
319 wire signed [31:0] n;
320 logos_delay #(.W(32), .D(@I2F@)) dn(.clk(clk), .d(n1), .q(n));
321 wire [31:0] h, l, l_d, x_d, r1, r0;
322 logos_fmul_p m1(.clk(clk), .a(nf), .b(`LOGOS_LN2_64_HI), .y(h));
323 logos_fmul_p m2(.clk(clk), .a(nf), .b(`LOGOS_LN2_64_LO), .y(l));
324 logos_delay #(.W(32), .D(@X_AT_S1@)) dx(.clk(clk), .d(x), .q(x_d));
325 logos_fadd_p s1(.clk(clk), .a(x_d), .b({~h[31], h[30:0]}), .y(r1));
326 logos_delay #(.W(32), .D(@FADD@)) dl(.clk(clk), .d(l), .q(l_d));
327 logos_fadd_p s2(.clk(clk), .a(r1), .b({~l_d[31], l_d[30:0]}), .y(r0));
328 // 1 + r·(1 + r·(1/2 + r/6)), r held for each multiply.
329 wire [31:0] p3, q2, p2, q1, p1, p, r0_a, r0_b;
330 logos_fmul_p m3(.clk(clk), .a(r0), .b(`LOGOS_ONE_SIXTH), .y(p3));
331 logos_fadd_p a3(.clk(clk), .a(32'h3f00_0000), .b(p3), .y(q2));
332 logos_delay #(.W(32), .D(@ROUND@)) dr0(.clk(clk), .d(r0), .q(r0_a));
333 logos_fmul_p m4(.clk(clk), .a(r0_a), .b(q2), .y(p2));
334 logos_fadd_p a2(.clk(clk), .a(32'h3f80_0000), .b(p2), .y(q1));
335 logos_delay #(.W(32), .D(@ROUND@)) dr1(.clk(clk), .d(r0_a), .q(r0_b));
336 logos_fmul_p m5(.clk(clk), .a(r0_b), .b(q1), .y(p1));
337 logos_fadd_p a1(.clk(clk), .a(32'h3f80_0000), .b(p1), .y(p));
338 wire [5:0] j = n[5:0];
339 wire signed [31:0] mexp = (n - $signed({26'd0, j})) >>> 6;
340 wire [31:0] tj_c, tj, tp, z;
341 wire [15:0] k;
342 logos_rom_exp2_64 rom(.i({1'b0, j}), .y(tj_c));
343 logos_delay #(.W(32), .D(@TABLE@)) dt(.clk(clk), .d(tj_c), .q(tj));
344 logos_fmul_p m6(.clk(clk), .a(tj), .b(p), .y(tp));
345 logos_delay #(.W(16), .D(@POWER@)) dk(.clk(clk), .d(mexp[15:0]), .q(k));
346 logos_ldexp ld(.x(tp), .k(k), .y(z));
347 wire [34:0] flags;
348 logos_delay #(.W(35), .D(@END@)) df(.clk(clk), .d({nan, above, below, x}), .q(flags));
349 always @(posedge clk) begin
350 if (flags[34]) y <= flags[31:0];
351 else if (flags[33]) y <= 32'h7f80_0000;
352 else if (flags[32]) y <= 32'd0;
353 else y <= z;
354 end
355endmodule
356"#;
357
358const VECTOR_VERILOG: &str = r#"
359// A denormal read as a zero of its sign.
360`define LOGOS_FTZ(x) (((x[30:23]) == 8'd0) ? {x[31], 31'd0} : (x))
361
362module logos_fsub(input [31:0] a, input [31:0] b, output [31:0] y);
363 logos_fadd u(.a(a), .b({~b[31], b[30:0]}), .y(y));
364endmodule
365
366// a < b over finite or infinite f32s, a zero of either sign equal to the other.
367module logos_flt(input [31:0] a, input [31:0] b, output y);
368 wire az = (a[30:0] == 31'd0), bz = (b[30:0] == 31'd0);
369 wire [31:0] ka = az ? 32'h8000_0000 : (a[31] ? ~a : {1'b1, a[30:0]});
370 wire [31:0] kb = bz ? 32'h8000_0000 : (b[31] ? ~b : {1'b1, b[30:0]});
371 assign y = ka < kb;
372endmodule
373
374// The nearest integer of a finite f32 below 2^30 in magnitude, ties to even.
375module logos_rne(input [31:0] x, output reg signed [31:0] n);
376 wire [7:0] ex = x[30:23];
377 wire [23:0] sig = {1'b1, x[22:0]};
378 integer s;
379 reg [55:0] wide;
380 reg [31:0] whole;
381 reg [23:0] frac;
382 reg up;
383 always @* begin
384 if (ex < 8'd126) n = 32'sd0;
385 else if (ex >= 8'd150) begin
386 whole = {8'd0, sig} << (ex - 8'd150);
387 n = x[31] ? -$signed(whole) : $signed(whole);
388 end else begin
389 s = 150 - ex;
390 wide = {32'd0, sig} >> s;
391 whole = wide[31:0];
392 frac = sig & ((24'd1 << s) - 24'd1);
393 up = (frac > (24'd1 << (s - 1))) || (frac == (24'd1 << (s - 1)) && whole[0]);
394 whole = whole + {31'd0, up};
395 n = x[31] ? -$signed(whole) : $signed(whole);
396 end
397 end
398endmodule
399
400// logos_rne for the user clock in two stages: the shifted magnitude and its round bit, nearest even, registered with
401// the sign; then the add and the sign. n is logos_rne of the f32 two clocks before.
402module logos_rne_p(input clk, input [31:0] x, output reg signed [31:0] n);
403 wire [7:0] ex = x[30:23];
404 wire [23:0] sig = {1'b1, x[22:0]};
405 integer s;
406 reg [55:0] wide;
407 reg [31:0] whole;
408 reg [23:0] frac;
409 reg up;
410 always @* begin
411 s = 0; wide = 56'd0; frac = 24'd0; up = 1'b0; whole = 32'd0;
412 if (ex >= 8'd150) whole = {8'd0, sig} << (ex - 8'd150);
413 else if (ex >= 8'd126) begin
414 s = 150 - ex;
415 wide = {32'd0, sig} >> s;
416 whole = wide[31:0];
417 frac = sig & ((24'd1 << s) - 24'd1);
418 up = (frac > (24'd1 << (s - 1))) || (frac == (24'd1 << (s - 1)) && whole[0]);
419 end
420 end
421 reg [31:0] whole1;
422 reg up1, neg1;
423 always @(posedge clk) begin whole1 <= whole; up1 <= up; neg1 <= x[31]; end
424 wire [31:0] mag = whole1 + {31'd0, up1};
425 always @(posedge clk) n <= neg1 ? -$signed(mag) : $signed(mag);
426endmodule
427
428// A signed integer below 2^24 in magnitude as an f32, exactly.
429module logos_i2f(input signed [31:0] n, output reg [31:0] y);
430 reg [31:0] mag, mant;
431 integer lead, i;
432 always @* begin
433 mag = n[31] ? -n : n;
434 lead = -1;
435 for (i = 0; i < 32; i = i + 1) if (mag[i]) lead = i;
436 mant = mag << (23 - lead);
437 if (lead < 0) y = 32'd0;
438 else y = {n[31], 8'd127 + lead[7:0], mant[22:0]};
439 end
440endmodule
441
442// x·2^k: exact while normal, infinite past the top, a zero of its sign below the bottom.
443module logos_ldexp(input [31:0] x, input signed [15:0] k, output reg [31:0] y);
444 wire [31:0] f = `LOGOS_FTZ(x);
445 wire signed [16:0] e = $signed({9'd0, f[30:23]}) + k;
446 always @* begin
447 if (f[30:0] == 31'd0 || f[30:23] == 8'hff) y = f;
448 else if (e >= 17'sd255) y = {f[31], 8'hff, 23'd0};
449 else if (e <= 17'sd0) y = {f[31], 31'd0};
450 else y = {f[31], e[7:0], f[22:0]};
451 end
452endmodule
453
454module logos_exp(input [31:0] x_in, output reg [31:0] y);
455 wire [31:0] x = `LOGOS_FTZ(x_in);
456 wire nan = (x[30:23] == 8'hff) && (x[22:0] != 23'd0);
457 wire above, below;
458 logos_flt top(.a(`LOGOS_EXP_TOP), .b(x), .y(above));
459 logos_flt bottom(.a(x), .b(`LOGOS_EXP_BOTTOM), .y(below));
460 wire [31:0] t;
461 logos_fmul m0(.a(x), .b(`LOGOS_INV_LN2_64), .y(t));
462 wire signed [31:0] n;
463 logos_rne r(.x(t), .n(n));
464 wire [31:0] nf;
465 logos_i2f c(.n(n), .y(nf));
466 wire [31:0] h, l, r1, r0;
467 logos_fmul m1(.a(nf), .b(`LOGOS_LN2_64_HI), .y(h));
468 logos_fmul m2(.a(nf), .b(`LOGOS_LN2_64_LO), .y(l));
469 logos_fsub s1(.a(x), .b(h), .y(r1));
470 logos_fsub s2(.a(r1), .b(l), .y(r0));
471 wire [31:0] p3, p2, p1, q2, q1, q0, p;
472 logos_fmul m3(.a(r0), .b(`LOGOS_ONE_SIXTH), .y(p3));
473 logos_fadd a3(.a(32'h3f00_0000), .b(p3), .y(q2));
474 logos_fmul m4(.a(r0), .b(q2), .y(p2));
475 logos_fadd a2(.a(32'h3f80_0000), .b(p2), .y(q1));
476 logos_fmul m5(.a(r0), .b(q1), .y(p1));
477 logos_fadd a1(.a(32'h3f80_0000), .b(p1), .y(p));
478 wire [5:0] j = n[5:0];
479 wire signed [31:0] mexp = (n - $signed({26'd0, j})) >>> 6;
480 wire [31:0] tj, tp, z;
481 logos_rom_exp2_64 rom(.i({1'b0, j}), .y(tj));
482 logos_fmul m6(.a(tj), .b(p), .y(tp));
483 logos_ldexp ld(.x(tp), .k(mexp[15:0]), .y(z));
484 always @* begin
485 if (nan) y = x;
486 else if (above) y = 32'h7f80_0000;
487 else if (below) y = 32'd0;
488 else y = z;
489 end
490endmodule
491
492module logos_recip(input [31:0] x_in, output reg [31:0] y);
493 wire [31:0] x = `LOGOS_FTZ(x_in);
494 wire nan = (x[30:23] == 8'hff) && (x[22:0] != 23'd0);
495 wire inf = (x[30:0] == 31'h7f80_0000);
496 wire zero = (x[30:0] == 31'd0);
497 wire signed [15:0] e = $signed({8'd0, x[30:23]}) - 16'sd127;
498 wire [31:0] seed_t, seed_m;
499 logos_rom_recip_seed rom(.i(x[22:16]), .y(seed_t));
500 logos_ldexp ld(.x(seed_t), .k(-e), .y(seed_m));
501 wire [31:0] seed = {x[31], seed_m[30:0]};
502 wire [31:0] xy0, d0, y1, xy1, d1, y2;
503 logos_fmul a0(.a(x), .b(seed), .y(xy0));
504 logos_fsub b0(.a(32'h4000_0000), .b(xy0), .y(d0));
505 logos_fmul c0(.a(seed), .b(d0), .y(y1));
506 logos_fmul a1(.a(x), .b(y1), .y(xy1));
507 logos_fsub b1(.a(32'h4000_0000), .b(xy1), .y(d1));
508 logos_fmul c1(.a(y1), .b(d1), .y(y2));
509 always @* begin
510 if (nan) y = x;
511 else if (zero) y = {x[31], 8'hff, 23'd0};
512 else if (inf) y = {x[31], 31'd0};
513 else y = y2;
514 end
515endmodule
516
517module logos_rsqrt(input [31:0] x_in, output reg [31:0] y);
518 wire [31:0] x = `LOGOS_FTZ(x_in);
519 wire nan = (x[30:23] == 8'hff) && (x[22:0] != 23'd0);
520 wire inf = (x[30:0] == 31'h7f80_0000);
521 wire zero = (x[30:0] == 31'd0);
522 wire signed [15:0] e = $signed({8'd0, x[30:23]}) - 16'sd127;
523 wire parity = e[0];
524 wire signed [15:0] half = -((e - $signed({15'd0, parity})) >>> 1);
525 wire [31:0] seed_t, seed;
526 logos_rom_rsqrt_seed rom(.i({parity, x[22:17]}), .y(seed_t));
527 logos_ldexp ld(.x(seed_t), .k(half), .y(seed));
528 wire [31:0] xy0, xyy0, h0, d0, y1, xy1, xyy1, h1, d1, y2;
529 logos_fmul a0(.a(x), .b(seed), .y(xy0));
530 logos_fmul b0(.a(xy0), .b(seed), .y(xyy0));
531 logos_fmul c0(.a(32'h3f00_0000), .b(xyy0), .y(h0));
532 logos_fsub d0u(.a(32'h3fc0_0000), .b(h0), .y(d0));
533 logos_fmul e0(.a(seed), .b(d0), .y(y1));
534 logos_fmul a1(.a(x), .b(y1), .y(xy1));
535 logos_fmul b1(.a(xy1), .b(y1), .y(xyy1));
536 logos_fmul c1(.a(32'h3f00_0000), .b(xyy1), .y(h1));
537 logos_fsub d1u(.a(32'h3fc0_0000), .b(h1), .y(d1));
538 logos_fmul e1(.a(y1), .b(d1), .y(y2));
539 always @* begin
540 if (nan || (x[31] && !zero)) y = 32'h7fc0_0000;
541 else if (zero) y = 32'h7f80_0000;
542 else if (inf) y = 32'd0;
543 else y = y2;
544 end
545endmodule
546
547module logos_ln(input [31:0] x_in, output reg [31:0] y);
548 wire [31:0] x = `LOGOS_FTZ(x_in);
549 wire nan = (x[30:23] == 8'hff) && (x[22:0] != 23'd0);
550 wire inf = (x[30:0] == 31'h7f80_0000);
551 wire zero = (x[30:0] == 31'd0);
552 // The significand in [1, 2), halved into [3/4, 3/2) when it is 3/2 or more.
553 wire big = x[22];
554 wire signed [31:0] be = $signed({24'd0, x[30:23]}) - 32'sd127 + (big ? 32'sd1 : 32'sd0);
555 wire [31:0] m = {1'b0, big ? 8'd126 : 8'd127, x[22:0]};
556 // (m − 3/4)·64 exactly, in units of 2^-23 of the significand: 64·m − 48, then its nearest integer.
557 wire [31:0] fixed = big ? ({8'd0, 1'b1, x[22:0]} << 5) : ({8'd0, 1'b1, x[22:0]} << 6);
558 wire [31:0] off = fixed - (32'd48 << 23);
559 wire [31:0] jw = (off >> 23) + (((off[22:0] > 23'h40_0000) || (off[22:0] == 23'h40_0000 && off[23])) ? 32'd1 : 32'd0);
560 wire [6:0] j = jw[6:0];
561 wire [31:0] cnum, c;
562 logos_i2f ci(.n($signed({25'd0, j}) + 32'sd48), .y(cnum));
563 logos_ldexp cl(.x(cnum), .k(-16'sd6), .y(c));
564 wire [31:0] invc, lnc, dm, d, d2, t0, t1, t2, t3, t4, poly, ef, a0, a1, a2, a3;
565 logos_rom_inv_c r0(.i(j), .y(invc));
566 logos_rom_ln_c r1(.i(j), .y(lnc));
567 logos_fsub s0(.a(m), .b(c), .y(dm));
568 logos_fmul f0(.a(dm), .b(invc), .y(d));
569 logos_fmul f1(.a(d), .b(d), .y(d2));
570 logos_fmul f2(.a(d), .b(32'h3e80_0000), .y(t0));
571 logos_fsub s1(.a(`LOGOS_ONE_THIRD), .b(t0), .y(t1));
572 logos_fmul f3(.a(d), .b(t1), .y(t2));
573 logos_fsub s2(.a(32'h3f00_0000), .b(t2), .y(t3));
574 logos_fmul f4(.a(d2), .b(t3), .y(t4));
575 logos_fsub s3(.a(d), .b(t4), .y(poly));
576 logos_i2f ei(.n(be), .y(ef));
577 logos_fmul f5(.a(ef), .b(`LOGOS_LN2_HI), .y(a0));
578 logos_fadd g0(.a(lnc), .b(poly), .y(a1));
579 logos_fadd g1(.a(a0), .b(a1), .y(a2));
580 logos_fmul f6(.a(ef), .b(`LOGOS_LN2_LO), .y(a3));
581 wire [31:0] z;
582 logos_fadd g2(.a(a2), .b(a3), .y(z));
583 always @* begin
584 if (nan || (x[31] && !zero)) y = 32'h7fc0_0000;
585 else if (zero) y = 32'hff80_0000;
586 else if (inf) y = x;
587 else y = z;
588 end
589endmodule
590
591module logos_ln1p(input [31:0] t_in, output reg [31:0] y);
592 wire [31:0] t = `LOGOS_FTZ(t_in);
593 wire [31:0] u, lu, um1, err, ru, corr, z;
594 logos_fadd a0(.a(32'h3f80_0000), .b(t), .y(u));
595 logos_ln l0(.x_in(u), .y(lu));
596 logos_fsub s0(.a(u), .b(32'h3f80_0000), .y(um1));
597 logos_fsub s1(.a(t), .b(um1), .y(err));
598 logos_recip r0(.x_in(u), .y(ru));
599 logos_fmul m0(.a(err), .b(ru), .y(corr));
600 logos_fadd a1(.a(lu), .b(corr), .y(z));
601 always @* y = (u == 32'h3f80_0000) ? t : z;
602endmodule
603
604module logos_softplus(input [31:0] u_in, output reg [31:0] y);
605 wire [31:0] u = `LOGOS_FTZ(u_in);
606 wire above;
607 logos_flt top(.a(`LOGOS_SOFTPLUS_TOP), .b(u), .y(above));
608 wire [31:0] en, past, ep, near;
609 logos_exp e0(.x_in({~u[31], u[30:0]}), .y(en));
610 logos_fadd a0(.a(u), .b(en), .y(past));
611 logos_exp e1(.x_in(u), .y(ep));
612 logos_ln1p l0(.t_in(ep), .y(near));
613 always @* y = above ? past : near;
614endmodule
615
616module logos_sigmoid(input [31:0] x_in, output [31:0] y);
617 wire [31:0] x = `LOGOS_FTZ(x_in);
618 wire [31:0] en, d;
619 logos_exp e0(.x_in({~x[31], x[30:0]}), .y(en));
620 logos_fadd a0(.a(32'h3f80_0000), .b(en), .y(d));
621 logos_recip r0(.x_in(d), .y(y));
622endmodule
623
624module logos_silu(input [31:0] x, output [31:0] y);
625 wire [31:0] s;
626 logos_sigmoid g(.x_in(x), .y(s));
627 logos_fmul m(.a(x), .b(s), .y(y));
628endmodule
629"#;