Skip to main content

logicaffeine_compile/spatial/decode_engine/rtl/
vector_side.rs

1//! A hybrid engine's vector jobs in the user clock ([`super::engine::Streaming::vectors`]): `logos_vector_side` runs a
2//! [`crate::spatial::decode_engine::machine::Job::Vector`] job as the core's vector-job states do — the job's
3//! descriptors from the job words, then each pass of a row and a head: the loads into the vector unit's registers, the
4//! micro-program on the user clock's vector unit (`logos_simd_p`, [`super::simd::simd_p_verilog`]), the stores — on its
5//! own copies of the core's tables, written as the core's are, the token-row store's user-clock port and the fast port
6//! onto the channels ([`super::super::physical::fast_port_verilog`]).
7//!
8//! A load or a store into the token-row store streams a beat a clock. A load from the memory asks for a line whenever
9//! the port takes one and writes each line into its register as it comes back, in order; a ring row stored in binary16
10//! is widened half a line a beat. A store into the memory reads a beat a clock while the queue its beats wait in has room
11//! for every beat on its way, each beat's binary16 halves and int8 codes out of pipelined units; it gathers its beats in
12//! one line until a beat leaves the line or the pass ends, taking a beat a clock: a whole line goes to the write queue
13//! the clock the queue is free, a part of one is read once every write before it is on the card, merged and written. A ring load
14//! whose row's sequence stored a ring row earlier in the job waits until every write before it is on the card, so a
15//! sequence's next row reads the ring row its last row wrote; no other load reads what a vector job writes (another
16//! sequence's rows touch other rings, tensors and rotary tables are never written), so none waits. A kept load ([`crate::spatial::decode_engine::machine::VectorJob::kept`]: a
17//! tensor's span at stride 0 into a register nothing else writes) is read on the job's first pass alone. A job whose rows
18//! cross nowhere ([`crate::spatial::decode_engine::machine::VectorJob::chunked`]) and is deeper than the unit runs its
19//! passes once for each chunk of `VROWS` beats of its vectors, every load and store that chunk's beats, its kept loads
20//! read on each chunk's first pass; so the unit is as deep as the deepest job that reduces, rotates or turns, not the
21//! deepest elementwise one. The products
22//! of an element's place are split into sixteen-bit halves, each sum truncated to the width the core's expression has,
23//! so every place is the core's.
24//!
25//! A side of `PASSES` above one ([`super::engine::Streaming::vector_passes`]) runs a job whose passes stand alone
26//! ([`crate::spatial::decode_engine::machine::VectorJob::grouped`], its header's bit 54) in groups: as many passes as
27//! the unit holds rows for, up to `PASSES`, loaded one after another, each into rows of its own; the micro-program
28//! issued once over the group, each instruction taking the group's passes side by side (`logos_simd_p`'s `group`), each
29//! pass's operations in their own order; then each pass's stores from its rows and scalars, the line they gather in
30//! flushed after each pass as between passes. A pass's values are the ones it computes alone. The unit holds `DEPTH`
31//! times `VROWS` rows a register ([`super::engine::Streaming::vector_depth`]), so a job whose vectors are `VROWS` beats
32//! deep runs `DEPTH` passes a group; its chunks and its float matrices' held inputs stay `VROWS` beats a vector.
33//!
34//! A float matrix's job (a tensor's row a head dotted with each row's input, one scalar a pass) runs on a path of its
35//! own: every row's input held, `FM_ROWS` rows to a group of `16·FM_ROWS` lanes and the rows past them in further
36//! groups the same lanes take in turn; eight heads' weight rows streamed a line of each in turn, each line given to the
37//! lanes once for each group, every row's products added in `numerics::fdot`'s order, every row's head through the tree
38//! into its element. The lines come back into a queue and leave it for the lanes once their head's last sum of the group
39//! has left the add, so a group of fewer heads than the add is deep, or lines bunched by the port, still add each head's
40//! terms one after another. The weights stream once for every row the side holds.
41
42/// The rows of a float matrix's job a clock of its lanes takes: its lanes `16·FM_ROWS`.
43pub const FM_ROWS: usize = 4;
44
45/// Line words a memory store's queue holds (`q_data`): every beat of the stream in flight, and the one being merged.
46const MS_DEPTH: usize = 16;
47
48/// The vector side with the vector unit, the block RAM and the functions it is built from.
49pub fn vector_side_verilog() -> String {
50    use super::fp32::{F32_TO_F16_LATENCY, FADD_LATENCY, FMUL_LATENCY, TREE16_LATENCY};
51    use super::simd::SIMD_P_READ_LATENCY;
52    use super::vector::RNE_P_LATENCY;
53    let narrow = F32_TO_F16_LATENCY.max(RNE_P_LATENCY);
54    assert!(narrow >= 2, "a memory store's fields ride a delay line of the narrowing units' latency, two clocks or more");
55    assert!(FMUL_LATENCY >= 3, "a float matrix's sums are read the clock before its add takes them, their address registered a clock before that, inside the multiply");
56    // A memory store streams a beat a clock while its queue holds a beat being merged beside every stage of the stream.
57    assert!(SIMD_P_READ_LATENCY >= 2, "a store's beat waits in a shift of its unit's read latency, two clocks or more");
58    assert!(SIMD_P_READ_LATENCY + 1 + 1 + narrow + 1 < MS_DEPTH, "a memory store's queue has room for its stream at a beat a clock");
59    let side = [
60        ("@FMUL@", FMUL_LATENCY),
61        ("@FADD@", FADD_LATENCY),
62        ("@SPACE@", FADD_LATENCY + 1),
63        ("@TREE@", TREE16_LATENCY),
64        ("@NARROW_LESS@", narrow - 1),
65        ("@CODES_PAD@", narrow - RNE_P_LATENCY),
66        ("@NARROW@", narrow),
67        ("@FM_ROWS@", FM_ROWS),
68        ("@READ@", SIMD_P_READ_LATENCY),
69        ("@MS_OUT@", SIMD_P_READ_LATENCY + 1),
70        ("@MS_DEPTH@", MS_DEPTH),
71    ]
72        .iter()
73        .fold(VECTOR_SIDE_VERILOG.to_string(), |t, (k, v)| t.replace(k, &v.to_string()));
74    format!("{}\n{}\n{side}", super::ram::bram_verilog(&[64, 128, 512]), super::simd::simd_p_verilog())
75}
76
77const VECTOR_SIDE_VERILOG: &str = r#"// A hybrid engine's vector jobs in the user clock: the core hands a vector job over by its header's place in the job
78// words (`start`, `job_pc`) with the step's rows and its state's format, and the side runs it as the core's vector-job
79// states do — its descriptors read, then each pass of a row and a head: the loads into the vector unit's registers,
80// the micro-program, the stores — on its own copies of the core's tables (written as the core's are, at the core's
81// step), the token-row store's user-clock port and the fast port onto the channels. A pass's store beats stream a beat
82// a clock each way; its memory lines go out as the port takes them and come back in order. Its memory stores gather
83// in a line until the next store leaves the line: a whole line written as it is, a part of one read, merged and written
84// once every write before it is on the card (`written`); a ring load waits for the writes before it to be on the card
85// when its row's sequence stored a ring row earlier in the job, and no other load does. `busy` holds from `start` until the job's last write is on the card; a line the port faults on faults the
86// job. A job whose passes stand alone (its header's bit 54) runs in groups of up to PASSES passes, loaded each into rows of
87// its own, run as one, stored one after another.
88module logos_vector_side #(parameter ROW_MAX = 1, VROWS = 64, PROWS = VROWS, FU = 16, JOB_WORDS = 64, TENSORS = 1,
89                           LAYERS = 1, ROPES = 1, SA = 10, MAXL = 16, MAXP = 16, MAXI = 64, FM_ROWS = @FM_ROWS@,
90                           PASSES = 1, DEPTH = 1, SRL = 1)
91  (input clk, input rst,
92   input start, input [31:0] job_pc, input [15:0] rows, input state_f16, output busy, input port_fault, output reg fault,
93   input host_we, input [2:0] host_table, input [31:0] host_addr, input [511:0] host_wdata,
94   output reg [SA-1:0] st_ra, input [511:0] st_rd, output reg st_we, output reg [SA-1:0] st_wa, output reg [511:0] st_wd,
95   output reg [63:0] st_wm,
96   output reg rq_valid, input rq_ready, output reg [31:0] rq_line, input rs_valid, input [511:0] rs_data,
97   output reg wq_valid, input wq_ready, output reg [31:0] wq_line, output reg [511:0] wq_data, input written);
98  localparam JA = (JOB_WORDS > 1) ? $clog2(JOB_WORDS) : 1, TA = (TENSORS > 1) ? $clog2(TENSORS) : 1,
99             BA = (LAYERS > 1) ? $clog2(LAYERS) : 1, VA = (VROWS > 1) ? $clog2(VROWS) : 1;
100
101  // The tables, as the host writes the core's.
102  `LOGOS_RAMSTYLE_ASYNC(ROW_MAX) reg [127:0] rowt [0:ROW_MAX-1];
103  `LOGOS_RAMSTYLE_ASYNC(32) reg [63:0] slott [0:31];
104  reg [63:0] ropet [0:ROPES-1];
105  reg [JA-1:0] j_ra;
106  reg [TA-1:0] t_ra;
107  reg [BA-1:0] b_ra;
108  wire [63:0] jw;
109  wire [127:0] tw;
110  wire [511:0] bw;
111  logos_bram_w64 #(.D(JOB_WORDS), .A(JA)) jobs_ram(.clk(clk), .ra(j_ra), .rd(jw),
112    .we(host_we && host_table == 3'd0), .wa(host_addr[JA-1:0]), .wd(host_wdata[63:0]), .wm(8'hff));
113  logos_bram_w128 #(.D(TENSORS), .A(TA)) tens_ram(.clk(clk), .ra(t_ra), .rd(tw),
114    .we(host_we && host_table == 3'd3), .wa(host_addr[TA-1:0]), .wd(host_wdata[127:0]), .wm(16'hffff));
115  logos_bram_w512 #(.D(LAYERS), .A(BA)) block_ram(.clk(clk), .ra(b_ra), .rd(bw),
116    .we(host_we && host_table == 3'd5), .wa(host_addr[BA-1:0]), .wd(host_wdata), .wm({64{1'b1}}));
117  always @(posedge clk)
118    if (host_we) case (host_table)
119      3'd1: rowt[host_addr] <= host_wdata[127:0];
120      3'd2: slott[host_addr[4:0]] <= host_wdata[63:0];
121      3'd4: ropet[host_addr] <= host_wdata[63:0];
122      default: ;
123    endcase
124
125  // A job whose passes stand alone (its header's bit 54, machine::VectorJob::grouped) runs in groups when the side runs
126  // more than one pass a group: its vectors' beats a pass (grows), the most passes whose rows the unit holds (gmax), the
127  // pass at hand in the group (gi, its rows from goff) and the group's passes (gn), and the group's first pass (gk0,
128  // gh0), its stores' place. A job of one pass a group has gi and goff zero. gmax is a clock behind grows, which is set
129  // as the descriptors end, clocks before any pass's loads end.
130  reg [15:0] grows, goff, prow;
131  reg [7:0] gi, gn;
132  reg [15:0] gk0;
133  reg [31:0] gh0;
134  function [7:0] gfit(input [15:0] rows_a_pass);
135    integer q;
136    begin
137      gfit = 8'd1;
138      for (q = 2; q <= PASSES; q = q + 1) if (q * rows_a_pass <= $unsigned(VROWS * DEPTH)) gfit = q[7:0];
139    end
140  endfunction
141  reg [7:0] gmax;
142
143  // The vector unit.
144  reg s_issue, s_wr, s_len_wr;
145  reg [63:0] s_instr;
146  reg [3:0] s_wr_reg, s_rd_reg, s_rd_s;
147  reg [15:0] s_wr_row, s_rd_row;
148  reg [511:0] s_wr_data;
149  reg [19:0] s_len_val;
150  wire s_busy;
151  wire [511:0] s_rd_data;
152  wire [31:0] s_rd_s_val;
153  reg [7:0] s_group;
154  logos_simd_p #(.ROWS(VROWS * DEPTH), .VREGS(16), .SREGS(8), .FU(FU), .PAIRS(PROWS), .PASSES(PASSES)) vu(.clk(clk), .rst(rst),
155    .issue(s_issue), .instr(s_instr), .busy(s_busy), .group(s_group), .group_rows(grows), .wr(s_wr), .wr_reg(s_wr_reg),
156    .wr_row(s_wr_row), .wr_data(s_wr_data), .len_wr(s_len_wr), .len_val(s_len_val), .rd_reg(s_rd_reg), .rd_row(s_rd_row),
157    .rd_data(s_rd_data), .rd_s(s_rd_s), .rd_s_pass(gi), .rd_s_val(s_rd_s_val));
158
159  localparam [5:0] IDLE = 6'd0, HDR0 = 6'd1, HDR = 6'd2, HEADS0 = 6'd3, HEADS = 6'd4, DESC = 6'd5, PASS = 6'd6,
160                   LOAD = 6'd7, LB1 = 6'd8, LB2 = 6'd9, LB3 = 6'd10, LB4 = 6'd11, LBEAT = 6'd12, LLEN = 6'd13, RUN = 6'd14,
161                   RWAIT = 6'd15, PUT = 6'd16, PB1 = 6'd17, PB2 = 6'd18, PB3 = 6'd19, PB4 = 6'd20, PBEAT = 6'd21,
162                   MS = 6'd22, NEXT = 6'd26, DRAIN = 6'd27, PB5 = 6'd28,
163                   LB5 = 6'd30, PB4B = 6'd31, PBF = 6'd32, FM0 = 6'd34, FM_ACT = 6'd35,
164                   FM_W = 6'd36, FM_TREE = 6'd37, FM_NEXT = 6'd38, FM_T = 6'd39, GNEXT = 6'd40;
165  reg [5:0] state;
166  assign busy = state != IDLE;
167
168  // The job: its words' place, the step's rows and state format, its header, its heads, its descriptors.
169  reg [31:0] pc;
170  reg [15:0] nrows;
171  (* maxfan = 256 *) reg f16;
172  reg [63:0] hdr;
173  wire heads_first = hdr[4];
174  wire [7:0] nfresh = hdr[20:13], nload = hdr[28:21], nput = hdr[36:29];
175  wire [15:0] nprog = hdr[52:37];
176  // A job that runs a chunk of its vectors at a time (its header's bit 53): its passes over every row once a chunk, the
177  // chunk VROWS beats of every vector it loads and stores from beat cbase, the vectors jbeats beats long.
178  wire chunked = hdr[53];
179  wire grouped = PASSES > 1 && hdr[54] && !chunked;
180  localparam [31:0] VB = VROWS;
181  reg [31:0] cbase, jbeats;
182  reg [31:0] heads;
183  `LOGOS_RAMSTYLE_ASYNC(MAXL) reg [63:0] ld_a [0:MAXL-1];
184  `LOGOS_RAMSTYLE_ASYNC(MAXL) reg [63:0] ld_b [0:MAXL-1];
185  `LOGOS_RAMSTYLE_ASYNC(MAXP) reg [63:0] pt_a [0:MAXP-1];
186  `LOGOS_RAMSTYLE_ASYNC(MAXP) reg [63:0] pt_b [0:MAXP-1];
187  reg [63:0] prog [0:MAXI-1];
188  reg [15:0] dp, dt;
189  (* maxfan = 256 *) reg dv;
190  // A store load's beat asked (lbv, the clock after its address) and out of the store SRL clocks after its address (lbo).
191  reg lbv;
192  wire lbo, lb_flight, lb_none;
193  logos_landing #(.W(1), .D(SRL - 1)) lland(.clk(clk), .rst(rst), .in_v(lbv), .in_d(1'b0), .out_v(lbo), .out_d(lb_none),
194    .flight(lb_flight));
195  wire [15:0] nwords = {7'd0, nload, 1'b0} + {7'd0, nput, 1'b0} + nprog;
196  reg [15:0] k;
197  reg [31:0] h;
198  // The pass after (k, h) as NEXT takes it, and whether it is past the job's last: a clock behind (k, h), which every
199  // reader is at least two clocks after.
200  reg [15:0] nk;
201  reg [31:0] nh;
202  reg nend;
203  reg [7:0] li, pi;
204  reg [15:0] ii;
205  wire [31:0] st_bytes = f16 ? 32'd2 : 32'd4;
206
207  // The load at hand and its place: a store beat, or a line of the memory (and for a binary16 ring row the half of it).
208  (* maxfan = 256 *) reg [63:0] la;
209  reg [63:0] lbw;
210  wire [3:0] l_reg = la[3:0], l_kind = la[7:4];
211  wire [15:0] l_what = la[23:8];
212  wire [7:0] l_extra = la[31:24];
213  wire [31:0] l_len = la[63:32];
214  // The load's elements in the chunk at hand: past the chunk's first, as many as a chunk holds; a register a clock behind
215  // the entry and the chunk's base, its beats counted once its places are made, five states or more after the entry is read.
216  wire [31:0] l_left = l_len - (cbase << 4);
217  reg [31:0] l_chunk;
218  always @(posedge clk) l_chunk <= (l_left > (VB << 4)) ? (VB << 4) : l_left;
219  wire [31:0] l_beats = (l_chunk + 32'd15) >> 4;
220  reg [127:0] row_e;
221  reg [63:0] slot_e, rope_e;
222  reg [47:0] elem_lo, elem_hi, rp_lo, rp_hi;
223  reg [31:0] elem, kprod, rprod, ring_sum;
224  reg [63:0] ring_prod;
225  reg [31:0] lbase;
226  reg [63:0] lbyte;
227  reg [31:0] lp, lt, lr;
228  // A load's beat's line: its tensor's or rotary row's line plus the beat, or its ring row's byte's line.
229  function [31:0] l_line(input [31:0] i);
230    reg [63:0] byte_at;
231    begin
232      byte_at = lbyte + {32'd0, i} * (f16 ? 64'd32 : 64'd64);
233      l_line = (l_kind == 4'd1 || l_kind == 4'd2) ? lbase + i : byte_at[37:6];
234    end
235  endfunction
236  wire [63:0] l_byte_r = lbyte + {32'd0, lr} * (f16 ? 64'd32 : 64'd64);
237  wire [31:0] lp_next = lp + ((rq_valid && rq_ready) ? 32'd1 : 32'd0);
238  reg [511:0] rs_r;
239  reg rs_v;
240  (* maxfan = 256 *) reg rs_half;
241  wire [511:0] widened;
242  genvar g;
243  generate
244    for (g = 0; g < 16; g = g + 1) begin : widen
245      logos_f16_to_f32 wv(.h(rs_half ? rs_r[256 + 16*g +: 16] : rs_r[16*g +: 16]), .x(widened[32*g +: 32]));
246    end
247  endgenerate
248  wire from_f16 = (l_kind == 4'd3) && f16;
249
250  // The store at hand: its register, its place in the store, or its line and byte in the memory and what it writes.
251  reg [63:0] pa, pbw;
252  wire [3:0] p_reg = pa[3:0];
253  wire p_scalar = pa[4];
254  wire [2:0] p_kind = pa[7:5];
255  wire [15:0] p_what = pa[23:8];
256  wire p_values = pa[24];
257  wire [1:0] p_part = pa[26:25];
258  wire [31:0] p_len = pa[63:32];
259  // The store's elements in the chunk at hand, a register a clock behind the entry and the chunk's base: its beats are
260  // counted from it once its place is made, five states or more after the entry is read, and both hold through the store.
261  wire [31:0] p_left = p_len - (cbase << 4);
262  reg [31:0] p_chunk;
263  always @(posedge clk) p_chunk <= (p_left > (VB << 4)) ? (VB << 4) : p_left;
264  wire [31:0] p_beats = p_scalar ? 32'd1 : (p_chunk + 32'd15) >> 4;
265  reg [47:0] pelem_lo, pelem_hi, cached_lo, cached_hi;
266  reg [63:0] cprod_lo, cprod_hi;
267  reg [31:0] pelem, pkprod, pbase, cached;
268  reg [63:0] pbyte, cprod, prprod;
269  reg [31:0] pb, pt;
270  // A store into the token-row store's beats out of the unit: whether a beat is on its way a clock after its row is
271  // asked, and @READ@ clocks after, when its word is out (ptv); the beat's lanes beside it (pt_mask).
272  (* maxfan = 256 *) reg [@READ@-1:0] pt_on;
273  reg [16*@READ@-1:0] pt_lanes;
274  wire ptv = pt_on[@READ@-1];
275  wire [15:0] pt_mask = pt_lanes[16*@READ@-1 -: 16];
276  // A beat's lanes: those below the store's length.
277  function [15:0] lanes_of(input [31:0] beat);
278    reg [31:0] left, lanes;
279    begin
280      left = p_chunk - (beat << 4);
281      lanes = p_scalar ? 32'd1 : (left >= 32'd16 ? 32'd16 : left);
282      lanes_of = (lanes >= 32'd16) ? 16'hffff : ((16'd1 << lanes[4:0]) - 16'd1);
283    end
284  endfunction
285  // A memory store's stream, a stage a clock: a beat asked, its row the unit's the clock after (prow) and its value out
286  // @READ@ clocks after that (a_v and a_pt carry it @MS_OUT@ clocks, the last the clock its value is out), the value
287  // captured with its byte and lanes (pm_*), then its binary16 halves and int8 codes @NARROW@ clocks on, the captured
288  // fields carried beside them (d_*); there its line word is queued. A beat is asked only while the queue has room for it
289  // and every beat on its way.
290  localparam [4:0] MS_DEPTH = @MS_DEPTH@;
291  reg [31:0] ms_pt;
292  reg [@MS_OUT@:1] a_v;
293  reg [32*@MS_OUT@-1:0] a_pt;
294  wire a_out = a_v[@MS_OUT@];
295  wire [31:0] a_out_pt = a_pt[32*@MS_OUT@-1 -: 32];
296  reg pm_v;
297  reg [511:0] pm_data;
298  reg [15:0] pm_mask;
299  reg [63:0] pm_byte;
300  reg [@NARROW@:1] d_v;
301  reg [511:0] d_val [1:@NARROW@];
302  reg [15:0] d_mask [1:@NARROW@];
303  reg [37:0] d_byte [1:@NARROW@];
304  wire [255:0] halves;
305  wire [127:0] codes, codes_n;
306  generate
307    for (g = 0; g < 16; g = g + 1) begin : narrow
308      logos_f32_to_f16_p nh(.clk(clk), .x(pm_data[32*g +: 32]), .h(halves[16*g +: 16]));
309      wire signed [31:0] c;
310      logos_rne_p nc(.clk(clk), .x(pm_data[32*g +: 32]), .n(c));
311      assign codes_n[8*g +: 8] = c[7:0];
312    end
313  endgenerate
314  // The codes held to the halves' latency.
315  logos_delay #(.W(128), .D(@CODES_PAD@)) dcodes(.clk(clk), .d(codes_n), .q(codes));
316  integer dsh;
317  // The beat out of the units: its lanes as bytes (four a lane of f32s, two a lane of binary16s) and its line word.
318  wire [15:0] o_mask = d_mask[@NARROW@];
319  wire [37:0] o_byte = d_byte[@NARROW@];
320  wire [63:0] lanes4;
321  wire [31:0] lanes2;
322  generate
323    for (g = 0; g < 16; g = g + 1) begin : lane_bytes
324      assign lanes4[4*g +: 4] = {4{o_mask[g]}};
325      assign lanes2[2*g +: 2] = {2{o_mask[g]}};
326    end
327  endgenerate
328  reg [511:0] o_data;
329  reg [63:0] o_wmask;
330  always @* begin
331    if (p_kind == 3'd1 && !f16) begin
332      o_data = d_val[@NARROW@];
333      o_wmask = lanes4;
334    end else if (p_kind == 3'd1 || p_part == 2'd0) begin
335      o_data = {256'd0, halves} << (8 * o_byte[5:0]);
336      o_wmask = {32'd0, lanes2} << o_byte[5:0];
337    end else if (p_part == 2'd1) begin
338      o_data = {384'd0, codes} << (8 * o_byte[5:0]);
339      o_wmask = {48'd0, o_mask} << o_byte[5:0];
340    end else begin
341      o_data = {496'd0, halves[15:0]} << (8 * o_byte[5:0]);
342      o_wmask = 64'd3 << o_byte[5:0];
343    end
344  end
345  // The queue of line words waiting for the line memory stores gather in.
346  (* ramstyle = "MLAB, no_rw_check" *) reg [511:0] q_data [0:MS_DEPTH-1];
347  `LOGOS_RAMSTYLE_ASYNC(MS_DEPTH) reg [63:0] q_mask [0:MS_DEPTH-1];
348  `LOGOS_RAMSTYLE_ASYNC(MS_DEPTH) reg [31:0] q_line [0:MS_DEPTH-1];
349  reg [3:0] q_wr, q_rd;
350  reg [4:0] q_n;
351  wire [4:0] ms_flight = a_v_count(a_v) + {4'd0, pm_v} + d_v_count(d_v);
352  function [4:0] d_v_count(input [@NARROW@:1] v);
353    integer dc;
354    begin
355      d_v_count = 5'd0;
356      for (dc = 1; dc <= @NARROW@; dc = dc + 1) d_v_count = d_v_count + {4'd0, v[dc]};
357    end
358  endfunction
359  function [4:0] a_v_count(input [@MS_OUT@:1] v);
360    integer ac;
361    begin
362      a_v_count = 5'd0;
363      for (ac = 1; ac <= @MS_OUT@; ac = ac + 1) a_v_count = a_v_count + {4'd0, v[ac]};
364    end
365  endfunction
366  // Whether no word is on its way into the queue: ms_flight is zero exactly when no valid behind it is set.
367  wire ms_none = ~|a_v && !pm_v && ~|d_v;
368  wire ms_ask = state == MS && ms_pt != p_beats && q_n + ms_flight < MS_DEPTH;
369  integer i;
370
371  // The line memory stores gather in: its line, its bytes and which of them are written; a part of a line read,
372  // merged and written; a whole one written.
373  localparam [2:0] CB_EMPTY = 3'd0, CB_HELD = 3'd1, CB_WAIT = 3'd2, CB_READ = 3'd3, CB_RESP = 3'd4, CB_WRITE = 3'd5;
374  (* maxfan = 256 *) reg [2:0] cb;
375  reg [31:0] cb_line;
376  reg [511:0] cb_data;
377  reg [63:0] cb_mask;
378  reg cb_flush;
379  wire cb_full = &cb_mask;
380  // The queue's head and whether the line takes it this clock: into an empty line, into its own held line, or as a
381  // fresh line while the held one, full, goes to the write queue.
382  wire q_have = q_n != 5'd0;
383  wire [31:0] h_line = q_line[q_rd];
384  wire [511:0] h_data = q_data[q_rd];
385  wire [63:0] h_mask = q_mask[q_rd];
386  wire wq_free = !wq_valid || wq_ready;
387  wire hand_off = cb == CB_HELD && cb_line != h_line && cb_full && wq_free;
388  wire cb_pop = q_have && (cb == CB_EMPTY || (cb == CB_HELD && (cb_line == h_line || hand_off)));
389  // Every write the side made is on the card: none gathering, none queued, the port's all done.
390  wire settled = cb == CB_EMPTY && !wq_valid && written;
391  // Whether a ring row has been stored this job (ring_open) and the region of the row that last stored one: a ring load
392  // of a row of that region waits for the card; the row at hand's region a clock behind its index.
393  reg ring_open;
394  reg [31:0] ring_region, row_region;
395  always @(posedge clk) row_region <= rowt[k][31:0];
396  wire ring_waits = ld_a[li][7:4] == 4'd3 && ring_open && ring_region == row_region;
397  function [511:0] merged(input [511:0] held, input [511:0] taken, input [63:0] m);
398    integer b;
399    begin
400      merged = held;
401      for (b = 0; b < 64; b = b + 1) if (m[b]) merged[8*b +: 8] = taken[8*b +: 8];
402    end
403  endfunction
404
405  // A float matrix's job (`fm`): every head's row of a tensor dotted with every row's input, a scalar a pass put at the
406  // head's element of the row's output — a weight row a head, the input a slot's whole row, one Dot, one scalar put.
407  // Every row it holds (up to FMM groups of FM_ROWS, row k of the pass group k / FM_ROWS's lanes k mod FM_ROWS) has
408  // its input held; eight heads at a time, their weight rows asked from the memory a line of each in turn, at most FQ
409  // of them owed, each back into a queue; the queue's head line goes to the lanes once for each group, then leaves the
410  // queue, each giving waiting until no line of its head left for its group in the last FADD clocks, so its add reads
411  // the sum the one before it wrote; its sixteen elements times every held row's of the group into 16·FM_ROWS lanes,
412  // element i of a row into lane i mod 16 in index order from +0, as numerics::fdot adds it; then each row's head
413  // through the tree into its output's element.
414  wire fm_shape = heads_first && nload == 8'd2 && nput == 8'd1 && nprog == 16'd1 && ld_a[0][7:0] == 8'h10 &&
415    ld_a[1][7:0] == 8'h01 && ld_a[0][63:32] == ld_a[1][63:32] && ld_a[0][35:32] == 4'd0 && ld_b[0][31:0] == 32'd0 &&
416    ld_b[0][63:32] == ld_a[0][63:32] && ld_b[1] == 64'd0 && prog[0] == 64'h1000 && pt_a[0][7:0] == 8'h10 &&
417    pt_a[0][63:32] == 32'd1 && pt_b[0] == {32'd1, 32'd0};
418  localparam [31:0] FH = 8, FMR = FM_ROWS;
419  // The groups of FM_ROWS rows the inputs and the sums are held for: every row the side's step holds.
420  localparam FMM = (ROW_MAX + FM_ROWS - 1) / FM_ROWS;
421  localparam [31:0] FMMR = FMM * FM_ROWS;
422  localparam FIA = (FMM * VROWS > 1) ? $clog2(FMM * VROWS) : 1;
423  // The sums' address: a group's place, at least a bit, above a head's three.
424  localparam FSG = (FMM > 1) ? $clog2(FMM) : 1, FSA = FSG + 3;
425  wire [31:0] fm_n = ld_a[0][63:32], fm_beats = fm_n >> 4;
426  wire [63:0] fm_from = slott[ld_a[1][12:8]], fm_to = slott[pt_a[0][12:8]];
427  reg [31:0] fm_k0, fm_h0, fm_rows, fm_heads, fm_tbase, fm_r, fm_b, fm_j, fm_rb, fm_rj, fm_tr, fm_tj;
428  reg [31:0] fm_asked, fm_back, fm_pop, fm_gline, fm_hline;
429  wire [31:0] fm_lines = fm_beats * fm_heads[3:0];
430  reg fm_av;
431  reg [31:0] fm_ar, fm_at;
432  // A held input's beat asked (fm_av and its place fm_wr_*, the clock after its address) and written the clock its word is
433  // out of the store, SRL clocks after its address (fm_ao, fm_ao_*).
434  wire fm_ao, fm_aflight;
435  wire [31:0] fm_ao_lg, fm_ao_at;
436  // The held inputs, a block RAM a lane group, a group of rows at each VROWS beats of it: the row an input's read
437  // writes next (its lane group fm_lg, its group's first beat fm_soff), and the beat a read writes, its lane group and
438  // its beat in the RAM, a clock behind; the groups the pass's rows fill (fm_subs).
439  reg [31:0] fm_lg, fm_soff, fm_subs, fm_wr_lg, fm_wr_at;
440  reg [7:0] fm_sub;
441  logos_landing #(.W(64), .D(SRL - 1)) fland(.clk(clk), .rst(rst), .in_v(fm_av), .in_d({fm_wr_lg, fm_wr_at}),
442    .out_v(fm_ao), .out_d({fm_ao_lg, fm_ao_at}), .flight(fm_aflight));
443  // The weight lines back, queued with their head and beat; the queue's head going to the lanes for each group in turn
444  // (fq_give, group fq_sub at fq_soff) and leaving once its last group has it (fq_go), each giving held while a line
445  // of its head left for the same group in the last FADD + 1 clocks: a head's sums are read a clock before its add takes
446  // them, so the next of its lines reads the last's sum once it is written.
447  localparam FQA = 5;
448  localparam [31:0] FQ = 32'd1 << FQA;
449  `LOGOS_RAMSTYLE(FQ) reg [511:0] fq_data [0:FQ-1];
450  `LOGOS_RAMSTYLE_ASYNC(FQ) reg [2:0] fq_j [0:FQ-1];
451  `LOGOS_RAMSTYLE_ASYNC(FQ) reg [15:0] fq_b [0:FQ-1];
452  wire [FQA-1:0] fq_wr = fm_back[FQA-1:0], fq_rd = fm_pop[FQA-1:0];
453  wire [2:0] fq_jh = fq_j[fq_rd];
454  wire [31:0] fq_bh = {16'd0, fq_b[fq_rd]};
455  reg [7:0] fq_sub;
456  reg [31:0] fq_soff;
457  reg [@SPACE@:1] pq_v;
458  reg [2:0] pq_j [1:@SPACE@];
459  reg [7:0] pq_s [1:@SPACE@];
460  reg fq_clash;
461  integer fk;
462  always @* begin
463    fq_clash = 1'b0;
464    for (fk = 1; fk <= @SPACE@; fk = fk + 1) if (pq_v[fk] && pq_j[fk] == fq_jh && pq_s[fk] == fq_sub) fq_clash = 1'b1;
465  end
466  wire fq_give = state == FM_W && fm_pop != fm_back && !fq_clash;
467  wire fq_last = {24'd0, fq_sub} + 32'd1 == fm_subs;
468  wire fq_go = fq_give && fq_last;
469  // The line given: its sixteen elements, head, group and beat (fw), carried a clock further (fw2) beside the group's held
470  // rows' beats of it, whose read address is a register (fm_act_ra) copied near the RAMs it reaches; the products and
471  // sums, each lane group's sums a block RAM of every group's heads.
472  reg [511:0] fw_q, fw2_q;
473  reg fw_v, fw_first, fw2_v, fw2_first;
474  reg [2:0] fw_j, fw2_j;
475  reg [7:0] fw_s, fw2_s;
476  (* maxfan = 64 *) reg [FIA-1:0] fm_act_ra;
477  wire [511:0] fa_q [0:FM_ROWS-1];
478  reg [@FMUL@:1] fp_v;
479  (* maxfan = 256 *) reg [@FMUL@:1] fp_first;
480  reg [2:0] fp_j [1:@FMUL@];
481  reg [7:0] fp_s [1:@FMUL@];
482  reg [@FADD@:1] fs_v;
483  reg [2:0] fs_j [1:@FADD@];
484  reg [7:0] fs_s [1:@FADD@];
485  wire [511:0] fm_sums [0:FM_ROWS-1];
486  wire [511:0] fm_held [0:FM_ROWS-1];
487  // The sums' read, from a register copied near the RAMs it reaches: the add's head of its group, taken two clocks before
488  // the add takes the sums so it is read the clock before; or a row's head the tree takes next, read the clock after it
489  // is taken.
490  reg [31:0] ft_sub;
491  (* maxfan = 64 *) reg [FSA-1:0] fm_acc_ra;
492  always @(posedge clk)
493    fm_acc_ra <= state == FM_TREE ? {ft_sub[FSG-1:0], fm_tj[2:0]} : {fp_s[@FMUL@-2][FSG-1:0], fp_j[@FMUL@-2]};
494  integer fr;
495  always @(posedge clk) begin
496    if (state == FM_W && rs_valid) begin fq_data[fq_wr] <= rs_data; fq_j[fq_wr] <= fm_rj[2:0]; fq_b[fq_wr] <= fm_rb[15:0]; end
497    pq_v <= rst ? {@SPACE@{1'b0}} : {pq_v[@SPACE@-1:1], fq_give};
498    pq_j[1] <= fq_jh; pq_s[1] <= fq_sub;
499    for (fr = 2; fr <= @SPACE@; fr = fr + 1) begin pq_j[fr] <= pq_j[fr-1]; pq_s[fr] <= pq_s[fr-1]; end
500    if (state != FM_W) begin fq_sub <= 8'd0; fq_soff <= 32'd0; end
501    else if (fq_give) begin
502      fq_sub <= fq_last ? 8'd0 : fq_sub + 8'd1;
503      fq_soff <= fq_last ? 32'd0 : fq_soff + VB;
504    end
505    fw_v <= !rst && fq_give;
506    fw_q <= fq_data[fq_rd]; fw_j <= fq_jh; fw_s <= fq_sub; fw_first <= fq_bh == 32'd0;
507    fm_act_ra <= fq_soff[FIA-1:0] + fq_bh[FIA-1:0];
508    fw2_v <= !rst && fw_v;
509    fw2_q <= fw_q; fw2_j <= fw_j; fw2_s <= fw_s; fw2_first <= fw_first;
510
511    fp_v <= rst ? {@FMUL@{1'b0}} : {fp_v[@FMUL@-1:1], fw2_v};
512    fp_first <= {fp_first[@FMUL@-1:1], fw2_first};
513    fp_j[1] <= fw2_j; fp_s[1] <= fw2_s;
514    for (fr = 2; fr <= @FMUL@; fr = fr + 1) begin fp_j[fr] <= fp_j[fr-1]; fp_s[fr] <= fp_s[fr-1]; end
515    fs_v <= rst ? {@FADD@{1'b0}} : {fs_v[@FADD@-1:1], fp_v[@FMUL@]};
516    fs_j[1] <= fp_j[@FMUL@]; fs_s[1] <= fp_s[@FMUL@];
517    for (fr = 2; fr <= @FADD@; fr = fr + 1) begin fs_j[fr] <= fs_j[fr-1]; fs_s[fr] <= fs_s[fr-1]; end
518  end
519  genvar fmr, fml;
520  generate
521    for (fmr = 0; fmr < FM_ROWS; fmr = fmr + 1) begin : fm_row
522      logos_bram_w512 #(.D(FMM * VROWS), .A(FIA)) act(.clk(clk), .ra(fm_act_ra), .rd(fa_q[fmr]),
523        .we(fm_ao && fm_ao_lg == fmr), .wa(fm_ao_at[FIA-1:0]), .wd(st_rd), .wm({64{1'b1}}));
524      logos_bram_w512 #(.D(FMM * 8), .A(FSA)) acc(.clk(clk), .ra(fm_acc_ra), .rd(fm_held[fmr]),
525        .we(fs_v[@FADD@]), .wa({fs_s[@FADD@][FSG-1:0], fs_j[@FADD@]}), .wd(fm_sums[fmr]), .wm({64{1'b1}}));
526      // The row's sums the add takes: its head's of its group, or +0 for a head's first line.
527      wire [511:0] acc_in = fp_first[@FMUL@] ? 512'd0 : fm_held[fmr];
528      for (fml = 0; fml < 16; fml = fml + 1) begin : lane
529        wire [31:0] p;
530        logos_fmul_p m(.clk(clk), .a(fw2_q[32*fml +: 32]), .b(fa_q[fmr][32*fml +: 32]), .y(p));
531        logos_fadd_p a(.clk(clk), .a(acc_in[32*fml +: 32]), .b(p), .y(fm_sums[fmr][32*fml +: 32]));
532      end
533    end
534  endgenerate
535  // A row's head through the tree, the scalar into its output's element: the row's lane group (ft_lg) and group
536  // (ft_sub) as FM_TREE walks the rows; its read's address registered a clock later (tr_*) and its word out of the row's
537  // lane group's RAM the clock after (tr2_*).
538  reg [511:0] ft_lanes;
539  reg ft_v, tr_v, tr2_v;
540  reg [31:0] ft_r, ft_h, tr_r, tr_h, ft_lg, tr_lg, tr2_r, tr2_h, tr2_lg;
541  wire [31:0] ft_sum;
542  logos_tree16_p ftree(.clk(clk), .lanes(ft_lanes), .sum(ft_sum));
543  reg [@TREE@:1] ftd_v;
544  reg [31:0] ftd_r [1:@TREE@];
545  reg [31:0] ftd_h [1:@TREE@];
546  always @(posedge clk) begin
547    tr2_v <= !rst && tr_v; tr2_lg <= tr_lg; tr2_r <= tr_r; tr2_h <= tr_h;
548    ft_v <= !rst && tr2_v;
549    ft_lanes <= fm_held[tr2_lg]; ft_r <= tr2_r; ft_h <= tr2_h;
550    ftd_v <= rst ? {@TREE@{1'b0}} : {ftd_v[@TREE@-1:1], ft_v};
551    ftd_r[1] <= ft_r; ftd_h[1] <= ft_h;
552    for (fr = 2; fr <= @TREE@; fr = fr + 1) begin ftd_r[fr] <= ftd_r[fr-1]; ftd_h[fr] <= ftd_h[fr-1]; end
553  end
554  wire fm_out = ftd_v[@TREE@];
555  wire [31:0] fm_out_at = fm_to[31:0] + ftd_r[@TREE@] * fm_to[63:32] + (ftd_h[@TREE@] >> 4);
556
557  // The job RAM's read address, and the store's: a pass's store load beat, or a store's beat; the vector unit's beat
558  // a store reads; a float matrix's held input's beat.
559  always @* begin
560    j_ra = pc[JA-1:0];
561    if (state == DESC) j_ra = pc[JA-1:0] + dp[JA-1:0];
562    st_ra = lbase[SA-1:0] + lp[SA-1:0];
563    if (state == FM_ACT) st_ra = fm_from[SA-1:0] + fm_ar[SA-1:0] * fm_from[SA-1+32:32] + fm_at[SA-1:0];
564    s_rd_reg = p_reg; s_rd_row = prow; s_rd_s = p_reg;
565  end
566
567  always @(posedge clk) begin
568    s_issue <= 1'b0; s_wr <= 1'b0; s_len_wr <= 1'b0; st_we <= 1'b0; cb_flush <= 1'b0;
569    rs_v <= 1'b0; tr_v <= 1'b0;
570    gmax <= gfit(grows);
571    nk <= heads_first ? ((k + 16'd1 == nrows) ? 16'd0 : k + 16'd1) : ((h + 32'd1 == heads) ? k + 16'd1 : k);
572    nh <= heads_first ? ((k + 16'd1 == nrows) ? h + 32'd1 : h) : ((h + 32'd1 == heads) ? 32'd0 : h + 32'd1);
573    nend <= k + 16'd1 == nrows && h + 32'd1 == heads;
574    if (fm_out) begin
575      st_we <= 1'b1; st_wa <= fm_out_at[SA-1:0]; st_wd <= {480'd0, ft_sum} << (32 * ftd_h[@TREE@][3:0]);
576      st_wm <= 64'hf << (4 * ftd_h[@TREE@][3:0]);
577    end
578    if (rst) begin
579      state <= IDLE; fault <= 1'b0; rq_valid <= 1'b0; wq_valid <= 1'b0; cb <= CB_EMPTY; cbase <= 32'd0;
580      a_v <= {@MS_OUT@{1'b0}}; pm_v <= 1'b0; d_v <= {@NARROW@{1'b0}}; q_wr <= 4'd0; q_rd <= 4'd0; q_n <= 5'd0;
581      gi <= 8'd0; goff <= 16'd0; gn <= 8'd1; lbv <= 1'b0; fm_av <= 1'b0;
582    end else begin
583      if (port_fault) fault <= 1'b1;
584      case (state)
585        IDLE: if (start) begin
586          pc <= job_pc; nrows <= rows; f16 <= state_f16; fault <= 1'b0; gi <= 8'd0; goff <= 16'd0; gn <= 8'd1; ring_open <= 1'b0;
587          state <= HDR0;
588        end
589        HDR0: state <= HDR;
590        HDR: begin hdr <= jw; pc <= pc + 32'd1; state <= HEADS0; end
591        HEADS0: state <= HEADS;
592        HEADS: begin
593          heads <= jw[31:0]; pc <= pc + 32'd1 + {24'd0, nfresh}; dp <= 16'd0; dt <= 16'd0; dv <= 1'b0; state <= DESC;
594        end
595        // The descriptors, a word a clock: the loads' and stores' two words each, then the instructions.
596        DESC: begin
597          dv <= dp < nwords;
598          if (dp < nwords) dp <= dp + 16'd1;
599          if (dv) begin
600            if (dt < {7'd0, nload, 1'b0}) begin
601              if (dt[0] == 1'b0) ld_a[dt >> 1] <= jw; else ld_b[dt >> 1] <= jw;
602              if (dt == 16'd0) jbeats <= (jw[63:32] + 32'd15) >> 4;
603            end else if (dt < {7'd0, nload, 1'b0} + {7'd0, nput, 1'b0}) begin
604              if (dt[0] == 1'b0) pt_a[(dt - {7'd0, nload, 1'b0}) >> 1] <= jw; else pt_b[(dt - {7'd0, nload, 1'b0}) >> 1] <= jw;
605            end else prog[dt - {7'd0, nload, 1'b0} - {7'd0, nput, 1'b0}] <= jw;
606            dt <= dt + 16'd1;
607          end
608          if (dt == nwords) begin
609            k <= 16'd0; h <= 32'd0; cbase <= 32'd0; state <= PASS;
610            grows <= (ld_a[0][63:32] == 32'd0) ? 16'd1 : ld_a[0][51:36] + {15'd0, ld_a[0][35:32] != 4'd0};
611          end
612        end
613        // Every pass of a row and a head; a chunked job's again for each chunk after its first.
614        PASS: begin
615          if ((heads_first && h == heads) || (!heads_first && k == nrows) || heads == 32'd0 || nrows == 16'd0) begin
616            if (chunked && heads != 32'd0 && nrows != 16'd0 && cbase + VB < jbeats) begin cbase <= cbase + VB; k <= 16'd0; h <= 32'd0; end
617            else state <= DRAIN;
618          end
619          else if (fm_shape) begin fm_k0 <= 32'd0; t_ra <= ld_a[0][8+TA-1:8]; state <= FM_T; end
620          else begin li <= 8'd0; gi <= 8'd0; goff <= 16'd0; gk0 <= k; gh0 <= h; state <= LOAD; end
621        end
622        // A float matrix: its tensor's word read; a group of rows' inputs held, a beat a clock; then its heads eight at a
623        // time.
624        FM_T: state <= FM0;
625        FM0: if (settled) begin
626          fm_tbase <= tw[31:0];
627          fm_rows <= ({16'd0, nrows} - fm_k0 < FMMR) ? {16'd0, nrows} - fm_k0 : FMMR;
628          fm_ar <= fm_k0; fm_at <= 32'd0; fm_r <= 32'd0; fm_av <= 1'b0; fm_h0 <= 32'd0;
629          fm_lg <= 32'd0; fm_soff <= 32'd0; fm_sub <= 8'd0; state <= FM_ACT;
630        end
631        FM_ACT: begin
632          fm_av <= fm_r != fm_rows;
633          fm_wr_lg <= fm_lg; fm_wr_at <= fm_soff + fm_at;
634          if (fm_r != fm_rows) begin
635            if (fm_at + 32'd1 == fm_beats) begin
636              fm_at <= 32'd0; fm_ar <= fm_ar + 32'd1; fm_r <= fm_r + 32'd1;
637              if (fm_lg + 32'd1 == FMR) begin fm_lg <= 32'd0; fm_soff <= fm_soff + VB; fm_sub <= fm_sub + 8'd1; end
638              else fm_lg <= fm_lg + 32'd1;
639            end
640            else fm_at <= fm_at + 32'd1;
641          end else if (!fm_av && !fm_aflight) begin
642            fm_subs <= {24'd0, fm_sub} + ((fm_lg != 32'd0) ? 32'd1 : 32'd0);
643            fm_heads <= (heads - fm_h0 < FH) ? heads - fm_h0 : FH;
644            fm_gline <= fm_tbase; fm_hline <= fm_tbase;
645            fm_b <= 32'd0; fm_j <= 32'd0; fm_rb <= 32'd0; fm_rj <= 32'd0; fm_asked <= 32'd0; fm_back <= 32'd0;
646            fm_pop <= 32'd0; rq_valid <= 1'b0; state <= FM_W;
647          end
648        end
649        // Eight heads' weight rows, a line of each in turn, asked whenever the port takes one and the queue has room
650        // for it; each line back into the queue, and from it into the lanes; once every line's sums are written, the
651        // trees.
652        FM_W: begin
653          // The line at hand is head fm_j's beat fm_b: asked until the port takes it, then the next head's of the beat.
654          if (rq_valid && rq_ready) begin
655            fm_asked <= fm_asked + 32'd1;
656            rq_valid <= fm_asked + 32'd1 != fm_lines && fm_asked + 32'd1 - fm_pop < FQ;
657            if (fm_j + 32'd1 == fm_heads) begin
658              fm_j <= 32'd0; fm_b <= fm_b + 32'd1; fm_hline <= fm_gline; rq_line <= fm_gline + fm_b + 32'd1;
659            end else begin
660              fm_j <= fm_j + 32'd1; fm_hline <= fm_hline + fm_beats; rq_line <= fm_hline + fm_beats + fm_b;
661            end
662          end else if (!rq_valid && fm_asked != fm_lines && fm_asked - fm_pop < FQ) begin
663            rq_valid <= 1'b1;
664            if (fm_asked == 32'd0) rq_line <= fm_hline + fm_b;
665          end
666          if (fq_go) fm_pop <= fm_pop + 32'd1;
667          if (rs_valid) begin
668            fm_back <= fm_back + 32'd1;
669            if (fm_rj + 32'd1 == fm_heads) begin fm_rj <= 32'd0; fm_rb <= fm_rb + 32'd1; end else fm_rj <= fm_rj + 32'd1;
670          end
671          if (fm_asked == fm_lines && fm_pop == fm_lines && !fw_v && !fw2_v && fp_v == {@FMUL@{1'b0}} && fs_v == {@FADD@{1'b0}}) begin
672            fm_tr <= 32'd0; fm_tj <= 32'd0; ft_lg <= 32'd0; ft_sub <= 32'd0; state <= FM_TREE;
673          end
674        end
675        // Each held row's heads through the tree, a clock each, its sums read the clock before.
676        FM_TREE: begin
677          tr_v <= fm_tr != fm_rows; tr_lg <= ft_lg; tr_r <= fm_k0 + fm_tr; tr_h <= fm_h0 + fm_tj;
678          if (fm_tr != fm_rows) begin
679            if (fm_tj + 32'd1 == fm_heads) begin
680              fm_tj <= 32'd0; fm_tr <= fm_tr + 32'd1;
681              if (ft_lg + 32'd1 == FMR) begin ft_lg <= 32'd0; ft_sub <= ft_sub + 32'd1; end else ft_lg <= ft_lg + 32'd1;
682            end
683            else fm_tj <= fm_tj + 32'd1;
684          end else if (ftd_v == {@TREE@{1'b0}} && !ft_v && !tr2_v && !tr_v) state <= FM_NEXT;
685        end
686        FM_NEXT: begin
687          if (fm_h0 + FH < heads) begin
688            fm_h0 <= fm_h0 + FH; fm_heads <= (heads - fm_h0 - FH < FH) ? heads - fm_h0 - FH : FH;
689            fm_gline <= fm_gline + FH * fm_beats; fm_hline <= fm_gline + FH * fm_beats;
690            fm_b <= 32'd0; fm_j <= 32'd0; fm_rb <= 32'd0; fm_rj <= 32'd0; fm_asked <= 32'd0; fm_back <= 32'd0; fm_pop <= 32'd0;
691            state <= FM_W;
692          end else if (fm_k0 + FMMR < {16'd0, nrows}) begin fm_k0 <= fm_k0 + FMMR; state <= FM0; end
693          else state <= DRAIN;
694        end
695        // A load: its fields and tables; its element's offset, the row's slot, the rotary row, the ring's row; its
696        // first beat's place; then its beats. A kept tensor load (its flag the word's bit 24) is loaded on the job's
697        // first pass alone: its register holds it for every pass after.
698        LOAD: begin
699          if (li == nload) begin
700            if (grouped && gi + 8'd1 < gmax && !nend) begin
701              k <= nk; h <= nh; gi <= gi + 8'd1; goff <= goff + grows; li <= 8'd0;
702            end else begin
703              gn <= gi + 8'd1; k <= gk0; h <= gh0; gi <= 8'd0; goff <= 16'd0; ii <= 16'd0; state <= RUN;
704            end
705          end
706          else if (ld_a[li][7:4] == 4'd1 && ld_a[li][24] && (gk0 != 16'd0 || gh0 != 32'd0)) li <= li + 8'd1;
707          else if (!ring_waits || settled) begin
708            la <= ld_a[li]; lbw <= ld_b[li]; state <= LB1;
709          end
710        end
711        LB1: begin
712          t_ra <= l_what[TA-1:0]; b_ra <= l_what[BA-1:0];
713          row_e <= rowt[k]; slot_e <= slott[l_what[4:0]]; rope_e <= ropet[l_what];
714          elem_lo <= {32'd0, h[15:0]} * {16'd0, lbw[63:32]}; elem_hi <= {32'd0, h[31:16]} * {16'd0, lbw[63:32]};
715          state <= LB2;
716        end
717        LB2: begin
718          elem <= lbw[31:0] + elem_lo[31:0] + {elem_hi[15:0], 16'd0};
719          kprod <= {16'd0, k} * slot_e[63:32];
720          rp_lo <= {16'd0, row_e[46:32], l_extra[0]} * {16'd0, rope_e[63:32]};
721          rp_hi <= {32'd0, row_e[62:47]} * {16'd0, rope_e[63:32]};
722          ring_sum <= {24'd0, row_e[71:64]} + {24'd0, l_extra};
723          state <= LB3;
724        end
725        LB3: begin
726          rprod <= rp_lo[31:0] + {rp_hi[15:0], 16'd0};
727          ring_prod <= {32'd0, (ring_sum >= bw[95:64]) ? ring_sum - bw[95:64] : ring_sum} * {32'd0, bw[63:32]};
728          state <= LB4;
729        end
730        LB4: begin
731          ring_prod <= f16 ? ring_prod << 1 : ring_prod << 2;
732          state <= LB5;
733        end
734        LB5: begin
735          case (l_kind)
736            4'd0: lbase <= slot_e[31:0] + kprod + (elem >> 4) + cbase;
737            4'd1: lbase <= tw[31:0] + (elem >> 4) + cbase;
738            4'd2: lbase <= rope_e[31:0] + (rprod >> 4);
739            default: lbase <= 32'd0;
740          endcase
741          lbyte <= {32'd0, row_e[31:0]} * 64'd64 + {32'd0, bw[31:0]} + ring_prod + {32'd0, cbase} * (f16 ? 64'd32 : 64'd64);
742          lp <= 32'd0; lt <= 32'd0; lr <= 32'd0; dv <= 1'b0; lbv <= 1'b0; state <= LBEAT;
743        end
744        // The beats: a store beat a clock, each written into the register the clock its word is out; or a memory line
745        // asked whenever the port takes one, written in as it comes back.
746        LBEAT: begin
747          if (l_kind == 4'd0) begin
748            if (lp != l_beats) lp <= lp + 32'd1;
749            if (lbo) begin
750              s_wr <= 1'b1; s_wr_reg <= l_reg; s_wr_row <= lt[15:0] + goff; s_wr_data <= st_rd; lt <= lt + 32'd1;
751            end
752            lbv <= lp != l_beats;
753            if (lt == l_beats) state <= LLEN;
754          end else begin
755            // A line asked until the port takes it, then the next beat's, until every beat's is asked.
756            lp <= lp_next;
757            if (!rq_valid || rq_ready) begin
758              rq_valid <= lp_next != l_beats;
759              rq_line <= l_line(lp_next);
760            end
761            if (rs_valid) begin rs_r <= rs_data; rs_v <= 1'b1; rs_half <= l_byte_r[5]; lr <= lr + 32'd1; end
762            if (rs_v) begin
763              s_wr <= 1'b1; s_wr_reg <= l_reg; s_wr_row <= lt[15:0] + goff; s_wr_data <= from_f16 ? widened : rs_r; lt <= lt + 32'd1;
764            end
765            if (lt == l_beats && !rs_v) state <= LLEN;
766          end
767        end
768        LLEN: begin
769          s_len_wr <= 1'b1; s_wr_reg <= l_reg; s_len_val <= l_chunk[19:0]; li <= li + 8'd1; dv <= 1'b0; state <= LOAD;
770        end
771        // The micro-program, an instruction issued once the one before is done.
772        RUN: begin
773          if (ii == nprog) begin pi <= 8'd0; state <= PUT; end
774          else begin s_issue <= 1'b1; s_instr <= prog[ii]; s_group <= gn; ii <= ii + 16'd1; state <= RWAIT; end
775        end
776        RWAIT: if (!s_issue && !s_busy) state <= RUN;
777        // A store: its fields and tables; its element's offset, the row's slot, the ring's or the cache's row; its
778        // first beat's place; then its beats.
779        PUT: begin
780          if (pi == nput) state <= (gi + 8'd1 < gn) ? GNEXT : NEXT;
781          else begin pa <= pt_a[pi]; pbw <= pt_b[pi]; state <= PB1; end
782        end
783        PB1: begin
784          b_ra <= p_what[BA-1:0];
785          row_e <= rowt[k]; slot_e <= slott[p_what[4:0]];
786          pelem_lo <= {32'd0, h[15:0]} * {16'd0, pbw[63:32]}; pelem_hi <= {32'd0, h[31:16]} * {16'd0, pbw[63:32]};
787          state <= PB2;
788        end
789        PB2: begin
790          pelem <= pbw[31:0] + pelem_lo[31:0] + {pelem_hi[15:0], 16'd0};
791          pkprod <= {16'd0, k} * slot_e[63:32];
792          state <= PB3;
793        end
794        PB3: begin
795          cached_lo <= {16'd0, row_e[47:32]} * {16'd0, bw[159:128]}; cached_hi <= {16'd0, row_e[63:48]} * {16'd0, bw[159:128]};
796          prprod <= {56'd0, row_e[71:64]} * {32'd0, bw[63:32]};
797          state <= PB4;
798        end
799        PB4: begin
800          cached <= cached_lo[31:0] + {cached_hi[15:0], 16'd0} + h;
801          prprod <= f16 ? prprod << 1 : prprod << 2;
802          state <= PB5;
803        end
804        PB5: begin
805          cprod_lo <= {48'd0, cached[15:0]} * {32'd0, p_values ? bw[223:192] : bw[191:160]};
806          cprod_hi <= {48'd0, cached[31:16]} * {32'd0, p_values ? bw[223:192] : bw[191:160]};
807          state <= PB4B;
808        end
809        PB4B: begin
810          cprod <= cprod_lo + (cprod_hi << 16);
811          state <= PBF;
812        end
813        PBF: begin
814          pbase <= slot_e[31:0] + pkprod + (pelem >> 4) + cbase;
815          if (p_kind == 3'd1) begin
816            pbyte <= {32'd0, row_e[31:0]} * 64'd64 + {32'd0, bw[31:0]} + prprod + {32'd0, cbase} * (f16 ? 64'd32 : 64'd64);
817            ring_open <= 1'b1; ring_region <= row_e[31:0];
818          end
819          else case (p_part)
820            2'd0: pbyte <= {32'd0, row_e[31:0]} * 64'd64 + {32'd0, p_values ? bw[63:32] : bw[31:0]} + cprod * 64'd2;
821            2'd1: pbyte <= {32'd0, row_e[31:0]} * 64'd64 + {32'd0, p_values ? bw[63:32] : bw[31:0]} + cprod;
822            default: pbyte <= {32'd0, row_e[31:0]} * 64'd64 + {32'd0, p_values ? bw[127:96] : bw[95:64]} + {32'd0, cached} * 64'd2;
823          endcase
824          pb <= 32'd0; prow <= goff; pt <= 32'd0; pt_on <= {@READ@{1'b0}}; ms_pt <= 32'd0;
825          if (p_beats == 32'd0) begin pi <= pi + 8'd1; state <= PUT; end
826          else state <= (p_kind == 3'd0) ? PBEAT : MS;
827        end
828        // A store into the token-row store: the register's beat asked a clock, written the clock it is out.
829        PBEAT: begin
830          if (pb != p_beats) begin pb <= pb + 32'd1; prow <= prow + 16'd1; end
831          pt_on <= {pt_on[@READ@-2:0], pb != p_beats};
832          pt_lanes <= {pt_lanes[16*@READ@-17:0], lanes_of(pb)};
833          if (ptv) begin
834            st_we <= 1'b1; st_wa <= pbase[SA-1:0] + pt[SA-1:0]; st_wd <= 512'd0; st_wm <= 64'd0;
835            if (p_scalar) begin
836              st_wd[32 * pelem[3:0] +: 32] <= s_rd_s_val; st_wm[4 * pelem[3:0] +: 4] <= 4'hf;
837            end else for (i = 0; i < 16; i = i + 1)
838              if (pt_mask[i]) begin st_wd[32*i +: 32] <= s_rd_data[32*i +: 32]; st_wm[4*i +: 4] <= 4'hf; end
839            pt <= pt + 32'd1;
840          end
841          if (ptv && pt + 32'd1 == p_beats) begin pi <= pi + 8'd1; state <= PUT; end
842        end
843        // A store into the memory: its beats asked a clock each while the queue has room (ms_ask, the stream below);
844        // done once every beat is asked and through, the queue empty and its last push taken.
845        MS: begin
846          if (ms_ask) begin ms_pt <= ms_pt + 32'd1; prow <= ms_pt[15:0] + goff; end
847          if (ms_pt == p_beats && ms_none && !q_have) begin pi <= pi + 8'd1; state <= PUT; end
848        end
849        NEXT: begin
850          cb_flush <= 1'b1;
851          if (heads_first) begin
852            if (k + 16'd1 == nrows) begin k <= 16'd0; h <= h + 32'd1; end else k <= k + 16'd1;
853          end else begin
854            if (h + 32'd1 == heads) begin h <= 32'd0; k <= k + 16'd1; end else h <= h + 32'd1;
855          end
856          state <= PASS;
857        end
858        // The group's next pass's stores, its line flushed.
859        GNEXT: begin
860          cb_flush <= 1'b1; k <= nk; h <= nh; gi <= gi + 8'd1; goff <= goff + grows; pi <= 8'd0; state <= PUT;
861        end
862        // The job's end: its last line written, every write on the card.
863        DRAIN: if (!cb_flush && settled) state <= IDLE;
864        default: state <= IDLE;
865      endcase
866
867      // The memory store's stream: the asked beat's value out of the unit @MS_OUT@ clocks after its ask, captured with
868      // its byte and lanes; carried beside its halves and codes; its line word queued.
869      a_v <= {a_v[@MS_OUT@-1:1], ms_ask};
870      a_pt <= {a_pt[32*@MS_OUT@-33:0], ms_pt};
871      pm_v <= a_out;
872      if (a_out) begin
873        pm_data <= p_scalar ? {480'd0, s_rd_s_val} : s_rd_data;
874        pm_mask <= lanes_of(a_out_pt);
875        case (p_kind == 3'd1 ? 2'd3 : p_part)
876          2'd0: pm_byte <= pbyte + {32'd0, a_out_pt} * 64'd32;
877          2'd1: pm_byte <= pbyte + {32'd0, a_out_pt} * 64'd16;
878          2'd2: pm_byte <= pbyte;
879          default: pm_byte <= pbyte + {32'd0, a_out_pt} * (f16 ? 64'd32 : 64'd64);
880        endcase
881      end
882      d_v <= {d_v[@NARROW_LESS@:1], pm_v};
883      d_val[1] <= pm_data; d_mask[1] <= pm_mask; d_byte[1] <= pm_byte[37:0];
884      for (dsh = 2; dsh <= @NARROW@; dsh = dsh + 1) begin d_val[dsh] <= d_val[dsh - 1]; d_mask[dsh] <= d_mask[dsh - 1]; d_byte[dsh] <= d_byte[dsh - 1]; end
885      if (d_v[@NARROW@]) begin q_data[q_wr] <= o_data; q_mask[q_wr] <= o_wmask; q_line[q_wr] <= o_byte[37:6]; q_wr <= q_wr + 4'd1; end
886      if (cb_pop) q_rd <= q_rd + 4'd1;
887      q_n <= q_n + {4'd0, d_v[@NARROW@]} - {4'd0, cb_pop};
888
889      // The line memory stores gather in: the queue's head into an empty line or merged into its own; a full line
890      // handed to the write queue in the clock the next line's first beat comes, the queue free; a full line flushed
891      // handed over once the queue is free; a part of a line read once every write before it is on the card, merged,
892      // and written.
893      if (wq_valid && wq_ready) wq_valid <= 1'b0;
894      case (cb)
895        CB_EMPTY: if (cb_pop) begin cb <= CB_HELD; cb_line <= h_line; cb_data <= h_data; cb_mask <= h_mask; end
896        CB_HELD: begin
897          if (cb_pop && cb_line == h_line) begin cb_data <= merged(cb_data, h_data, h_mask); cb_mask <= cb_mask | h_mask; end
898          else if (cb_pop) begin
899            wq_valid <= 1'b1; wq_line <= cb_line; wq_data <= cb_data;
900            cb_line <= h_line; cb_data <= h_data; cb_mask <= h_mask;
901          end
902          else if (cb_flush || (q_have && cb_line != h_line && !cb_full)) cb <= cb_full ? CB_WRITE : CB_WAIT;
903        end
904        CB_WAIT: if (!wq_valid && written) begin cb <= CB_READ; rq_valid <= 1'b1; rq_line <= cb_line; end
905        CB_READ: if (rq_ready) begin rq_valid <= 1'b0; cb <= CB_RESP; end
906        CB_RESP: if (rs_valid) begin cb_data <= merged(rs_data, cb_data, cb_mask); cb_mask <= {64{1'b1}}; cb <= CB_WRITE; end
907        CB_WRITE: if (!wq_valid || wq_ready) begin wq_valid <= 1'b1; wq_line <= cb_line; wq_data <= cb_data; cb <= CB_EMPTY; end
908        default: cb <= CB_EMPTY;
909      endcase
910    end
911  end
912endmodule
913"#;
914
915/// Cycles from a conv lane's operands to its value (`logos_conv_lane_p`): the products, the four adds one after another,
916/// the SiLU.
917pub const CONV_LATENCY: usize = {
918    use super::fp32::{FADD_LATENCY, FMUL_LATENCY};
919    FMUL_LATENCY + 4 * FADD_LATENCY + super::simd::LANE_FUNC_P_LATENCY
920};
921
922// The path decides what a narrowed beat does a stage before it is out of the binary16 rounding.
923const _: () = assert!(super::fp32::F32_TO_F16_LATENCY >= 2, "the conv path reads the narrowing's stage before its last");
924
925/// `logos_conv_lane_p` and `logos_conv_path`, a convolution job's rows through the vector side
926/// ([`super::engine::Streaming::with_conv_path`]).
927pub fn conv_path_verilog() -> String {
928    CONV_PATH
929        .replace("@FADD@", &super::fp32::FADD_LATENCY.to_string())
930        .replace("@CVLAT@", &CONV_LATENCY.to_string())
931        .replace("@NARROW@", &super::fp32::F32_TO_F16_LATENCY.to_string())
932}
933
934const CONV_PATH: &str = r#"// A convolution's lane (the machine's conv job, numerics::conv): a channel's ring values r0..r2, oldest first, and the
935// token's x, each by its tap, CVLAT clocks to the SiLU of their sum: the four products side by side, the sum from +0 in
936// tap order (each next product held to its add), the SiLU on the function unit — as the conv micro-program's
937// multiply-adds and its Map compute them.
938module logos_conv_lane_p(input clk, input [31:0] r0, input [31:0] r1, input [31:0] r2, input [31:0] x,
939                         input [31:0] k0, input [31:0] k1, input [31:0] k2, input [31:0] k3, output [31:0] y);
940  wire [31:0] p0, p1, p2, p3, p1d, p2d, p3d, s0, s1, s2, s3;
941  logos_fmul_p m0(.clk(clk), .a(r0), .b(k0), .y(p0));
942  logos_fmul_p m1(.clk(clk), .a(r1), .b(k1), .y(p1));
943  logos_fmul_p m2(.clk(clk), .a(r2), .b(k2), .y(p2));
944  logos_fmul_p m3(.clk(clk), .a(x), .b(k3), .y(p3));
945  logos_delay #(.W(32), .D(@FADD@)) d1(.clk(clk), .d(p1), .q(p1d));
946  logos_delay #(.W(32), .D(2*@FADD@)) d2(.clk(clk), .d(p2), .q(p2d));
947  logos_delay #(.W(32), .D(3*@FADD@)) d3(.clk(clk), .d(p3), .q(p3d));
948  logos_fadd_p a0(.clk(clk), .a(32'd0), .b(p0), .y(s0));
949  logos_fadd_p a1(.clk(clk), .a(s0), .b(p1d), .y(s1));
950  logos_fadd_p a2(.clk(clk), .a(s1), .b(p2d), .y(s2));
951  logos_fadd_p a3(.clk(clk), .a(s2), .b(p3d), .y(s3));
952  logos_lane_func_p silu(.clk(clk), .f(4'd4), .x(s3), .y(y));
953endmodule
954
955// A convolution job's rows through the vector side (the machine's conv job, numerics::conv, its taps four): the kernel's
956// taps read from the memory once a job into block RAM, tap j's beat b at word b of its RAM; then each row of the step: its
957// sequence's three ring rows (the layer's ring at region·64 + ring_off, row (ring + age) mod ring_taps, oldest first)
958// asked a line of each in turn, at most CRQ owed, each back into a queue and three at a time into the slot of two the
959// lanes do not read; its x from the token-row store a beat at a time into a queue (at most XQ asked ahead), the head the
960// beat at hand's; LANES channels a clock through logos_conv_lane_p, every output beat written into the store once its
961// last lane is out; and x, narrowed to the state's format, into the ring's oldest row through the side's line queue (q_v,
962// a line word a push). A row of the region whose ring the row before wrote waits for every write to be on the card
963// (settled) before it asks a ring line. No capture of a wide word waits on logic: the beat's kernel words and x are read
964// in place, each slot fills under a register, the outputs are taken every clock and marked by their valid.
965module logos_conv_path #(parameter SA = 10, CVB = 640, LANES = 4, SRL = 1)
966  (input clk, input rst, input start, input [15:0] rows, input f16,
967   input [31:0] k_line, input [31:0] beats, input [31:0] x_base, input [31:0] x_stride, input [31:0] o_base,
968   input [31:0] o_stride, input [31:0] ring_off, input [7:0] ring_taps, input [31:0] row_bytes,
969   output reg [15:0] row_k, input [127:0] row_e, input settled, output busy,
970   output [SA-1:0] st_ra, input [511:0] st_rd, output reg st_we, output reg [SA-1:0] st_wa, output reg [511:0] st_wd,
971   output reg rq_valid, input rq_ready, output reg [31:0] rq_line, input rs_valid, input [511:0] rs_data,
972   output reg q_v, output reg [511:0] q_data, output reg [63:0] q_mask, output reg [31:0] q_line, input [4:0] q_n,
973   output reg flush);
974  localparam CBA = (CVB > 1) ? $clog2(CVB) : 1, CVLAT = @CVLAT@, NARROW = @NARROW@;
975  localparam [7:0] S = 16 / LANES;
976  localparam [31:0] CRQ = 16;
977  localparam [3:0] XQ = 8;
978  localparam [3:0] IDLE = 4'd0, KASK = 4'd1, ROW = 4'd2, R1 = 4'd3, R2 = 4'd4, WAITR = 4'd5, RUN = 4'd6, FIN = 4'd7,
979                   FIN2 = 4'd8;
980  // The state and the job's fields every lane's operands read: registers Quartus copies beside each group of loads.
981  (* maxfan = 64 *) reg [3:0] state;
982  assign busy = state != IDLE || q_v || flush;
983  reg [15:0] r, nrows;
984  (* maxfan = 64 *) reg ff16;
985  // The card's lines back, a clock on.
986  (* maxfan = 64 *) reg rsv;
987  reg [511:0] rsd;
988  // The kernel: tap j's beat b at word b of kr j; the fill's next line asked (ka_j, ka_b) and the next back (kg_j, kg_b).
989  reg [CBA-1:0] k_ra, k_wa;
990  reg k_we;
991  reg [1:0] k_wj;
992  reg [511:0] k_wd;
993  wire [511:0] kv0, kv1, kv2, kv3;
994  logos_bram_w512 #(.D(CVB), .A(CBA)) kr0(.clk(clk), .ra(k_ra), .rd(kv0), .we(k_we && k_wj == 2'd0), .wa(k_wa), .wd(k_wd), .wm({64{1'b1}}));
995  logos_bram_w512 #(.D(CVB), .A(CBA)) kr1(.clk(clk), .ra(k_ra), .rd(kv1), .we(k_we && k_wj == 2'd1), .wa(k_wa), .wd(k_wd), .wm({64{1'b1}}));
996  logos_bram_w512 #(.D(CVB), .A(CBA)) kr2(.clk(clk), .ra(k_ra), .rd(kv2), .we(k_we && k_wj == 2'd2), .wa(k_wa), .wd(k_wd), .wm({64{1'b1}}));
997  logos_bram_w512 #(.D(CVB), .A(CBA)) kr3(.clk(clk), .ra(k_ra), .rd(kv3), .we(k_we && k_wj == 2'd3), .wa(k_wa), .wd(k_wd), .wm({64{1'b1}}));
998  reg [31:0] ka_b, kg_b, kg_n;
999  reg [1:0] ka_j, kg_j;
1000  reg k_done_ask;
1001  // The row: its ring rows' first lines, the line its x goes to, its x and output beats' first, whether it waits.
1002  reg [31:0] region, rl0, rl1, rl2, xrow, orow;
1003  reg [7:0] ring;
1004  reg ring_open;
1005  reg [31:0] last_region;
1006  wire [7:0] i0 = ring, i1 = (ring + 8'd1 >= ring_taps) ? ring + 8'd1 - ring_taps : ring + 8'd1,
1007             i2 = (ring + 8'd2 >= ring_taps) ? ring + 8'd2 - ring_taps : ring + 8'd2;
1008  function [31:0] at_row(input [7:0] idx);
1009    at_row = (idx == 8'd0) ? 32'd0 : (idx == 8'd1) ? row_bytes : row_bytes << 1;
1010  endfunction
1011  // Each ring row's first byte, the row's region in lines.
1012  wire [37:0] rb0 = {region, 6'd0} + {6'd0, ring_off} + {6'd0, at_row(i0)}, rb1 = {region, 6'd0} + {6'd0, ring_off} + {6'd0, at_row(i1)},
1013              rb2 = {region, 6'd0} + {6'd0, ring_off} + {6'd0, at_row(i2)};
1014  wire [31:0] lpr = ff16 ? (beats + 32'd1) >> 1 : beats;
1015  // The ring rows' lines asked: line ra_l of age ra_a; how many asked, back, and taken into the triple.
1016  reg [31:0] ra_l, ra_n, rg_n, rp_n;
1017  reg [1:0] ra_a;
1018  wire [31:0] ra_line = ((ra_a == 2'd0) ? rl0 : (ra_a == 2'd1) ? rl1 : rl2) + ra_l;
1019  `LOGOS_RAMSTYLE(CRQ) reg [511:0] cq [0:CRQ-1];
1020  reg [3:0] cq_w, cq_r;
1021  // The ring triples: two slots, the lanes reading slot cur_s (its line cur_l) while the queue fills the other (nx_n lines
1022  // in), each line shifted in at the top under the register that marks it the slot's, the oldest at the bottom once three
1023  // are in.
1024  reg [511:0] sa0, sa1, sa2, sb0, sb1, sb2, cq_q;
1025  reg [1:0] nx_n;
1026  (* maxfan = 64 *) reg cur_s;
1027  (* maxfan = 64 *) reg fill_a, fill_b;
1028  reg cur_v, cq_rv;
1029  reg [31:0] cur_l;
1030  wire [511:0] cur0 = cur_s ? sb2 : sa2, cur1 = cur_s ? sb1 : sa1, cur2 = cur_s ? sb0 : sa0;
1031  // x: beats asked from the store (xa_n), out of it SRL clocks on and a clock more into the queue (xlq, xld); the beats
1032  // asked and not yet taken by a beat's last step (x_out), those on their way from the store and those queued, so a beat
1033  // is asked only while the queue holds room for every one of them, whatever the store's latency.
1034  reg [31:0] xa_n;
1035  reg xa_v;
1036  reg [3:0] x_out;
1037  wire x_ask = state == RUN && xa_n != beats && x_out < XQ - 4'd1;
1038  wire xl_v, xl_flight;
1039  wire [0:0] xl_none;
1040  logos_landing #(.W(1), .D(SRL - 1)) xland(.clk(clk), .rst(rst), .in_v(xa_v), .in_d(1'b0), .out_v(xl_v), .out_d(xl_none), .flight(xl_flight));
1041  assign st_ra = xrow[SA-1:0] + xa_n[SA-1:0];
1042  (* maxfan = 64 *) reg xlq;
1043  reg [511:0] xld;
1044  `LOGOS_RAMSTYLE_ASYNC(XQ) reg [511:0] xq [0:XQ-1];
1045  reg [2:0] xq_w, xq_r;
1046  reg [3:0] xq_n;
1047  wire [511:0] xh = xq[xq_r];
1048  // The beat at hand (b) and its step (c): the lanes take channels LANES·c on of it; a beat starts with its ring triple,
1049  // its x at the queue's head, its kernel words out of the RAMs, and room in the line queue for its ring line.
1050  (* maxfan = 64 *) reg [31:0] b;
1051  (* maxfan = 64 *) reg [7:0] c;
1052  reg on;
1053  reg [31:0] kv_beat;
1054  wire [31:0] need_l = ff16 ? b >> 1 : b;
1055  wire beat_go = state == RUN && !on && b != beats && cur_v && cur_l == need_l && xq_n != 4'd0 && kv_beat == b && q_n < 5'd10;
1056  // The lanes' operands, registered: each lane's three ring values (binary16 widened), x and four taps.
1057  reg [32*LANES-1:0] lr0, lr1, lr2, lx, lk0, lk1, lk2, lk3;
1058  wire [32*LANES-1:0] ly;
1059  wire [511:0] w0, w1, w2;
1060  genvar g;
1061  generate
1062    for (g = 0; g < 16; g = g + 1) begin : widen
1063      logos_f16_to_f32 v0(.h(b[0] ? cur0[256 + 16*g +: 16] : cur0[16*g +: 16]), .x(w0[32*g +: 32]));
1064      logos_f16_to_f32 v1(.h(b[0] ? cur1[256 + 16*g +: 16] : cur1[16*g +: 16]), .x(w1[32*g +: 32]));
1065      logos_f16_to_f32 v2(.h(b[0] ? cur2[256 + 16*g +: 16] : cur2[16*g +: 16]), .x(w2[32*g +: 32]));
1066    end
1067    for (g = 0; g < LANES; g = g + 1) begin : lane
1068      logos_conv_lane_p u(.clk(clk), .r0(lr0[32*g +: 32]), .r1(lr1[32*g +: 32]), .r2(lr2[32*g +: 32]), .x(lx[32*g +: 32]),
1069        .k0(lk0[32*g +: 32]), .k1(lk1[32*g +: 32]), .k2(lk2[32*g +: 32]), .k3(lk3[32*g +: 32]), .y(ly[32*g +: 32]));
1070    end
1071  endgenerate
1072  wire [511:0] rr0 = ff16 ? w0 : cur0, rr1 = ff16 ? w1 : cur1, rr2 = ff16 ? w2 : cur2;
1073  // Each step's lanes in flight: its beat and step, its output's lanes gathered into the beat once out.
1074  (* maxfan = 64 *) reg [CVLAT:0] f_v;
1075  reg [31:0] f_b [0:CVLAT];
1076  reg [7:0] f_c [0:CVLAT];
1077  reg [511:0] ob;
1078  // A step's lanes shifted in above the steps before it: once a beat's last step is in, its first step's lanes are at
1079  // the bottom, each lane at its channel's place.
1080  wire [511:0] ob_next;
1081  generate
1082    if (LANES == 16) begin : whole
1083      assign ob_next = ly;
1084    end else begin : shifted
1085      assign ob_next = {ly, ob[511:32*LANES]};
1086    end
1087  endgenerate
1088  integer i;
1089  // x narrowed for the ring: the beat taken (n_v, its beat), its binary16 halves NARROW clocks on; a line of two halves
1090  // (or a whole f32 beat) pushed into the side's queue.
1091  reg nv;
1092  reg [31:0] n_b;
1093  reg [511:0] n_x;
1094  reg [NARROW:1] nd_v;
1095  reg [31:0] nd_b [1:NARROW];
1096  reg [511:0] nd_x [1:NARROW];
1097  wire [255:0] halves;
1098  generate
1099    for (g = 0; g < 16; g = g + 1) begin : narrow
1100      logos_f32_to_f16_p nh(.clk(clk), .x(n_x[32*g +: 32]), .h(halves[16*g +: 16]));
1101    end
1102  endgenerate
1103  reg [255:0] lo_half;
1104  // What the narrowed beat out next does, decided a stage before it is out: pushed (a whole f32 beat, or a line of two
1105  // halves or the last alone), or its halves kept for the next.
1106  reg p_v, p_lo;
1107  (* maxfan = 64 *) reg p_odd;
1108  reg [31:0] wrow;
1109  wire narrowing = nv || nd_v != {NARROW{1'b0}};
1110  wire lanes_busy = f_v != {(CVLAT+1){1'b0}};
1111  always @(posedge clk) begin
1112    flush <= 1'b0; k_we <= 1'b0; xa_v <= 1'b0; nv <= 1'b0;
1113    // The card's lines, a clock on: the kernel's into its RAMs, the ring's into the queue.
1114    rsv <= rs_valid; rsd <= rs_data; k_wd <= rsd;
1115    if (state == KASK && rsv) begin
1116      k_we <= 1'b1; k_wj <= kg_j; k_wa <= kg_b[CBA-1:0];
1117      if (kg_b + 32'd1 == beats) begin kg_b <= 32'd0; kg_j <= kg_j + 2'd1; end else kg_b <= kg_b + 32'd1;
1118      kg_n <= kg_n + 32'd1;
1119    end
1120    if (state == RUN && rsv) begin cq[cq_w] <= rsd; cq_w <= cq_w + 4'd1; rg_n <= rg_n + 32'd1; end
1121    // The slots: the queue's head read a clock, then shifted into the slot the lanes do not read; that slot made the
1122    // lanes' once its three lines are in and the lanes are past their line.
1123    cq_rv <= 1'b0; fill_a <= 1'b0; fill_b <= 1'b0;
1124    if (state == RUN && {1'b0, nx_n} + {1'b0, cq_rv} < 3'd3 && rp_n != rg_n) begin
1125      cq_q <= cq[cq_r]; cq_r <= cq_r + 4'd1; rp_n <= rp_n + 32'd1; cq_rv <= 1'b1; fill_a <= cur_s; fill_b <= !cur_s;
1126    end
1127    if (fill_a) begin sa0 <= cq_q; sa1 <= sa0; sa2 <= sa1; end
1128    if (fill_b) begin sb0 <= cq_q; sb1 <= sb0; sb2 <= sb1; end
1129    if (cq_rv) nx_n <= nx_n + 2'd1;
1130    if (state == RUN && nx_n == 2'd3 && !cq_rv && (!cur_v || (cur_l != need_l && !on))) begin
1131      cur_s <= !cur_s; cur_v <= 1'b1; cur_l <= cur_v ? cur_l + 32'd1 : 32'd0; nx_n <= 2'd0;
1132    end
1133    // x: a beat asked while the queue has room for it beside those on their way; into the queue a clock after it is out.
1134    if (x_ask) begin xa_v <= 1'b1; xa_n <= xa_n + 32'd1; end
1135    xlq <= xl_v; xld <= st_rd;
1136    if (xlq) begin xq[xq_w] <= xld; xq_w <= xq_w + 3'd1; end
1137    // The kernel's words: the beat at hand's out of the RAMs from before its start through its last step's read, the
1138    // next beat's address taken at the edge before that read, so its words are out (kv_beat the beat they are) the clock
1139    // after the beat's last step.
1140    kv_beat <= {{(32-CBA){1'b0}}, k_ra};
1141    if (state == RUN && ((beat_go && S == 8'd1) || (on && c + 8'd2 == S))) k_ra <= b[CBA-1:0] + 1'b1;
1142    // A beat's steps, LANES channels a step; its x popped from the queue at its last.
1143    if (beat_go) begin
1144      on <= 1'b1; c <= 8'd0; nv <= 1'b1;
1145    end else if (on) begin
1146      if (c + 8'd1 == S) begin on <= 1'b0; b <= b + 32'd1; xq_r <= xq_r + 3'd1; end
1147      c <= c + 8'd1;
1148    end
1149    xq_n <= xq_n + {3'd0, xlq} - {3'd0, on && c + 8'd1 == S};
1150    x_out <= x_out + {3'd0, x_ask} - {3'd0, on && c + 8'd1 == S};
1151    n_b <= b; n_x <= xh;
1152    for (i = 0; i < LANES; i = i + 1) begin
1153      lr0[32*i +: 32] <= rr0[32*(LANES*c + i) +: 32];
1154      lr1[32*i +: 32] <= rr1[32*(LANES*c + i) +: 32];
1155      lr2[32*i +: 32] <= rr2[32*(LANES*c + i) +: 32];
1156      lx[32*i +: 32] <= xh[32*(LANES*c + i) +: 32];
1157      lk0[32*i +: 32] <= kv0[32*(LANES*c + i) +: 32];
1158      lk1[32*i +: 32] <= kv1[32*(LANES*c + i) +: 32];
1159      lk2[32*i +: 32] <= kv2[32*(LANES*c + i) +: 32];
1160      lk3[32*i +: 32] <= kv3[32*(LANES*c + i) +: 32];
1161    end
1162    // The steps in flight; an output beat written as its last step's lanes are out.
1163    f_v <= {f_v[CVLAT-1:0], on};
1164    f_b[0] <= b; f_c[0] <= c;
1165    for (i = 1; i <= CVLAT; i = i + 1) begin f_b[i] <= f_b[i-1]; f_c[i] <= f_c[i-1]; end
1166    if (f_v[CVLAT]) ob <= ob_next;
1167    st_we <= f_v[CVLAT] && f_c[CVLAT] + 8'd1 == S; st_wa <= orow[SA-1:0] + f_b[CVLAT][SA-1:0]; st_wd <= ob_next;
1168    // x narrowed into the ring, a line of two beats' halves (the last beat of an odd count alone), or a beat of f32s.
1169    nd_v <= {nd_v[NARROW-1:1], nv};
1170    nd_b[1] <= n_b; nd_x[1] <= n_x;
1171    for (i = 2; i <= NARROW; i = i + 1) begin nd_b[i] <= nd_b[i-1]; nd_x[i] <= nd_x[i-1]; end
1172    p_v <= nd_v[NARROW-1] && (!ff16 || nd_b[NARROW-1][0] || nd_b[NARROW-1] + 32'd1 == beats);
1173    p_lo <= nd_v[NARROW-1] && ff16 && !nd_b[NARROW-1][0] && nd_b[NARROW-1] + 32'd1 != beats;
1174    p_odd <= nd_b[NARROW-1][0];
1175    q_v <= p_v;
1176    if (p_lo) lo_half <= halves;
1177    q_line <= wrow + (ff16 ? nd_b[NARROW] >> 1 : nd_b[NARROW]);
1178    q_data <= !ff16 ? nd_x[NARROW] : p_odd ? {halves, lo_half} : {256'd0, halves};
1179    q_mask <= (!ff16 || p_odd) ? {64{1'b1}} : {32'd0, {32{1'b1}}};
1180    if (rst) begin
1181      state <= IDLE; rq_valid <= 1'b0; f_v <= {(CVLAT+1){1'b0}}; nd_v <= {NARROW{1'b0}}; on <= 1'b0; ring_open <= 1'b0;
1182      rsv <= 1'b0; xlq <= 1'b0; cq_rv <= 1'b0; fill_a <= 1'b0; fill_b <= 1'b0; p_v <= 1'b0; p_lo <= 1'b0; q_v <= 1'b0;
1183      st_we <= 1'b0; nv <= 1'b0; cur_s <= 1'b0; x_out <= 4'd0;
1184    end else case (state)
1185      IDLE: if (start) begin
1186        nrows <= rows; ff16 <= f16; r <= 16'd0; ka_b <= 32'd0; ka_j <= 2'd0; kg_b <= 32'd0; kg_j <= 2'd0; kg_n <= 32'd0; ring_open <= 1'b0;
1187        rq_valid <= 1'b1; rq_line <= k_line; k_done_ask <= 1'b0; state <= KASK;
1188      end
1189      // The kernel: tap j's line b at k_line + j·beats + b, asked in turn; done once every line is back.
1190      KASK: begin
1191        if (rq_valid && rq_ready) begin
1192          if (ka_b + 32'd1 == beats) begin
1193            ka_b <= 32'd0; ka_j <= ka_j + 2'd1;
1194            if (ka_j == 2'd3) begin rq_valid <= 1'b0; k_done_ask <= 1'b1; end
1195            else rq_line <= k_line + (({30'd0, ka_j} + 32'd1) * beats);
1196          end else begin ka_b <= ka_b + 32'd1; rq_line <= rq_line + 32'd1; end
1197        end
1198        if (k_done_ask && kg_n == 32'd4 * beats) state <= ROW;
1199      end
1200      ROW: if (r == nrows) state <= IDLE; else begin row_k <= r; state <= R1; end
1201      R1: begin
1202        region <= row_e[31:0]; ring <= row_e[71:64];
1203        xrow <= x_base + {16'd0, r} * x_stride; orow <= o_base + {16'd0, r} * o_stride;
1204        state <= R2;
1205      end
1206      R2: begin
1207        rl0 <= rb0[37:6]; rl1 <= rb1[37:6]; rl2 <= rb2[37:6]; wrow <= rb0[37:6];
1208        state <= WAITR;
1209      end
1210      WAITR: if (!(ring_open && last_region == region) || settled) begin
1211        ra_l <= 32'd0; ra_a <= 2'd0; ra_n <= 32'd0; rg_n <= 32'd0; rp_n <= 32'd0; cq_w <= 4'd0; cq_r <= 4'd0;
1212        nx_n <= 2'd0; cur_v <= 1'b0; cur_l <= 32'd0; xa_n <= 32'd0; xq_w <= 3'd0; xq_r <= 3'd0; xq_n <= 4'd0; x_out <= 4'd0; b <= 32'd0;
1213        k_ra <= {CBA{1'b0}}; rq_valid <= 1'b0; state <= RUN;
1214      end
1215      // The row's lines asked whenever the port takes one and the queue has room; done once every output beat is
1216      // written and every ring line pushed.
1217      RUN: begin
1218        if (rq_valid && rq_ready) begin
1219          ra_n <= ra_n + 32'd1;
1220          if (ra_a == 2'd2) begin ra_a <= 2'd0; ra_l <= ra_l + 32'd1; end else ra_a <= ra_a + 2'd1;
1221        end
1222        rq_valid <= (ra_n + {31'd0, rq_valid && rq_ready}) != 32'd3 * lpr && (ra_n + {31'd0, rq_valid && rq_ready}) - rp_n < CRQ - 2;
1223        rq_line <= (rq_valid && rq_ready) ? ((ra_a == 2'd2) ? rl0 + ra_l + 32'd1 : ((ra_a == 2'd0) ? rl1 : rl2) + ra_l) : ra_line;
1224        if (b == beats && !on && !lanes_busy && !narrowing && !q_v) state <= FIN;
1225      end
1226      FIN: begin flush <= 1'b1; ring_open <= 1'b1; last_region <= region; r <= r + 16'd1; state <= FIN2; end
1227      FIN2: state <= ROW;
1228      default: state <= IDLE;
1229    endcase
1230  end
1231endmodule
1232"#;