Skip to main content

logicaffeine_compile/spatial/decode_engine/rtl/
simd.rs

1//! The vector unit as Verilog: sixteen f32 lanes running the instruction set of
2//! [`crate::spatial::decode_engine::vector`] over vector and scalar registers, one sixteen-element row a cycle.
3//!
4//! A vector register holds up to `ROWS` rows of sixteen lanes and its length. An instruction over a vector walks its
5//! rows: elementwise instructions write lane by lane, `Dot` and `Sum` accumulate each lane over the rows and fold the
6//! lanes by the pairwise tree once the rows are done, `Max` and `AbsMax` keep each lane's largest and fold the lanes
7//! the same way, a largest zero `+0`, and `ArgMax` keeps each lane's largest with the first index holding it and folds
8//! the lanes to the larger, the lower index of equals. `Hadamard` runs its stages in place: the strides below sixteen
9//! within each row, the strides of sixteen and up between rows, a pair of rows read at once and written in two cycles.
10//! `Rotate` copies its operand, then turns sixteen pairs from one read of the rows they sit in, and writes those rows
11//! one a cycle under lane masks. Scalar instructions take a cycle. Lanes past a register's length stay out of every
12//! reduction and every write. The functions run on `FU` function units, a divisor of sixteen, each in
13//! [`LANE_FUNC_STAGES`] stages a cycle apart: a `Map`'s rows go in a slice of `FU` lanes a cycle, `16 / FU` cycles a row
14//! and one row after another, each row written when its last slice's values are out, and a `ScalarMap` takes the first
15//! unit's stages.
16//!
17//! The registers live in block RAM (`rtl::ram`), a bank a register with three copies written alike: copy A read at
18//! 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 a
19//! rotary block's two rows, and one write a bank a cycle.
20//!
21//! `logos_simd_p` is the same unit for the user clock ([`simd_p_verilog`]): every register and value the same, on the
22//! pipelined f32 units and function units (`logos_lane_func_p`, [`LANE_FUNC_P_LATENCY`]), its registers block RAM read
23//! on the rising edge. An instruction's rows stream through its units a row a clock and are written as they come out;
24//! `Dot` and `Sum` add a row each add's latency, each lane's running sum through one pipelined add, and fold the lanes by
25//! the pipelined tree; the maxima take a row a clock and fold in four registered levels; a Hadamard stage streams its
26//! rows, one stage after another; a rotary block is read, turned and written before the next.
27//!
28//! An instruction is 64 bits: `[3:0]` the opcode, `[7:4]` the operation or function (`Rotate`'s NEOX flag), `[11:8]`
29//! `a`, `[15:12]` `b` (`Rotate`'s cosines), `[19:16]` `d`, `[23:20]` the scalar operand (`Rotate`'s sines, a
30//! multiply-add's vector addend), `[63:32]` a constant ([`encode`]). A multiply-add's product goes into its sum as the
31//! multiply's result goes into an add after it.
32
33use crate::spatial::decode_engine::vector::{Bin, Func, Op};
34
35/// The instruction word for `op`.
36pub 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
77/// The instruction an instruction word stands for, or `None` for a word no instruction encodes to.
78pub 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
112/// The vector unit, with the f32 units, the functions, the tree and the block RAM it is built from.
113pub fn simd_verilog() -> String {
114    format!("{}\n{}\n{}", super::ram::ram_verilog(&[512]), super::vector::vector_verilog(), SIMD_VERILOG)
115}
116
117/// The vector unit in the user clock (`logos_simd_p`), with its pipelined function units (`logos_lane_func_p`), the
118/// pipelined f32 units, the functions and the block RAM it is built from, beside the vector unit it is held to
119/// ([`simd_verilog`]).
120pub 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    // An elementwise row's write: its read, its operands registered, then its code's multiply (its operands chosen and
125    // registered first), its nearest integer, its code registered and its f32 — the longest of its values, a sum and a
126    // product held to it.
127    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    // A maximum's fold: four levels of pairs, then the arg-max's index as an f32, then the write.
131    let fold_write = 5 + I2F_LATENCY;
132    // A scalar product is held to the scalar sum's latency, so a pass a clock writes each pass's value in turn.
133    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
168/// Clocks the user clock's vector unit (`logos_simd_p`) takes from a row asked (`rd_row`, `rd_reg`) to its word on
169/// `rd_data`: the row registered, then read.
170pub const SIMD_P_READ_LATENCY: usize = 2;
171
172/// Clocks a pipelined function unit's value (`logos_lane_func_p`) takes from its operand: the operand decoded and
173/// registered; the exponential; the add after it; the reciprocal, the logarithm, the reciprocal square root and one
174/// less than the sum, each held to the slowest; ln1p's error, its product and its sum; the choice registered.
175pub 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
181/// Cycles a function unit's value takes from its operand (`logos_lane_func` with `STAGED`): the exponential; the add
182/// after it; the reciprocal, the logarithm, the reciprocal square root and one less than the sum; ln1p's correction
183/// and the choice. No stage is deeper than one of the functions' cores, so the vector unit's longest path is a core's,
184/// not the chain of them.
185pub 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"#;