1pub const FM_ROWS: usize = 4;
44
45const MS_DEPTH: usize = 16;
47
48pub 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 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
915pub 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
922const _: () = assert!(super::fp32::F32_TO_F16_LATENCY >= 2, "the conv path reads the narrowing's stage before its last");
924
925pub 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"#;