1use crate::spatial::decode_engine::vector::{Bin, Func, Op};
34
35pub fn encode(op: &Op) -> u64 {
37 let bin = |b: Bin| match b {
38 Bin::Mul => 0u64,
39 Bin::Add => 1,
40 Bin::Sub => 2,
41 };
42 let func = |f: Func| match f {
43 Func::Exp => 0u64,
44 Func::Recip => 1,
45 Func::Rsqrt => 2,
46 Func::Sigmoid => 3,
47 Func::Silu => 4,
48 Func::Softplus => 5,
49 Func::Ln1p => 6,
50 Func::Neg => 7,
51 Func::F16 => 8,
52 };
53 let word = |opcode: u64, sub: u64, a: u8, b: u8, d: u8, s: u8, k: u32| {
54 opcode | sub << 4 | (a as u64) << 8 | (b as u64) << 12 | (d as u64) << 16 | (s as u64) << 20 | (k as u64) << 32
55 };
56 match *op {
57 Op::Dot { a, b, d } => word(0, 0, a, b, d, 0, 0),
58 Op::Sum { a, d } => word(1, 0, a, 0, d, 0, 0),
59 Op::Max { a, d } => word(2, 0, a, 0, d, 0, 0),
60 Op::Vec { op, a, b, d } => word(3, bin(op), a, b, d, 0, 0),
61 Op::VecScalar { op, a, s, d } => word(4, bin(op), a, 0, d, s, 0),
62 Op::Map { f, a, d } => word(5, func(f), a, 0, d, 0, 0),
63 Op::Scalar { op, a, b, d } => word(6, bin(op), a, b, d, 0, 0),
64 Op::ScalarMap { f, a, d } => word(7, func(f), a, 0, d, 0, 0),
65 Op::Const { value, d } => word(8, 0, 0, 0, d, 0, value.to_bits()),
66 Op::Hadamard { a, d } => word(9, 0, a, 0, d, 0, 0),
67 Op::Rotate { a, cos, sin, d, neox } => word(10, neox as u64, a, cos, d, sin, 0),
68 Op::ArgMax { a, d } => word(11, 0, a, 0, d, 0, 0),
69 Op::AbsMax { a, d } => word(12, 0, a, 0, d, 0, 0),
70 Op::Codes { a, s, d } => word(13, 0, a, 0, d, s, 0),
71 Op::Scale { a, n, d } => word(14, 0, a, 0, d, 0, n as i32 as u32),
72 Op::MulAdd { a, b, c, d } => word(15, 0, a, b, d, c, 0),
73 Op::MulAddScalar { a, b, s, d } => word(15, 1, a, b, d, s, 0),
74 }
75}
76
77pub fn decode(word: u64) -> Option<Op> {
79 let field = |at: u32| (word >> at & 0xf) as u8;
80 let (opcode, sub, a, b, d, s) = (field(0), field(4), field(8), field(12), field(16), field(20));
81 let k = (word >> 32) as u32;
82 let bin = match sub {
83 0 => Some(Bin::Mul),
84 1 => Some(Bin::Add),
85 2 => Some(Bin::Sub),
86 _ => None,
87 };
88 let func = [Func::Exp, Func::Recip, Func::Rsqrt, Func::Sigmoid, Func::Silu, Func::Softplus, Func::Ln1p, Func::Neg, Func::F16].get(sub as usize).copied();
89 let op = match opcode {
90 0 => Op::Dot { a, b, d },
91 1 => Op::Sum { a, d },
92 2 => Op::Max { a, d },
93 3 => Op::Vec { op: bin?, a, b, d },
94 4 => Op::VecScalar { op: bin?, a, s, d },
95 5 => Op::Map { f: func?, a, d },
96 6 => Op::Scalar { op: bin?, a, b, d },
97 7 => Op::ScalarMap { f: func?, a, d },
98 8 => Op::Const { value: f32::from_bits(k), d },
99 9 => Op::Hadamard { a, d },
100 10 if sub <= 1 => Op::Rotate { a, cos: b, sin: s, d, neox: sub == 1 },
101 11 => Op::ArgMax { a, d },
102 12 => Op::AbsMax { a, d },
103 13 => Op::Codes { a, s, d },
104 14 => Op::Scale { a, n: k as i32 as i8, d },
105 15 if sub == 0 => Op::MulAdd { a, b, c: s, d },
106 15 if sub == 1 => Op::MulAddScalar { a, b, s, d },
107 _ => return None,
108 };
109 (encode(&op) == word).then_some(op)
110}
111
112pub fn simd_verilog() -> String {
114 format!("{}\n{}\n{}", super::ram::ram_verilog(&[512]), super::vector::vector_verilog(), SIMD_VERILOG)
115}
116
117pub fn simd_p_verilog() -> String {
121 use super::fp32::{F32_TO_F16_LATENCY, FADD_LATENCY, FMUL_LATENCY, TREE16_LATENCY};
122 use super::vector::{EXP_LATENCY, I2F_LATENCY, LN_LATENCY, RECIP_LATENCY, RNE_P_LATENCY, RSQRT_LATENCY};
123 assert!(LN_LATENCY >= RECIP_LATENCY.max(RSQRT_LATENCY).max(FADD_LATENCY), "the logarithm is the slowest function core");
124 assert!(FMUL_LATENCY + 1 + RNE_P_LATENCY + 1 + I2F_LATENCY > FADD_LATENCY + 1, "an elementwise row's code is its last value");
128 let ew = 4 + FMUL_LATENCY + RNE_P_LATENCY + I2F_LATENCY;
129 assert!(FMUL_LATENCY + 1 + FADD_LATENCY + 1 <= ew - 2, "a multiply-add's sum is out by an elementwise row's write");
130 let fold_write = 5 + I2F_LATENCY;
132 assert!(FADD_LATENCY >= FMUL_LATENCY, "a scalar operation's product waits for its sum's latency");
134 assert!(fold_write < 8, "a fold's write is a step of its three-bit level");
135 let z = LANE_FUNC_P_LATENCY - 1;
136 let lane = [
137 ("@RECIP_PAD@", LN_LATENCY - RECIP_LATENCY),
138 ("@RSQRT_PAD@", LN_LATENCY - RSQRT_LATENCY),
139 ("@UM1_PAD@", LN_LATENCY - FADD_LATENCY),
140 ("@TO_Z@", FMUL_LATENCY + FADD_LATENCY),
141 ("@YH@", z - 2 - F32_TO_F16_LATENCY),
142 ("@EXP@", EXP_LATENCY),
143 ("@LN@", LN_LATENCY),
144 ("@LANE@", LANE_FUNC_P_LATENCY),
145 ("@FMUL@", FMUL_LATENCY),
146 ("@FADD@", FADD_LATENCY),
147 ]
148 .iter()
149 .fold(LANE_FUNC_P_VERILOG.to_string(), |t, (k, v)| t.replace(k, &v.to_string()));
150 let unit = [
151 ("@FMUL@", FMUL_LATENCY),
152 ("@FADD@", FADD_LATENCY),
153 ("@LANE@", LANE_FUNC_P_LATENCY),
154 ("@TREE@", TREE16_LATENCY),
155 ("@EW@", ew),
156 ("@RNE@", RNE_P_LATENCY),
157 ("@SALIGN@", FADD_LATENCY - FMUL_LATENCY),
158 ("@ADD@", FADD_LATENCY + 1),
159 ("@MAP@", 2 + LANE_FUNC_P_LATENCY),
160 ("@FOLD_WRITE@", fold_write),
161 ("@READ@", SIMD_P_READ_LATENCY),
162 ]
163 .iter()
164 .fold(SIMD_P_VERILOG.to_string(), |t, (k, v)| t.replace(k, &v.to_string()));
165 format!("{}\n{}\n{lane}\n{unit}", super::ram::bram_verilog(&[512]), simd_verilog())
166}
167
168pub const SIMD_P_READ_LATENCY: usize = 2;
171
172pub const LANE_FUNC_P_LATENCY: usize = {
176 use super::fp32::{FADD_LATENCY, FMUL_LATENCY};
177 use super::vector::{EXP_LATENCY, LN_LATENCY};
178 1 + EXP_LATENCY + FADD_LATENCY + LN_LATENCY + FADD_LATENCY + FMUL_LATENCY + FADD_LATENCY + 1
179};
180
181pub const LANE_FUNC_STAGES: usize = 4;
186
187const LANE_FUNC_P_VERILOG: &str = r#"
188// One lane's function of one element on the pipelined units, an operand a clock: y is logos_lane_func's value of the
189// function and operand @LANE@ clocks before, its cores the pipelined ones in the same order — the exponential; the add
190// after it; the reciprocal, the logarithm, the reciprocal square root and one less than the sum, each value held to the
191// slowest's; ln1p's correction; the choice — what each stage needs of the operand held beside it. A core no chosen
192// function uses holds its operand at zero.
193module logos_lane_func_p(input clk, input [3:0] f, input [31:0] x, output reg [31:0] y);
194 // The operand decoded and registered: the exponential's operand, the binary16 round trip's, the flags.
195 wire [31:0] xfc = `LOGOS_FTZ(x);
196 wire spa;
197 logos_flt top(.a(`LOGOS_SOFTPLUS_TOP), .b(xfc), .y(spa));
198 wire is_exp = f == 4'd0, is_recip = f == 4'd1, is_rsqrt = f == 4'd2, is_sig = f == 4'd3, is_silu = f == 4'd4;
199 wire is_sp = f == 4'd5, is_l1p = f == 4'd6, is_half = f == 4'd8;
200 wire gate_c = is_sig || is_silu, past_c = is_sp && spa, near_c = is_sp && !spa;
201 reg [3:0] f1;
202 reg [31:0] x1, xf1, eo1, hx1;
203 reg [7:0] flags1;
204 always @(posedge clk) begin
205 f1 <= f; x1 <= x; xf1 <= xfc;
206 eo1 <= is_exp ? x : (gate_c || past_c) ? {~xfc[31], xfc[30:0]} : near_c ? xfc : 32'd0;
207 hx1 <= is_half ? x : 32'd0;
208 flags1 <= {spa, past_c, gate_c, is_l1p || near_c, is_l1p, is_recip, is_rsqrt, is_silu};
209 end
210 // The binary16 round trip, the rounding on logos_f32_to_f16_p and the widening registered, held to the choice.
211 wire [15:0] half;
212 wire [31:0] yh_c, yh;
213 logos_f32_to_f16_p uh(.clk(clk), .x(hx1), .h(half));
214 logos_f16_to_f32 uw(.h(half), .x(yh_c));
215 reg [31:0] yh_r;
216 always @(posedge clk) yh_r <= yh_c;
217 logos_delay #(.W(32), .D(@YH@)) dyh(.clk(clk), .d(yh_r), .q(yh));
218 // The exponential.
219 wire [31:0] e;
220 logos_exp_p ue(.clk(clk), .x_in(eo1), .y(e));
221 wire [3:0] f_e;
222 wire [31:0] x_e, xf_e;
223 wire [7:0] fl_e;
224 logos_delay #(.W(76), .D(@EXP@)) d1(.clk(clk), .d({f1, x1, xf1, flags1}), .q({f_e, x_e, xf_e, fl_e}));
225 // One plus the exponential for the gates, u plus it past softplus's top; one plus ln1p's operand, softplus's
226 // exponential below it.
227 wire past_e = fl_e[6], gate_e = fl_e[5], on_e = fl_e[4], l1p_e = fl_e[3];
228 wire [31:0] t_e = l1p_e ? xf_e : `LOGOS_FTZ(e);
229 wire [31:0] s;
230 logos_fadd_p ua(.clk(clk), .a(past_e ? xf_e : 32'h3f80_0000), .b((gate_e || past_e) ? e : on_e ? t_e : 32'd0), .y(s));
231 wire [3:0] f_s;
232 wire [31:0] x_s, e_s, t_s;
233 wire [7:0] fl_s;
234 logos_delay #(.W(108), .D(@FADD@)) d2(.clk(clk), .d({f_e, x_e, e, t_e, fl_e}), .q({f_s, x_s, e_s, t_s, fl_s}));
235 // The reciprocal, the logarithm, the reciprocal square root, one less than the sum; each held to the logarithm.
236 wire gate_s = fl_s[5], on_s = fl_s[4], recip_s = fl_s[2], rsqrt_s = fl_s[1];
237 wire [31:0] r_c, lu, um1_c, q_c, r, um1, q;
238 logos_recip_p ur(.clk(clk), .x_in(recip_s ? x_s : (gate_s || on_s) ? s : 32'd0), .y(r_c));
239 logos_ln_p ul(.clk(clk), .x_in(on_s ? s : 32'd0), .y(lu));
240 logos_fadd_p uo(.clk(clk), .a(on_s ? s : 32'd0), .b(32'hbf80_0000), .y(um1_c));
241 logos_rsqrt_p uq(.clk(clk), .x_in(rsqrt_s ? x_s : 32'd0), .y(q_c));
242 logos_delay #(.W(32), .D(@RECIP_PAD@)) dr(.clk(clk), .d(r_c), .q(r));
243 logos_delay #(.W(32), .D(@UM1_PAD@)) du(.clk(clk), .d(um1_c), .q(um1));
244 logos_delay #(.W(32), .D(@RSQRT_PAD@)) dq(.clk(clk), .d(q_c), .q(q));
245 wire [3:0] f_r;
246 wire [31:0] x_r, e_r, t_r, s_r;
247 wire [7:0] fl_r;
248 logos_delay #(.W(140), .D(@LN@)) d3(.clk(clk), .d({f_s, x_s, e_s, t_s, s, fl_s}), .q({f_r, x_r, e_r, t_r, s_r, fl_r}));
249 // silu's product with its sigmoid, or ln1p's correction: the error, then its product with the reciprocal, then the sum
250 // with the logarithm.
251 wire on_r = fl_r[4];
252 wire [31:0] err;
253 logos_fadd_p ud(.clk(clk), .a(on_r ? t_r : 32'd0), .b({~um1[31], um1[30:0]}), .y(err));
254 wire [3:0] f_m;
255 wire [31:0] x_m, e_m, t_m, s_m, r_m, q_m, lu_m;
256 wire [7:0] fl_m;
257 logos_delay #(.W(236), .D(@FADD@)) d4(.clk(clk), .d({f_r, x_r, e_r, t_r, s_r, r, q, lu, fl_r}),
258 .q({f_m, x_m, e_m, t_m, s_m, r_m, q_m, lu_m, fl_m}));
259 wire [31:0] p, z, p_z;
260 logos_fmul_p um(.clk(clk), .a(fl_m[0] ? x_m : fl_m[4] ? err : 32'd0), .b(r_m), .y(p));
261 wire [31:0] lu_p;
262 logos_delay #(.W(32), .D(@FMUL@)) dlu(.clk(clk), .d(lu_m), .q(lu_p));
263 logos_fadd_p uz(.clk(clk), .a(lu_p), .b(p), .y(z));
264 logos_delay #(.W(32), .D(@FADD@)) dp(.clk(clk), .d(p), .q(p_z));
265 wire [3:0] f_z;
266 wire [31:0] x_z, e_z, t_z, s_z, r_z, q_z;
267 wire [7:0] fl_z;
268 logos_delay #(.W(204), .D(@TO_Z@)) d5(.clk(clk), .d({f_m, x_m, e_m, t_m, s_m, r_m, q_m, fl_m}),
269 .q({f_z, x_z, e_z, t_z, s_z, r_z, q_z, fl_z}));
270 // The function chosen.
271 wire [31:0] l1p = (s_z == 32'h3f80_0000) ? t_z : z;
272 always @(posedge clk) case (f_z)
273 4'd0: y <= e_z; 4'd1: y <= r_z; 4'd2: y <= q_z; 4'd3: y <= r_z; 4'd4: y <= p_z; 4'd5: y <= fl_z[7] ? s_z : l1p;
274 4'd6: y <= l1p; 4'd8: y <= yh;
275 default: y <= {~x_z[31], x_z[30:0]};
276 endcase
277endmodule
278"#;
279
280const SIMD_P_VERILOG: &str = r#"// The vector unit in the user clock: logos_simd's instruction set over the same registers, every value the same, on the
281// pipelined units. Each instruction's rows stream through its units a row a clock, written as they come out: a row read
282// at a clock, its operands registered the clock after the read, its result written @EW@ clocks after the read for an
283// elementwise instruction, its function values @MAP@ clocks after for a Map. Dot and Sum add a row each @ADD@ clocks,
284// each lane's running sum through one pipelined add, then fold the lanes by the pipelined tree; Max, AbsMax and ArgMax
285// take a row a clock and fold the lanes in four registered levels. A Hadamard stage streams its rows, a pair of rows
286// two writes, the stages one after another; a rotary block's three rows are read, turned and written before the next
287// block's. A ScalarMap runs on the first function unit, a scalar operation on its own multiply and add. Lanes past a
288// register's length stay out of every reduction; an instruction over an empty register walks its first row, as
289// logos_simd's does. The registers are block RAM read on the rising edge (logos_bram_w512), three copies a bank:
290// copy A at the row at hand, or at the engine's row while the unit is idle; copies B and C at a Hadamard partner row and
291// a rotary block's two rows, which are rows of the instruction's operand, so they hold a register's first PAIRS rows,
292// every program's Hadamard and rotary operands within them. A row asked is registered (copied near the memories it
293// reaches) and read the clock after, its word out the clock after that: @READ@ clocks from the ask, the engine's reads
294// too. The one write a clock is registered alike, its banks, row, data and mask a clock after its choice, so the
295// memories see every read and write a clock later than they are asked and each read sees the writes it would unregistered.
296//
297// An instruction runs over a group of passes, `group` of them (one when zero, at most PASSES) taken at its issue: pass
298// p's rows of every register from p·group_rows, its scalar registers its own (rd_s_pass reads them). Elementwise
299// instructions and Maps walk the passes' rows one pass after another; Dot and Sum take row r of each pass of a subgroup
300// of ADD passes in turn, ADD clocks a round, so each pass's running sum takes its next row the clock its last add is out
301// and a subgroup of ADD passes adds a row a clock, each subgroup's rows to their end before the next subgroup's; Max, AbsMax and ArgMax fold one pass before the next; a scalar operation or a ScalarMap
302// takes a pass a clock and writes each pass's value as it comes out; a constant every pass's. Each pass's values, and
303// their order within it, are a group of one's; a group of one is the unit clock for clock. A Hadamard or a rotary
304// instruction runs in a group of one.
305module logos_simd_p #(parameter ROWS = 320, VREGS = 8, SREGS = 8, FU = 16, PAIRS = ROWS, PASSES = 1)
306 (input clk, input rst,
307 input issue, input [63:0] instr, output busy, input [7:0] group, input [15:0] group_rows,
308 input wr, input [3:0] wr_reg, input [15:0] wr_row, input [511:0] wr_data, input len_wr, input [19:0] len_val,
309 input [3:0] rd_reg, input [15:0] rd_row, output [511:0] rd_data, input [3:0] rd_s, input [7:0] rd_s_pass,
310 output [31:0] rd_s_val);
311 localparam FMUL = @FMUL@, FADD = @FADD@, LANE = @LANE@, TREE = @TREE@, EW = @EW@;
312 // A lane's add's clocks from the operands chosen to its sum, and its multiply's to its product: the choice
313 // registered, then the pipelined unit.
314 localparam ADD = FADD + 1, MUL = FMUL + 1, RNEP = @RNE@, SALIGN = @SALIGN@;
315 // A walk's waits and slots at the widths of the counters compared with them.
316 localparam [15:0] WAIT_ADD = FADD, WAIT_LANE = LANE;
317 localparam [7:0] SLOTS = ADD;
318 `LOGOS_RAMSTYLE_ASYNC(VREGS) reg [19:0] len [0:VREGS-1];
319 // The scalar registers, SREGS a pass: pass p's register k at p·SREGS + k.
320 (* ramstyle = "logic" *) reg [31:0] sr [0:SREGS*PASSES-1];
321 assign rd_s_val = sr[rd_s_pass * SREGS + rd_s];
322
323 reg [63:0] ir;
324 reg running;
325 assign busy = running;
326 wire [3:0] opc = ir[3:0], sub = ir[7:4], ra = ir[11:8], rb = ir[15:12], rdd = ir[19:16], rs = ir[23:20];
327 // The operand's length, taken at issue, and its rows a pass: an empty register walks its first row.
328 reg [19:0] n;
329 // The group of passes taken at issue (gp of them, gr rows a pass), the pass a walk reads (pp) and its first row
330 // (pbase); a Dot's or Sum's subgroup (its first pass sg, that pass's first row sgbase), its slot in its round of ADD
331 // clocks and the slot's pass's first row (sbase); a Map's pass
332 // written (o_pp) and its first row (o_pbase); the clocks of a scalar operation's walk (wt).
333 wire [7:0] g_in = (group == 8'd0) ? 8'd1 : group;
334 reg [7:0] gp, pp, slot, o_pp, sg;
335 reg [15:0] gr, pbase, sbase, o_pbase, wt, sgbase;
336 wire [19:0] rows_n = (n + 20'd15) >> 4;
337 wire [15:0] rows = (rows_n == 20'd0) ? 16'd1 : rows_n[15:0];
338
339 // The registers in block RAM, a bank a register, three copies a bank written alike below PAIRS rows, each read
340 // registered. The rows each copy is asked (row_a, row_b, row_c) and the write chosen (w_*), and both registered: the
341 // rows read (ra_*), and each bank's enables, the row, the data and the mask written (wq_*).
342 localparam RA = (ROWS > 1) ? $clog2(ROWS) : 1;
343 localparam PA = (PAIRS > 1) ? $clog2(PAIRS) : 1;
344 localparam [16:0] PAIR_ROWS = PAIRS;
345 wire [512*VREGS-1:0] qa, qb, qc;
346 reg [15:0] row_a, row_b, row_c;
347 (* maxfan = 64 *) reg [15:0] ra_a, ra_b, ra_c;
348 reg w_en;
349 reg [3:0] w_reg;
350 reg [15:0] w_row;
351 reg [511:0] w_data;
352 reg [63:0] w_mask;
353 (* maxfan = 64 *) reg [VREGS-1:0] wq_a, wq_p;
354 (* maxfan = 64 *) reg [15:0] wq_row;
355 (* maxfan = 64 *) reg [63:0] wq_mask;
356 reg [511:0] wq_data;
357 integer kb;
358 always @(posedge clk) begin
359 ra_a <= row_a; ra_b <= row_b; ra_c <= row_c;
360 for (kb = 0; kb < VREGS; kb = kb + 1) begin
361 wq_a[kb] <= !rst && w_en && w_reg == kb[3:0];
362 wq_p[kb] <= !rst && w_en && w_reg == kb[3:0] && (PAIRS >= ROWS || {1'b0, w_row} < PAIR_ROWS);
363 end
364 wq_row <= w_row; wq_data <= w_data; wq_mask <= w_mask;
365 end
366 genvar l, r, k2;
367 generate
368 for (r = 0; r < VREGS; r = r + 1) begin : bank
369 logos_bram_w512 #(.D(ROWS), .A(RA)) ca(.clk(clk), .ra(ra_a[RA-1:0]), .rd(qa[512*r +: 512]), .we(wq_a[r]),
370 .wa(wq_row[RA-1:0]), .wd(wq_data), .wm(wq_mask));
371 logos_bram_w512 #(.D(PAIRS), .A(PA)) cb(.clk(clk), .ra(ra_b[PA-1:0]), .rd(qb[512*r +: 512]), .we(wq_p[r]),
372 .wa(wq_row[PA-1:0]), .wd(wq_data), .wm(wq_mask));
373 logos_bram_w512 #(.D(PAIRS), .A(PA)) cc(.clk(clk), .ra(ra_c[PA-1:0]), .rd(qc[512*r +: 512]), .we(wq_p[r]),
374 .wa(wq_row[PA-1:0]), .wd(wq_data), .wm(wq_mask));
375 end
376 endgenerate
377 reg [3:0] rd_q, rd_q0;
378 always @(posedge clk) begin rd_q0 <= rd_reg; rd_q <= rd_q0; end
379 assign rd_data = qa[512*rd_q +: 512];
380
381 // The phases of an instruction.
382 localparam [3:0] P_IDLE = 4'd0, P_EW = 4'd1, P_MAP = 4'd2, P_SUM = 4'd3, P_MAX = 4'd4, P_FOLD = 4'd5, P_WAIT = 4'd6,
383 P_NEAR = 4'd7, P_FAR = 4'd8, P_ROT = 4'd9, P_TURN = 4'd10, P_PAIRS = 4'd11;
384 reg [3:0] phase;
385 // Reads: the row the next read takes, the slice of it, and whether the phase's reads are done.
386 reg [15:0] row;
387 reg [3:0] slice;
388 reg reading;
389 reg [19:0] stride;
390 reg [7:0] timer;
391 wire [15:0] partner = row + stride[19:4];
392 wire row_low = (row & stride[19:4]) == 16'd0;
393 wire [19:0] pairs_now = len[rb];
394 reg [19:0] pairs;
395 wire [19:0] blocks_n = (pairs + 20'd15) >> 4;
396 wire [19:0] q_first = {row, 4'd0} + pairs;
397 // This clock's read: whether one is asked, and its row and slice; the same a clock later (the row read), two clocks
398 // later (the word out) and three (the operands registered).
399 // Each read's pass, its row within the pass, and whether it is the walk's last, beside it.
400 reg iss, iss_last;
401 reg [15:0] iss_row, iss_rel;
402 reg [3:0] iss_slice;
403 reg [7:0] iss_pass;
404 reg t0_v, t1_v, t2_v, t0_last, t1_last, t2_last;
405 reg [15:0] t0_row, t1_row, t2_row, t0_rel, t1_rel, t2_rel;
406 reg [3:0] t0_slice, t1_slice, t2_slice;
407 reg [7:0] t0_pass, t1_pass, t2_pass;
408 always @(posedge clk) begin
409 t0_v <= iss && !rst; t0_row <= iss_row; t0_slice <= iss_slice;
410 t1_v <= t0_v && !rst; t1_row <= t0_row; t1_slice <= t0_slice;
411 t2_v <= t1_v && !rst; t2_row <= t1_row; t2_slice <= t1_slice;
412 t0_pass <= iss_pass; t0_rel <= iss_rel; t0_last <= iss_last;
413 t1_pass <= t0_pass; t1_rel <= t0_rel; t1_last <= t0_last;
414 t2_pass <= t1_pass; t2_rel <= t1_rel; t2_last <= t1_last;
415 end
416 // The operands, registered the clock after their read.
417 reg [511:0] va, vb, da, db, dc, sn;
418 always @(posedge clk) begin
419 va <= qa[512*ra +: 512]; vb <= qa[512*rb +: 512]; da <= qa[512*rdd +: 512]; db <= qb[512*rdd +: 512];
420 dc <= qc[512*rdd +: 512]; sn <= qa[512*rs +: 512];
421 end
422 wire [31:0] sv = sr[t2_pass * SREGS + rs];
423
424 // Each lane's units: four multiplies and two adds every instruction shares, their operands by instruction and
425 // phase. A Dot's or Sum's lane adds its running sum (acc_in) to the row's term; a Hadamard pair's sum and difference;
426 // a rotary pair's four products, their difference and their sum.
427 // A pass's running sums or maxima (pacc[0] a maximum's, every pass's its own for Dot and Sum), and the sums a Dot's
428 // or Sum's add takes this clock: its pass's.
429 (* ramstyle = "logic" *) reg [511:0] pacc [0:PASSES-1];
430 reg [511:0] acc_in;
431 wire [511:0] acc = pacc[0];
432 wire [511:0] m0v, m1v, m2v, m3v, a0v, a1v, xs;
433 wire [511:0] term = (opc == 4'd0) ? m0v : xs;
434 logos_delay #(.W(512), .D(MUL)) dxs(.clk(clk), .d(va), .q(xs));
435 // A rotary step's rows and pairs, held through the step: lane l turns pair 16·row + l, its elements p and q.
436 wire [16*20-1:0] rp, rq;
437 wire [15:0] ron;
438 wire near = stride < 20'd16;
439 wire [3:0] h = stride[3:0];
440 wire rot = phase == P_TURN, had = phase == P_NEAR || phase == P_FAR, sum = phase == P_SUM, fma = opc == 4'd15;
441 // A multiply-add's addend, MUL clocks on, beside its product: the register the scalar field names, or the pass's
442 // scalar.
443 wire [511:0] sn_m;
444 wire [31:0] sv_m;
445 logos_delay #(.W(512), .D(MUL)) dma_v(.clk(clk), .d(sn), .q(sn_m));
446 logos_delay #(.W(32), .D(MUL)) dma_s(.clk(clk), .d(sv), .q(sv_m));
447 reg [7:0] turn_t;
448 generate
449 for (l = 0; l < 16; l = l + 1) begin : lane
450 wire [31:0] x = va[32*l +: 32], y = vb[32*l +: 32];
451 wire [31:0] mine = da[32*l +: 32];
452 wire [31:0] other = (h == 4'd1) ? da[32*(l ^ 1) +: 32] : (h == 4'd2) ? da[32*(l ^ 2) +: 32]
453 : (h == 4'd4) ? da[32*(l ^ 4) +: 32] : da[32*(l ^ 8) +: 32];
454 wire [31:0] far = db[32*l +: 32];
455 wire low = ((l & h) == 0);
456 wire [19:0] i = {row, 4'd0} + l;
457 assign ron[l] = i < pairs;
458 wire [19:0] p = sub[0] ? i : {i[18:0], 1'b0};
459 wire [19:0] q = sub[0] ? i + pairs : {i[18:0], 1'b1};
460 assign rp[20*l +: 20] = p;
461 assign rq[20*l +: 20] = q;
462 wire [511:0] wp = (p[19:4] == row_b) ? db : (p[19:4] == row_c) ? dc : da;
463 wire [511:0] wq = (q[19:4] == row_b) ? db : (q[19:4] == row_c) ? dc : da;
464 wire [31:0] x0 = wp[32*p[3:0] +: 32], x1 = wq[32*q[3:0] +: 32];
465 wire [31:0] c = vb[32*l +: 32], s = sn[32*l +: 32];
466 wire [31:0] by = (opc == 4'd3 || opc == 4'd0 || fma) ? y : (opc == 4'd4 || opc == 4'd13) ? sv : 32'h3f80_0000;
467 wire [31:0] bin_b = (opc == 4'd3) ? y : sv;
468 wire [31:0] nb = (sub == 4'd2) ? {~bin_b[31], bin_b[30:0]} : bin_b;
469 wire [31:0] m0, m1, m2, m3;
470 reg [31:0] m0_a, m0_b, m1_a, m1_b, m2_a, m2_b, m3_a, m3_b;
471 always @(posedge clk) begin
472 m0_a <= rot ? x0 : x; m0_b <= rot ? c : by;
473 m1_a <= rot ? x1 : 32'd0; m1_b <= rot ? s : 32'd0;
474 m2_a <= rot ? x0 : 32'd0; m2_b <= rot ? s : 32'd0;
475 m3_a <= rot ? x1 : 32'd0; m3_b <= rot ? c : 32'd0;
476 end
477 logos_fmul_p um0(.clk(clk), .a(m0_a), .b(m0_b), .y(m0));
478 logos_fmul_p um1(.clk(clk), .a(m1_a), .b(m1_b), .y(m1));
479 logos_fmul_p um2(.clk(clk), .a(m2_a), .b(m2_b), .y(m2));
480 logos_fmul_p um3(.clk(clk), .a(m3_a), .b(m3_b), .y(m3));
481 assign m0v[32*l +: 32] = m0;
482 assign m1v[32*l +: 32] = m1;
483 assign m2v[32*l +: 32] = m2;
484 assign m3v[32*l +: 32] = m3;
485 // A rotary block's adds take its products, held as its operands are; a multiply-add's its product and its addend,
486 // MUL clocks after its read; every other add its operands; each add's operands chosen and registered the clock
487 // before the add takes them.
488 reg [31:0] a0_a, a0_b, a1_a, a1_b;
489 always @(posedge clk) begin
490 a0_a <= rot ? m2 : fma ? m0 : had ? (near ? (low ? mine : other) : mine) : sum ? acc_in[32*l +: 32] : x;
491 a0_b <= rot ? m3 : fma ? (sub[0] ? sv_m : sn_m[32*l +: 32]) : had ? (near ? (low ? other : mine) : far) : sum ? term[32*l +: 32] : nb;
492 a1_a <= rot ? m0 : !had ? 32'd0 : near ? other : mine;
493 a1_b <= rot ? {~m1[31], m1[30:0]} : !had ? 32'd0 : near ? {~mine[31], mine[30:0]} : {~far[31], far[30:0]};
494 end
495 logos_fadd_p ua0(.clk(clk), .a(a0_a), .b(a0_b), .y(a0v[32*l +: 32]));
496 logos_fadd_p ua1(.clk(clk), .a(a1_a), .b(a1_b), .y(a1v[32*l +: 32]));
497 end
498 endgenerate
499 // A rotary block's sums out: its operands registered the clock after its words, its products MUL clocks on, its sums
500 // ADD after those; then its turned elements placed two lanes a clock, and its three rows written.
501 localparam [7:0] CAP = @READ@ + MUL + ADD, PLACED = CAP + 8'd8;
502
503 // An elementwise instruction's row, the clock it is written: a product, a sum, a multiply-add's sum, a code, a scaled
504 // or copied row.
505 wire [511:0] mul_w, add_w, fma_w, va_w, code_w, scale_w;
506 logos_delay #(.W(512), .D(EW - 2 - MUL)) dmw(.clk(clk), .d(m0v), .q(mul_w));
507 logos_delay #(.W(512), .D(EW - 2 - ADD)) daw(.clk(clk), .d(a0v), .q(add_w));
508 logos_delay #(.W(512), .D(EW - 2 - MUL - ADD)) dfw(.clk(clk), .d(a0v), .q(fma_w));
509 // The operand a clock short of its write, then registered beside its power-of-two scale, so the scale's shift is a
510 // stage of its own.
511 wire [511:0] va_w3;
512 reg [511:0] va_wr, scale_wr;
513 logos_delay #(.W(512), .D(EW - 3)) dvw(.clk(clk), .d(va), .q(va_w3));
514 always @(posedge clk) begin va_wr <= va_w3; scale_wr <= scale_w; end
515 assign va_w = va_wr;
516 wire sv_inf;
517 logos_delay #(.W(1), .D(MUL + RNEP)) dsi(.clk(clk), .d((sv[30:23] == 8'hff) && (sv[22:0] == 23'd0)), .q(sv_inf));
518 generate
519 for (l = 0; l < 16; l = l + 1) begin : code
520 // The int8 code of the product under the inverse scale (logos_code8_of in stages): the product's nearest integer
521 // on logos_rne_p, its class held beside it; within ±127, a NaN zero, past 127 saturated, every code zero under an
522 // infinite scale, registered; then as an f32 on logos_i2f_p.
523 wire [31:0] p = m0v[32*l +: 32];
524 wire signed [31:0] n1;
525 wire nan1, big1, neg1;
526 logos_rne_p rn(.clk(clk), .x(p), .n(n1));
527 logos_delay #(.W(3), .D(@RNE@)) dcl(.clk(clk), .d({(p[30:23] == 8'hff) && (p[22:0] != 23'd0), p[30:23] >= 8'd134, p[31]}),
528 .q({nan1, big1, neg1}));
529 wire signed [31:0] near_c = (n1 > 32'sd127) ? 32'sd127 : (n1 < -32'sd127) ? -32'sd127 : n1;
530 reg signed [31:0] code;
531 always @(posedge clk) code <= (sv_inf || nan1) ? 32'sd0 : big1 ? (neg1 ? -32'sd127 : 32'sd127) : near_c;
532 logos_i2f_p ci(.clk(clk), .n(code), .y(code_w[32*l +: 32]));
533 logos_ldexp sc(.x(va_w3[32*l +: 32]), .k(ir[47:32]), .y(scale_w[32*l +: 32]));
534 end
535 endgenerate
536 wire [511:0] ew_w = (opc == 4'd3 || opc == 4'd4) ? ((sub == 4'd0) ? mul_w : add_w) : (opc == 4'd13) ? code_w
537 : (opc == 4'd14) ? scale_wr : fma ? fma_w : va_w;
538 // The elementwise writes: a read's row EW clocks on.
539 wire ew_v, ew_last;
540 wire [15:0] ew_row;
541 logos_delay #(.W(18), .D(EW - 2)) dew(.clk(clk), .d({t2_v && (phase == P_EW), t2_last, t2_row}), .q({ew_v, ew_last, ew_row}));
542
543 // The pass a scalar operation's walk feeds this clock: its wt-th, the group's last once every pass is fed.
544 wire [7:0] fpass = (wt < {8'd0, gp}) ? wt[7:0] : gp - 8'd1;
545 // The function units, each on the pipelined lane function: a Map's slices a clock from the operands, each slice's
546 // values out LANE clocks later in order, held in fbuf until its row's last slice is out and the row is written
547 // whole; a ScalarMap's operand on the first unit.
548 localparam [4:0] SLICES = 16 / FU;
549 reg sfeed;
550 wire f_in = (phase == P_MAP && t2_v) || sfeed;
551 wire [32*FU-1:0] fx, fy;
552 generate
553 for (l = 0; l < FU; l = l + 1) begin : fun
554 wire [3:0] fcode = (f_in && (opc == 4'd5 || l == 0)) ? sub : 4'd15;
555 wire [32*SLICES-1:0] cand;
556 for (k2 = 0; k2 < SLICES; k2 = k2 + 1) begin : at
557 assign cand[32*k2 +: 32] = va[32*(FU*k2 + l) +: 32];
558 end
559 wire [31:0] lane_x = (SLICES == 1) ? cand[31:0] : cand[32*t2_slice +: 32];
560 assign fx[32*l +: 32] = (opc == 4'd7 && l == 0) ? sr[fpass * SREGS + ra] : lane_x;
561 logos_lane_func_p fu(.clk(clk), .f(fcode), .x(fx[32*l +: 32]), .y(fy[32*l +: 32]));
562 end
563 endgenerate
564 wire f_out;
565 logos_delay #(.W(1), .D(LANE)) dfo(.clk(clk), .d(f_in && !rst), .q(f_out));
566 // The slice a Map's units give next (o_slice) and whether it is its row's last (o_end); the row a Map writes next
567 // (o_row) and its place in the registers (o_at, its pass's first row and o_row).
568 reg [3:0] o_slice;
569 reg o_end;
570 reg [15:0] o_row, o_at;
571 reg [511:0] fbuf;
572 wire [511:0] func_v;
573 generate
574 for (l = 0; l < 16; l = l + 1) begin : fsl
575 localparam [3:0] MINE = l / FU;
576 assign func_v[32*l +: 32] = (f_out && o_slice == MINE) ? fy[32*(l % FU) +: 32] : fbuf[32*l +: 32];
577 end
578 endgenerate
579 wire last_slice = f_out && o_end;
580
581 // A reduction's lanes: the running sums or maxima, the rows' live lanes and, for ArgMax, each lane's index.
582 reg [15:0] found;
583 `LOGOS_RAMSTYLE_ASYNC(PASSES) reg [15:0] plive [0:PASSES-1];
584 reg [PASSES-1:0] phave;
585 reg [19:0] aidx [0:15];
586 wire [15:0] live2;
587 wire [15:0] take;
588 wire [511:0] max_next;
589 wire largest = (opc == 4'd2) || (opc == 4'd11) || (opc == 4'd12);
590 // The row a Dot's or Sum's add takes: its live lanes, MUL clocks after its operands.
591 wire s_last;
592 wire [15:0] s_live;
593 wire [7:0] s_pass;
594 logos_delay #(.W(25), .D(MUL)) dsv(.clk(clk), .d({t2_rel + 16'd1 >= rows, live2, t2_pass}), .q({s_last, s_live, s_pass}));
595 // A pass's last add out ADD clocks after it is taken: its sums into the tree; their fold out TREE + 1 clocks later.
596 // Each valid in registers the reset clears, its pass beside it.
597 reg [MUL-1:0] sv_sh;
598 reg [ADD-1:0] fold_sh;
599 reg [TREE:0] tw_sh;
600 wire s_v = sv_sh[MUL-1], fold_v = fold_sh[ADD-1], tw_v = tw_sh[TREE];
601 always @(posedge clk) begin
602 sv_sh <= rst ? {MUL{1'b0}} : {sv_sh[MUL-2:0], t2_v && phase == P_SUM};
603 fold_sh <= rst ? {ADD{1'b0}} : {fold_sh[ADD-2:0], s_v && s_last};
604 tw_sh <= rst ? {(TREE+1){1'b0}} : {tw_sh[TREE-1:0], fold_v};
605 end
606 wire [7:0] fold_pass, tw_pass;
607 logos_delay #(.W(8), .D(ADD)) dfd(.clk(clk), .d(s_pass), .q(fold_pass));
608 logos_delay #(.W(8), .D(TREE + 1)) dtw(.clk(clk), .d(fold_pass), .q(tw_pass));
609 generate
610 for (l = 0; l < 16; l = l + 1) begin : red
611 assign live2[l] = ({t2_rel, 4'd0} + l) < n;
612 wire [31:0] x = va[32*l +: 32];
613 wire [31:0] cand = (opc == 4'd12) ? ((x[30:23] == 8'd0) ? 32'd0 : {1'b0, x[30:0]}) : x;
614 wire nan = (x[30:23] == 8'hff) && (x[22:0] != 23'd0);
615 wire gt;
616 logos_fgt g(.a(cand), .b(acc[32*l +: 32]), .y(gt));
617 assign take[l] = (opc == 4'd11) ? (live2[l] && !nan && (!found[l] || gt)) : (live2[l] && gt);
618 assign max_next[32*l +: 32] = take[l] ? cand : acc[32*l +: 32];
619 always @* acc_in[32*l +: 32] = (phave[s_pass] && plive[s_pass][l]) ? a0v[32*l +: 32] : pacc[s_pass][32*l +: 32];
620 end
621 endgenerate
622
623 // The folds: the sums by the pipelined tree, from the clock the last row's add comes out; the maxima and arg-maxima
624 // by four levels of pairs, a level a clock.
625 reg [511:0] tree_in;
626 wire [31:0] tree_sum;
627 logos_tree16_p ts(.clk(clk), .lanes(tree_in), .sum(tree_sum));
628 function [31:0] fkey(input [31:0] v);
629 fkey = (v[30:0] == 31'd0) ? 32'h8000_0000 : (v[31] ? ~v : {1'b1, v[30:0]});
630 endfunction
631 function bigger(input [31:0] a, input [31:0] b);
632 begin
633 bigger = 1'b0;
634 if (!((b[30:23] == 8'hff) && (b[22:0] != 23'd0))) begin
635 if (a[31] == 1'b0 && b[31] == 1'b0) bigger = b[30:0] > a[30:0];
636 else if (a[31] == 1'b1 && b[31] == 1'b1) bigger = b[30:0] < a[30:0];
637 else bigger = (b[31] == 1'b0) && !(b[30:0] == 31'd0 && a[30:0] == 31'd0);
638 end
639 end
640 endfunction
641 // Whether the lane-pair's upper value (b, its index ib, its holding fb) takes the lower's place: ArgMax's the one
642 // holding the larger value, of equal values the lower index; Max's and AbsMax's the larger, a NaN never larger.
643 function wins(input arg, input [31:0] a, input [31:0] b, input [19:0] ia, input [19:0] ib, input fa, input fb);
644 wins = arg ? fb && (!fa || (fkey(b) > fkey(a)) || (fkey(b) == fkey(a) && ib < ia)) : bigger(a, b);
645 endfunction
646 reg [31:0] mv [0:15];
647 reg [19:0] mi [0:15];
648 reg mf [0:15];
649 reg [2:0] level;
650 integer t;
651 // The arg-max's index as an f32, @FOLD_WRITE@ - 5 clocks after the last level leaves it.
652 wire [31:0] arg_f;
653 logos_i2f_p ia(.clk(clk), .n(mf[0] ? {12'd0, mi[0]} : 32'sd0), .y(arg_f));
654
655 // A scalar operation's multiply and add.
656 wire [31:0] s_ra = sr[fpass * SREGS + ra], s_rb = sr[fpass * SREGS + rb];
657 wire [31:0] s_m, s_a, s_bin, s_m_at;
658 logos_fmul_p sm(.clk(clk), .a(s_ra), .b(s_rb), .y(s_m));
659 logos_fadd_p sa(.clk(clk), .a(s_ra), .b(sub == 4'd2 ? {~s_rb[31], s_rb[30:0]} : s_rb), .y(s_a));
660 generate
661 if (SALIGN > 0) begin : align
662 logos_delay #(.W(32), .D(SALIGN)) dsm(.clk(clk), .d(s_m), .q(s_m_at));
663 end else begin : same
664 assign s_m_at = s_m;
665 end
666 endgenerate
667 assign s_bin = (sub == 4'd0) ? s_m_at : s_a;
668
669 // A Hadamard stage's writes: a near stage's row ADD clocks after its operands; a far stage's pair, its low row's sum
670 // and the clock after its partner's difference.
671 reg [15:0] partner_t0, partner_t1, partner_t2;
672 always @(posedge clk) begin partner_t0 <= partner; partner_t1 <= partner_t0; partner_t2 <= partner_t1; end
673 wire hd_v;
674 wire [31:0] hd_rows;
675 logos_delay #(.W(33), .D(ADD)) dhd(.clk(clk), .d({t2_v && had, partner_t2, t2_row}), .q({hd_v, hd_rows}));
676 // A far stage's pairs between their read and their sum's write.
677 reg [7:0] in_far;
678 always @(posedge clk)
679 if (rst || phase != P_FAR) in_far <= 8'd0;
680 else in_far <= in_far + (iss ? 8'd1 : 8'd0) - (hd_v ? 8'd1 : 8'd0);
681 wire [511:0] hd_near;
682 generate
683 for (l = 0; l < 16; l = l + 1) begin : hn
684 assign hd_near[32*l +: 32] = ((l & h) == 0) ? a0v[32*l +: 32] : a1v[32*l +: 32];
685 end
686 endgenerate
687 reg hi_v;
688 reg [15:0] hi_row;
689 reg [511:0] hi_d;
690
691 // A rotary block's turned elements as three masked rows, each element in the row it was read from: each lane's pair's
692 // rows (0 the row at hand, 1 copy B's, 2 copy C's) and places registered with the block's operands; the rows taken
693 // as read and the turned values as they come out; then two lanes a clock placed in lane order, a later lane's element
694 // over an earlier one's, as logos_simd's loop places them.
695 reg [1:0] p_to [0:15], q_to [0:15];
696 reg [3:0] p_at [0:15], q_at [0:15];
697 reg [15:0] on_r;
698 reg [511:0] ry0, ry1;
699 reg [511:0] rw_a, rw_b, rw_c;
700 reg [63:0] rw_am, rw_bm, rw_cm;
701 reg [511:0] nx_a, nx_b, nx_c;
702 reg [63:0] nx_am, nx_bm, nx_cm;
703 integer k, kk, j, kp;
704 // The block's steps marked a clock ahead, each from the step it marks, so no load or write waits on a compare of the
705 // step: its operands' capture (t_cap), its placing steps (t_place), and its three writes (t_w1..t_w3).
706 reg t_cap, t_place, t_w1, t_w2, t_w3;
707 wire [7:0] t_next = turn_t + 8'd1;
708 wire [7:0] t_next_lane = t_next - (CAP + 8'd1);
709 // A placing step's two lanes, selected a clock ahead with the step's marks: each lane's whether, its two elements'
710 // rows and places (one-hot), and its two values — the sums as they are taken, at the step that takes them — so a
711 // place is an enable of a lane, not a shift.
712 reg s_on [0:1];
713 reg [1:0] s_pto [0:1], s_qto [0:1];
714 reg [15:0] s_pat [0:1], s_qat [0:1];
715 reg [31:0] s_pv [0:1], s_qv [0:1];
716 always @* begin
717 nx_a = rw_a; nx_b = rw_b; nx_c = rw_c; nx_am = rw_am; nx_bm = rw_bm; nx_cm = rw_cm;
718 for (kk = 0; kk < 2; kk = kk + 1)
719 if (s_on[kk])
720 for (j = 0; j < 16; j = j + 1) begin
721 if (s_pat[kk][j])
722 case (s_pto[kk])
723 2'd1: begin nx_b[32*j +: 32] = s_pv[kk]; nx_bm[4*j +: 4] = 4'hf; end
724 2'd2: begin nx_c[32*j +: 32] = s_pv[kk]; nx_cm[4*j +: 4] = 4'hf; end
725 default: begin nx_a[32*j +: 32] = s_pv[kk]; nx_am[4*j +: 4] = 4'hf; end
726 endcase
727 if (s_qat[kk][j])
728 case (s_qto[kk])
729 2'd1: begin nx_b[32*j +: 32] = s_qv[kk]; nx_bm[4*j +: 4] = 4'hf; end
730 2'd2: begin nx_c[32*j +: 32] = s_qv[kk]; nx_cm[4*j +: 4] = 4'hf; end
731 default: begin nx_a[32*j +: 32] = s_qv[kk]; nx_am[4*j +: 4] = 4'hf; end
732 endcase
733 end
734 end
735
736 // The rows asked: copy A at the row at hand while the unit runs, at the engine's row while it is idle; copies B and C
737 // at a far Hadamard stage's partner row and a rotary block's two rows.
738 always @* begin
739 iss = 1'b0; iss_row = pbase + row; iss_slice = slice; iss_rel = row; iss_pass = pp;
740 iss_last = row + 16'd1 >= rows && pp + 8'd1 >= gp;
741 if (phase == P_SUM) begin iss_row = sbase + row; iss_pass = sg + slot; end
742 if (running) case (phase)
743 P_EW, P_MAP, P_MAX, P_NEAR: iss = reading;
744 P_SUM: iss = reading && sg + slot < gp;
745 P_FAR: iss = reading && row_low && timer == 8'd0;
746 default: ;
747 endcase
748 row_a = running ? iss_row : rd_row;
749 row_b = 16'd0; row_c = 16'd0;
750 if (phase == P_FAR) row_b = partner;
751 else if (phase == P_ROT || phase == P_TURN) begin
752 if (sub[0]) begin row_b = q_first[19:4]; row_c = q_first[19:4] + 16'd1; end
753 else begin row_b = {row[14:0], 1'b0}; row_c = {row[14:0], 1'b1}; end
754 end
755 end
756
757 // A turn step's rows B and C a clock behind, for its writes: the step holds its rows from its start.
758 reg [15:0] wrow_b, wrow_c;
759 always @(posedge clk) begin wrow_b <= row_b; wrow_c <= row_c; end
760
761 // The one write a clock: the engine's beat while the unit is idle, or the row the instruction writes.
762 always @* begin
763 w_en = 1'b0; w_reg = rdd; w_row = row; w_data = 512'd0; w_mask = {64{1'b1}};
764 if (wr && !running) begin
765 w_en = 1'b1; w_reg = wr_reg; w_row = wr_row; w_data = wr_data;
766 end else if (running) begin
767 if (ew_v) begin w_en = 1'b1; w_row = ew_row; w_data = ew_w; end
768 else if (phase == P_MAP && last_slice) begin w_en = 1'b1; w_row = o_at; w_data = func_v; end
769 else if (hd_v && phase == P_NEAR) begin w_en = 1'b1; w_row = hd_rows[15:0]; w_data = hd_near; end
770 else if (hd_v && phase == P_FAR) begin w_en = 1'b1; w_row = hd_rows[15:0]; w_data = a0v; end
771 else if (hi_v) begin w_en = 1'b1; w_row = hi_row; w_data = hi_d; end
772 else if (phase == P_TURN && t_w1) begin w_en = 1'b1; w_row = row; w_data = rw_a; w_mask = rw_am; end
773 else if (phase == P_TURN && t_w2) begin w_en = 1'b1; w_row = wrow_b; w_data = rw_b; w_mask = rw_bm; end
774 else if (phase == P_TURN && t_w3) begin w_en = 1'b1; w_row = wrow_c; w_data = rw_c; w_mask = rw_cm; end
775 end
776 end
777
778 always @(posedge clk) begin
779 if (rst) begin
780 running <= 1'b0; phase <= P_IDLE; reading <= 1'b0; sfeed <= 1'b0; hi_v <= 1'b0;
781 end else begin
782 sfeed <= 1'b0; hi_v <= 1'b0;
783 if (len_wr) len[wr_reg] <= len_val;
784 if (issue && !running) begin
785 ir <= instr; running <= 1'b1; n <= len[instr[11:8]]; row <= 16'd0; slice <= 4'd0; reading <= 1'b1;
786 stride <= 20'd1; timer <= 8'd0; found <= 16'd0; phave <= {PASSES{1'b0}}; o_slice <= 4'd0; o_end <= SLICES == 5'd1;
787 o_row <= 16'd0;
788 gp <= g_in; gr <= group_rows; pp <= 8'd0; pbase <= 16'd0; slot <= 8'd0; sbase <= 16'd0; o_pp <= 8'd0;
789 sg <= 8'd0; sgbase <= 16'd0;
790 o_pbase <= 16'd0; o_at <= 16'd0; wt <= 16'd0;
791 for (kp = 0; kp < PASSES; kp = kp + 1)
792 if (instr[3:0] == 4'd2 || instr[3:0] == 4'd11) for (k = 0; k < 16; k = k + 1) pacc[kp][32*k +: 32] <= 32'hff80_0000;
793 else pacc[kp] <= 512'd0;
794 case (instr[3:0])
795 4'd0, 4'd1: phase <= P_SUM;
796 4'd2, 4'd11, 4'd12: phase <= P_MAX;
797 4'd5: phase <= P_MAP;
798 4'd6: phase <= P_WAIT;
799 4'd7: begin phase <= P_WAIT; sfeed <= 1'b1; end
800 4'd8: begin
801 for (kp = 0; kp < PASSES; kp = kp + 1) if (kp[7:0] < g_in) sr[kp * SREGS + instr[19:16]] <= instr[63:32];
802 running <= 1'b0;
803 end
804 4'd3, 4'd4, 4'd13, 4'd14, 4'd15, 4'd9, 4'd10: phase <= P_EW;
805 default: running <= 1'b0;
806 endcase
807 end else if (running) begin
808 case (phase)
809 // Elementwise rows, and a Hadamard's or a rotary's copy: a row a clock, done once the last is written.
810 P_EW: begin
811 len[rdd] <= n;
812 if (reading) begin
813 if (row + 16'd1 < rows) row <= row + 16'd1;
814 else if (pp + 8'd1 < gp) begin row <= 16'd0; pp <= pp + 8'd1; pbase <= pbase + gr; end
815 else reading <= 1'b0;
816 end else if (ew_v && ew_last) begin
817 row <= 16'd0; pp <= 8'd0; pbase <= 16'd0;
818 if (opc == 4'd9) begin
819 if (n <= 20'd1) running <= 1'b0; else begin phase <= P_NEAR; reading <= 1'b1; end
820 end else if (opc == 4'd10) phase <= P_PAIRS;
821 else running <= 1'b0;
822 end
823 end
824 // A rotary's pairs, once its copy is written.
825 P_PAIRS: begin
826 pairs <= pairs_now;
827 if (pairs_now == 20'd0) running <= 1'b0; else begin phase <= P_ROT; turn_t <= 8'd0; end
828 end
829 // A rotary block: its rows asked; the operands registered the clock after their words, and each lane's
830 // pair's rows and places; the products MUL clocks after; the sums ADD after them; the rows as read and the
831 // sums taken; the elements placed two lanes a clock; then the rows written a row a clock.
832 P_ROT: begin
833 phase <= P_TURN; turn_t <= 8'd0;
834 t_cap <= CAP == 8'd0; t_place <= 1'b0; t_w1 <= 1'b0; t_w2 <= 1'b0; t_w3 <= 1'b0;
835 end
836 P_TURN: begin
837 turn_t <= t_next;
838 t_cap <= t_next == CAP; t_place <= t_next > CAP && t_next <= PLACED;
839 t_w1 <= t_next == PLACED + 8'd1; t_w2 <= t_next == PLACED + 8'd2; t_w3 <= t_next == PLACED + 8'd3;
840 for (kp = 0; kp < 2; kp = kp + 1) begin
841 k = 2 * t_next_lane[2:0] + kp;
842 s_on[kp] <= on_r[k]; s_pto[kp] <= p_to[k]; s_qto[kp] <= q_to[k];
843 s_pat[kp] <= 16'd1 << p_at[k]; s_qat[kp] <= 16'd1 << q_at[k];
844 s_pv[kp] <= t_cap ? a1v[32*k +: 32] : ry0[32*k +: 32];
845 s_qv[kp] <= t_cap ? a0v[32*k +: 32] : ry1[32*k +: 32];
846 end
847 if (turn_t == 8'd1)
848 for (k = 0; k < 16; k = k + 1) begin
849 p_to[k] <= (rp[20*k+4 +: 16] == row_b) ? 2'd1 : (rp[20*k+4 +: 16] == row_c) ? 2'd2 : 2'd0;
850 q_to[k] <= (rq[20*k+4 +: 16] == row_b) ? 2'd1 : (rq[20*k+4 +: 16] == row_c) ? 2'd2 : 2'd0;
851 p_at[k] <= rp[20*k +: 4]; q_at[k] <= rq[20*k +: 4]; on_r[k] <= ron[k];
852 end
853 if (t_cap) begin
854 rw_a <= da; rw_b <= db; rw_c <= dc; rw_am <= 64'd0; rw_bm <= 64'd0; rw_cm <= 64'd0; ry0 <= a1v; ry1 <= a0v;
855 end
856 if (t_place) begin
857 rw_a <= nx_a; rw_b <= nx_b; rw_c <= nx_c; rw_am <= nx_am; rw_bm <= nx_bm; rw_cm <= nx_cm;
858 end
859 if (t_w3) begin
860 if ({4'd0, row} + 20'd1 >= blocks_n) running <= 1'b0;
861 else begin row <= row + 16'd1; phase <= P_ROT; end
862 end
863 end
864 // A Map: slices a clock, rows one after another; each row written when its last slice is out.
865 P_MAP: begin
866 len[rdd] <= n;
867 if (reading) begin
868 if ({1'b0, slice} + 5'd1 >= SLICES) begin
869 slice <= 4'd0;
870 if (row + 16'd1 < rows) row <= row + 16'd1;
871 else if (pp + 8'd1 < gp) begin row <= 16'd0; pp <= pp + 8'd1; pbase <= pbase + gr; end
872 else reading <= 1'b0;
873 end else slice <= slice + 4'd1;
874 end
875 if (f_out) begin
876 if (!last_slice) begin fbuf <= func_v; o_slice <= o_slice + 4'd1; o_end <= {1'b0, o_slice} + 5'd2 >= SLICES; end
877 else begin
878 o_slice <= 4'd0;
879 o_end <= SLICES == 5'd1;
880 if (o_row + 16'd1 < rows) begin o_row <= o_row + 16'd1; o_at <= o_at + 16'd1; end
881 else if (o_pp + 8'd1 < gp) begin
882 o_row <= 16'd0; o_pp <= o_pp + 8'd1; o_pbase <= o_pbase + gr; o_at <= o_pbase + gr;
883 end
884 else begin o_row <= 16'd0; running <= 1'b0; end
885 end
886 end
887 end
888 // A scalar operation or a ScalarMap: its value the clock its unit's latency ends.
889 P_WAIT: begin
890 sfeed <= opc == 4'd7 && wt + 16'd1 < {8'd0, gp};
891 if (wt >= ((opc == 4'd6) ? WAIT_ADD : WAIT_LANE)) begin
892 sr[(wt - ((opc == 4'd6) ? FADD : LANE)) * SREGS + rdd] <= (opc == 4'd6) ? s_bin : fy[31:0];
893 if (wt - ((opc == 4'd6) ? FADD : LANE) + 16'd1 >= {8'd0, gp}) running <= 1'b0;
894 end
895 wt <= wt + 16'd1;
896 end
897 // Dot and Sum: a row each ADD clocks; each add of a row's terms to the lanes' sums MUL clocks after the
898 // row's operands, its sums out ADD clocks later, the next row's add the same clock; a subgroup's rows to their
899 // end, then the next subgroup's.
900 P_SUM: begin
901 if (reading) begin
902 if (slot + 8'd1 < SLOTS) begin slot <= slot + 8'd1; sbase <= sbase + gr; end
903 else begin
904 slot <= 8'd0; sbase <= sgbase;
905 if (row + 16'd1 < rows) row <= row + 16'd1;
906 else if (sg + SLOTS < gp) begin
907 row <= 16'd0; sg <= sg + SLOTS; sgbase <= sgbase + SLOTS * gr; sbase <= sgbase + SLOTS * gr;
908 end else reading <= 1'b0;
909 end
910 end
911 if (s_v) begin pacc[s_pass] <= acc_in; plive[s_pass] <= s_live; phave[s_pass] <= 1'b1; end
912 if (fold_v)
913 for (k = 0; k < 16; k = k + 1)
914 tree_in[32*k +: 32] <= plive[fold_pass][k] ? a0v[32*k +: 32] : pacc[fold_pass][32*k +: 32];
915 if (tw_v) begin
916 sr[tw_pass * SREGS + rdd] <= tree_sum;
917 if (tw_pass + 8'd1 >= gp) running <= 1'b0;
918 end
919 end
920 // Max, AbsMax and ArgMax: a row a clock, each lane keeping its largest.
921 P_MAX: begin
922 if (reading) begin
923 if (row + 16'd1 >= rows) reading <= 1'b0; else row <= row + 16'd1;
924 end
925 if (t2_v) begin
926 pacc[0] <= max_next;
927 if (opc == 4'd11)
928 for (k = 0; k < 16; k = k + 1)
929 if (take[k]) begin aidx[k] <= {t2_rel, 4'd0} + k[19:0]; found[k] <= 1'b1; end
930 if (t2_rel + 16'd1 >= rows) begin phase <= P_FOLD; level <= 3'd0; end
931 end
932 end
933 P_FOLD: begin
934 begin
935 level <= level + 3'd1;
936 case (level)
937 3'd0: for (t = 0; t < 16; t = t + 1) begin mv[t] <= acc[32*t +: 32]; mi[t] <= aidx[t]; mf[t] <= found[t]; end
938 3'd1: for (t = 0; t < 16; t = t + 2)
939 if (wins(opc == 4'd11, mv[t], mv[t+1], mi[t], mi[t+1], mf[t], mf[t+1])) begin mv[t] <= mv[t+1]; mi[t] <= mi[t+1]; mf[t] <= 1'b1; end
940 3'd2: for (t = 0; t < 16; t = t + 4)
941 if (wins(opc == 4'd11, mv[t], mv[t+2], mi[t], mi[t+2], mf[t], mf[t+2])) begin mv[t] <= mv[t+2]; mi[t] <= mi[t+2]; mf[t] <= 1'b1; end
942 3'd3: for (t = 0; t < 16; t = t + 8)
943 if (wins(opc == 4'd11, mv[t], mv[t+4], mi[t], mi[t+4], mf[t], mf[t+4])) begin mv[t] <= mv[t+4]; mi[t] <= mi[t+4]; mf[t] <= 1'b1; end
944 3'd4:
945 if (wins(opc == 4'd11, mv[0], mv[8], mi[0], mi[8], mf[0], mf[8])) begin mv[0] <= mv[8]; mi[0] <= mi[8]; mf[0] <= 1'b1; end
946 3'd@FOLD_WRITE@: begin
947 sr[pp * SREGS + rdd] <= (opc == 4'd11) ? arg_f : (mv[0][30:0] == 31'd0) ? 32'd0 : mv[0];
948 if (pp + 8'd1 < gp) begin
949 pp <= pp + 8'd1; pbase <= pbase + gr; row <= 16'd0; reading <= 1'b1; found <= 16'd0; phase <= P_MAX;
950 if (opc == 4'd11 || opc == 4'd2) for (k = 0; k < 16; k = k + 1) pacc[0][32*k +: 32] <= 32'hff80_0000;
951 else pacc[0] <= 512'd0;
952 end else running <= 1'b0;
953 end
954 default: ;
955 endcase
956 end
957 end
958 // A Hadamard stage within rows: a row a clock; the next stage once the last row is written.
959 P_NEAR: begin
960 if (reading) begin
961 if (row + 16'd1 >= rows) reading <= 1'b0; else row <= row + 16'd1;
962 end else if (hd_v && hd_rows[15:0] + 16'd1 >= rows) begin
963 row <= 16'd0; reading <= 1'b1; timer <= 8'd0;
964 if ((stride << 1) >= len[rdd]) running <= 1'b0;
965 else if ((stride << 1) >= 20'd16) phase <= P_FAR;
966 stride <= stride << 1;
967 end
968 end
969 // A Hadamard stage between rows: a low row and its partner every other clock, their sum and difference
970 // written in turn.
971 P_FAR: begin
972 if (hd_v) begin hi_v <= 1'b1; hi_row <= hd_rows[31:16]; hi_d <= a1v; end
973 if (reading) begin
974 if (timer != 8'd0) timer <= timer - 8'd1;
975 else begin
976 if (row_low) timer <= 8'd1;
977 if (row + 16'd1 >= rows) reading <= 1'b0; else row <= row + 16'd1;
978 end
979 end else if (in_far == 8'd0 && !hi_v) begin
980 row <= 16'd0; reading <= 1'b1; timer <= 8'd0;
981 if ((stride << 1) >= len[rdd]) running <= 1'b0;
982 stride <= stride << 1;
983 end
984 end
985 default: running <= 1'b0;
986 endcase
987 end
988 end
989 end
990endmodule
991"#;
992
993const SIMD_VERILOG: &str = r#"
994// One lane's function of one element through one core of each function: sigmoid is recip(1 + exp(-x)) and silu x
995// times it, softplus u + exp(-u) above its top and ln1p(exp(u)) below it, ln1p(t) ln(1 + t) corrected by
996// recip(1 + t) — each the f32 operations of its module in their order. A core no chosen function uses holds its
997// operand at zero. STAGED takes the function in four stages a step apart (LANE_FUNC_STAGES): the exponential; the add
998// after it; the reciprocal, the logarithm, the reciprocal square root and one less than the sum; ln1p's correction and
999// the choice — an operand taken at a step its value four steps later; without it the value is the operand's at once.
1000module logos_lane_func #(parameter STAGED = 0) (input clk, input rst, input [3:0] f, input [31:0] x, output [31:0] y);
1001 // Stage 1: the operand's decoding, the exponential, the binary16 round trip.
1002 wire [31:0] xf = `LOGOS_FTZ(x);
1003 wire [31:0] nxf = {~xf[31], xf[30:0]};
1004 wire is_exp = f == 4'd0, is_recip = f == 4'd1, is_rsqrt = f == 4'd2, is_sig = f == 4'd3, is_silu = f == 4'd4;
1005 wire is_sp = f == 4'd5, is_l1p = f == 4'd6, is_half = f == 4'd8;
1006 wire sp_above;
1007 logos_flt top(.a(`LOGOS_SOFTPLUS_TOP), .b(xf), .y(sp_above));
1008 wire sp_past = is_sp && sp_above, sp_near = is_sp && !sp_above;
1009 wire gate = is_sig || is_silu;
1010 wire on_l1p = is_l1p || sp_near;
1011 wire [31:0] e;
1012 logos_exp ue(.x_in(is_exp ? x : (gate || sp_past) ? nxf : sp_near ? xf : 32'd0), .y(e));
1013 wire [15:0] half;
1014 wire [31:0] yh;
1015 logos_f32_to_f16 uh(.x(is_half ? x : 32'd0), .h(half));
1016 logos_f16_to_f32 uw(.h(half), .x(yh));
1017 // Each stage's operands: the stage before's values, or its registers a step later.
1018 wire [3:0] f1, f2, f3;
1019 wire [31:0] x1, xf1, e1, yh1, x2, e2, yh2, t2, s2, x3, e3, yh3, t3, s3, r3, lu3, um13, q3;
1020 wire sp_above1, sp_past1, gate1, on_l1p1, is_l1p1, is_recip1, is_rsqrt1, is_silu1;
1021 wire sp_above2, gate2, on_l1p2, is_recip2, is_rsqrt2, is_silu2, sp_above3, on_l1p3, is_silu3;
1022 // Stage 2: one plus the exponential for the gates, u plus it past softplus's top; one plus ln1p's operand,
1023 // softplus's exponential below it.
1024 wire [31:0] t = is_l1p1 ? xf1 : `LOGOS_FTZ(e1);
1025 wire [31:0] s;
1026 logos_fadd ua(.a(sp_past1 ? xf1 : 32'h3f80_0000), .b((gate1 || sp_past1) ? e1 : on_l1p1 ? t : 32'd0), .y(s));
1027 // Stage 3: the reciprocal, the logarithm, the reciprocal square root, one less than the sum.
1028 wire [31:0] r, lu, um1, q;
1029 logos_recip ur(.x_in(is_recip2 ? x2 : (gate2 || on_l1p2) ? s2 : 32'd0), .y(r));
1030 logos_ln ul(.x_in(on_l1p2 ? s2 : 32'd0), .y(lu));
1031 logos_fsub uo(.a(on_l1p2 ? s2 : 32'd0), .b(32'h3f80_0000), .y(um1));
1032 logos_rsqrt uq(.x_in(is_rsqrt2 ? x2 : 32'd0), .y(q));
1033 // Stage 4: silu's product with its sigmoid, or ln1p's correction, and the function chosen.
1034 wire [31:0] err, p, z;
1035 logos_fsub ud(.a(on_l1p3 ? t3 : 32'd0), .b(um13), .y(err));
1036 logos_fmul um(.a(is_silu3 ? x3 : on_l1p3 ? err : 32'd0), .b(r3), .y(p));
1037 logos_fadd uz(.a(lu3), .b(p), .y(z));
1038 wire [31:0] l1p = (s3 == 32'h3f80_0000) ? t3 : z;
1039 reg [31:0] pick;
1040 always @* case (f3)
1041 4'd0: pick = e3; 4'd1: pick = r3; 4'd2: pick = q3; 4'd3: pick = r3; 4'd4: pick = p; 4'd5: pick = sp_above3 ? s3 : l1p;
1042 4'd6: pick = l1p; 4'd8: pick = yh3;
1043 default: pick = {~x3[31], x3[30:0]};
1044 endcase
1045 generate
1046 if (STAGED) begin : stages
1047 reg [3:0] rf1, rf2, rf3;
1048 reg [31:0] rx1, rxf1, re1, ryh1, rx2, re2, ryh2, rt2, rs2, rx3, re3, ryh3, rt3, rs3, rr3, rlu3, rum13, rq3, ry;
1049 reg ra1, rp1, rg1, ro1, rl1, rr1, rq1, rs1, ra2, rg2, ro2, rr2, rq2, rsi2, ra3, ro3, rsi3;
1050 always @(posedge clk) begin
1051 rf1 <= f; rx1 <= x; rxf1 <= xf; re1 <= e; ryh1 <= yh;
1052 ra1 <= sp_above; rp1 <= sp_past; rg1 <= gate; ro1 <= on_l1p; rl1 <= is_l1p; rr1 <= is_recip; rq1 <= is_rsqrt; rs1 <= is_silu;
1053 rf2 <= rf1; rx2 <= rx1; re2 <= re1; ryh2 <= ryh1; rt2 <= t; rs2 <= s;
1054 ra2 <= ra1; rg2 <= rg1; ro2 <= ro1; rr2 <= rr1; rq2 <= rq1; rsi2 <= rs1;
1055 rf3 <= rf2; rx3 <= rx2; re3 <= re2; ryh3 <= ryh2; rt3 <= rt2; rs3 <= rs2; rr3 <= r; rlu3 <= lu; rum13 <= um1; rq3 <= q;
1056 ra3 <= ra2; ro3 <= ro2; rsi3 <= rsi2;
1057 ry <= pick;
1058 end
1059 assign {f1, x1, xf1, e1, yh1, sp_above1, sp_past1, gate1, on_l1p1, is_l1p1, is_recip1, is_rsqrt1, is_silu1} =
1060 {rf1, rx1, rxf1, re1, ryh1, ra1, rp1, rg1, ro1, rl1, rr1, rq1, rs1};
1061 assign {f2, x2, e2, yh2, t2, s2, sp_above2, gate2, on_l1p2, is_recip2, is_rsqrt2, is_silu2} =
1062 {rf2, rx2, re2, ryh2, rt2, rs2, ra2, rg2, ro2, rr2, rq2, rsi2};
1063 assign {f3, x3, e3, yh3, t3, s3, r3, lu3, um13, q3, sp_above3, on_l1p3, is_silu3} =
1064 {rf3, rx3, re3, ryh3, rt3, rs3, rr3, rlu3, rum13, rq3, ra3, ro3, rsi3};
1065 assign y = ry;
1066 end else begin : at_once
1067 assign {f1, x1, xf1, e1, yh1, sp_above1, sp_past1, gate1, on_l1p1, is_l1p1, is_recip1, is_rsqrt1, is_silu1} =
1068 {f, x, xf, e, yh, sp_above, sp_past, gate, on_l1p, is_l1p, is_recip, is_rsqrt, is_silu};
1069 assign {f2, x2, e2, yh2, t2, s2, sp_above2, gate2, on_l1p2, is_recip2, is_rsqrt2, is_silu2} =
1070 {f1, x1, e1, yh1, t, s, sp_above1, gate1, on_l1p1, is_recip1, is_rsqrt1, is_silu1};
1071 assign {f3, x3, e3, yh3, t3, s3, r3, lu3, um13, q3, sp_above3, on_l1p3, is_silu3} =
1072 {f2, x2, e2, yh2, t2, s2, r, lu, um1, q, sp_above2, on_l1p2, is_silu2};
1073 assign y = pick;
1074 end
1075 endgenerate
1076endmodule
1077
1078module logos_lane_bin(input [3:0] op, input [31:0] a, input [31:0] b, output [31:0] y);
1079 wire [31:0] m, s;
1080 logos_fmul um(.a(a), .b(b), .y(m));
1081 logos_fadd ua(.a(a), .b(op == 4'd2 ? {~b[31], b[30:0]} : b), .y(s));
1082 assign y = (op == 4'd0) ? m : s;
1083endmodule
1084
1085// a > b as the maximum takes it: a NaN never larger.
1086module logos_fgt(input [31:0] a, input [31:0] b, output y);
1087 wire an = (a[30:23] == 8'hff) && (a[22:0] != 23'd0), bn = (b[30:23] == 8'hff) && (b[22:0] != 23'd0);
1088 wire lt;
1089 logos_flt l(.a(b), .b(a), .y(lt));
1090 assign y = !an && (bn || lt);
1091endmodule
1092
1093// The int8 code of x under the inverse scale s, as an f32: x·s to the nearest integer, ties to even, within ±127, a
1094// NaN product zero, every code zero under an infinite s.
1095module logos_code8(input [31:0] x, input [31:0] s, output [31:0] y);
1096 wire [31:0] p;
1097 logos_fmul m(.a(x), .b(s), .y(p));
1098 logos_code8_of c(.p(p), .s(s), .y(y));
1099endmodule
1100
1101// The int8 code of the product p = x·s under the inverse scale s.
1102module logos_code8_of(input [31:0] p, input [31:0] s, output [31:0] y);
1103 wire nan = (p[30:23] == 8'hff) && (p[22:0] != 23'd0);
1104 // |p| ≥ 128, infinities included, saturates; below it the nearest integer is within logos_rne's range.
1105 wire big = (p[30:23] >= 8'd134);
1106 wire signed [31:0] n;
1107 logos_rne r(.x(p), .n(n));
1108 wire signed [31:0] near = (n > 32'sd127) ? 32'sd127 : (n < -32'sd127) ? -32'sd127 : n;
1109 wire signed [31:0] code = nan ? 32'sd0 : big ? (p[31] ? -32'sd127 : 32'sd127) : near;
1110 wire inf_s = (s[30:23] == 8'hff) && (s[22:0] == 23'd0);
1111 logos_i2f c(.n(inf_s ? 32'sd0 : code), .y(y));
1112endmodule
1113
1114module logos_simd #(parameter ROWS = 320, VREGS = 8, SREGS = 8, FU = 16)
1115 (input clk, input rst,
1116 input issue, input [63:0] instr, output busy,
1117 input wr, input [3:0] wr_reg, input [15:0] wr_row, input [511:0] wr_data, input len_wr, input [19:0] len_val,
1118 input [3:0] rd_reg, input [15:0] rd_row, output [511:0] rd_data, input [3:0] rd_s, output [31:0] rd_s_val);
1119 `LOGOS_RAMSTYLE_ASYNC(VREGS) reg [19:0] len [0:VREGS-1];
1120 `LOGOS_RAMSTYLE_ASYNC(SREGS) reg [31:0] sr [0:SREGS-1];
1121 assign rd_s_val = sr[rd_s];
1122
1123 reg [63:0] ir;
1124 reg running;
1125 reg [15:0] row;
1126 reg [19:0] stride;
1127 reg folding;
1128 // A row's write steps: a Hadamard pair's low then high row, a rotary block's rows one after another.
1129 reg [1:0] phase;
1130 reg [511:0] acc;
1131 // Each lane's arg-max: whether it holds an element, and the element's index.
1132 reg [15:0] found;
1133 reg [19:0] aidx [0:15];
1134 assign busy = running;
1135 wire [3:0] opc = ir[3:0], sub = ir[7:4], ra = ir[11:8], rb = ir[15:12], rdd = ir[19:16], rs = ir[23:20];
1136 wire [19:0] n = len[ra];
1137 wire [19:0] rows_n = (n + 20'd15) >> 4;
1138 wire [15:0] rows = rows_n[15:0];
1139 wire largest = (opc == 4'd2) || (opc == 4'd11) || (opc == 4'd12);
1140
1141 // The register file in block RAM, a bank a register, three copies a bank written alike: copy A read at the row at
1142 // hand (the engine's row while the unit is idle), copies B and C at a Hadamard stage's partner row and a rotary
1143 // block's two rows; one write a cycle into a bank, under a byte mask.
1144 localparam RA = (ROWS > 1) ? $clog2(ROWS) : 1;
1145 wire [512*VREGS-1:0] qa, qb, qc;
1146 reg [15:0] row_b, row_c;
1147 reg w_en;
1148 reg [3:0] w_reg;
1149 reg [15:0] w_row;
1150 reg [511:0] w_data;
1151 reg [63:0] w_mask;
1152 wire [15:0] row_a = running ? row : rd_row;
1153 genvar l, r, k2;
1154 generate
1155 for (r = 0; r < VREGS; r = r + 1) begin : bank
1156 wire we = w_en && w_reg == r;
1157 logos_ram_w512 #(.D(ROWS), .A(RA)) ca(.clk(clk), .ra(row_a[RA-1:0]), .rd(qa[512*r +: 512]), .we(we),
1158 .wa(w_row[RA-1:0]), .wd(w_data), .wm(w_mask));
1159 logos_ram_w512 #(.D(ROWS), .A(RA)) cb(.clk(clk), .ra(row_b[RA-1:0]), .rd(qb[512*r +: 512]), .we(we),
1160 .wa(w_row[RA-1:0]), .wd(w_data), .wm(w_mask));
1161 logos_ram_w512 #(.D(ROWS), .A(RA)) cc(.clk(clk), .ra(row_c[RA-1:0]), .rd(qc[512*r +: 512]), .we(we),
1162 .wa(w_row[RA-1:0]), .wd(w_data), .wm(w_mask));
1163 end
1164 endgenerate
1165 assign rd_data = qa[512*rd_reg +: 512];
1166 wire [511:0] va = qa[512*ra +: 512], vb = qa[512*rb +: 512];
1167 wire [31:0] sv = sr[rs];
1168
1169 // A Hadamard stage reads d's row at hand and its partner row; a rotary step d's rows its pairs lie in and the turn's
1170 // tables. Strides below sixteen pair lanes of one row, strides of sixteen and up whole rows. A rotary step's lane l
1171 // turns pair 16·row + l of d by the tables' cosine and sine at the same place; the block's elements lie in d's row
1172 // at hand and the two rows copies B and C read: interleaved, rows 2·row and 2·row + 1; split, the rows the q
1173 // elements 16·row + l + pairs fall in.
1174 wire [511:0] hd_in = qa[512*rdd +: 512];
1175 wire [15:0] partner = row + stride[19:4];
1176 wire [511:0] hd_far = qb[512*rdd +: 512];
1177 wire near = stride < 20'd16;
1178 wire [19:0] pairs = len[rb];
1179 wire [19:0] blocks_n = (pairs + 20'd15) >> 4;
1180 wire [15:0] blocks = blocks_n[15:0];
1181 wire [511:0] rc = qa[512*rb +: 512], rsn = qa[512*rs +: 512];
1182 wire [19:0] q_first = {row, 4'd0} + pairs;
1183 wire [511:0] d_a = qa[512*rdd +: 512], d_b = qb[512*rdd +: 512], d_c = qc[512*rdd +: 512];
1184 wire rot = opc == 4'd10, had = opc == 4'd9, dot = opc == 4'd0, sum = opc == 4'd1, fma = opc == 4'd15;
1185
1186 // The lanes of this row, each with one set of four multiplies and two adds that every instruction shares: a product
1187 // or a sum of the operation, a multiply-add's product and its sum, a dot's product and its running sum, the code's
1188 // product, a Hadamard pair's sum and difference, a rotary pair's four products, its difference and its sum.
1189 wire [511:0] bin_vv, bin_vs, fma_v, acc_next, max_next, codes_v, scale_v, hd_near, hd_lo, hd_hi, ry0, ry1;
1190 wire [16*20-1:0] rp, rq;
1191 wire [15:0] take, ron;
1192 generate
1193 for (l = 0; l < 16; l = l + 1) begin : lane
1194 wire live = ({row, 4'd0} + l) < n;
1195 wire [31:0] x = va[32*l +: 32];
1196 wire [31:0] y = vb[32*l +: 32];
1197 // Lane l's Hadamard pair: lane l xor the stride, the lower of the two taking the sum; or the partner row's lane.
1198 wire [3:0] h = stride[3:0];
1199 wire low = ((l & h) == 0);
1200 wire [31:0] mine = hd_in[32*l +: 32];
1201 wire [31:0] other = (h == 4'd1) ? hd_in[32*(l ^ 1) +: 32] : (h == 4'd2) ? hd_in[32*(l ^ 2) +: 32]
1202 : (h == 4'd4) ? hd_in[32*(l ^ 4) +: 32] : hd_in[32*(l ^ 8) +: 32];
1203 wire [31:0] far = hd_far[32*l +: 32];
1204 // Lane l's rotary pair.
1205 wire [19:0] i = {row, 4'd0} + l;
1206 assign ron[l] = i < pairs;
1207 wire [19:0] p = sub[0] ? i : {i[18:0], 1'b0};
1208 wire [19:0] q = sub[0] ? i + pairs : {i[18:0], 1'b1};
1209 assign rp[20*l +: 20] = p;
1210 assign rq[20*l +: 20] = q;
1211 wire [511:0] wp = (p[19:4] == row_b) ? d_b : (p[19:4] == row_c) ? d_c : d_a;
1212 wire [511:0] wq = (q[19:4] == row_b) ? d_b : (q[19:4] == row_c) ? d_c : d_a;
1213 wire [31:0] x0 = wp[32*p[3:0] +: 32], x1 = wq[32*q[3:0] +: 32];
1214 wire [31:0] c = rc[32*l +: 32], s = rsn[32*l +: 32];
1215 // The shared units, their operands by instruction; a unit no instruction at hand uses holds its operands at zero.
1216 wire [31:0] m0, m1, m2, m3, a0, a1;
1217 wire [31:0] by = (opc == 4'd3 || fma || dot) ? y : (opc == 4'd4 || opc == 4'd13) ? sv : 32'h3f80_0000;
1218 logos_fmul um0(.a(rot ? x0 : x), .b(rot ? c : by), .y(m0));
1219 logos_fmul um1(.a(rot ? x1 : 32'd0), .b(rot ? s : 32'd0), .y(m1));
1220 logos_fmul um2(.a(rot ? x0 : 32'd0), .b(rot ? s : 32'd0), .y(m2));
1221 logos_fmul um3(.a(rot ? x1 : 32'd0), .b(rot ? c : 32'd0), .y(m3));
1222 wire [31:0] bin_b = (opc == 4'd3) ? y : sv;
1223 wire [31:0] nb = (sub == 4'd2) ? {~bin_b[31], bin_b[30:0]} : bin_b;
1224 logos_fadd ua0(.a(rot ? m2 : fma ? m0 : had ? (near ? (low ? mine : other) : mine) : (dot || sum) ? acc[32*l +: 32] : x),
1225 .b(rot ? m3 : fma ? (sub[0] ? sv : s) : had ? (near ? (low ? other : mine) : far) : dot ? m0 : sum ? x : nb), .y(a0));
1226 logos_fadd ua1(.a(rot ? m0 : !had ? 32'd0 : near ? other : mine),
1227 .b(rot ? {~m1[31], m1[30:0]} : !had ? 32'd0 : near ? {~mine[31], mine[30:0]} : {~far[31], far[30:0]}), .y(a1));
1228 assign bin_vv[32*l +: 32] = (sub == 4'd0) ? m0 : a0;
1229 assign bin_vs[32*l +: 32] = (sub == 4'd0) ? m0 : a0;
1230 assign fma_v[32*l +: 32] = a0;
1231 assign acc_next[32*l +: 32] = live ? a0 : acc[32*l +: 32];
1232 assign hd_near[32*l +: 32] = low ? a0 : a1;
1233 assign hd_lo[32*l +: 32] = a0;
1234 assign hd_hi[32*l +: 32] = a1;
1235 assign ry0[32*l +: 32] = a1;
1236 assign ry1[32*l +: 32] = a0;
1237 logos_code8_of cd(.p(m0), .s(sv), .y(codes_v[32*l +: 32]));
1238 logos_ldexp sc(.x(x), .k(ir[47:32]), .y(scale_v[32*l +: 32]));
1239 // The magnitude AbsMax reads: a denormal a zero, the sign dropped.
1240 wire [31:0] cand = (opc == 4'd12) ? ((x[30:23] == 8'd0) ? 32'd0 : {1'b0, x[30:0]}) : x;
1241 wire nan = (x[30:23] == 8'hff) && (x[22:0] != 23'd0);
1242 wire gt;
1243 logos_fgt g(.a(cand), .b(acc[32*l +: 32]), .y(gt));
1244 assign take[l] = (opc == 4'd11) ? (live && !nan && (!found[l] || gt)) : (live && gt);
1245 assign max_next[32*l +: 32] = take[l] ? cand : acc[32*l +: 32];
1246 end
1247 endgenerate
1248 // The function units, each four stages a step apart (logos_lane_func_s): a Map's rows a slice of FU lanes a step into
1249 // them, one row's after another's with no gap, each slice's values out four steps later in the order they went in,
1250 // held in fbuf until its row's last slice is out and the row is written whole; a ScalarMap's operand on the first
1251 // unit. A unit computes only while an instruction that uses it issues to it, its function otherwise the one that
1252 // holds its cores' operands at zero.
1253 localparam [4:0] SLICES = 16 / FU;
1254 localparam STAGES = 4;
1255 reg [3:0] slice;
1256 reg issuing;
1257 reg [511:0] fbuf;
1258 // A slice in each stage, and its row and place: the last stage's the values out this step.
1259 reg [STAGES-1:0] fv;
1260 reg [20*STAGES-1:0] ft;
1261 wire f_out = fv[STAGES-1];
1262 wire [3:0] f_slice = ft[20*(STAGES-1) +: 4];
1263 wire [15:0] f_row = ft[20*(STAGES-1) + 4 +: 16];
1264 wire f_issue = running && issuing && (opc == 4'd5 || opc == 4'd7);
1265 wire [32*FU-1:0] fx, fy;
1266 generate
1267 for (l = 0; l < FU; l = l + 1) begin : fun
1268 wire [3:0] fcode = (f_issue && (opc == 4'd5 || l == 0)) ? sub : 4'd15;
1269 // The unit's lane of each slice, each a fixed part of the row; the slice at hand picks one.
1270 wire [32*SLICES-1:0] cand;
1271 for (k2 = 0; k2 < SLICES; k2 = k2 + 1) begin : at
1272 assign cand[32*k2 +: 32] = va[32*(FU*k2 + l) +: 32];
1273 end
1274 wire [31:0] lane_x = (SLICES == 1) ? cand[31:0] : cand[32*slice +: 32];
1275 assign fx[32*l +: 32] = (opc == 4'd7 && l == 0) ? sr[ra] : lane_x;
1276 logos_lane_func #(.STAGED(1)) fu(.clk(clk), .rst(rst), .f(fcode), .x(fx[32*l +: 32]), .y(fy[32*l +: 32]));
1277 end
1278 endgenerate
1279 // The row with the slice out this step in place: lane l from unit l mod FU when its slice is out.
1280 wire [511:0] func_v;
1281 generate
1282 for (l = 0; l < 16; l = l + 1) begin : fsl
1283 localparam [3:0] MINE = l / FU;
1284 assign func_v[32*l +: 32] = (f_out && f_slice == MINE) ? fy[32*(l % FU) +: 32] : fbuf[32*l +: 32];
1285 end
1286 endgenerate
1287 wire last_slice = f_out && {1'b0, f_slice} + 5'd1 >= SLICES;
1288
1289 // Whether this row is the low half of its stride's pair.
1290 wire row_low = (stride >= 20'd16) ? ((row & stride[19:4]) == 16'd0) : 1'b1;
1291
1292 // The lanes folded by the pairwise tree, a sum or a maximum.
1293 wire [31:0] tree_sum;
1294 logos_tree16 ts(.lanes(acc), .sum(tree_sum));
1295 reg [31:0] tmax;
1296 reg [31:0] mlv [0:15];
1297 integer t, w;
1298 reg big;
1299 always @* begin
1300 for (t = 0; t < 16; t = t + 1) mlv[t] = acc[32*t +: 32];
1301 for (w = 1; w < 16; w = w * 2)
1302 for (t = 0; t < 16; t = t + 2*w) begin
1303 big = 1'b0;
1304 if (!((mlv[t+w][30:23] == 8'hff) && (mlv[t+w][22:0] != 23'd0))) begin
1305 if (mlv[t][31] == 1'b0 && mlv[t+w][31] == 1'b0) big = mlv[t+w][30:0] > mlv[t][30:0];
1306 else if (mlv[t][31] == 1'b1 && mlv[t+w][31] == 1'b1) big = mlv[t+w][30:0] < mlv[t][30:0];
1307 else big = (mlv[t+w][31] == 1'b0) && !(mlv[t+w][30:0] == 31'd0 && mlv[t][30:0] == 31'd0);
1308 end
1309 if (big) mlv[t] = mlv[t+w];
1310 end
1311 tmax = (mlv[0][30:0] == 31'd0) ? 32'd0 : mlv[0];
1312 end
1313
1314 // The lanes' arg-maxes folded: the one holding the larger value, of equal values the lower index. The values a lane
1315 // holds are never NaN, so they order by the key that makes the two zeros one.
1316 function [31:0] fkey(input [31:0] v);
1317 fkey = (v[30:0] == 31'd0) ? 32'h8000_0000 : (v[31] ? ~v : {1'b1, v[30:0]});
1318 endfunction
1319 reg [31:0] av [0:15];
1320 reg [19:0] ai [0:15];
1321 reg af [0:15];
1322 reg beat;
1323 always @* begin
1324 for (t = 0; t < 16; t = t + 1) begin av[t] = acc[32*t +: 32]; ai[t] = aidx[t]; af[t] = found[t]; end
1325 for (w = 1; w < 16; w = w * 2)
1326 for (t = 0; t < 16; t = t + 2*w) begin
1327 beat = af[t+w] && (!af[t] || (fkey(av[t+w]) > fkey(av[t]))
1328 || (fkey(av[t+w]) == fkey(av[t]) && ai[t+w] < ai[t]));
1329 if (beat) begin av[t] = av[t+w]; ai[t] = ai[t+w]; af[t] = 1'b1; end
1330 end
1331 end
1332 wire [31:0] arg_f;
1333 logos_i2f ia(.n(af[0] ? {12'd0, ai[0]} : 32'sd0), .y(arg_f));
1334
1335 // Scalar operations.
1336 wire [31:0] s_bin;
1337 wire [31:0] s_func = fy[31:0];
1338 logos_lane_bin sb(.op(sub), .a(sr[ra]), .b(sr[rb]), .y(s_bin));
1339
1340 // The block's turned elements as three masked rows, each element in the row it was read from.
1341 reg [511:0] rec_a, rec_b, rec_c;
1342 reg [63:0] rec_am, rec_bm, rec_cm;
1343 integer k;
1344 always @* begin
1345 rec_a = d_a; rec_b = d_b; rec_c = d_c; rec_am = 64'd0; rec_bm = 64'd0; rec_cm = 64'd0;
1346 for (k = 0; k < 16; k = k + 1)
1347 if (ron[k]) begin
1348 if (rp[20*k+4 +: 16] == row_b) begin rec_b[32*rp[20*k +: 4] +: 32] = ry0[32*k +: 32]; rec_bm[4*rp[20*k +: 4] +: 4] = 4'hf; end
1349 else if (rp[20*k+4 +: 16] == row_c) begin rec_c[32*rp[20*k +: 4] +: 32] = ry0[32*k +: 32]; rec_cm[4*rp[20*k +: 4] +: 4] = 4'hf; end
1350 else begin rec_a[32*rp[20*k +: 4] +: 32] = ry0[32*k +: 32]; rec_am[4*rp[20*k +: 4] +: 4] = 4'hf; end
1351 if (rq[20*k+4 +: 16] == row_b) begin rec_b[32*rq[20*k +: 4] +: 32] = ry1[32*k +: 32]; rec_bm[4*rq[20*k +: 4] +: 4] = 4'hf; end
1352 else if (rq[20*k+4 +: 16] == row_c) begin rec_c[32*rq[20*k +: 4] +: 32] = ry1[32*k +: 32]; rec_cm[4*rq[20*k +: 4] +: 4] = 4'hf; end
1353 else begin rec_a[32*rq[20*k +: 4] +: 32] = ry1[32*k +: 32]; rec_am[4*rq[20*k +: 4] +: 4] = 4'hf; end
1354 end
1355 end
1356 always @* begin
1357 row_b = 16'd0; row_c = 16'd0;
1358 if (opc == 4'd9) row_b = partner;
1359 else if (opc == 4'd10) begin
1360 if (sub[0]) begin row_b = q_first[19:4]; row_c = q_first[19:4] + 16'd1; end
1361 else begin row_b = {row[14:0], 1'b0}; row_c = {row[14:0], 1'b1}; end
1362 end
1363 end
1364
1365 // The rows a step writes after its first: a Hadamard pair's high row, a rotary block's rows B and C.
1366 reg [511:0] hold_d1, hold_d2;
1367 reg [63:0] hold_m1, hold_m2;
1368 reg [15:0] hold_r1, hold_r2;
1369 // The one write a cycle: the engine's beat, or the row the step at hand writes.
1370 always @* begin
1371 w_en = 1'b0; w_reg = rdd; w_row = row; w_data = 512'd0; w_mask = {64{1'b1}};
1372 if (wr) begin
1373 w_en = 1'b1; w_reg = wr_reg; w_row = wr_row; w_data = wr_data;
1374 end else if (running && !rst) case (opc)
1375 4'd3, 4'd4, 4'd5, 4'd13, 4'd14, 4'd15: begin
1376 w_en = (opc != 4'd5) || last_slice;
1377 if (opc == 4'd5) w_row = f_row;
1378 w_data = (opc == 4'd3) ? bin_vv : (opc == 4'd4) ? bin_vs : (opc == 4'd5) ? func_v : (opc == 4'd13) ? codes_v
1379 : (opc == 4'd14) ? scale_v : fma_v;
1380 end
1381 4'd9:
1382 if (!folding) begin w_en = 1'b1; w_data = va; end
1383 else if (stride < 20'd16) begin w_en = 1'b1; w_data = hd_near; end
1384 else if (phase == 2'd0) begin w_en = row_low; w_data = hd_lo; end
1385 else begin w_en = 1'b1; w_row = hold_r1; w_data = hold_d1; end
1386 4'd10:
1387 if (!folding) begin w_en = 1'b1; w_data = va; end
1388 else case (phase)
1389 2'd0: begin w_en = 1'b1; w_data = rec_a; w_mask = rec_am; end
1390 2'd1: begin w_en = 1'b1; w_row = hold_r1; w_data = hold_d1; w_mask = hold_m1; end
1391 default: begin w_en = 1'b1; w_row = hold_r2; w_data = hold_d2; w_mask = hold_m2; end
1392 endcase
1393 default: ;
1394 endcase
1395 end
1396
1397 always @(posedge clk) begin
1398 if (rst) begin
1399 running <= 1'b0; slice <= 4'd0; issuing <= 1'b0; fv <= {STAGES{1'b0}};
1400 end else begin
1401 fv <= {fv[STAGES-2:0], f_issue};
1402 ft <= {ft[20*(STAGES-1)-1:0], row, slice};
1403 if (len_wr) len[wr_reg] <= len_val;
1404 if (issue && !running) begin
1405 ir <= instr; running <= 1'b1; row <= 16'd0; folding <= 1'b0; stride <= 20'd1; found <= 16'd0; phase <= 2'd0;
1406 slice <= 4'd0; issuing <= 1'b1;
1407 if (instr[3:0] == 4'd2 || instr[3:0] == 4'd11) for (k = 0; k < 16; k = k + 1) acc[32*k +: 32] <= 32'hff80_0000;
1408 else acc <= 512'd0;
1409 end else if (running) begin
1410 case (opc)
1411 4'd0, 4'd1, 4'd2, 4'd11, 4'd12: begin
1412 if (folding) begin
1413 sr[rdd] <= (opc == 4'd11) ? arg_f : largest ? tmax : tree_sum;
1414 running <= 1'b0;
1415 end else begin
1416 acc <= largest ? max_next : acc_next;
1417 if (opc == 4'd11)
1418 for (k = 0; k < 16; k = k + 1)
1419 if (take[k]) begin aidx[k] <= {row, 4'd0} + k[19:0]; found[k] <= 1'b1; end
1420 if (row + 16'd1 >= rows) folding <= 1'b1; else row <= row + 16'd1;
1421 end
1422 end
1423 4'd3, 4'd4, 4'd13, 4'd14, 4'd15: begin
1424 len[rdd] <= n;
1425 if (row + 16'd1 >= rows) running <= 1'b0; else row <= row + 16'd1;
1426 end
1427 4'd5: begin
1428 if (issuing) begin
1429 if ({1'b0, slice} + 5'd1 >= SLICES) begin
1430 slice <= 4'd0;
1431 if (row + 16'd1 >= rows) issuing <= 1'b0; else row <= row + 16'd1;
1432 end else slice <= slice + 4'd1;
1433 end
1434 if (f_out) begin
1435 if (!last_slice) fbuf <= func_v;
1436 else begin
1437 len[rdd] <= n;
1438 if (f_row + 16'd1 >= rows) running <= 1'b0;
1439 end
1440 end
1441 end
1442 4'd6: begin sr[rdd] <= s_bin; running <= 1'b0; end
1443 4'd7: begin issuing <= 1'b0; if (f_out) begin sr[rdd] <= s_func; running <= 1'b0; end end
1444 4'd8: begin sr[rdd] <= ir[63:32]; running <= 1'b0; end
1445 4'd9: begin
1446 // The first pass copies a into d; then each stage walks d's rows, a pair of rows two writes.
1447 if (!folding) begin
1448 len[rdd] <= n;
1449 if (row + 16'd1 >= rows) begin
1450 row <= 16'd0;
1451 if (n <= 20'd1) running <= 1'b0; else folding <= 1'b1;
1452 end else row <= row + 16'd1;
1453 end else if (stride >= 20'd16 && phase == 2'd0 && row_low) begin
1454 phase <= 2'd1; hold_r1 <= partner; hold_d1 <= hd_hi;
1455 end else begin
1456 phase <= 2'd0;
1457 if (row + 16'd1 >= rows) begin
1458 row <= 16'd0;
1459 if ((stride << 1) >= len[rdd]) running <= 1'b0;
1460 stride <= stride << 1;
1461 end else row <= row + 16'd1;
1462 end
1463 end
1464 4'd10: begin
1465 // a copied into d; then each block of sixteen pairs turned from one read, its rows written in turn.
1466 if (!folding) begin
1467 len[rdd] <= n;
1468 if (row + 16'd1 >= rows) begin
1469 row <= 16'd0;
1470 if (pairs == 20'd0) running <= 1'b0; else folding <= 1'b1;
1471 end else row <= row + 16'd1;
1472 end else if (phase == 2'd0) begin
1473 phase <= 2'd1;
1474 hold_r1 <= row_b; hold_d1 <= rec_b; hold_m1 <= rec_bm;
1475 hold_r2 <= row_c; hold_d2 <= rec_c; hold_m2 <= rec_cm;
1476 end else if (phase == 2'd1) phase <= 2'd2;
1477 else begin
1478 phase <= 2'd0;
1479 if (row + 16'd1 >= blocks) running <= 1'b0; else row <= row + 16'd1;
1480 end
1481 end
1482 default: running <= 1'b0;
1483 endcase
1484 end
1485 end
1486 end
1487endmodule
1488"#;