1pub fn gdn_verilog() -> String {
40 format!("{}{}\n{}", super::ram::RAMSTYLE, super::fp32::fp32_verilog(), GDN_VERILOG)
41}
42
43pub const LANE_CREDIT: usize = 32;
48
49pub const LANE_CREDIT_MAX: usize = 256;
52
53pub const FOLD_M_READ_LATENCY: usize = 2;
56
57pub fn lanes_verilog() -> String {
62 let lane = GDN_LANE_VERILOG.replace("@FADD@", &FADD_LATENCY.to_string()).replace("@F16@", &F32_TO_F16_LATENCY.to_string());
63 format!("{}{GDN_LANES_VERILOG}\n{lane}\n{}", super::ram::RAMSTYLE, super::ram::bram_verilog(&[512]))
64}
65
66pub fn lanes_trailing_verilog(window: usize) -> String {
70 assert!(window > 0, "a trailing flush replays a window of tokens");
71 let edit = |text: &str, from: &str, to: &str| -> String {
72 assert_eq!(text.matches(from).count(), 1, "the lanes' `{from}`");
73 text.replacen(from, to, 1)
74 };
75 let mut top = GDN_LANES_VERILOG.to_string();
76 top = edit(&top, "// A recurrence job's token rows, each a lazily flushed Gated-DeltaNet block, on L head lanes.", &format!(
77 "// A recurrence job's token rows, each a Gated-DeltaNet block flushed a window of {window} tokens behind, on L head lanes\n// (logos_gdn_lane_t, the coordinator's ring and each row's share handed on as co_lo)."));
78 top = edit(&top, " wire co_ready, lb_we, co_flush, co_busy, co_idle, co_reading;", " wire co_ready, lb_we, co_flush, co_busy, co_idle, co_reading;\n wire [15:0] co_lo;");
79 top = edit(&top, " wire [39:0] log_need = {32'd0, tokens} * {14'd0, entry[31:6]};", " wire [39:0] log_need = {31'd0, tokens, 1'b0} * {14'd0, entry[31:6]};");
80 top = edit(&top, ".MODE(1), .LBA(LBA)) co(", &format!(".MODE(1), .LBA(LBA), .WINDOW({window})) co("));
81 top = edit(&top, " .log_first(log_first), .log_lines(log_lines), .co_reading(co_reading)", " .log_first(log_first), .log_lines(log_lines), .co_lo(co_lo), .co_reading(co_reading)");
82 top = edit(&top, " logos_gdn_lane #(.SRL(SRL), .HD(HD), .TMAX(TMAX), .SA(SA), .OUTSTANDING(OUTSTANDING), .W(W), .STEP(L), .LBA(LBA), .FLUSHES(FLUSHES), .COPY_LINES(LOG_LINES))",
83 &format!(" logos_gdn_lane_t #(.SRL(SRL), .HD(HD), .TMAX(TMAX), .SA(SA), .OUTSTANDING(OUTSTANDING), .W(W), .STEP(L), .LBA(LBA), .WINDOW({window}), .COPY_LINES(LOG_LINES))"));
84 top = edit(&top, ".in_count(co_count), .in_flush(co_flush), .in_par(c_par)", ".in_count(co_count), .in_flush(co_flush), .in_from_row(co_lo), .in_par(c_par)");
85 let lane = GDN_LANE_T_VERILOG.replace("@FADD@", &FADD_LATENCY.to_string()).replace("@F16@", &F32_TO_F16_LATENCY.to_string());
86 format!("{}{top}\n{lane}\n{}\n{}", super::ram::RAMSTYLE, flush_m_verilog(), super::ram::bram_verilog(&[512]))
87}
88
89pub fn gate_verilog() -> String {
94 GDN_GATE_VERILOG
95 .replace("@FMUL@", &FMUL_LATENCY.to_string())
96 .replace("@FADD@", &FADD_LATENCY.to_string())
97 .replace("@TREE16@", &TREE16_LATENCY.to_string())
98 .replace("@RSQRT@", &RSQRT_LATENCY.to_string())
99 .replace("@EXP@", &EXP_LATENCY.to_string())
100 .replace("@RECIP@", &RECIP_LATENCY.to_string())
101}
102
103pub fn delta_normed_lanes(lanes: &str, hd: usize, fp: FpUnit) -> String {
113 let edit = |text: &str, from: &str, to: &str| -> String {
114 assert_eq!(text.matches(from).count(), 1, "the normed lanes' `{from}`");
115 text.replacen(from, to, 1)
116 };
117 assert!(16 >= 3 * FADD_LATENCY, "the gate's slots hold two batches loaded ahead and one gated");
121 let trailing = lanes.contains("module logos_gdn_lane_t #(");
122 assert!(trailing != lanes.contains("module logos_gdn_lane #("), "one kind of head lane");
123 let rn = numerics::recip(fp, hd as f32).to_bits();
124 let mut t = lanes.to_string();
125 t = edit(&t, " input [SA-1:0] gt_beat,\n", " input [SA-1:0] gt_beat, input norm, input [31:0] eps, input [SA-1:0] z_beat, input [31:0] w_line,\n");
127 t = edit(&t, ".rq_valid(rq_valid), .rq_ready(rq_ready), .rq_line(rq_line),\n .rs_valid(rs_valid), .rs_data(rs_data),",
128 ".rq_valid(co_rq_valid), .rq_ready(co_rq_ready), .rq_line(co_rq_line),\n .rs_valid(co_rs_valid), .rs_data(rs_data),");
129 t = edit(&t, " reg [SA-1:0] c_q, c_out;\n", r#" reg [SA-1:0] c_q, c_out, c_z;
130 // The norm's weight, fetched at the job's start on the coordinator's port before its first row (HD / 16 lines from
131 // w_line, asked and taken in order), each line given to every lane as it comes (wf_we, its beat wf_got).
132 localparam [15:0] WBEATS = HD / 16;
133 reg wf_on;
134 reg [15:0] wf_asked, wf_got;
135 wire co_rq_valid, co_rs_valid;
136 wire [31:0] co_rq_line;
137 wire co_rq_ready = !wf_on && rq_ready;
138 assign rq_valid = wf_on ? wf_asked != WBEATS : co_rq_valid;
139 assign rq_line = wf_on ? w_line + {16'd0, wf_asked} : co_rq_line;
140 assign co_rs_valid = !wf_on && rs_valid;
141 wire wf_we = wf_on && rs_valid;
142 always @(posedge clk) begin
143 if (wf_on && rq_valid && rq_ready) wf_asked <= wf_asked + 16'd1;
144 if (wf_we) wf_got <= wf_got + 16'd1;
145 if (wf_we && wf_got + 16'd1 == WBEATS) wf_on <= 1'b0;
146 if (start) begin wf_on <= norm; wf_asked <= 16'd0; wf_got <= 16'd0; end
147 if (rst) wf_on <= 1'b0;
148 end
149"#);
150 t = edit(&t, "c_q <= q_beat; c_out <= out_beat;", "c_q <= q_beat; c_out <= out_beat; c_z <= z_beat;");
151 t = edit(&t, " wire co_go = job && !fault && row_valid &&", " wire co_go = job && !wf_on && !fault && row_valid &&");
152 t = edit(&t, "&& written && !co_busy;\n", "&& written && !co_busy && !wf_on;\n");
153 t = edit(&t, ".in_out_beat(c_out), .first(FIRST),",
154 ".in_out_beat(c_out), .norm(norm), .eps(eps), .in_z_beat(c_z), .w_we(wf_we), .w_wa(wf_got[7:0]), .w_wd(rs_data),\n .first(FIRST),");
155 t = edit(&t, "COPY_LINES = 4096, SRL = 1)", "COPY_LINES = 4096, SRL = 1, NZ = 16)");
158 t = edit(&t, " output reg st_we, output reg [SA-1:0] st_wa, output reg [511:0] st_wd,\n output [LBA:0] lb_ra, input [511:0] lb_rd,",
159 " output st_we, output [SA-1:0] st_wa, output [511:0] st_wd,\n input norm, input [31:0] eps, input [SA-1:0] in_z_beat, input w_we, input [7:0] w_wa, input [511:0] w_wd,\n output [LBA:0] lb_ra, input [511:0] lb_rd,");
160 t = edit(&t, " wire can_load = !f_busy && !f_start && !f_run && ", &format!(
161 r#" // The gate's slots: heads take them in turn as they are loaded (zn, the heads loaded), each free again once the gate has
162 // written the head (g_gated); a head's gate beats are read after its query's, into its slot.
163 localparam ZB = $clog2(NZ), GB = BEATS > 1 ? $clog2(BEATS) : 1;
164 localparam [15:0] SLOTS = NZ;
165 localparam [31:0] RN = 32'h{rn:08x};
166 reg [15:0] zn;
167 wire [15:0] g_gated;
168 wire g_busy;
169 wire [15:0] q_end = norm ? 2 * BEATS : BEATS;
170 wire [15:0] z_slot = zn + {{8'd0, q_lo_slot}}, z_at = q_lo_at - BEATS;
171 wire g_room = !norm || zn - g_gated + {{8'd0, bs}} <= SLOTS;
172 wire can_load = g_room && !f_busy && !f_start && !f_run && "#));
173 t = edit(&t, " assign st_rq = ls == L_Q && qj != BEATS;", " assign st_rq = ls == L_Q && qj != q_end;");
174 t = edit(&t, " L_Q: if (qj == BEATS) begin", " L_Q: if (qj == q_end) begin");
175 t = edit(&t, " if (lslot + 8'd1 == bs || lh + STEP >= nv) ls <= L_FOLD;",
176 " if (lslot + 8'd1 == bs || lh + STEP >= nv) begin ls <= L_FOLD; zn <= zn + {8'd0, lslot} + 16'd1; end");
177 t = edit(&t, " if (q_lo) begin f_q_wr <= 1'b1;", " if (q_lo && q_lo_at < BEATS) begin f_q_wr <= 1'b1;");
178 t = edit(&t, "f_start <= 1'b0; st_we <= 1'b0; q_pending", "f_start <= 1'b0; o_we <= 1'b0; q_pending");
179 t = edit(&t, "begin st_we <= 1'b1; st_wa <= o_wa[SA-1:0]; st_wd <= {r_out, o_beat[479:0]}; end",
180 "begin o_we <= 1'b1; o_wq <= o_wa[SA-1:0]; o_wd <= {r_out, o_beat[479:0]}; end");
181 t = edit(&t, " always @(posedge clk) begin\n f_log_wr <= 1'b0; f_q_wr <= 1'b0; f_start <= 1'b0;", r#" // The read-outs into the store: through the gate (logos_gdn_gate_p), which norms each head by the weight and gates it
182 // by the head's gate beats, when the job has a norm; as they are made otherwise.
183 reg o_we;
184 reg [SA-1:0] o_wq;
185 reg [511:0] o_wd;
186 wire g_we;
187 wire [SA-1:0] g_wa;
188 wire [511:0] g_wd;
189 wire [15:0] g_zwa = {z_slot[ZB-1:0], z_at[GB-1:0]};
190 logos_gdn_gate_p #(.HD(HD), .SA(SA), .NZ(NZ), .RN(RN)) gate(.clk(clk), .rst(rst), .eps(eps), .w_we(w_we), .w_wa(w_wa),
191 .w_wd(w_wd), .z_we(q_lo && q_lo_at >= BEATS), .z_wa(g_zwa), .z_wd(st_rd), .e_v(norm && r_ov),
192 .e_o(r_out), .e_at(o_wa[SA-1:0]), .st_we(g_we), .st_wa(g_wa), .st_wd(g_wd), .gated(g_gated), .busy(g_busy));
193 assign st_we = norm ? g_we : o_we;
194 assign st_wa = norm ? g_wa : o_wq;
195 assign st_wd = norm ? g_wd : o_wd;
196 always @(posedge clk) begin
197 f_log_wr <= 1'b0; f_q_wr <= 1'b0; f_start <= 1'b0;"#);
198 if trailing {
199 t = edit(&t, " `LOGOS_RAMSTYLE(2) reg [SA-1:0] e_out [0:1];\n", " `LOGOS_RAMSTYLE(2) reg [SA-1:0] e_out [0:1];\n `LOGOS_RAMSTYLE(2) reg [SA-1:0] e_z [0:1];\n");
202 t = edit(&t, "e_out[in_n[0]] <= in_out_beat;", "e_out[in_n[0]] <= in_out_beat; e_z[in_n[0]] <= in_z_beat;");
203 t = edit(&t, " reg [SA-1:0] l_q;\n", " reg [SA-1:0] l_q, l_z;\n");
204 t = edit(&t, "l_q <= e_q[ld_n[0]];", "l_q <= e_q[ld_n[0]]; l_z <= e_z[ld_n[0]];");
205 t = edit(&t, " wire [31:0] q_ra = l_q + ((lg * HD) >> 4) + qj;", " wire [31:0] q_ra = qj < BEATS ? l_q + ((lg * HD) >> 4) + qj : l_z + ((lh * HD) >> 4) + qj - BEATS;");
206 t = edit(&t, " wire dn_past = o_n != dn_n && st_n != dn_n && (dn_n[0] ? xr1 == 4'd0 && w_np1 == 5'd0 : xr0 == 4'd0 && w_np0 == 5'd0);",
207 " reg [15:0] gin, o_mark0, o_mark1;\n wire dn_past = o_n != dn_n && st_n != dn_n && (dn_n[0] ? xr1 == 4'd0 && w_np1 == 5'd0 : xr0 == 4'd0 && w_np0 == 5'd0)\n && (!norm || g_gated - (dn_n[0] ? o_mark1 : o_mark0) < 16'h8000);");
208 t = edit(&t, " assign busy = in_n != dn_n || w_n != 5'd0;", " assign busy = in_n != dn_n || w_n != 5'd0 || g_busy;");
209 t = edit(&t, " if (o_row + 16'd1 == HD_ROWS) begin\n o_row <= 16'd0;\n if (oh + STEP >= nv) begin\n o_n <= o_n + 8'd1;",
210 " if (o_row + 16'd1 == HD_ROWS) begin\n o_row <= 16'd0;\n gin <= gin + 16'd1;\n if (oh + STEP >= nv) begin\n if (o_n[0]) o_mark1 <= gin + 16'd1; else o_mark0 <= gin + 16'd1;\n o_n <= o_n + 8'd1;");
211 t = edit(&t, " else if (o_set && oh >= nv) begin o_n <= o_n + 8'd1; o_set <= 1'b0; end",
212 " else if (o_set && oh >= nv) begin\n if (o_n[0]) o_mark1 <= gin; else o_mark0 <= gin;\n o_n <= o_n + 8'd1; o_set <= 1'b0;\n end");
213 t = edit(&t, "in_n <= 8'd0; ld_n <= 8'd0; st_n <= 8'd0; o_n <= 8'd0; dn_n <= 8'd0;", "in_n <= 8'd0; ld_n <= 8'd0; st_n <= 8'd0; o_n <= 8'd0; dn_n <= 8'd0; zn <= 16'd0; gin <= 16'd0;");
214 } else {
215 t = edit(&t, " reg [SA-1:0] q_beat, out_beat;\n", " reg [SA-1:0] q_beat, out_beat, z_beat;\n");
217 t = edit(&t, "out_beat <= in_out_beat;", "out_beat <= in_out_beat; z_beat <= in_z_beat;");
218 t = edit(&t, " wire [31:0] q_ra = q_beat + ((lg * HD) >> 4) + qj;", " wire [31:0] q_ra = qj < BEATS ? q_beat + ((lg * HD) >> 4) + qj : z_beat + ((lh * HD) >> 4) + qj - BEATS;");
219 t = edit(&t, " assign busy = running || w_n != 5'd0;", " assign busy = running || w_n != 5'd0 || g_busy;");
220 t = edit(&t, " if (running && ls == L_DONE && ss == S_DONE && outs_done && (!flush || xh >= nv) && w_n == 5'd0) begin",
221 " if (running && ls == L_DONE && ss == S_DONE && outs_done && (!flush || xh >= nv) && w_n == 5'd0 && !g_busy) begin");
222 t = edit(&t, " running <= 1'b0; ls <= L_IDLE; ss <= S_IDLE; f_run <= 1'b0; t_wr <= {QB{1'b0}};",
223 " running <= 1'b0; ls <= L_IDLE; ss <= S_IDLE; f_run <= 1'b0; zn <= 16'd0; t_wr <= {QB{1'b0}};");
224 }
225 format!("{t}\n{}", gate_verilog())
226}
227
228pub fn delta_normed_rows(rows: &str) -> String {
231 let edit = |text: &str, from: &str, to: &str| -> String {
232 assert_eq!(text.matches(from).count(), 1, "the normed rows' `{from}`");
233 text.replacen(from, to, 1)
234 };
235 let mut t = rows.to_string();
236 t = edit(&t, " input [4:0] gt_slot,\n", " input [4:0] gt_slot, input [4:0] z_slot,\n");
237 t = edit(&t, "output reg [SA-1:0] gt_beat);", "output reg [SA-1:0] gt_beat, output reg [SA-1:0] z_beat);");
238 t = edit(&t, " reg [SA-1:0] q_st, out_st, k_st, v_st, ld_st, gt_st;", " reg [SA-1:0] q_st, out_st, k_st, v_st, ld_st, gt_st, z_st;");
239 t = edit(&t, " sg = slott[gt_slot];", " sg = slott[gt_slot], sz = slott[z_slot];");
240 t = edit(&t, " ld_beat <= sl[SA-1:0]; gt_beat <= sg[SA-1:0];", " ld_beat <= sl[SA-1:0]; gt_beat <= sg[SA-1:0]; z_beat <= sz[SA-1:0];");
241 t = edit(&t, " ld_st <= sl[32+SA-1:32]; gt_st <= sg[32+SA-1:32];", " ld_st <= sl[32+SA-1:32]; gt_st <= sg[32+SA-1:32]; z_st <= sz[32+SA-1:32];");
242 t = edit(&t, " ld_beat <= ld_beat + ld_st; gt_beat <= gt_beat + gt_st;", " ld_beat <= ld_beat + ld_st; gt_beat <= gt_beat + gt_st; z_beat <= z_beat + z_st;");
243 t
244}
245
246pub fn rows_verilog() -> String {
250 GDN_ROWS_VERILOG.to_string()
251}
252
253pub fn gdn_stream_verilog() -> String {
257 format!("{}{}\n{}", super::ram::RAMSTYLE, super::vector::vector_verilog(), filled(&format!("{GDN_STREAM_VERILOG}{GDN_WIDE_VERILOG}{GDN_FOLD_M_VERILOG}")))
258}
259
260pub fn flush_m_verilog() -> String {
263 format!("{}{}", super::ram::RAMSTYLE, filled(GDN_FLUSH_M_VERILOG))
264}
265
266fn filled(text: &str) -> String {
268 let products = FMUL_LATENCY;
271 let tree = products + FADD_LATENCY + TREE16_LATENCY;
272 let carried = tree + FADD_LATENCY;
273 let lag = carried + FMUL_LATENCY;
274 let value = tree + FMUL_LATENCY;
275 let coef = value + FADD_LATENCY + FMUL_LATENCY;
276 let updated = FMUL_LATENCY + FADD_LATENCY;
277 let bc = tree + FMUL_LATENCY;
278 let folded = FMUL_LATENCY + FADD_LATENCY + FMUL_LATENCY;
279 let units = [
280 ("@PRODUCTS@", products),
281 ("@TREE@", tree),
282 ("@CARRIED@", carried),
283 ("@LAG@", lag),
284 ("@VALUE@", value),
285 ("@COEF@", coef),
286 ("@UPDATED@", updated),
287 ("@BC@", bc),
288 ("@FOLDED@", folded),
289 ("@FADD@", FADD_LATENCY),
290 ("@FMUL@", FMUL_LATENCY),
291 ("@EXP@", EXP_LATENCY),
292 ("@F16@", F32_TO_F16_LATENCY),
293 ]
294 .iter()
295 .fold(text.to_string(), |t, (k, v)| t.replace(k, &v.to_string()));
296 units
297}
298
299const _: () = assert!(FADD_LATENCY == 4, "logos_gdn_read_p's groups of four rows are the add's four clocks");
302
303use super::fp32::{F32_TO_F16_LATENCY, FADD_LATENCY, FMUL_LATENCY, TREE16_LATENCY};
304use super::super::numerics::{self, FpUnit};
305use super::vector::{EXP_LATENCY, RECIP_LATENCY, RSQRT_LATENCY};
306
307const GDN_VERILOG: &str = r#"// One value row of N elements through one token, sixteen elements a cycle.
308module logos_gdn_row_engine #(parameter N = 128)
309 (input clk, input rst,
310 input [32*N-1:0] k, input [32*N-1:0] q, input [31:0] decay, input [31:0] beta, input [31:0] kq, input [31:0] scale,
311 input f16,
312 input in_valid, input in_first, input [511:0] in_data, input [31:0] in_v,
313 output reg out_valid, output reg out_last, output reg [511:0] out_data, output reg [31:0] out_read);
314 localparam [15:0] BEATS = N / 16;
315 reg [32*N-1:0] kept;
316 reg [511:0] acc_k, acc_q;
317 reg [31:0] v;
318 reg [15:0] beat;
319 reg [15:0] out_beat;
320 reg emitting;
321 // This beat's lane products and sums, against the beat's sixteen key and query elements.
322 wire [511:0] beat_k = k[512*beat +: 512], beat_q = q[512*beat +: 512];
323 wire [511:0] next_k, next_q;
324 genvar l;
325 generate
326 for (l = 0; l < 16; l = l + 1) begin : lane
327 wire [31:0] pk, pq;
328 logos_fmul mk(.a(in_data[32*l +: 32]), .b(beat_k[32*l +: 32]), .y(pk));
329 logos_fmul mq(.a(in_data[32*l +: 32]), .b(beat_q[32*l +: 32]), .y(pq));
330 logos_fadd ak(.a(in_first ? 32'd0 : acc_k[32*l +: 32]), .b(pk), .y(next_k[32*l +: 32]));
331 logos_fadd aq(.a(in_first ? 32'd0 : acc_q[32*l +: 32]), .b(pq), .y(next_q[32*l +: 32]));
332 end
333 endgenerate
334 // The folded dots, the coefficient and the read-out.
335 wire [31:0] dk, dq, u, vu, coef, dsq, ckq, sum, read;
336 logos_tree16 tk(.lanes(acc_k), .sum(dk));
337 logos_tree16 tq(.lanes(acc_q), .sum(dq));
338 logos_fmul m0(.a(decay), .b(dk), .y(u));
339 logos_fadd s0(.a(v), .b({~u[31], u[30:0]}), .y(vu));
340 logos_fmul m1(.a(beta), .b(vu), .y(coef));
341 logos_fmul m2(.a(decay), .b(dq), .y(dsq));
342 logos_fmul m3(.a(coef), .b(kq), .y(ckq));
343 logos_fadd s1(.a(dsq), .b(ckq), .y(sum));
344 logos_fmul m4(.a(sum), .b(scale), .y(read));
345 // The update of the out beat's sixteen kept elements.
346 wire [511:0] old = kept[512*out_beat +: 512], key = k[512*out_beat +: 512];
347 wire [511:0] updated;
348 generate
349 for (l = 0; l < 16; l = l + 1) begin : upd
350 wire [31:0] dm, ck, nm, back;
351 wire [15:0] half;
352 logos_fmul md(.a(decay), .b(old[32*l +: 32]), .y(dm));
353 logos_fmul mc(.a(coef), .b(key[32*l +: 32]), .y(ck));
354 logos_fadd an(.a(dm), .b(ck), .y(nm));
355 logos_f32_to_f16 enc(.x(nm), .h(half));
356 logos_f16_to_f32 dec(.h(half), .x(back));
357 assign updated[32*l +: 32] = f16 ? back : nm;
358 end
359 endgenerate
360 always @(posedge clk) begin
361 if (rst) begin
362 beat <= 16'd0; out_beat <= 16'd0; emitting <= 1'b0; out_valid <= 1'b0; out_last <= 1'b0;
363 end else begin
364 out_valid <= 1'b0;
365 out_last <= 1'b0;
366 if (in_valid) begin
367 acc_k <= next_k;
368 acc_q <= next_q;
369 kept[512*beat +: 512] <= in_data;
370 if (in_first) v <= in_v;
371 if (beat == BEATS - 1) begin beat <= 16'd0; emitting <= 1'b1; out_beat <= 16'd0; end
372 else beat <= beat + 16'd1;
373 end else if (emitting) begin
374 out_valid <= 1'b1;
375 out_data <= updated;
376 if (out_beat == BEATS - 1) begin
377 out_last <= 1'b1; out_read <= read; emitting <= 1'b0; out_beat <= 16'd0;
378 end else out_beat <= out_beat + 16'd1;
379 end
380 end
381 end
382endmodule
383
384// One value row of N elements under a lazily flushed memory: its read-out through the folded query y, and when
385// flushing, up to T_MAX logged updates replayed over it in order and the row stored once.
386module logos_gdn_lazy_row #(parameter N = 128, T_MAX = 16)
387 (input clk, input rst,
388 input [32*N-1:0] y, input [31:0] scale, input f16, input [7:0] entries, input flush,
389 input log_wr, input [7:0] log_at, input [32*N-1:0] log_k, input [31:0] log_decay, input [31:0] log_beta,
390 input in_valid, input in_first, input [511:0] in_data, input [31:0] in_carried, input [32*T_MAX-1:0] in_values,
391 output reg out_valid, output reg out_last, output reg [511:0] out_data, output reg [31:0] out_read, output busy);
392 localparam [15:0] BEATS = N / 16;
393 localparam IN = 3'd0, READ = 3'd1, DOT = 3'd2, COEF = 3'd3, UPD = 3'd4, OUT = 3'd5;
394 reg [2:0] state;
395 reg [32*N-1:0] kept;
396 `LOGOS_RAMSTYLE_ASYNC(T_MAX) reg [32*N-1:0] ks [0:T_MAX-1];
397 `LOGOS_RAMSTYLE_ASYNC(T_MAX) reg [31:0] decays [0:T_MAX-1];
398 `LOGOS_RAMSTYLE_ASYNC(T_MAX) reg [31:0] betas [0:T_MAX-1];
399 reg [32*T_MAX-1:0] values;
400 reg [511:0] lanes;
401 reg [31:0] carried, coef;
402 reg [15:0] beat;
403 reg [7:0] e;
404 assign busy = (state != IN);
405 // The input beat's products with the folded query, and a held beat's with the token's key.
406 wire [511:0] y_beat = y[512*beat +: 512];
407 wire [511:0] row_beat = kept[512*beat +: 512];
408 wire [511:0] k_beat = ks[e][512*beat +: 512];
409 wire [511:0] in_next, dot_next, upd;
410 genvar l;
411 generate
412 for (l = 0; l < 16; l = l + 1) begin : lane
413 wire [31:0] py, pk, dm, ck;
414 logos_fmul my(.a(in_data[32*l +: 32]), .b(y_beat[32*l +: 32]), .y(py));
415 logos_fadd ay(.a(in_first ? 32'd0 : lanes[32*l +: 32]), .b(py), .y(in_next[32*l +: 32]));
416 logos_fmul mk(.a(row_beat[32*l +: 32]), .b(k_beat[32*l +: 32]), .y(pk));
417 logos_fadd ak(.a(beat == 16'd0 ? 32'd0 : lanes[32*l +: 32]), .b(pk), .y(dot_next[32*l +: 32]));
418 logos_fmul md(.a(decays[e]), .b(row_beat[32*l +: 32]), .y(dm));
419 logos_fmul mc(.a(coef), .b(k_beat[32*l +: 32]), .y(ck));
420 logos_fadd au(.a(dm), .b(ck), .y(upd[32*l +: 32]));
421 end
422 endgenerate
423 // The folded lanes: the read-out, or a token's coefficient.
424 wire [31:0] folded, with_carried, read, u, vu, c;
425 logos_tree16 tf(.lanes(lanes), .sum(folded));
426 logos_fadd ac(.a(folded), .b(carried), .y(with_carried));
427 logos_fmul ms(.a(with_carried), .b(scale), .y(read));
428 logos_fmul mu(.a(decays[e]), .b(folded), .y(u));
429 logos_fadd sv(.a(values[32*e +: 32]), .b({~u[31], u[30:0]}), .y(vu));
430 logos_fmul mb(.a(betas[e]), .b(vu), .y(c));
431 // The held row as the state stores it.
432 wire [511:0] stored;
433 generate
434 for (l = 0; l < 16; l = l + 1) begin : st
435 wire [15:0] half;
436 wire [31:0] back;
437 logos_f32_to_f16 enc(.x(row_beat[32*l +: 32]), .h(half));
438 logos_f16_to_f32 dec(.h(half), .x(back));
439 assign stored[32*l +: 32] = f16 ? back : row_beat[32*l +: 32];
440 end
441 endgenerate
442 always @(posedge clk) begin
443 out_valid <= 1'b0;
444 out_last <= 1'b0;
445 if (log_wr) begin ks[log_at] <= log_k; decays[log_at] <= log_decay; betas[log_at] <= log_beta; end
446 if (rst) begin state <= IN; beat <= 16'd0; end
447 else case (state)
448 IN: if (in_valid) begin
449 lanes <= in_next;
450 kept[512*beat +: 512] <= in_data;
451 if (in_first) begin carried <= in_carried; values <= in_values; end
452 if (beat == BEATS - 1) begin beat <= 16'd0; state <= READ; end else beat <= beat + 16'd1;
453 end
454 READ: begin
455 out_read <= read;
456 if (flush && entries != 8'd0) begin e <= 8'd0; state <= DOT; end
457 else begin out_valid <= 1'b1; out_last <= 1'b1; state <= IN; end
458 end
459 DOT: begin
460 lanes <= dot_next;
461 if (beat == BEATS - 1) begin beat <= 16'd0; state <= COEF; end else beat <= beat + 16'd1;
462 end
463 COEF: begin coef <= c; state <= UPD; end
464 UPD: begin
465 kept[512*beat +: 512] <= upd;
466 if (beat == BEATS - 1) begin
467 beat <= 16'd0;
468 if (e + 8'd1 == entries) state <= OUT; else begin e <= e + 8'd1; state <= DOT; end
469 end else beat <= beat + 16'd1;
470 end
471 OUT: begin
472 out_valid <= 1'b1;
473 out_data <= stored;
474 if (beat == BEATS - 1) begin out_last <= 1'b1; beat <= 16'd0; state <= IN; end else beat <= beat + 16'd1;
475 end
476 endcase
477 end
478endmodule
479"#;
480
481const GDN_STREAM_VERILOG: &str = r#"// A lazily flushed head's read-out a beat a clock: rows of N elements in beats of sixteen f32 (element 16·b + l in
482// lane l of beat b), row-major, in fours, each row's carried value with its first beat and the head's folded query y
483// and scale with each group's first beat; out, in the rows' order, (row·y + carried)·scale of each row.
484module logos_gdn_read_p #(parameter N = 128)
485 (input clk, input rst,
486 input [32*N-1:0] y, input [31:0] scale,
487 input in_valid, output in_ready, input [511:0] in_beat, input [31:0] in_carried,
488 output reg out_valid, output reg [31:0] out_read);
489 localparam [15:0] BEATS = N / 16;
490 localparam [15:0] GB = 4 * BEATS;
491 localparam LAG = @LAG@;
492 // Two groups of four rows, row-major; each group's carried values, query and scale.
493 `LOGOS_RAMSTYLE(2*GB) reg [511:0] gbuf [0:2*GB-1];
494 `LOGOS_RAMSTYLE(8) reg [31:0] gcar [0:7];
495 `LOGOS_RAMSTYLE_ASYNC(2) reg [32*N-1:0] gy [0:1];
496 `LOGOS_RAMSTYLE(2) reg [31:0] gscale [0:1];
497 reg [1:0] full;
498 // The group being gathered and the group being sent.
499 reg wr, rd, sending;
500 reg [1:0] wrow, sj;
501 reg [15:0] wbeat, sb;
502 assign in_ready = !full[wr];
503 wire take = in_valid && in_ready;
504 wire group_in = take && wrow == 2'd3 && wbeat == BEATS - 1;
505 wire group_out = sending && sj == 2'd3 && sb == BEATS - 1;
506 always @(posedge clk) begin
507 if (take) begin
508 gbuf[{wr, wrow} * BEATS + wbeat] <= in_beat;
509 if (wbeat == 16'd0) gcar[{wr, wrow}] <= in_carried;
510 if (wrow == 2'd0 && wbeat == 16'd0) begin gy[wr] <= y; gscale[wr] <= scale; end
511 end
512 if (rst) begin
513 full <= 2'b00; wr <= 1'b0; rd <= 1'b0; sending <= 1'b0; wrow <= 2'd0; wbeat <= 16'd0; sj <= 2'd0; sb <= 16'd0;
514 end else begin
515 if (take) begin
516 if (wbeat == BEATS - 1) begin wbeat <= 16'd0; wrow <= wrow + 2'd1; end else wbeat <= wbeat + 16'd1;
517 if (group_in) wr <= !wr;
518 end
519 if (group_in) full[wr] <= 1'b1;
520 if (group_out) begin full[rd] <= 1'b0; rd <= !rd; sending <= 1'b0; end
521 else if (!sending && full[rd]) begin sending <= 1'b1; sj <= 2'd0; sb <= 16'd0; end
522 if (sending && !group_out) begin
523 if (sj == 2'd3) begin sj <= 2'd0; sb <= sb + 16'd1; end else sj <= sj + 2'd1;
524 end
525 end
526 end
527 // The beat at hand, its query beat, and its tags: whether it opens or closes its row, the row's carried and scale.
528 reg [511:0] beat_q, yb_q;
529 reg v_q, first_q, last_q;
530 reg [31:0] car_q, scale_q;
531 always @(posedge clk) begin
532 beat_q <= gbuf[{rd, sj} * BEATS + sb];
533 yb_q <= gy[rd][512*sb +: 512];
534 v_q <= !rst && sending;
535 first_q <= sb == 16'd0;
536 last_q <= sb == BEATS - 1;
537 car_q <= gcar[{rd, sj}];
538 scale_q <= gscale[rd];
539 end
540 // The tags through the multiply, the add, the tree, the carried value's add and the scale's multiply.
541 reg [LAG:1] v_d, first_d, last_d;
542 reg [31:0] car_d [1:LAG];
543 reg [31:0] scale_d [1:LAG];
544 integer i;
545 always @(posedge clk) begin
546 v_d <= rst ? {LAG{1'b0}} : {v_d[LAG-1:1], v_q};
547 first_d <= {first_d[LAG-1:1], first_q};
548 last_d <= {last_d[LAG-1:1], last_q};
549 car_d[1] <= car_q;
550 scale_d[1] <= scale_q;
551 for (i = 2; i <= LAG; i = i + 1) begin car_d[i] <= car_d[i-1]; scale_d[i] <= scale_d[i-1]; end
552 end
553 // The lanes: a product a clock, each lane's sum for a row back at its add four clocks later as the row's next term
554 // enters, a row's first term added to zero.
555 wire [511:0] sums;
556 genvar l;
557 generate
558 for (l = 0; l < 16; l = l + 1) begin : lane
559 wire [31:0] p;
560 logos_fmul_p m(.clk(clk), .a(beat_q[32*l +: 32]), .b(yb_q[32*l +: 32]), .y(p));
561 logos_fadd_p a(.clk(clk), .a(first_d[@PRODUCTS@] ? 32'd0 : sums[32*l +: 32]), .b(p), .y(sums[32*l +: 32]));
562 end
563 endgenerate
564 // A row's lanes leave the adds the clock after its last term entered them; the tree, the carried value, the scale.
565 wire [31:0] dot, with_carried, read;
566 logos_tree16_p t(.clk(clk), .lanes(sums), .sum(dot));
567 logos_fadd_p c(.clk(clk), .a(dot), .b(car_d[@TREE@]), .y(with_carried));
568 logos_fmul_p s(.clk(clk), .a(with_carried), .b(scale_d[@CARRIED@]), .y(read));
569 always @(posedge clk) begin
570 out_valid <= !rst && v_d[LAG] && last_d[LAG];
571 out_read <= read;
572 end
573endmodule
574
575// A lazily flushed head's rows through its log, on the pipelined units: the head's log written beat by beat (token
576// log_at, beat log_beat: its key's beat and its values' beat), each token's decay exponent and gate with its beat 0,
577// each decay through the pipelined exponential; then the head's rows of N elements in beats of sixteen f32, row-major,
578// in fours, taken once every decay is in. Every logged update is replayed over each row in the log's order — the row's
579// own value of each token, element r of the token's values for the head's row r — no rounding between, and the rows
580// stream out row-major, each element stored once (rounded to binary16 and back when f16): a beat leaves the clock after
581// each clock out_ready is high.
582module logos_gdn_flush_p #(parameter N = 128, T_MAX = 16)
583 (input clk, input rst,
584 input log_wr, input [7:0] log_at, input [15:0] log_beat, input [511:0] log_k, input [511:0] log_v,
585 input [31:0] log_decay, input [31:0] log_beta, input [7:0] entries, input f16,
586 input in_valid, output in_ready, input [511:0] in_beat,
587 output out_valid, input out_ready, output [511:0] out_beat, output [255:0] out_half);
588 localparam [15:0] BEATS = N / 16;
589 localparam [15:0] GB = 4 * BEATS;
590 localparam IN = 3'd0, DOT = 3'd1, COEF = 3'd2, UPD = 3'd3, FILL = 3'd4, OUT = 3'd5;
591 `LOGOS_RAMSTYLE(T_MAX*BEATS) reg [511:0] kmem [0:T_MAX*BEATS-1];
592 `LOGOS_RAMSTYLE(T_MAX*BEATS) reg [511:0] vmem [0:T_MAX*BEATS-1];
593 `LOGOS_RAMSTYLE_ASYNC(T_MAX) reg [31:0] decays [0:T_MAX-1];
594 `LOGOS_RAMSTYLE_ASYNC(T_MAX) reg [31:0] betas [0:T_MAX-1];
595 // Each token's decay: its exponent through the exponential as its beat 0 is written.
596 wire [31:0] ex;
597 reg [@EXP@:1] ex_v;
598 reg [7:0] ex_at [1:@EXP@];
599 reg [7:0] exps;
600 logos_exp_p xp(.clk(clk), .x_in(log_decay), .y(ex));
601 integer i;
602 always @(posedge clk) begin
603 if (log_wr) begin
604 kmem[log_at * BEATS + log_beat] <= log_k;
605 vmem[log_at * BEATS + log_beat] <= log_v;
606 if (log_beat == 16'd0) betas[log_at] <= log_beta;
607 end
608 ex_v <= rst ? {@EXP@{1'b0}} : {ex_v[@EXP@-1:1], log_wr && log_beat == 16'd0};
609 ex_at[1] <= log_at;
610 for (i = 2; i <= @EXP@; i = i + 1) ex_at[i] <= ex_at[i-1];
611 if (ex_v[@EXP@]) decays[ex_at[@EXP@]] <= ex;
612 if (rst) exps <= 8'd0;
613 else exps <= exps + ((log_wr && log_beat == 16'd0) ? 8'd1 : 8'd0) - (ex_v[@EXP@] ? 8'd1 : 8'd0);
614 end
615 // The group: four rows of beats, row-major, each row's coefficient; the rows taken since the log was written, the
616 // token at hand, the beat the walk has reached.
617 `LOGOS_RAMSTYLE(GB) reg [511:0] gbuf [0:GB-1];
618 `LOGOS_RAMSTYLE(4) reg [31:0] coef [0:3];
619 reg [15:0] row_base;
620 reg [2:0] state;
621 reg [7:0] e;
622 reg [1:0] j;
623 reg [15:0] b;
624 reg [2:0] coefs;
625 reg [15:0] written;
626 assign in_ready = state == IN && exps == 8'd0;
627 wire take = in_valid && in_ready;
628 // A dot's or an update's beat: the row's beat, the token's key beat, and the tags.
629 reg [511:0] x_q, k_q;
630 reg v_q, upd_q, first_q, last_q;
631 reg [1:0] j_q;
632 reg [15:0] b_q;
633 reg [31:0] coef_q, decay_q;
634 always @(posedge clk) begin
635 x_q <= gbuf[j * BEATS + b];
636 k_q <= kmem[e * BEATS + b];
637 v_q <= !rst && (state == DOT || state == UPD);
638 upd_q <= state == UPD;
639 first_q <= b == 16'd0;
640 last_q <= b == BEATS - 1;
641 j_q <= j;
642 b_q <= b;
643 coef_q <= coef[j];
644 decay_q <= decays[e];
645 end
646 // The tags a clock at a time: [k] the beat's tag k clocks after its operands.
647 reg [@COEF@:1] v_d, upd_d, first_d, last_d;
648 reg [1:0] j_d [1:@COEF@];
649 reg [15:0] b_d [1:@UPDATED@];
650 always @(posedge clk) begin
651 v_d <= rst ? {@COEF@{1'b0}} : {v_d[@COEF@-1:1], v_q};
652 upd_d <= rst ? {@COEF@{1'b0}} : {upd_d[@COEF@-1:1], upd_q};
653 first_d <= {first_d[@COEF@-1:1], first_q};
654 last_d <= {last_d[@COEF@-1:1], last_q};
655 j_d[1] <= j_q;
656 b_d[1] <= b_q;
657 for (i = 2; i <= @COEF@; i = i + 1) j_d[i] <= j_d[i-1];
658 for (i = 2; i <= @UPDATED@; i = i + 1) b_d[i] <= b_d[i-1];
659 end
660 // The lanes: in a dot, each lane's sum for a row back at its add as the row's next term enters; in an update,
661 // decay·m + coef·k.
662 wire [511:0] sums, upd;
663 genvar l;
664 generate
665 for (l = 0; l < 16; l = l + 1) begin : lane
666 wire [31:0] p, dm, ck;
667 logos_fmul_p m(.clk(clk), .a(x_q[32*l +: 32]), .b(k_q[32*l +: 32]), .y(p));
668 logos_fadd_p a(.clk(clk), .a(first_d[@PRODUCTS@] ? 32'd0 : sums[32*l +: 32]), .b(p), .y(sums[32*l +: 32]));
669 logos_fmul_p md(.clk(clk), .a(decay_q), .b(x_q[32*l +: 32]), .y(dm));
670 logos_fmul_p mc(.clk(clk), .a(coef_q), .b(k_q[32*l +: 32]), .y(ck));
671 logos_fadd_p au(.clk(clk), .a(dm), .b(ck), .y(upd[32*l +: 32]));
672 end
673 endgenerate
674 // A row's lanes leave the adds the clock after its last term entered them; its dot through the tree, then its
675 // coefficient beta·(v − decay·dot).
676 wire [31:0] dot, u, vu, c;
677 logos_tree16_p t(.clk(clk), .lanes(sums), .sum(dot));
678 logos_fmul_p mu(.clk(clk), .a(decays[e]), .b(dot), .y(u));
679 // The row's own value of the token, read two clocks before it meets decay·dot and its element taken the clock after:
680 // element r of the token's values beat.
681 wire [15:0] v_row = row_base + {14'd0, j_d[@VALUE@ - 2]};
682 reg [511:0] v_line;
683 reg [3:0] v_lane;
684 reg [31:0] v_val;
685 always @(posedge clk) begin
686 v_line <= vmem[e * BEATS + (v_row >> 4)]; v_lane <= v_row[3:0];
687 v_val <= v_line[32 * v_lane +: 32];
688 end
689 logos_fadd_p sv(.clk(clk), .a(v_val), .b({~u[31], u[30:0]}), .y(vu));
690 logos_fmul_p mb(.clk(clk), .a(betas[e]), .b(vu), .y(c));
691 wire coef_in = v_d[@COEF@] && !upd_d[@COEF@] && last_d[@COEF@];
692 wire write_back = v_d[@UPDATED@] && upd_d[@UPDATED@];
693 always @(posedge clk) begin
694 if (take) gbuf[j * BEATS + b] <= in_beat;
695 else if (write_back) gbuf[j_d[@UPDATED@] * BEATS + b_d[@UPDATED@]] <= upd;
696 if (coef_in) coef[j_d[@COEF@]] <= c;
697 end
698 always @(posedge clk) begin
699 if (rst) begin state <= IN; j <= 2'd0; b <= 16'd0; e <= 8'd0; coefs <= 3'd0; written <= 16'd0; row_base <= 16'd0; end
700 else begin
701 case (state)
702 IN: if (take) begin
703 if (b == BEATS - 1) begin
704 b <= 16'd0; j <= j + 2'd1;
705 if (j == 2'd3) begin e <= 8'd0; state <= (entries == 8'd0) ? OUT : DOT; end
706 end else b <= b + 16'd1;
707 end
708 DOT, UPD: begin
709 if (j == 2'd3) begin
710 j <= 2'd0;
711 if (b == BEATS - 1) begin
712 b <= 16'd0;
713 if (state == DOT) begin coefs <= 3'd0; state <= COEF; end else state <= FILL;
714 end else b <= b + 16'd1;
715 end else j <= j + 2'd1;
716 end
717 COEF: if (coefs == 3'd4) begin written <= 16'd0; state <= UPD; end
718 FILL: if (written == GB) begin
719 if (e + 8'd1 == entries) state <= OUT; else begin e <= e + 8'd1; state <= DOT; end
720 end
721 OUT: if (out_ready) begin
722 if (b == BEATS - 1) begin
723 b <= 16'd0; j <= j + 2'd1;
724 if (j == 2'd3) begin row_base <= row_base + 16'd4; state <= IN; end
725 end else b <= b + 16'd1;
726 end
727 endcase
728 if (coef_in) coefs <= coefs + 3'd1;
729 if (write_back) written <= written + 16'd1;
730 if (log_wr) row_base <= 16'd0;
731 end
732 end
733 // The rows out, each element stored: a beat read, then its binary16 rounding (logos_f32_to_f16_p) with the beat held
734 // beside it; out_half the stored binary16, out_beat the element as stored.
735 reg [511:0] o_beat;
736 reg o_v;
737 always @(posedge clk) begin
738 o_v <= !rst && state == OUT && out_ready;
739 o_beat <= gbuf[j * BEATS + b];
740 end
741 // The beat held beside its rounding, its valid in registers the reset clears.
742 wire [511:0] o_raw;
743 logos_delay #(.W(512), .D(@F16@)) dob(.clk(clk), .d(o_beat), .q(o_raw));
744 reg ov_d [1:@F16@];
745 integer ok;
746 always @(posedge clk) begin
747 ov_d[1] <= !rst && o_v;
748 for (ok = 2; ok <= @F16@; ok = ok + 1) ov_d[ok] <= !rst && ov_d[ok-1];
749 end
750 assign out_valid = ov_d[@F16@] && !rst;
751 genvar o;
752 generate
753 for (o = 0; o < 16; o = o + 1) begin : st
754 wire [31:0] back;
755 logos_f32_to_f16_p enc(.clk(clk), .x(o_beat[32*o +: 32]), .h(out_half[16*o +: 16]));
756 logos_f16_to_f32 dec(.h(out_half[16*o +: 16]), .x(back));
757 assign out_beat[32*o +: 32] = f16 ? back : o_raw[32*o +: 32];
758 end
759 endgenerate
760endmodule
761
762// A lazily flushed head's query folded back through its log: the log's tokens written beat by beat (token log_at, beat
763// log_beat: its key's beat and its values' beat), each token's log decay and gate with its beat 0, and the query beat by
764// beat; then start folds the query through the first `entries` tokens newest first, and y_beat and carried_beat give
765// beat rd_beat of the folded query and the collected values, decay_out token rd_token's decay, once busy is low.
766module logos_gdn_fold_p #(parameter N = 128, T_MAX = 16)
767 (input clk, input rst,
768 input log_wr, input [7:0] log_at, input [15:0] log_beat, input [511:0] log_k, input [511:0] log_v,
769 input [31:0] log_decay, input [31:0] log_beta,
770 input q_wr, input [15:0] q_beat, input [511:0] q_data,
771 input start, input [7:0] entries, output busy,
772 input [15:0] rd_beat, output [511:0] y_beat, output [511:0] carried_beat, input [7:0] rd_token, output [31:0] decay_out);
773 localparam [15:0] BEATS = N / 16;
774 localparam IDLE = 3'd0, EXPS = 3'd1, DOT = 3'd2, BCW = 3'd3, UPD = 3'd4, FILL = 3'd5;
775 `LOGOS_RAMSTYLE(T_MAX*BEATS) reg [511:0] kmem [0:T_MAX*BEATS-1];
776 `LOGOS_RAMSTYLE(T_MAX*BEATS) reg [511:0] vmem [0:T_MAX*BEATS-1];
777 `LOGOS_RAMSTYLE_ASYNC(T_MAX) reg [31:0] log_decays [0:T_MAX-1];
778 `LOGOS_RAMSTYLE_ASYNC(T_MAX) reg [31:0] betas [0:T_MAX-1];
779 (* ramstyle = "logic" *) reg [511:0] qmem [0:BEATS-1];
780 always @(posedge clk) begin
781 if (log_wr) begin
782 kmem[log_at * BEATS + log_beat] <= log_k;
783 vmem[log_at * BEATS + log_beat] <= log_v;
784 if (log_beat == 16'd0) begin log_decays[log_at] <= log_decay; betas[log_at] <= log_beta; end
785 end
786 if (q_wr) qmem[q_beat] <= q_data;
787 end
788 (* ramstyle = "logic" *) reg [511:0] ybuf [0:BEATS-1];
789 (* ramstyle = "logic" *) reg [511:0] cbuf [0:BEATS-1];
790 assign y_beat = ybuf[rd_beat];
791 assign carried_beat = cbuf[rd_beat];
792 `LOGOS_RAMSTYLE_ASYNC(T_MAX) reg [31:0] decays [0:T_MAX-1];
793 assign decay_out = decays[rd_token];
794 reg [2:0] state;
795 reg [7:0] e, sent, got;
796 reg [15:0] b;
797 reg [1:0] gap;
798 reg [15:0] written;
799 reg [31:0] bc;
800 assign busy = state != IDLE;
801 // The decays: every token's log decay through the exponential, back to back.
802 wire [31:0] ex;
803 reg [@EXP@:1] ex_v;
804 reg [7:0] ex_at [1:@EXP@];
805 logos_exp_p xp(.clk(clk), .x_in(log_decays[sent]), .y(ex));
806 integer i;
807 always @(posedge clk) begin
808 ex_v <= rst ? {@EXP@{1'b0}} : {ex_v[@EXP@-1:1], state == EXPS && sent != entries};
809 ex_at[1] <= sent;
810 for (i = 2; i <= @EXP@; i = i + 1) ex_at[i] <= ex_at[i-1];
811 if (ex_v[@EXP@]) decays[ex_at[@EXP@]] <= ex;
812 end
813 // A dot's or an update's beat: the folded query's, the key's, the values', the collected values'.
814 wire dot_issue = state == DOT && gap == 2'd0;
815 wire upd_issue = state == UPD;
816 reg [511:0] y_q, k_q, v_q, c_q;
817 reg d_q, u_q, first_q, last_q;
818 reg [15:0] b_q;
819 reg [31:0] bc_q, decay_q;
820 always @(posedge clk) begin
821 y_q <= ybuf[b];
822 k_q <= kmem[e * BEATS + b];
823 v_q <= vmem[e * BEATS + b];
824 c_q <= cbuf[b];
825 d_q <= !rst && dot_issue;
826 u_q <= !rst && upd_issue;
827 first_q <= b == 16'd0;
828 last_q <= b == BEATS - 1;
829 b_q <= b;
830 bc_q <= bc;
831 decay_q <= decays[e];
832 end
833 reg [@BC@:1] d_d, last_d, first_d;
834 reg [@FOLDED@:1] u_d;
835 reg [15:0] b_d [1:@FOLDED@];
836 reg [511:0] c_d [1:@FMUL@];
837 reg [511:0] y_d [1:@FMUL@];
838 always @(posedge clk) begin
839 d_d <= rst ? {@BC@{1'b0}} : {d_d[@BC@-1:1], d_q};
840 last_d <= {last_d[@BC@-1:1], last_q};
841 first_d <= {first_d[@BC@-1:1], first_q};
842 u_d <= rst ? {@FOLDED@{1'b0}} : {u_d[@FOLDED@-1:1], u_q};
843 b_d[1] <= b_q;
844 for (i = 2; i <= @FOLDED@; i = i + 1) b_d[i] <= b_d[i-1];
845 c_d[1] <= c_q;
846 y_d[1] <= y_q;
847 for (i = 2; i <= @FMUL@; i = i + 1) begin c_d[i] <= c_d[i-1]; y_d[i] <= y_d[i-1]; end
848 end
849 // The lanes: the key's dot with the folded query, each lane's sum back at its add as its next term enters; and the
850 // update, carried + bc·v and decay·(y − bc·k).
851 wire [511:0] sums, cnew, ynew;
852 genvar l;
853 generate
854 for (l = 0; l < 16; l = l + 1) begin : lane
855 wire [31:0] p, cv, bk, t;
856 logos_fmul_p m(.clk(clk), .a(k_q[32*l +: 32]), .b(y_q[32*l +: 32]), .y(p));
857 logos_fadd_p a(.clk(clk), .a(first_d[@PRODUCTS@] ? 32'd0 : sums[32*l +: 32]), .b(p), .y(sums[32*l +: 32]));
858 logos_fmul_p mv(.clk(clk), .a(bc_q), .b(v_q[32*l +: 32]), .y(cv));
859 logos_fadd_p ac(.clk(clk), .a(c_d[@FMUL@][32*l +: 32]), .b(cv), .y(cnew[32*l +: 32]));
860 logos_fmul_p mk(.clk(clk), .a(bc_q), .b(k_q[32*l +: 32]), .y(bk));
861 logos_fadd_p sy(.clk(clk), .a(y_d[@FMUL@][32*l +: 32]), .b({~bk[31], bk[30:0]}), .y(t));
862 logos_fmul_p md(.clk(clk), .a(decay_q), .b(t), .y(ynew[32*l +: 32]));
863 end
864 endgenerate
865 // The dot through the tree and the gate's product; the collected values written back an add after their products,
866 // the query a multiply after that.
867 wire [31:0] dot, bcw;
868 logos_tree16_p tr(.clk(clk), .lanes(sums), .sum(dot));
869 logos_fmul_p mb(.clk(clk), .a(betas[e]), .b(dot), .y(bcw));
870 wire bc_in = d_d[@BC@] && last_d[@BC@];
871 wire c_back = u_d[@UPDATED@];
872 wire y_back = u_d[@FOLDED@];
873 always @(posedge clk) begin
874 if (state == IDLE && start) for (i = 0; i < N / 16; i = i + 1) begin ybuf[i] <= qmem[i]; cbuf[i] <= 512'd0; end
875 else begin
876 if (c_back) cbuf[b_d[@UPDATED@]] <= cnew;
877 if (y_back) ybuf[b_d[@FOLDED@]] <= ynew;
878 end
879 end
880 always @(posedge clk) begin
881 if (rst) begin state <= IDLE; e <= 8'd0; sent <= 8'd0; got <= 8'd0; b <= 16'd0; gap <= 2'd0; written <= 16'd0; end
882 else begin
883 case (state)
884 IDLE: if (start) begin sent <= 8'd0; got <= 8'd0; state <= EXPS; end
885 EXPS: begin
886 if (sent != entries) sent <= sent + 8'd1;
887 if (got == entries) begin
888 if (entries == 8'd0) state <= IDLE; else begin e <= entries - 8'd1; b <= 16'd0; gap <= 2'd0; state <= DOT; end
889 end
890 end
891 DOT: begin
892 gap <= gap + 2'd1;
893 if (gap == 2'd0) begin
894 if (b == BEATS - 1) begin b <= 16'd0; state <= BCW; end else b <= b + 16'd1;
895 end
896 end
897 BCW: if (bc_in) begin bc <= bcw; written <= 16'd0; state <= UPD; end
898 UPD: if (b == BEATS - 1) begin b <= 16'd0; state <= FILL; end else b <= b + 16'd1;
899 FILL: if (written == BEATS) begin
900 if (e == 8'd0) state <= IDLE; else begin e <= e - 8'd1; gap <= 2'd0; state <= DOT; end
901 end
902 endcase
903 if (ex_v[@EXP@]) got <= got + 8'd1;
904 if (y_back) written <= written + 16'd1;
905 end
906 end
907endmodule
908
909// A token row's lazily flushed Gated-DeltaNet block for the user clock: the log's count from its line; the row's entry —
910// its key heads, value heads, decay exponents and gates from the store at k_beat, v_beat, ld_beat and gt_beat, each
911// field's lanes past its elements zeroed — written after the log's entries as the state stores it, in whole lines, and
912// the rest once every write is written (`written`); then for each value head h (its key group h mod nk) the group's
913// query from the store and the head's log from memory into the fold, the query folded; each of its value rows from
914// memory through the read-out and, once the log holds `tokens` entries, the flush; the read-outs into the store a beat
915// at a time from out_beat, the flushed rows back where they were read; and the count written back, none once flushed.
916// The block's place is byte offsets within the row's region (`region`, a line): its memory, its log (the count's line,
917// then the entries), an entry's bytes, its fields' elements; the state in binary16 when f16, f32 otherwise. Memory is
918// logical 64-byte lines: a read's lines come back in order, at most OUTSTANDING of them owed. The store reads a clock
919// after its address. Busy until its last line is out. W is the read-out's and the flush's sixteens of elements a clock:
920// at 1 logos_gdn_read_p and logos_gdn_flush_p take half a binary16 line a clock, at 2 logos_gdn_read_w and
921// logos_gdn_flush_w take a whole one (two f32 lines a beat), the flush its log two of the fold's beats at a time.
922// MODE 0 is the whole block. MODE 1 is logos_gdn_lanes' coordinator: the count and the entry as MODE 0, then the log's
923// count entries read from its first line (log_first, log_lines lines) into the lanes' log buffer (lb_*), the count and
924// whether the row flushes given to the lanes (co_count, co_flush) while co_ready, and the count written back once
925// co_taken (every lane has taken the row); it keeps no fold, read-out or flush. It reads the store only while appending
926// (co_reading), and takes the next row once idle (co_idle). The row's region and store beats are taken at its start. A coordinator of a trailing flush of WINDOW tokens (MODE 1, WINDOW
927// above 0) keeps the log as a ring of 2·WINDOW slots, the count line's second word the slot of its oldest token: the
928// row's entry goes to the slot after its newest, the log is read into the buffer oldest token first, and once the log
929// holds a window past the first the row replays (co_flush) the first window into the share of each head's rows from
930// row co_lo (share count − WINDOW − 1 of HD / WINDOW rows); the count is written back with its oldest, a window
931// dropped once the log holds two.
932module logos_gdn_side #(parameter HD = 128, TMAX = 16, SA = 10, parameter [8:0] OUTSTANDING = 32, parameter W = 1,
933 parameter MODE = 0, parameter LBA = 13, WINDOW = 0, SRL = 1)
934 (input clk, input rst,
935 input start, output busy,
936 input [31:0] region, input [31:0] mem_off, input [31:0] log_off, input [31:0] entry, input [31:0] nk, input [31:0] nv,
937 input [31:0] fval, input [31:0] fdec, input [31:0] fgate, input [31:0] scale, input [7:0] tokens, input f16,
938 input [SA-1:0] q_beat, input [SA-1:0] out_beat, input [SA-1:0] k_beat, input [SA-1:0] v_beat, input [SA-1:0] ld_beat,
939 input [SA-1:0] gt_beat,
940 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,
941 output rq_valid, input rq_ready, output [31:0] rq_line, input rs_valid, input [511:0] rs_data,
942 output wq_valid, input wq_ready, output [31:0] wq_line, output [511:0] wq_data, input written,
943 output co_ready, input co_taken, output reg lb_we, output reg [LBA-1:0] lb_wa, output reg [511:0] lb_wd,
944 output [7:0] co_count, output co_flush, output [31:0] log_first, output [31:0] log_lines, output [15:0] co_lo,
945 output co_reading, output co_idle);
946 localparam [15:0] BEATS = HD / 16, HD_ROWS = HD;
947 localparam IDLE = 4'd0, QLOAD = 4'd1, LOG = 4'd2, WAITF = 4'd3, COPY = 4'd4, ROWS = 4'd5, DRAIN = 4'd6, COUNT = 4'd7,
948 APPEND = 4'd8, SETTLE = 4'd9, KEEP = 4'd10, LOGRD = 4'd11, LWAIT = 4'd12;
949 localparam K_DEC = 3'd0, K_BETA = 3'd1, K_KEY = 3'd2, K_VAL = 3'd3, K_ROW = 3'd4, K_COUNT = 3'd5, K_LOGB = 3'd6;
950 reg [3:0] state;
951 // The row's own fields, taken at its start: its region and the store beats of its vectors.
952 reg [31:0] rw_region;
953 reg [SA-1:0] rw_q, rw_out, rw_k, rw_v, rw_ld, rw_gt;
954 // The log's entries with the row's, and whether they flush the memory.
955 reg [7:0] count;
956 reg flush;
957 // A trailing flush's ring: the slot of the log's oldest token, and a slot past the ring's end brought back into it.
958 localparam [8:0] SLOTS2 = 2 * WINDOW;
959 localparam [7:0] WINDOW8 = WINDOW;
960 localparam [15:0] SHARE = WINDOW > 0 ? HD / WINDOW : 0;
961 reg [7:0] oldest;
962 function [7:0] ring(input [8:0] x);
963 reg [8:0] r;
964 begin
965 r = (x >= SLOTS2) ? x - SLOTS2 : x;
966 ring = r[7:0];
967 end
968 endfunction
969 reg [4:0] w_n;
970 assign busy = state != IDLE || w_n != 5'd0;
971 // The head whose rows are walked (h), the head whose query and log are (lh, its key group g): a token the log does
972 // not flush reads the next head's log and folds its query while this head's rows come back.
973 reg [31:0] h, g, lh;
974 // Log and row lines asked and not yet taken; whether this head's rows are still to be asked after the next head's
975 // log; the fold armed (its log asked), running, and done.
976 reg [9:0] l_owed, r_owed;
977 reg rows_due, fold_arm, fold_run, fold_done;
978 reg [7:0] ge;
979 reg [15:0] gi, row_req;
980 wire [47:0] base = {16'd0, rw_region} * 48'd64;
981 wire [31:0] eb = f16 ? 32'd2 : 32'd4;
982 wire [31:0] row_bytes = HD * eb;
983 wire [15:0] row_lines = row_bytes[21:6];
984 // The line a walk reads next: in the log, each token's decay exponent and gate, then its key's and values' beats in
985 // turn; in the rows, each row's lines.
986 wire [2:0] g_kind = gi == 16'd0 ? K_DEC : gi == 16'd1 ? K_BETA : gi[0] ? K_VAL : K_KEY;
987 wire [15:0] g_dj = (gi - 16'd2) >> 1;
988 wire [47:0] g_entry = base + log_off + 48'd64 + ge * entry;
989 wire [47:0] g_byte =
990 g_kind == K_DEC ? g_entry + (fdec + lh) * eb :
991 g_kind == K_BETA ? g_entry + (fgate + lh) * eb :
992 g_kind == K_KEY ? g_entry + (g * HD + 16 * g_dj) * eb :
993 g_entry + (fval + lh * HD + 16 * g_dj) * eb;
994 wire [47:0] r_byte = base + mem_off + (h * HD + row_req) * HD * eb + 64 * gi;
995 // The log's count line.
996 wire [47:0] c_byte = base + log_off;
997 // A coordinator's log, read line by line into the lanes' log buffer: its count entries from the first's line, a
998 // trailing one's oldest token first (lr_e its token, lr_l its line in it).
999 reg [15:0] lr_i, lr_l;
1000 reg [7:0] lr_e;
1001 wire [7:0] lr_slot = WINDOW > 0 ? ring({1'b0, oldest} + {1'b0, lr_e}) : lr_e;
1002 wire [31:0] lr_line = WINDOW > 0 ? {24'd0, lr_slot} * (entry >> 6) + {16'd0, lr_l} : {16'd0, lr_i};
1003 wire [47:0] lr_first_byte = base + log_off + 48'd64;
1004 assign log_first = lr_first_byte[37:6];
1005 assign log_lines = {8'd0, count} * (entry >> 6);
1006 assign co_count = count;
1007 assign co_flush = flush;
1008 assign co_lo = (WINDOW > 0 && flush) ? ({8'd0, count} - {8'd0, WINDOW8} - 16'd1) * SHARE : 16'd0;
1009 assign co_ready = MODE == 1 && state == LWAIT;
1010 assign co_idle = state == IDLE;
1011 wire lr_walk = MODE == 1 && state == LOGRD && {16'd0, lr_i} != log_lines;
1012 wire [47:0] req_byte = state == ROWS ? r_byte : state == COUNT ? c_byte : state == LOGRD ? {log_first + lr_line, 6'd0} : g_byte;
1013 wire log_walk = MODE != 1 && state == LOG && ge != count;
1014 wire row_walk = MODE != 1 && state == ROWS && row_req != HD_ROWS;
1015 reg c_asked;
1016 wire count_walk = state == COUNT && !c_asked;
1017 reg [9:0] owed;
1018 assign rq_valid = (log_walk || row_walk || count_walk || lr_walk) && owed < {1'b0, OUTSTANDING};
1019 assign rq_line = req_byte[37:6];
1020 wire rq_take = rq_valid && rq_ready;
1021 // Every read's tag — its kind, its token or row, its beat or line, its byte within the line — in order; and every line
1022 // as the side takes it, in order: an arriving line registered with its tag, a clock on its binary16 halves widened to
1023 // f32 — the low half's beat in lo, the high half's in hi — and the element at its tag's byte widened, the line itself
1024 // in lo where its elements are f32 or it is a count's or a log buffer's line.
1025 localparam QB = OUTSTANDING > 9'd32 ? $clog2(OUTSTANDING) + 1 : 6;
1026 localparam QD = 1 << QB;
1027 reg [37:0] tags [0:QD-1];
1028 reg [511:0] lo_q [0:QD-1];
1029 reg [511:0] hi_q [0:QD-1];
1030 reg [31:0] el_q [0:QD-1];
1031 reg [QB-1:0] t_wr, t_rd, l_in, l_wr;
1032 wire [37:0] t_new = state == ROWS ? {K_ROW, row_req[7:0], gi, req_byte[5:0], 5'd0} :
1033 state == COUNT ? {K_COUNT, 35'd0} : state == LOGRD ? {K_LOGB, 8'd0, lr_i, 11'd0} : {g_kind, ge, g_dj, req_byte[5:0], 5'd0};
1034 // The key group of the head after this one, its group's key heads over the row's.
1035 wire [31:0] g_after = (g + 32'd1 == nk) ? 32'd0 : g + 32'd1;
1036 // The arriving line, a clock on: its tag's kind and its element at the tag's byte, then its widened form.
1037 reg a1_v;
1038 reg [511:0] a1_d;
1039 reg [2:0] a1_kind;
1040 reg [31:0] a1_e;
1041 wire [37:0] tag_in = tags[l_in];
1042 wire [511:0] in_lo, in_hi;
1043 wire [31:0] in_e;
1044 genvar l;
1045 generate
1046 if (MODE == 1) begin : raw_lines
1047 assign in_lo = 512'd0;
1048 assign in_hi = 512'd0;
1049 assign in_e = 32'd0;
1050 end else begin : widened_lines
1051 for (l = 0; l < 16; l = l + 1) begin : widen
1052 logos_f16_to_f32 lo(.h(a1_d[16*l +: 16]), .x(in_lo[32*l +: 32]));
1053 logos_f16_to_f32 hi(.h(a1_d[256 + 16*l +: 16]), .x(in_hi[32*l +: 32]));
1054 end
1055 logos_f16_to_f32 we(.h(a1_e[15:0]), .x(in_e));
1056 end
1057 endgenerate
1058 wire a1_raw = !f16 || a1_kind == K_COUNT || a1_kind == K_LOGB;
1059 wire [511:0] in_lo_q = a1_raw ? a1_d : in_lo;
1060 wire [31:0] in_el = f16 ? in_e : a1_e;
1061 always @(posedge clk) begin
1062 if (rq_take) tags[t_wr] <= t_new;
1063 a1_v <= !rst && rs_valid;
1064 a1_d <= rs_data;
1065 a1_kind <= tag_in[37:35];
1066 a1_e <= f16 ? {16'd0, rs_data[8 * tag_in[10:5] +: 16]} : rs_data[8 * tag_in[10:5] +: 32];
1067 if (a1_v) begin lo_q[l_wr] <= in_lo_q; hi_q[l_wr] <= in_hi; el_q[l_wr] <= in_el; end
1068 end
1069 wire have = l_wr != t_rd;
1070 // The head's tag and line, read a clock ahead (below, once the head's pop is known).
1071 reg [37:0] tag;
1072 wire [2:0] t_kind = tag[37:35];
1073 wire [7:0] t_e = tag[34:27];
1074 wire [15:0] t_j = tag[26:11];
1075 wire [5:0] t_off = tag[10:5];
1076 wire [511:0] lo, hi;
1077 wire [31:0] elem;
1078 wire [37:0] tag_n;
1079 wire [511:0] beat_at = f16 ? (t_off[5] ? hi : lo) : lo;
1080 reg [31:0] ldec_h, beta_h;
1081 reg [511:0] key_h;
1082 // The fold, and the flush beside it: the head's log, written into both as it is read.
1083 reg f_log_wr, f_q_wr, f_start;
1084 reg [7:0] f_log_at;
1085 reg [15:0] f_log_beat, f_q_beat, f_rd_beat;
1086 reg [511:0] f_log_k, f_log_v, f_q_data;
1087 reg [31:0] f_log_decay, f_log_beta;
1088 wire f_busy;
1089 wire [511:0] f_y, f_c;
1090 wire [31:0] f_decay;
1091 generate
1092 if (MODE == 1) begin : coordinating_fold
1093 assign f_busy = 1'b0;
1094 assign f_y = 512'd0;
1095 assign f_c = 512'd0;
1096 assign f_decay = 32'd0;
1097 end else begin : folding
1098 logos_gdn_fold_p #(.N(HD), .T_MAX(TMAX)) fold(.clk(clk), .rst(rst), .log_wr(f_log_wr), .log_at(f_log_at),
1099 .log_beat(f_log_beat), .log_k(f_log_k), .log_v(f_log_v), .log_decay(f_log_decay), .log_beta(f_log_beta), .q_wr(f_q_wr),
1100 .q_beat(f_q_beat), .q_data(f_q_data), .start(f_start), .entries(count), .busy(f_busy), .rd_beat(f_rd_beat), .y_beat(f_y),
1101 .carried_beat(f_c), .rd_token(8'd0), .decay_out(f_decay));
1102 end
1103 endgenerate
1104 // The folded query and the collected values, held for the head's rows.
1105 reg [32*HD-1:0] ybuf;
1106 `LOGOS_RAMSTYLE_ASYNC(BEATS) reg [511:0] cbuf [0:BEATS-1];
1107 // A beat of the fold's out, copied in a clock after it is read: the folded query's and the collected values'.
1108 reg cp_v;
1109 reg [15:0] cp_at;
1110 reg [511:0] cp_y, cp_c;
1111 always @(posedge clk) begin
1112 cp_v <= !rst && state == COPY;
1113 cp_at <= f_rd_beat;
1114 cp_y <= f_y;
1115 cp_c <= f_c;
1116 if (cp_v) cbuf[cp_at] <= cp_c;
1117 end
1118 genvar cq;
1119 generate
1120 for (cq = 0; cq < BEATS; cq = cq + 1) begin : copy_in
1121 always @(posedge clk) if (cp_v && cp_at == cq) ybuf[512*cq +: 512] <= cp_y;
1122 end
1123 endgenerate
1124 // A row's lines a beat a clock to the read-out and, when flushing, the flush; its carried value with each beat (the
1125 // read-out takes it with a row's first).
1126 reg half;
1127 wire row_line = have && t_kind == K_ROW;
1128 wire [511:0] c_beat = cbuf[t_e >> 4];
1129 wire [31:0] row_carried = c_beat[32 * t_e[3:0] +: 32];
1130 wire r_ready, x_ready, r_ov, x_ov;
1131 wire [31:0] r_out;
1132 wire [512*W-1:0] x_out;
1133 wire [256*W-1:0] x_halves;
1134 // A row line into the units: at W 1 a beat a half line in binary16, a line in f32; at W 2 a binary16 line, or two f32
1135 // lines, the first held (row_lo) and not fed.
1136 wire held_line = W > 1 && !f16 && !half;
1137 wire feed = row_line && (held_line || (r_ready && (!flush || x_ready)));
1138 wire to_units = row_line && !held_line;
1139 wire last_half = W > 1 || !f16 || half;
1140 reg [511:0] row_lo;
1141 wire [512*W-1:0] row_beat;
1142 // Writes the flush's out-stream leaves: at W 2 in f32 two lines a beat, the second the clock after the first, so the
1143 // stream is asked for a beat every other clock. A beat's lines leave XD and XD + 1 clocks after the ask the stream
1144 // takes (its read, then its binary16 rounding), so whether the stream is asked next clock (x_ask, a register) is
1145 // decided on this clock's queue: it holds room for every line of every ask of the last XD + 1 clocks and this one,
1146 // whose lines may not be in it yet, and of the next — an ask's one line (half a line a beat at W 1 in binary16), or
1147 // at W 2 in f32 its two.
1148 localparam XD = 1 + @F16@;
1149 reg x_pend, x_tog, x_hi, x_ask;
1150 reg [255:0] x_low;
1151 reg [511:0] x_out_hi;
1152 reg [XD:0] x_hist;
1153 reg [4:0] x_way;
1154 integer xi;
1155 always @* begin
1156 x_way = {4'd0, x_ask};
1157 for (xi = 0; xi <= XD; xi = xi + 1) x_way = x_way + {4'd0, x_hist[xi]};
1158 end
1159 wire x_two = W > 1 && !f16;
1160 wire [5:0] x_owed = x_two ? {x_way, 1'b0} + 6'd2 : {1'b0, x_way} + 6'd1;
1161 // The flush's log at W 2: two of the fold's beats, key and values, the even held.
1162 reg fw_wr;
1163 reg [15:0] fw_beat;
1164 reg [511:0] lo_k, lo_v;
1165 reg [1023:0] fw_k, fw_v;
1166 // The flushed line written this clock, and whether one is.
1167 wire [511:0] x_line_data;
1168 wire x_line_push;
1169 generate
1170 if (MODE == 1) begin : coordinating
1171 assign row_beat = {512*W{1'b0}};
1172 assign r_ready = 1'b0;
1173 assign r_ov = 1'b0;
1174 assign r_out = 32'd0;
1175 assign x_ready = 1'b0;
1176 assign x_ov = 1'b0;
1177 assign x_out = {512*W{1'b0}};
1178 assign x_halves = {256*W{1'b0}};
1179 assign x_line_data = 512'd0;
1180 assign x_line_push = 1'b0;
1181 end else if (W == 1) begin : narrow
1182 assign row_beat = f16 ? (half ? hi : lo) : lo;
1183 logos_gdn_read_p #(.N(HD)) rd(.clk(clk), .rst(rst), .y(ybuf), .scale(scale), .in_valid(row_line && (!flush || x_ready)),
1184 .in_ready(r_ready), .in_beat(row_beat), .in_carried(row_carried), .out_valid(r_ov), .out_read(r_out));
1185 logos_gdn_flush_p #(.N(HD), .T_MAX(TMAX)) fl(.clk(clk), .rst(rst), .log_wr(f_log_wr), .log_at(f_log_at), .log_beat(f_log_beat),
1186 .log_k(f_log_k), .log_v(f_log_v), .log_decay(f_log_decay), .log_beta(f_log_beta), .entries(count), .f16(f16),
1187 .in_valid(flush && row_line && r_ready), .in_ready(x_ready), .in_beat(row_beat),
1188 .out_valid(x_ov), .out_ready(x_ask), .out_beat(x_out), .out_half(x_halves));
1189 assign x_line_data = f16 ? {x_halves, x_low} : x_out;
1190 assign x_line_push = x_ov && (!f16 || x_hi);
1191 end else begin : wide
1192 assign row_beat = f16 ? {hi, lo} : {lo, row_lo};
1193 logos_gdn_read_w #(.N(HD), .W(W)) rd(.clk(clk), .rst(rst), .y(ybuf), .scale(scale), .in_valid(to_units && (!flush || x_ready)),
1194 .in_ready(r_ready), .in_beat(row_beat), .in_carried(row_carried), .out_valid(r_ov), .out_read(r_out));
1195 logos_gdn_flush_w #(.N(HD), .W(W), .T_MAX(TMAX)) fl(.clk(clk), .rst(rst), .log_wr(fw_wr), .log_at(f_log_at),
1196 .log_beat(fw_beat), .log_k(fw_k[512*W-1:0]), .log_v(fw_v[512*W-1:0]), .log_decay(f_log_decay), .log_beta(f_log_beta),
1197 .entries(count), .f16(f16), .in_valid(flush && to_units && r_ready), .in_ready(x_ready), .in_beat(row_beat),
1198 .out_valid(x_ov), .out_ready(x_ask), .out_beat(x_out), .out_half(x_halves));
1199 assign x_line_data = f16 ? x_halves[511:0] : (x_pend ? x_out_hi : x_out[511:0]);
1200 assign x_line_push = x_ov || x_pend;
1201 end
1202 endgenerate
1203 // A tag's line consumed: a log line at once, a row's line with its last beat.
1204 wire pop = have && (t_kind != K_ROW || (feed && last_half));
1205 // The next head read into the head's registers every clock: the tag and line after this one when this one is
1206 // consumed, this one again when it is not; a tag or a line written the clock it is read taken as it is written.
1207 wire [QB-1:0] t_next = rst ? {QB{1'b0}} : t_rd + {{(QB-1){1'b0}}, pop};
1208 assign tag_n = (rq_take && t_wr == t_next) ? t_new : tags[t_next];
1209 wire landing = a1_v && l_wr == t_next;
1210 // The head's line: the queue's word at the next head, registered as block RAM reads it, or the line landing there
1211 // that clock, registered beside it.
1212 reg [511:0] lo_m, hi_m, lo_b, hi_b;
1213 reg [31:0] el_m, el_b;
1214 reg land_d;
1215 always @(posedge clk) begin
1216 tag <= tag_n;
1217 lo_m <= lo_q[t_next];
1218 hi_m <= hi_q[t_next];
1219 el_m <= el_q[t_next];
1220 lo_b <= in_lo_q;
1221 hi_b <= in_hi;
1222 el_b <= in_el;
1223 land_d <= landing;
1224 end
1225 assign lo = land_d ? lo_b : lo_m;
1226 assign hi = land_d ? hi_b : hi_m;
1227 assign elem = land_d ? el_b : el_m;
1228 // The flushed rows packed into lines, two beats a line in binary16 (the flush's stored halves) and one in f32, and
1229 // queued for memory.
1230 `LOGOS_RAMSTYLE_ASYNC(16) reg [511:0] wfifo_d [0:15];
1231 `LOGOS_RAMSTYLE_ASYNC(16) reg [31:0] wfifo_l [0:15];
1232 reg [3:0] w_wr, w_rd;
1233 // The row's log entry from the store a beat a cycle — its key heads, value heads, decay exponents and gates, each
1234 // field's lanes past its elements zeroed — packed into whole lines from the entry's first, as the state stores them
1235 // (a head a multiple of 32 wide, so a binary16 entry is an even number of beats, two a line), and the log's count line
1236 // once the row is done.
1237 wire [31:0] k_beats = (nk * HD) >> 4, v_beats = (nv * HD) >> 4, s_beats = (nv + 32'd15) >> 4;
1238 wire [31:0] e_beats = k_beats + v_beats + 2 * s_beats;
1239 reg [31:0] ab, a_line;
1240 reg a_pend, a_hi;
1241 reg [4:0] a_lanes;
1242 // An entry's beat asked (a_pend and its field's lanes, the clock after its address) and out of the store SRL clocks
1243 // after its address (a_lo, a_lo_lanes).
1244 wire a_lo;
1245 wire [4:0] a_lo_lanes;
1246 wire a_flight;
1247 logos_landing #(.W(5), .D(SRL - 1)) aland(.clk(clk), .rst(rst), .in_v(a_pend), .in_d(a_lanes), .out_v(a_lo),
1248 .out_d(a_lo_lanes), .flight(a_flight));
1249 reg [255:0] a_low;
1250 // Beats asked of the store and not yet out of the encoder: each one line's room in the queue, held from its asking.
1251 reg [2:0] a_fly;
1252 wire a_key = ab < k_beats, a_val = !a_key && ab < k_beats + v_beats, a_dec = !a_key && !a_val && ab < k_beats + v_beats + s_beats;
1253 wire [31:0] a_off = a_key ? ab : a_val ? ab - k_beats : a_dec ? ab - k_beats - v_beats : ab - k_beats - v_beats - s_beats;
1254 wire [31:0] a_len = (a_key ? nk * HD : a_val ? nv * HD : nv) - 16 * a_off;
1255 wire [SA-1:0] a_ra = (a_key ? rw_k : a_val ? rw_v : a_dec ? rw_ld : rw_gt) + a_off[SA-1:0];
1256 wire a_issue = state == APPEND && ab != e_beats && w_n + {2'd0, a_fly} < 5'd6;
1257 assign co_reading = MODE == 1 && a_issue;
1258 // A beat read: its lanes past the field's elements zeroed, registered, then in binary16 @F16@ clocks on
1259 // (logos_f32_to_f16_p), the beat carried beside and its valid in registers the reset clears.
1260 wire [511:0] a_lanes_in;
1261 reg [511:0] a_masked;
1262 reg a_m;
1263 wire a_v;
1264 wire [511:0] a_beat;
1265 wire [255:0] a_halves;
1266 generate
1267 for (l = 0; l < 16; l = l + 1) begin : field
1268 assign a_lanes_in[32*l +: 32] = l < a_lo_lanes ? st_rd[32*l +: 32] : 32'd0;
1269 end
1270 for (l = 0; l < 16; l = l + 1) begin : encode
1271 logos_f32_to_f16_p n(.clk(clk), .x(a_masked[32*l +: 32]), .h(a_halves[16*l +: 16]));
1272 end
1273 endgenerate
1274 logos_delay #(.W(512), .D(@F16@)) adl(.clk(clk), .d(a_masked), .q(a_beat));
1275 generate
1276 endgenerate
1277 reg av_d [1:@F16@];
1278 integer ak;
1279 always @(posedge clk) begin
1280 av_d[1] <= !rst && a_m;
1281 for (ak = 2; ak <= @F16@; ak = ak + 1) av_d[ak] <= !rst && av_d[ak-1];
1282 end
1283 assign a_v = av_d[@F16@];
1284 wire a_push = a_v && (!f16 || a_hi);
1285 wire [511:0] a_data = f16 ? {a_halves, a_low} : a_beat;
1286 wire [7:0] a_slot = WINDOW > 0 ? ring({1'b0, oldest} + {1'b0, count}) : count;
1287 wire [47:0] a_byte = base + log_off + 48'd64 + a_slot * entry + 64 * a_line;
1288 wire c_push = state == KEEP && w_n < 5'd16;
1289 // What a trailing flush's count line keeps: a window dropped, and its oldest a window on, once the log holds two.
1290 wire dropping = WINDOW > 0 && {1'b0, count} == SLOTS2;
1291 wire [7:0] keep_count = dropping ? count - WINDOW8 : count;
1292 wire [7:0] keep_oldest = dropping ? ring({1'b0, oldest} + {1'b0, WINDOW8}) : oldest;
1293 reg [15:0] x_row, x_line;
1294 wire [47:0] x_byte = base + mem_off + (h * HD + x_row) * HD * eb + 64 * x_line;
1295 wire x_push = x_line_push;
1296 assign wq_valid = w_n != 5'd0;
1297 assign wq_line = wfifo_l[w_rd];
1298 assign wq_data = wfifo_d[w_rd];
1299 wire w_pop = wq_valid && wq_ready;
1300 // The read-outs gathered into store beats.
1301 reg [511:0] o_beat;
1302 // The read-outs come out head by head behind the walk, which reads the next head's log once a head's rows are asked:
1303 // the head and row the next read-out is of.
1304 reg [31:0] oh;
1305 reg [15:0] o_row;
1306 wire [31:0] o_wa = rw_out + ((oh * HD + o_row) >> 4);
1307 // The store's query beats while a head's query loads.
1308 reg [15:0] qj, q_at;
1309 reg q_pending;
1310 // A query beat asked (q_pending and its beat, the clock after its address) and out of the store SRL clocks after its
1311 // address (q_lo, q_lo_at); the head's fold waits until none is on its way.
1312 wire q_lo, q_flight;
1313 wire [15:0] q_lo_at;
1314 logos_landing #(.W(16), .D(SRL - 1)) qland(.clk(clk), .rst(rst), .in_v(q_pending), .in_d(q_at), .out_v(q_lo),
1315 .out_d(q_lo_at), .flight(q_flight));
1316 wire [31:0] q_ra = rw_q + ((g * HD) >> 4) + qj;
1317 assign st_ra = state == APPEND ? a_ra : q_ra[SA-1:0];
1318 always @(posedge clk) begin
1319 f_log_wr <= 1'b0; f_q_wr <= 1'b0; f_start <= 1'b0; st_we <= 1'b0; q_pending <= 1'b0; a_pend <= 1'b0; fw_wr <= 1'b0;
1320 lb_we <= 1'b0;
1321 x_tog <= !x_tog;
1322 x_hist <= {x_hist[XD-1:0], x_ask};
1323 x_ask <= !rst && (!x_two || !x_tog) && {1'b0, w_n} + x_owed <= 6'd16;
1324 if (rq_take) t_wr <= t_wr + 1'b1;
1325 if (rs_valid) l_in <= l_in + 1'b1;
1326 if (a1_v) l_wr <= l_wr + 1'b1;
1327 owed <= owed + (rq_take ? 10'd1 : 10'd0) - (pop ? 10'd1 : 10'd0);
1328 l_owed <= l_owed + ((rq_take && state == LOG) ? 10'd1 : 10'd0) - ((have && t_kind != K_ROW && t_kind != K_COUNT) ? 10'd1 : 10'd0);
1329 r_owed <= r_owed + ((rq_take && state == ROWS) ? 10'd1 : 10'd0) - ((pop && t_kind == K_ROW) ? 10'd1 : 10'd0);
1330 // The fold starts once its head's log lines are all in, and is done when it falls idle after starting.
1331 if (fold_arm && l_owed == 10'd0) begin f_start <= 1'b1; fold_arm <= 1'b0; fold_run <= 1'b1; end
1332 if (fold_run && !f_start && !f_busy) begin fold_run <= 1'b0; fold_done <= 1'b1; end
1333 // The log's lines: the decay exponent and the gate held, a key beat held, a value beat written with it into the
1334 // fold and the flush.
1335 if (have && t_kind != K_ROW) begin
1336 t_rd <= t_rd + 1'b1;
1337 case (t_kind)
1338 K_DEC: ldec_h <= elem;
1339 K_BETA: beta_h <= elem;
1340 K_KEY: key_h <= beat_at;
1341 K_COUNT: begin count <= lo[7:0]; oldest <= WINDOW > 0 ? lo[39:32] : 8'd0; end
1342 K_LOGB: begin lb_we <= 1'b1; lb_wa <= t_j[LBA-1:0]; lb_wd <= lo; end
1343 default: begin
1344 f_log_wr <= 1'b1; f_log_at <= t_e; f_log_beat <= t_j; f_log_k <= key_h; f_log_v <= beat_at;
1345 f_log_decay <= ldec_h; f_log_beta <= beta_h;
1346 if (!t_j[0]) begin lo_k <= key_h; lo_v <= beat_at; end
1347 else begin fw_wr <= W > 1; fw_beat <= t_j >> 1; fw_k <= {key_h, lo_k}; fw_v <= {beat_at, lo_v}; end
1348 end
1349 endcase
1350 end
1351 if (feed) begin
1352 if (W > 1) begin
1353 t_rd <= t_rd + 1'b1;
1354 if (!f16) begin half <= !half; if (held_line) row_lo <= lo; end
1355 end else if (last_half) begin half <= 1'b0; t_rd <= t_rd + 1'b1; end else half <= 1'b1;
1356 end
1357 if (x_line_push) begin
1358 wfifo_d[w_wr] <= x_line_data;
1359 wfifo_l[w_wr] <= x_byte[37:6];
1360 w_wr <= w_wr + 4'd1;
1361 if (x_line + 16'd1 == row_lines) begin x_line <= 16'd0; x_row <= x_row + 16'd1; end else x_line <= x_line + 16'd1;
1362 end
1363 if (W == 1 && x_ov) begin
1364 if (f16 && !x_hi) begin x_low <= x_halves[255:0]; x_hi <= 1'b1; end else x_hi <= 1'b0;
1365 end
1366 x_pend <= W > 1 && x_ov && !f16;
1367 if (x_ov) x_out_hi <= x_out[512*W-1:512*W-512];
1368 if (a_issue) begin a_pend <= 1'b1; a_lanes <= a_len >= 32'd16 ? 5'd16 : a_len[4:0]; ab <= ab + 32'd1; end
1369 a_m <= !rst && a_lo;
1370 a_masked <= a_lanes_in;
1371 a_fly <= a_fly + (a_issue ? 3'd1 : 3'd0) - (a_v ? 3'd1 : 3'd0);
1372 if (a_v && f16 && !a_hi) begin a_low <= a_halves; a_hi <= 1'b1; end
1373 if (a_push) begin
1374 a_hi <= 1'b0;
1375 wfifo_d[w_wr] <= a_data;
1376 wfifo_l[w_wr] <= a_byte[37:6];
1377 w_wr <= w_wr + 4'd1;
1378 a_line <= a_line + 32'd1;
1379 end
1380 if (c_push) begin
1381 wfifo_d[w_wr] <= WINDOW > 0 ? {472'd0, keep_oldest, 24'd0, keep_count} : {504'd0, flush ? 8'd0 : count};
1382 wfifo_l[w_wr] <= c_byte[37:6];
1383 w_wr <= w_wr + 4'd1;
1384 end
1385 if (w_pop) w_rd <= w_rd + 4'd1;
1386 w_n <= w_n + ((x_push || a_push || c_push) ? 5'd1 : 5'd0) - (w_pop ? 5'd1 : 5'd0);
1387 if (r_ov) begin
1388 o_beat[32 * o_row[3:0] +: 32] <= r_out;
1389 if (o_row[3:0] == 4'd15) begin st_we <= 1'b1; st_wa <= o_wa[SA-1:0]; st_wd <= {r_out, o_beat[479:0]}; end
1390 if (o_row + 16'd1 == HD_ROWS) begin o_row <= 16'd0; oh <= oh + 32'd1; end else o_row <= o_row + 16'd1;
1391 end
1392 if (q_lo) begin f_q_wr <= 1'b1; f_q_beat <= q_lo_at; f_q_data <= st_rd; end
1393 case (state)
1394 IDLE: if (start) begin
1395 rw_region <= region; rw_q <= q_beat; rw_out <= out_beat; rw_k <= k_beat; rw_v <= v_beat; rw_ld <= ld_beat; rw_gt <= gt_beat;
1396 qj <= 16'd0; c_asked <= 1'b0; o_row <= 16'd0; rows_due <= 1'b0;
1397 h <= 32'd0; lh <= 32'd0; g <= 32'd0; oh <= 32'd0; state <= COUNT;
1398 end
1399 // The count, then the row's entry after it; the log's lines read once the entry is written.
1400 COUNT: begin
1401 if (rq_take) c_asked <= 1'b1;
1402 if (c_asked && owed == 10'd0) begin ab <= 32'd0; a_line <= 32'd0; a_hi <= 1'b0; state <= APPEND; end
1403 end
1404 APPEND: if (ab == e_beats && a_fly == 3'd0) state <= SETTLE;
1405 SETTLE: if (w_n == 4'd0 && written) begin
1406 count <= count + 8'd1; flush <= WINDOW > 0 ? count + 8'd1 > WINDOW8 : count + 8'd1 >= tokens;
1407 lr_i <= 16'd0; lr_e <= 8'd0; lr_l <= 16'd0; state <= (MODE == 1) ? LOGRD : QLOAD;
1408 end
1409 // A coordinator's log into the lanes' buffer, every line in; the row handed to every lane; then the count.
1410 LOGRD: begin
1411 if (rq_take) begin
1412 lr_i <= lr_i + 16'd1;
1413 if ({16'd0, lr_l} + 32'd1 == (entry >> 6)) begin lr_l <= 16'd0; lr_e <= lr_e + 8'd1; end else lr_l <= lr_l + 16'd1;
1414 end
1415 if ({16'd0, lr_i} == log_lines && owed == 10'd0) state <= LWAIT;
1416 end
1417 LWAIT: if (co_taken) state <= KEEP;
1418 KEEP: if (c_push) state <= IDLE;
1419 // Head lh's query into the fold, then its log asked; then, when head h's rows are due, they are asked behind it.
1420 QLOAD: begin
1421 if (qj == 16'd0) begin x_row <= 16'd0; x_line <= 16'd0; x_hi <= 1'b0; end
1422 if (qj == BEATS) begin qj <= 16'd0; ge <= 8'd0; gi <= 16'd0; state <= LOG; end
1423 else begin q_pending <= 1'b1; q_at <= qj; qj <= qj + 16'd1; end
1424 end
1425 LOG: begin
1426 if (rq_take) begin
1427 if (gi == 16'd1 + 2 * BEATS) begin gi <= 16'd0; ge <= ge + 8'd1; end else gi <= gi + 16'd1;
1428 end
1429 if (ge == count && (SRL == 1 || (!q_pending && !q_flight))) begin
1430 fold_arm <= 1'b1; gi <= 16'd0; row_req <= 16'd0; state <= rows_due ? ROWS : WAITF;
1431 end
1432 end
1433 // Head lh's fold done and head h's rows all taken: the folded query and the collected values copied in.
1434 WAITF: if (fold_done && r_owed == 10'd0) begin fold_done <= 1'b0; f_rd_beat <= 16'd0; state <= COPY; end
1435 COPY: begin
1436 if (f_rd_beat + 16'd1 == BEATS) begin
1437 f_rd_beat <= 16'd0; h <= lh;
1438 if (!flush && lh + 32'd1 < nv) begin
1439 lh <= lh + 32'd1; g <= g_after; qj <= 16'd0; rows_due <= 1'b1; state <= QLOAD;
1440 end else begin gi <= 16'd0; row_req <= 16'd0; state <= ROWS; end
1441 end else f_rd_beat <= f_rd_beat + 16'd1;
1442 end
1443 // Head h's rows asked: behind the next head's log, then its fold awaited; or, the last head's or a flushed one's,
1444 // then its read-outs and flushed rows in DRAIN.
1445 ROWS: begin
1446 if (rq_take) begin
1447 if (gi + 16'd1 == row_lines) begin gi <= 16'd0; row_req <= row_req + 16'd1; end else gi <= gi + 16'd1;
1448 end
1449 if (row_req == HD_ROWS) begin
1450 if (rows_due) begin rows_due <= 1'b0; state <= WAITF; end
1451 else if (owed == 10'd0) state <= DRAIN;
1452 end
1453 end
1454 DRAIN: if (oh == h + 32'd1 && (!flush || (x_row == HD_ROWS && w_n == 4'd0))) begin
1455 if (h + 32'd1 >= nv) state <= KEEP;
1456 else begin lh <= h + 32'd1; g <= g_after; qj <= 16'd0; state <= QLOAD; end
1457 end
1458 default: state <= IDLE;
1459 endcase
1460 if (rst) begin
1461 state <= IDLE; t_wr <= {QB{1'b0}}; t_rd <= {QB{1'b0}}; l_in <= {QB{1'b0}}; l_wr <= {QB{1'b0}}; w_wr <= 4'd0; w_rd <= 4'd0; w_n <= 5'd0; half <= 1'b0;
1462 owed <= 10'd0; a_fly <= 3'd0; x_pend <= 1'b0; x_hist <= {(XD+1){1'b0}}; x_tog <= 1'b0; l_owed <= 10'd0; r_owed <= 10'd0;
1463 fold_arm <= 1'b0; fold_run <= 1'b0; fold_done <= 1'b0;
1464 end
1465 end
1466endmodule
1467"#;
1468
1469const GDN_LANE_VERILOG: &str = r#"
1471// A head lane of logos_gdn_lanes: value heads first, first + STEP, … below nv of a token row whose log is in one of the
1472// lane's two copies of it (lb_ra, a line the clock after its address; each copy LBA bits of address, the second
1473// COPY_LINES lines in) with `count` entries, flushing the memory when `flush`. A
1474// loader takes the heads a batch at a time — H heads of an ordinary token (logos_gdn_fold_m's slots), one of a
1475// flushing token — each head's query from the store on the clocks st_gnt grants the read it asks (st_rq) and its log
1476// from the copy a line a clock into its slot (and, flushing, into the flush), then folds the batch into a bank; the
1477// next batch is loaded and folded into the other bank while this one's rows stream (a flushing token's after its head
1478// is flushed, the flush holding one head's log). A streamer takes each head of a folded batch: its folded query and
1479// collected values copied out of the bank, then its rows from memory (rq/rs, in the order asked, at most OUTSTANDING
1480// lines owed, no wait taken on an answer) through the read-out and, flushing, the flush, the flushed rows back where
1481// they were read (wq); each sixteen rows' read-outs leave as a store beat (st_we). Busy from its start until its last
1482// read-out and flushed line are out.
1483module logos_gdn_lane #(parameter HD = 128, TMAX = 16, SA = 10, parameter [8:0] OUTSTANDING = 32, parameter W = 1,
1484 parameter [31:0] STEP = 4, parameter LBA = 13, FLUSHES = 1, COPY_LINES = 4096, SRL = 1)
1485 (input clk, input rst,
1486 input start, output busy, output room, output reg done,
1487 input [31:0] in_region, input [31:0] mem_off, input [31:0] entry, input [31:0] nk, input [31:0] nv,
1488 input [31:0] fval, input [31:0] fdec, input [31:0] fgate, input [31:0] scale, input f16,
1489 input [SA-1:0] in_q_beat, input [SA-1:0] in_out_beat,
1490 input [31:0] first, input [7:0] in_count, input in_flush, input in_par,
1491 output st_rq, input st_gnt, output [SA-1:0] st_ra, input [511:0] st_rd,
1492 output reg st_we, output reg [SA-1:0] st_wa, output reg [511:0] st_wd,
1493 output [LBA:0] lb_ra, input [511:0] lb_rd,
1494 output rq_valid, input rq_ready, output [31:0] rq_line, input rs_valid, input [511:0] rs_data,
1495 output wq_valid, input wq_ready, output [31:0] wq_line, output [511:0] wq_data);
1496 localparam H = @FADD@;
1497 localparam [15:0] BEATS = HD / 16, HD_ROWS = HD;
1498 localparam K_DEC = 2'd0, K_BETA = 2'd1, K_KEY = 2'd2, K_VAL = 2'd3;
1499 // The row's fields, taken at its start: its region, its query's and read-outs' store beats, its log's count, whether it
1500 // replays, and the copy of the log it reads (its first line COPY_LINES lines in for the second).
1501 reg [31:0] region;
1502 reg [SA-1:0] q_beat, out_beat;
1503 reg [7:0] count;
1504 reg flush, par;
1505 always @(posedge clk)
1506 if (start) begin region <= in_region; q_beat <= in_q_beat; out_beat <= in_out_beat; count <= in_count; flush <= in_flush; par <= in_par; end
1507 wire [47:0] base = {16'd0, region} * 48'd64;
1508 wire [31:0] eb = f16 ? 32'd2 : 32'd4;
1509 wire [31:0] row_bytes = HD * eb;
1510 wire [15:0] row_lines = row_bytes[21:6];
1511 localparam [7:0] HS = H;
1512 wire [7:0] bs = flush ? 8'd1 : HS;
1513 // The key group a head's step moves by, and the first head's, each a register of the job's fields.
1514 function [31:0] step_mod(input [31:0] k);
1515 reg [31:0] r;
1516 integer si;
1517 begin
1518 r = STEP;
1519 for (si = 0; si < 4; si = si + 1) if (r >= k) r = r - k;
1520 step_mod = r;
1521 end
1522 endfunction
1523 reg [31:0] g_step, first_g;
1524 always @(posedge clk) begin
1525 g_step <= step_mod(nk);
1526 first_g <= (first >= nk) ? ((first - nk >= nk) ? ((first - 32'd2 * nk >= nk) ? first - 32'd3 * nk : first - 32'd2 * nk) : first - nk) : first;
1527 end
1528 reg running;
1529 // Batches: loaded (their folds started), folded (finished), copied out (their bank free) and drained (every row of
1530 // theirs through, flushed rows written).
1531 reg [7:0] loaded, folded, copied, drained;
1532 // ── The loader ──────────────────────────────────────────────────────────────────────────────────────────────────
1533 localparam [2:0] L_IDLE = 3'd0, L_Q = 3'd1, L_LOG = 3'd2, L_TAIL = 3'd3, L_FOLD = 3'd4, L_DONE = 3'd5;
1534 reg [2:0] ls;
1535 reg [31:0] lh, lg;
1536 reg [7:0] lslot;
1537 reg [15:0] qj;
1538 reg q_pending;
1539 reg [15:0] q_at;
1540 // A query beat asked (q_pending, its beat and slot, the clock after its address) and out of the store SRL clocks after
1541 // its address (q_lo, q_lo_at, q_lo_slot); a batch's fold waits until none is on its way.
1542 wire q_lo, q_flight;
1543 wire [15:0] q_lo_at;
1544 wire [7:0] q_lo_slot;
1545 logos_landing #(.W(24), .D(SRL - 1)) qland(.clk(clk), .rst(rst), .in_v(q_pending), .in_d({q_at, lslot}), .out_v(q_lo),
1546 .out_d({q_lo_at, q_lo_slot}), .flight(q_flight));
1547 wire f_busy;
1548 reg f_start, f_run;
1549 wire can_load = !f_busy && !f_start && !f_run && (flush ? loaded == drained : loaded - copied < 8'd2);
1550 wire [31:0] q_ra = q_beat + ((lg * HD) >> 4) + qj;
1551 assign st_rq = ls == L_Q && qj != BEATS;
1552 assign st_ra = q_ra[SA-1:0];
1553 // The log walk of head lh (key group lg): for each entry its decay exponent's line, its gate's, then its key's and its
1554 // values' beats in turn; each line's place in the copy and the byte of its field within it.
1555 reg [7:0] ge;
1556 reg [15:0] gi;
1557 reg [31:0] e_at;
1558 wire [1:0] g_kind = gi == 16'd0 ? K_DEC : gi == 16'd1 ? K_BETA : gi[0] ? K_VAL : K_KEY;
1559 wire [15:0] g_dj = (gi - 16'd2) >> 1;
1560 wire [31:0] g_off = g_kind == K_DEC ? (fdec + lh) * eb : g_kind == K_BETA ? (fgate + lh) * eb :
1561 g_kind == K_KEY ? (lg * HD + 16 * g_dj) * eb : (fval + lh * HD + 16 * g_dj) * eb;
1562 wire [31:0] g_byte = e_at + g_off;
1563 wire walking = ls == L_LOG && ge != count;
1564 assign lb_ra = g_byte[LBA+6:6];
1565 // A copy line's tag beside it: its kind, entry, beat and byte; the line a clock after its address, registered with its
1566 // tag the clock after that, then widened.
1567 reg w1_v, w2_v;
1568 reg [1:0] w1_k, w2_k;
1569 reg [7:0] w1_e, w2_e;
1570 reg [15:0] w1_j, w2_j;
1571 reg [5:0] w1_o, w2_o;
1572 reg [511:0] w2_d;
1573 wire [511:0] w_lo, w_hi;
1574 wire [31:0] w_e16;
1575 genvar l;
1576 generate
1577 for (l = 0; l < 16; l = l + 1) begin : walk_widen
1578 logos_f16_to_f32 lo(.h(w2_d[16*l +: 16]), .x(w_lo[32*l +: 32]));
1579 logos_f16_to_f32 hi(.h(w2_d[256 + 16*l +: 16]), .x(w_hi[32*l +: 32]));
1580 end
1581 endgenerate
1582 wire [31:0] w_raw = w2_d[8 * w2_o +: 32];
1583 logos_f16_to_f32 we(.h(w2_d[8 * w2_o +: 16]), .x(w_e16));
1584 wire [511:0] w_beat = f16 ? (w2_o[5] ? w_hi : w_lo) : w2_d;
1585 wire [31:0] w_elem = f16 ? w_e16 : w_raw;
1586 always @(posedge clk) begin
1587 w1_v <= !rst && walking;
1588 w1_k <= g_kind; w1_e <= ge; w1_j <= g_dj; w1_o <= g_byte[5:0];
1589 w2_v <= !rst && w1_v;
1590 w2_k <= w1_k; w2_e <= w1_e; w2_j <= w1_j; w2_o <= w1_o; w2_d <= lb_rd;
1591 end
1592 // The fold's and the flush's log writes: a value beat with the key beat, decay exponent and gate before it.
1593 reg [31:0] ldec_h, beta_h;
1594 reg [511:0] key_h;
1595 reg f_log_wr, f_q_wr, fw_wr;
1596 reg [7:0] f_log_at, f_q_slot;
1597 reg [15:0] f_log_beat, f_q_beat, fw_beat;
1598 reg [511:0] f_log_k, f_log_v, f_q_data, lo_k, lo_v;
1599 reg [1023:0] fw_k, fw_v;
1600 reg [31:0] f_log_decay, f_log_beta;
1601 reg fold_bank;
1602 // ── The streamer ────────────────────────────────────────────────────────────────────────────────────────────────
1603 localparam [2:0] S_IDLE = 3'd0, S_COPY = 3'd1, S_ROWS = 3'd2, S_NEXT = 3'd3, S_DRAIN = 3'd4, S_DONE = 3'd5;
1604 reg [2:0] ss;
1605 reg [31:0] sh;
1606 reg [7:0] sslot;
1607 reg [15:0] cb;
1608 wire [511:0] f_y, f_c;
1609 wire [31:0] f_decay;
1610 logos_gdn_fold_m #(.N(HD), .T_MAX(TMAX)) fold(.clk(clk), .rst(rst), .log_wr(f_log_wr), .log_slot(lslot), .log_at(f_log_at),
1611 .log_beat(f_log_beat), .log_k(f_log_k), .log_v(f_log_v), .log_decay(f_log_decay), .log_beta(f_log_beta), .q_wr(f_q_wr),
1612 .q_slot(f_q_slot), .q_beat(f_q_beat), .q_data(f_q_data), .start(f_start), .bank(fold_bank), .entries(count), .older(8'd0),
1613 .busy(f_busy), .rd_bank(copied[0]), .rd_slot(sslot), .rd_beat(cb), .y_beat(f_y), .carried_beat(f_c),
1614 .rd_token(8'd0), .decay_out(f_decay));
1615 // The streamed head's folded query and collected values, into buffer sb, the buffer the head before did not take: a
1616 // beat of each asked a clock (cb), out of the fold's bank two clocks after, copied in the clock after that; the copy
1617 // over once its last beat is in.
1618 reg sb;
1619 reg [32*HD-1:0] ybuf0, ybuf1;
1620 `LOGOS_RAMSTYLE_ASYNC(BEATS) reg [511:0] cbuf0 [0:BEATS-1];
1621 `LOGOS_RAMSTYLE_ASYNC(BEATS) reg [511:0] cbuf1 [0:BEATS-1];
1622 reg cp_a, cp_m, cp_v, cp_b;
1623 reg [15:0] cp_ab, cp_mb, cp_at;
1624 reg [511:0] cp_y, cp_c;
1625 always @(posedge clk) begin
1626 cp_a <= !rst && ss == S_COPY && cb < BEATS;
1627 cp_ab <= cb;
1628 cp_m <= !rst && cp_a;
1629 cp_mb <= cp_ab;
1630 cp_v <= !rst && cp_m;
1631 cp_at <= cp_mb;
1632 cp_b <= sb;
1633 cp_y <= f_y;
1634 cp_c <= f_c;
1635 if (cp_v && !cp_b) cbuf0[cp_at] <= cp_c;
1636 if (cp_v && cp_b) cbuf1[cp_at] <= cp_c;
1637 end
1638 genvar cq;
1639 generate
1640 for (cq = 0; cq < BEATS; cq = cq + 1) begin : copy_in
1641 always @(posedge clk) begin
1642 if (cp_v && !cp_b && cp_at == cq) ybuf0[512*cq +: 512] <= cp_y;
1643 if (cp_v && cp_b && cp_at == cq) ybuf1[512*cq +: 512] <= cp_y;
1644 end
1645 end
1646 endgenerate
1647 // The rows asked: each its row; lines answered in order, registered as they come, binary16 halves widened a clock
1648 // on and registered, then into the lines.
1649 localparam QB = OUTSTANDING > 9'd32 ? $clog2(OUTSTANDING) + 1 : 6;
1650 localparam QD = 1 << QB;
1651 reg [15:0] row_req, ri;
1652 reg [8:0] t_row [0:QD-1];
1653 reg [511:0] lo_q [0:QD-1];
1654 reg [511:0] hi_q [0:QD-1];
1655 reg [QB-1:0] t_wr, t_rd, l_in, l_wr;
1656 // The lines asked and not yet through the read-out, and of them those of each buffer's head.
1657 reg [9:0] owed, owed_b0, owed_b1;
1658 wire [47:0] r_byte = base + mem_off + (sh * HD + row_req) * HD * eb + 64 * ri;
1659 wire row_walk = ss == S_ROWS && row_req != HD_ROWS;
1660 assign rq_valid = row_walk && owed < {1'b0, OUTSTANDING};
1661 assign rq_line = r_byte[37:6];
1662 wire rq_take = rq_valid && rq_ready;
1663 reg a1_v;
1664 reg [511:0] a1_d;
1665 wire [511:0] in_lo, in_hi;
1666 generate
1667 for (l = 0; l < 16; l = l + 1) begin : row_widen
1668 logos_f16_to_f32 lo(.h(a1_d[16*l +: 16]), .x(in_lo[32*l +: 32]));
1669 logos_f16_to_f32 hi(.h(a1_d[256 + 16*l +: 16]), .x(in_hi[32*l +: 32]));
1670 end
1671 endgenerate
1672 wire [511:0] in_lo_q = f16 ? in_lo : a1_d;
1673 // A line widened, registered (a2) before its queue and the head's bypass take it.
1674 (* maxfan = 64 *) reg a2_v;
1675 reg [511:0] a2_lo, a2_hi;
1676 always @(posedge clk) begin
1677 if (rq_take) t_row[t_wr] <= {sb, row_req[7:0]};
1678 a1_v <= !rst && rs_valid;
1679 a1_d <= rs_data;
1680 a2_v <= !rst && a1_v;
1681 a2_lo <= in_lo_q;
1682 a2_hi <= in_hi;
1683 if (a2_v) begin lo_q[l_wr] <= a2_lo; hi_q[l_wr] <= a2_hi; end
1684 end
1685 wire have = l_wr != t_rd;
1686 reg [7:0] tr;
1687 reg tb;
1688 wire [511:0] lo, hi;
1689 // A row's lines a beat a clock to the read-out and, flushing, the flush; its carried value with each beat.
1690 reg half;
1691 wire [511:0] c_beat = tb ? cbuf1[tr >> 4] : cbuf0[tr >> 4];
1692 wire [32*HD-1:0] ybuf = tb ? ybuf1 : ybuf0;
1693 wire [31:0] row_carried = c_beat[32 * tr[3:0] +: 32];
1694 wire r_ready, x_ready, r_ov, x_ov;
1695 wire [31:0] r_out;
1696 wire [512*W-1:0] x_out;
1697 wire [256*W-1:0] x_halves;
1698 wire held_line = W > 1 && !f16 && !half;
1699 wire feed = have && (held_line || (r_ready && (!flush || x_ready)));
1700 wire to_units = have && !held_line;
1701 wire last_half = W > 1 || !f16 || half;
1702 reg [511:0] row_lo;
1703 wire [512*W-1:0] row_beat;
1704 // The flush's out-stream asked as the side asks it, against a queue of sixteen lines: an ask holds room for the lines it
1705 // may bring (two at W > 1 in f32, else one) until it has passed the XD + 1 clocks after it, and the stream is asked
1706 // next clock (x_ask, a register) only while the queue's lines and the room every ask out holds, this clock's ask's
1707 // included, leave room for one ask more. The room the queue and the asks before this clock's leave is kept a clock
1708 // ahead for each size of ask (x_free1, x_free2), from the queue's next count, so the ask waits on a compare alone.
1709 localparam XD = 1 + @F16@;
1710 reg x_pend, x_tog, x_hi, x_ask;
1711 reg [255:0] x_low;
1712 reg [511:0] x_out_hi;
1713 reg [XD:0] x_hist;
1714 reg [4:0] w_n;
1715 reg signed [6:0] x_free1, x_free2;
1716 // A row open from its start until its last read-out and flushed line are out: done the clock it closes; room for the
1717 // next once closed (the lanes' top starts a lane once a row).
1718 reg row_open;
1719 wire closes = row_open && !start && !running && w_n == 5'd0;
1720 always @(posedge clk) begin
1721 done <= !rst && closes;
1722 if (start) row_open <= 1'b1; else if (closes) row_open <= 1'b0;
1723 if (rst) row_open <= 1'b0;
1724 end
1725 assign room = !row_open;
1726 // The asks out next clock but the one it makes: this clock's and the history's but its oldest.
1727 reg [2:0] x_held;
1728 integer xi;
1729 always @* begin
1730 x_held = {2'd0, x_ask};
1731 for (xi = 0; xi < XD; xi = xi + 1) x_held = x_held + {2'd0, x_hist[xi]};
1732 end
1733 wire x_two = W > 1 && !f16;
1734 wire [511:0] x_line_data;
1735 wire x_line_push;
1736 generate
1737 if (W == 1) begin : narrow
1738 assign row_beat = f16 ? (half ? hi : lo) : lo;
1739 logos_gdn_read_p #(.N(HD)) rd(.clk(clk), .rst(rst), .y(ybuf), .scale(scale), .in_valid(have && (!flush || x_ready)),
1740 .in_ready(r_ready), .in_beat(row_beat), .in_carried(row_carried), .out_valid(r_ov), .out_read(r_out));
1741 logos_gdn_flush_p #(.N(HD), .T_MAX(TMAX)) fl(.clk(clk), .rst(rst), .log_wr(f_log_wr && flush), .log_at(f_log_at),
1742 .log_beat(f_log_beat), .log_k(f_log_k), .log_v(f_log_v), .log_decay(f_log_decay), .log_beta(f_log_beta),
1743 .entries(count), .f16(f16), .in_valid(flush && have && r_ready), .in_ready(x_ready), .in_beat(row_beat),
1744 .out_valid(x_ov), .out_ready(x_ask), .out_beat(x_out), .out_half(x_halves));
1745 assign x_line_data = f16 ? {x_halves, x_low} : x_out;
1746 assign x_line_push = x_ov && (!f16 || x_hi);
1747 end else begin : wide
1748 assign row_beat = f16 ? {hi, lo} : {lo, row_lo};
1749 logos_gdn_read_w #(.N(HD), .W(W)) rd(.clk(clk), .rst(rst), .y(ybuf), .scale(scale), .in_valid(to_units && (!flush || x_ready)),
1750 .in_ready(r_ready), .in_beat(row_beat), .in_carried(row_carried), .out_valid(r_ov), .out_read(r_out));
1751 // The head's rows dealt over FLUSHES flushes in order, each holding HD / FLUSHES of them through the same passes;
1752 // its rows asked of one flush after another, each flush's once the one before has given its last, so they come
1753 // back in row order: each ask's beat comes the same clocks after it, so at most one flush gives a beat a clock.
1754 localparam [15:0] FB = HD / FLUSHES * (HD / (16 * W));
1755 localparam FS = FLUSHES > 1 ? $clog2(FLUSHES) : 1;
1756 localparam [FS-1:0] FLAST = FLUSHES - 1;
1757 reg [FS-1:0] f_in, f_out;
1758 reg [15:0] f_inb, f_outb;
1759 wire [FLUSHES-1:0] f_ready, f_ov;
1760 wire [512*W*FLUSHES-1:0] f_beats;
1761 wire [256*W*FLUSHES-1:0] f_halves;
1762 wire f_take = flush && to_units && r_ready && x_ready;
1763 genvar fk;
1764 for (fk = 0; fk < FLUSHES; fk = fk + 1) begin : part
1765 logos_gdn_flush_w #(.N(HD), .W(W), .T_MAX(TMAX), .ROWS(HD / FLUSHES), .FIRST(fk * (HD / FLUSHES))) fl(.clk(clk), .rst(rst),
1766 .log_wr(fw_wr && flush), .log_at(f_log_at), .log_beat(fw_beat), .log_k(fw_k[512*W-1:0]), .log_v(fw_v[512*W-1:0]),
1767 .log_decay(f_log_decay), .log_beta(f_log_beta), .entries(count), .f16(f16),
1768 .in_valid(flush && to_units && r_ready && f_in == fk), .in_ready(f_ready[fk]), .in_beat(row_beat),
1769 .out_valid(f_ov[fk]), .out_ready(x_ask && f_out == fk), .out_beat(f_beats[512*W*fk +: 512*W]),
1770 .out_half(f_halves[256*W*fk +: 256*W]));
1771 end
1772 assign x_ready = f_ready[f_in];
1773 assign x_ov = |f_ov;
1774 reg [512*W-1:0] f_beat;
1775 reg [256*W-1:0] f_half;
1776 integer fo;
1777 always @* begin
1778 f_beat = f_beats[512*W-1:0];
1779 f_half = f_halves[256*W-1:0];
1780 for (fo = 1; fo < FLUSHES; fo = fo + 1)
1781 if (f_ov[fo]) begin f_beat = f_beats[512*W*fo +: 512*W]; f_half = f_halves[256*W*fo +: 256*W]; end
1782 end
1783 assign x_out = f_beat;
1784 assign x_halves = f_half;
1785 always @(posedge clk) begin
1786 if (rst) begin f_in <= {FS{1'b0}}; f_inb <= 16'd0; f_out <= {FS{1'b0}}; f_outb <= 16'd0; end
1787 else begin
1788 if (f_take) begin
1789 if (f_inb == FB - 16'd1) begin f_inb <= 16'd0; f_in <= (f_in == FLAST) ? {FS{1'b0}} : f_in + 1'b1; end
1790 else f_inb <= f_inb + 16'd1;
1791 end
1792 if (f_ov[f_out]) begin
1793 if (f_outb == FB - 16'd1) begin f_outb <= 16'd0; f_out <= (f_out == FLAST) ? {FS{1'b0}} : f_out + 1'b1; end
1794 else f_outb <= f_outb + 16'd1;
1795 end
1796 end
1797 end
1798 assign x_line_data = f16 ? x_halves[511:0] : (x_pend ? x_out_hi : x_out[511:0]);
1799 assign x_line_push = x_ov || x_pend;
1800 end
1801 endgenerate
1802 wire pop = have && feed && last_half;
1803 wire [QB-1:0] t_next = rst ? {QB{1'b0}} : t_rd + {{(QB-1){1'b0}}, pop};
1804 wire landing = a2_v && l_wr == t_next;
1805 wire [8:0] tr_n = (rq_take && t_wr == t_next) ? {sb, row_req[7:0]} : t_row[t_next];
1806 // The head's line: the queue's word at the next head, registered as block RAM reads it, or the line landing there
1807 // that clock, registered beside it.
1808 reg [511:0] lo_m, hi_m, lo_b, hi_b;
1809 reg land_d;
1810 always @(posedge clk) begin
1811 tr <= tr_n[7:0];
1812 tb <= tr_n[8];
1813 lo_m <= lo_q[t_next];
1814 hi_m <= hi_q[t_next];
1815 lo_b <= a2_lo;
1816 hi_b <= a2_hi;
1817 land_d <= landing;
1818 end
1819 assign lo = land_d ? lo_b : lo_m;
1820 assign hi = land_d ? hi_b : hi_m;
1821 // The flushed rows packed into lines and queued for memory, the flushed head's rows in order.
1822 `LOGOS_RAMSTYLE_ASYNC(16) reg [511:0] wfifo_d [0:15];
1823 `LOGOS_RAMSTYLE_ASYNC(16) reg [31:0] wfifo_l [0:15];
1824 reg [3:0] w_wr, w_rd;
1825 reg [31:0] xh;
1826 reg [15:0] x_row, x_line;
1827 wire [47:0] x_byte = base + mem_off + (xh * HD + x_row) * HD * eb + 64 * x_line;
1828 assign wq_valid = w_n != 5'd0;
1829 assign wq_line = wfifo_l[w_rd];
1830 assign wq_data = wfifo_d[w_rd];
1831 wire w_pop = wq_valid && wq_ready;
1832 wire [4:0] w_next = w_n + (x_line_push ? 5'd1 : 5'd0) - (w_pop ? 5'd1 : 5'd0);
1833 // The read-outs, head by head behind the rows: the head and row the next is of.
1834 reg [511:0] o_beat;
1835 reg [31:0] oh;
1836 reg [15:0] o_row;
1837 wire [31:0] o_wa = out_beat + ((oh * HD + o_row) >> 4);
1838 wire outs_done = oh >= nv;
1839 assign busy = running || w_n != 5'd0;
1840 always @(posedge clk) begin
1841 f_log_wr <= 1'b0; f_q_wr <= 1'b0; f_start <= 1'b0; st_we <= 1'b0; q_pending <= 1'b0; fw_wr <= 1'b0;
1842 x_tog <= !x_tog;
1843 x_hist <= {x_hist[XD-1:0], x_ask};
1844 x_ask <= !rst && (!x_two || !x_tog) && (x_two ? x_free2 >= (x_ask ? 7'sd4 : 7'sd2) : x_free1 >= (x_ask ? 7'sd2 : 7'sd1));
1845 x_free1 <= 7'sd16 - $signed({2'b0, w_next}) - $signed({4'b0, x_held});
1846 x_free2 <= 7'sd16 - $signed({2'b0, w_next}) - $signed({3'b0, x_held, 1'b0});
1847 if (rq_take) t_wr <= t_wr + 1'b1;
1848 if (rs_valid) l_in <= l_in + 1'b1;
1849 if (a2_v) l_wr <= l_wr + 1'b1;
1850 owed <= owed + (rq_take ? 10'd1 : 10'd0) - (pop ? 10'd1 : 10'd0);
1851 owed_b0 <= owed_b0 + ((rq_take && !sb) ? 10'd1 : 10'd0) - ((pop && !tb) ? 10'd1 : 10'd0);
1852 owed_b1 <= owed_b1 + ((rq_take && sb) ? 10'd1 : 10'd0) - ((pop && tb) ? 10'd1 : 10'd0);
1853 // The fold's run: started by the loader, finished once it falls idle after its start.
1854 if (f_start) f_run <= 1'b1;
1855 else if (f_run && !f_busy) begin f_run <= 1'b0; folded <= folded + 8'd1; end
1856 // ── the loader ──
1857 if (q_lo) begin f_q_wr <= 1'b1; f_q_slot <= q_lo_slot; f_q_beat <= q_lo_at; f_q_data <= st_rd; end
1858 if (w2_v) case (w2_k)
1859 K_DEC: ldec_h <= w_elem;
1860 K_BETA: beta_h <= w_elem;
1861 K_KEY: key_h <= w_beat;
1862 default: begin
1863 f_log_wr <= 1'b1; f_log_at <= w2_e; f_log_beat <= w2_j; f_log_k <= key_h; f_log_v <= w_beat;
1864 f_log_decay <= ldec_h; f_log_beta <= beta_h;
1865 if (!w2_j[0]) begin lo_k <= key_h; lo_v <= w_beat; end
1866 else begin fw_wr <= W > 1; fw_beat <= w2_j >> 1; fw_k <= {key_h, lo_k}; fw_v <= {w_beat, lo_v}; end
1867 end
1868 endcase
1869 case (ls)
1870 L_IDLE: if (running && lh < nv && can_load) begin lslot <= 8'd0; qj <= 16'd0; ls <= L_Q; end
1871 else if (running && lh >= nv) ls <= L_DONE;
1872 L_Q: if (qj == BEATS) begin ge <= 8'd0; gi <= 16'd0; e_at <= par ? COPY_LINES * 32'd64 : 32'd0; ls <= L_LOG; end
1873 else if (st_gnt) begin q_pending <= 1'b1; q_at <= qj; qj <= qj + 16'd1; end
1874 L_LOG: if (walking) begin
1875 if (gi == 16'd1 + 2 * BEATS) begin gi <= 16'd0; ge <= ge + 8'd1; e_at <= e_at + entry; end else gi <= gi + 16'd1;
1876 end else ls <= L_TAIL;
1877 // The walk's last lines through the copy and the widen, then the batch's next head or its fold.
1878 L_TAIL: if (!w1_v && !w2_v && !f_log_wr && (SRL == 1 || (!q_pending && !q_flight))) begin
1879 lh <= lh + STEP;
1880 lg <= (lg + g_step >= nk) ? lg + g_step - nk : lg + g_step;
1881 if (lslot + 8'd1 == bs || lh + STEP >= nv) ls <= L_FOLD;
1882 else begin lslot <= lslot + 8'd1; qj <= 16'd0; ls <= L_Q; end
1883 end
1884 L_FOLD: begin f_start <= 1'b1; fold_bank <= loaded[0]; loaded <= loaded + 8'd1; ls <= L_IDLE; end
1885 default: ;
1886 endcase
1887 // ── the streamer ──
1888 if (feed) begin
1889 if (W > 1) begin
1890 t_rd <= t_rd + 1'b1;
1891 if (!f16) begin half <= !half; if (held_line) row_lo <= lo; end
1892 end else if (last_half) begin half <= 1'b0; t_rd <= t_rd + 1'b1; end else half <= 1'b1;
1893 end
1894 if (x_line_push) begin
1895 wfifo_d[w_wr] <= x_line_data;
1896 wfifo_l[w_wr] <= x_byte[37:6];
1897 w_wr <= w_wr + 4'd1;
1898 if (x_line + 16'd1 == row_lines) begin
1899 x_line <= 16'd0;
1900 if (x_row + 16'd1 == HD_ROWS) begin x_row <= 16'd0; xh <= xh + STEP; end else x_row <= x_row + 16'd1;
1901 end else x_line <= x_line + 16'd1;
1902 end
1903 if (W == 1 && x_ov) begin
1904 if (f16 && !x_hi) begin x_low <= x_halves[255:0]; x_hi <= 1'b1; end else x_hi <= 1'b0;
1905 end
1906 x_pend <= W > 1 && x_ov && !f16;
1907 if (x_ov) x_out_hi <= x_out[512*W-1:512*W-512];
1908 if (w_pop) w_rd <= w_rd + 4'd1;
1909 w_n <= w_next;
1910 if (r_ov) begin
1911 o_beat[32 * o_row[3:0] +: 32] <= r_out;
1912 if (o_row[3:0] == 4'd15) begin st_we <= 1'b1; st_wa <= o_wa[SA-1:0]; st_wd <= {r_out, o_beat[479:0]}; end
1913 if (o_row + 16'd1 == HD_ROWS) begin o_row <= 16'd0; oh <= oh + STEP; end else o_row <= o_row + 16'd1;
1914 end
1915 case (ss)
1916 // A folded batch's next head, once every line of the head two back, the last to take its buffer, is through the
1917 // units.
1918 S_IDLE: if (running && sh < nv && folded != copied && (sb ? owed_b1 : owed_b0) == 10'd0) begin cb <= 16'd0; ss <= S_COPY; end
1919 else if (running && sh >= nv) ss <= S_DONE;
1920 S_COPY: if (cb == BEATS + 16'd2) begin cb <= 16'd0; row_req <= 16'd0; ri <= 16'd0; ss <= S_ROWS; end else cb <= cb + 16'd1;
1921 S_ROWS: begin
1922 if (rq_take) begin
1923 if (ri + 16'd1 == row_lines) begin ri <= 16'd0; row_req <= row_req + 16'd1; end else ri <= ri + 16'd1;
1924 end
1925 if (row_req == HD_ROWS) ss <= flush ? S_DRAIN : S_NEXT;
1926 end
1927 // Flushing: the head's read-outs and flushed rows out before the next head's log goes into the flush.
1928 S_DRAIN: if (oh == sh + STEP && x_row == 16'd0 && xh == sh + STEP && w_n == 5'd0) ss <= S_NEXT;
1929 S_NEXT: begin
1930 sh <= sh + STEP;
1931 sb <= !sb;
1932 if (sslot + 8'd1 == bs || sh + STEP >= nv) begin
1933 sslot <= 8'd0; copied <= copied + 8'd1;
1934 if (flush) drained <= drained + 8'd1;
1935 end else sslot <= sslot + 8'd1;
1936 ss <= S_IDLE;
1937 end
1938 default: ;
1939 endcase
1940 // The row is done once every head's read-outs and flushed rows are out.
1941 if (running && ls == L_DONE && ss == S_DONE && outs_done && (!flush || xh >= nv) && w_n == 5'd0) begin
1942 running <= 1'b0; ls <= L_IDLE; ss <= S_IDLE;
1943 end
1944 if (start && !running && first < nv) begin
1945 running <= 1'b1; lh <= first; lg <= first_g; sh <= first; oh <= first; xh <= first; o_row <= 16'd0;
1946 x_row <= 16'd0; x_line <= 16'd0; x_hi <= 1'b0; sslot <= 8'd0; sb <= 1'b0;
1947 loaded <= 8'd0; folded <= 8'd0; copied <= 8'd0; drained <= 8'd0; ls <= L_IDLE; ss <= S_IDLE;
1948 end
1949 if (rst) begin
1950 running <= 1'b0; ls <= L_IDLE; ss <= S_IDLE; f_run <= 1'b0; t_wr <= {QB{1'b0}}; t_rd <= {QB{1'b0}}; l_in <= {QB{1'b0}};
1951 l_wr <= {QB{1'b0}}; w_wr <= 4'd0; w_rd <= 4'd0; w_n <= 5'd0; half <= 1'b0; owed <= 10'd0; owed_b0 <= 10'd0;
1952 owed_b1 <= 10'd0; x_pend <= 1'b0;
1953 x_hist <= {(XD+1){1'b0}}; x_tog <= 1'b0; x_free1 <= 7'sd16; x_free2 <= 7'sd16;
1954 end
1955 end
1956endmodule
1957"#;
1958
1959const GDN_LANE_T_VERILOG: &str = r#"
1962// A head lane of logos_gdn_lanes under a trailing flush of WINDOW tokens: value heads first, first + STEP, … below nv of
1963// each token row it takes (start, while it has room: two rows at most), each row's fields taken with it — its region, its
1964// query's and read-outs' store beats, its log's count and the copy of the lane's log that holds it (lb_ra, a line the
1965// clock after its address; each copy LBA bits of address, the second COPY_LINES lines in), and whether it replays the
1966// log's first WINDOW tokens into each head's share of SHARE = HD / WINDOW rows from its row `in_from_row` (the log holds
1967// a window past the first). The rows go through back to back, a row's first batch loaded and folded while the last
1968// row's last batches stream and
1969// replay; `done` the clock after each row's last read-out and flushed line are out, in order. A loader takes the heads a
1970// batch at a time, H heads (logos_gdn_fold_m's
1971// slots): each head's query from the store on the clocks st_gnt grants the read it asks (st_rq) and its log from the
1972// copy a line a clock into its slot, the log's first WINDOW tokens into the head's slot of the flush's bank for the
1973// batch as well, then folds the batch into a bank, keeping each head's fold over the tokens after those WINDOW too; the
1974// next batch is loaded and folded into the other bank while this one's rows stream, once the flush has given back the
1975// rows of the batch that last held its flush bank. A streamer takes each head of a folded batch: its two folds copied
1976// out of the bank, then its rows from memory (rq/rs, in the order asked, at most OUTSTANDING lines owed, no wait taken
1977// on an answer) through the read-out — a row below `from_row` through the fold over the newer tokens, its share replayed —
1978// and each row of its share into its slot of the flush's bank as the read-out takes it; once a batch's rows are all
1979// through, its bank is replayed (logos_gdn_flush_m), and its flushed rows go back where they were read (wq); each
1980// sixteen rows' read-outs leave as a store beat (st_we). Busy while a row it has taken is not done or a flushed line is
1981// queued.
1982module logos_gdn_lane_t #(parameter HD = 128, TMAX = 16, SA = 10, parameter [8:0] OUTSTANDING = 32, parameter W = 1,
1983 parameter [31:0] STEP = 4, parameter LBA = 13, WINDOW = 8, COPY_LINES = 4096, SRL = 1)
1984 (input clk, input rst,
1985 input start, output busy, output room, output reg done,
1986 input [31:0] in_region, input [31:0] mem_off, input [31:0] entry, input [31:0] nk, input [31:0] nv,
1987 input [31:0] fval, input [31:0] fdec, input [31:0] fgate, input [31:0] scale, input f16,
1988 input [SA-1:0] in_q_beat, input [SA-1:0] in_out_beat,
1989 input [31:0] first, input [7:0] in_count, input in_flush, input [15:0] in_from_row, input in_par,
1990 output st_rq, input st_gnt, output [SA-1:0] st_ra, input [511:0] st_rd,
1991 output reg st_we, output reg [SA-1:0] st_wa, output reg [511:0] st_wd,
1992 output [LBA:0] lb_ra, input [511:0] lb_rd,
1993 output rq_valid, input rq_ready, output [31:0] rq_line, input rs_valid, input [511:0] rs_data,
1994 output wq_valid, input wq_ready, output [31:0] wq_line, output [511:0] wq_data);
1995 localparam H = @FADD@;
1996 localparam [15:0] BEATS = HD / 16, HD_ROWS = HD;
1997 localparam [15:0] SHARE = HD / WINDOW;
1998 localparam [7:0] WINDOW8 = WINDOW;
1999 localparam K_DEC = 2'd0, K_BETA = 2'd1, K_KEY = 2'd2, K_VAL = 2'd3;
2000 // The rows the lane has taken, two at most, each row's fields in the entry its count's low bit names: its region, its
2001 // query's and read-outs' store beats, its log's count, whether it replays, its share's first row, and the copy of the
2002 // log it reads (its first line COPY_LINES lines in for the second). Rows go through in order — taken (in_n), loaded
2003 // (ld_n), streamed (st_n), read out (o_n), done (dn_n) — each stage holding its row's fields in registers of its own
2004 // from the clock it takes the row up.
2005 reg [7:0] in_n, ld_n, st_n, o_n, dn_n;
2006 `LOGOS_RAMSTYLE(2) reg [31:0] e_region [0:1];
2007 `LOGOS_RAMSTYLE(2) reg [SA-1:0] e_q [0:1];
2008 `LOGOS_RAMSTYLE(2) reg [SA-1:0] e_out [0:1];
2009 `LOGOS_RAMSTYLE(2) reg [7:0] e_count [0:1];
2010 `LOGOS_RAMSTYLE(2) reg [15:0] e_from [0:1];
2011 reg [1:0] e_flush, e_par;
2012 always @(posedge clk)
2013 if (start) begin
2014 e_region[in_n[0]] <= in_region; e_q[in_n[0]] <= in_q_beat; e_out[in_n[0]] <= in_out_beat; e_count[in_n[0]] <= in_count;
2015 e_flush[in_n[0]] <= in_flush; e_from[in_n[0]] <= in_from_row; e_par[in_n[0]] <= in_par;
2016 end
2017 assign room = in_n - dn_n < 8'd2;
2018 wire [31:0] eb = f16 ? 32'd2 : 32'd4;
2019 wire [31:0] row_bytes = HD * eb;
2020 wire [15:0] row_lines = row_bytes[21:6];
2021 localparam [7:0] HS = H;
2022 wire [7:0] bs = HS;
2023 // The key group a head's step moves by, and the first head's, each a register of the job's fields.
2024 function [31:0] step_mod(input [31:0] k);
2025 reg [31:0] r;
2026 integer si;
2027 begin
2028 r = STEP;
2029 for (si = 0; si < 4; si = si + 1) if (r >= k) r = r - k;
2030 step_mod = r;
2031 end
2032 endfunction
2033 reg [31:0] g_step, first_g;
2034 always @(posedge clk) begin
2035 g_step <= step_mod(nk);
2036 first_g <= (first >= nk) ? ((first - nk >= nk) ? ((first - 32'd2 * nk >= nk) ? first - 32'd3 * nk : first - 32'd2 * nk) : first - nk) : first;
2037 end
2038 // Batches, every row's in turn: loaded (their folds started), folded (finished) and copied out (their fold bank free).
2039 // The folds' two banks go by the batch's count's low bit; the flush's three by the loaded and copied batches' counts
2040 // mod 3 (lbank, cbank), a bank busy (fb_busy) from the load of a batch that replays until its replay's last line is
2041 // queued.
2042 reg [7:0] loaded, folded, copied;
2043 reg [2:0] fb_busy;
2044 // ── The loader ──────────────────────────────────────────────────────────────────────────────────────────────────
2045 localparam [2:0] L_IDLE = 3'd0, L_Q = 3'd1, L_LOG = 3'd2, L_TAIL = 3'd3, L_FOLD = 3'd4;
2046 reg [2:0] ls;
2047 reg [31:0] lh, lg;
2048 reg [7:0] lslot;
2049 reg [15:0] qj;
2050 reg q_pending;
2051 reg [15:0] q_at;
2052 // A query beat asked (q_pending, its beat and slot, the clock after its address) and out of the store SRL clocks after
2053 // its address (q_lo, q_lo_at, q_lo_slot); a batch's fold waits until none is on its way.
2054 wire q_lo, q_flight;
2055 wire [15:0] q_lo_at;
2056 wire [7:0] q_lo_slot;
2057 logos_landing #(.W(24), .D(SRL - 1)) qland(.clk(clk), .rst(rst), .in_v(q_pending), .in_d({q_at, lslot}), .out_v(q_lo),
2058 .out_d({q_lo_at, q_lo_slot}), .flight(q_flight));
2059 wire f_busy;
2060 reg f_start, f_run;
2061 reg [1:0] lbank, cbank;
2062 // The loader's row: its log's count, whether it replays, the copy it reads, its query's beats.
2063 reg l_act, l_flush, l_par;
2064 reg [7:0] l_count;
2065 reg [SA-1:0] l_q;
2066 wire can_load = !f_busy && !f_start && !f_run && loaded - copied < 8'd2 && !(l_flush && fb_busy[lbank]);
2067 wire [31:0] q_ra = l_q + ((lg * HD) >> 4) + qj;
2068 assign st_rq = ls == L_Q && qj != BEATS;
2069 assign st_ra = q_ra[SA-1:0];
2070 // The log walk of head lh (key group lg): for each entry its decay exponent's line, its gate's, then its key's and its
2071 // values' beats in turn; each line's place in the copy and the byte of its field within it.
2072 reg [7:0] ge;
2073 reg [15:0] gi;
2074 reg [31:0] e_at;
2075 wire [1:0] g_kind = gi == 16'd0 ? K_DEC : gi == 16'd1 ? K_BETA : gi[0] ? K_VAL : K_KEY;
2076 wire [15:0] g_dj = (gi - 16'd2) >> 1;
2077 wire [31:0] g_off = g_kind == K_DEC ? (fdec + lh) * eb : g_kind == K_BETA ? (fgate + lh) * eb :
2078 g_kind == K_KEY ? (lg * HD + 16 * g_dj) * eb : (fval + lh * HD + 16 * g_dj) * eb;
2079 wire [31:0] g_byte = e_at + g_off;
2080 wire walking = ls == L_LOG && ge != l_count;
2081 assign lb_ra = g_byte[LBA+6:6];
2082 // A copy line's tag beside it: its kind, entry, beat and byte; the line a clock after its address, registered with its
2083 // tag the clock after that, then widened.
2084 reg w1_v, w2_v;
2085 reg [1:0] w1_k, w2_k;
2086 reg [7:0] w1_e, w2_e;
2087 reg [15:0] w1_j, w2_j;
2088 reg [5:0] w1_o, w2_o;
2089 reg [511:0] w2_d;
2090 wire [511:0] w_lo, w_hi;
2091 wire [31:0] w_e16;
2092 genvar l;
2093 generate
2094 for (l = 0; l < 16; l = l + 1) begin : walk_widen
2095 logos_f16_to_f32 lo(.h(w2_d[16*l +: 16]), .x(w_lo[32*l +: 32]));
2096 logos_f16_to_f32 hi(.h(w2_d[256 + 16*l +: 16]), .x(w_hi[32*l +: 32]));
2097 end
2098 endgenerate
2099 wire [31:0] w_raw = w2_d[8 * w2_o +: 32];
2100 logos_f16_to_f32 we(.h(w2_d[8 * w2_o +: 16]), .x(w_e16));
2101 wire [511:0] w_beat = f16 ? (w2_o[5] ? w_hi : w_lo) : w2_d;
2102 wire [31:0] w_elem = f16 ? w_e16 : w_raw;
2103 always @(posedge clk) begin
2104 w1_v <= !rst && walking;
2105 w1_k <= g_kind; w1_e <= ge; w1_j <= g_dj; w1_o <= g_byte[5:0];
2106 w2_v <= !rst && w1_v;
2107 w2_k <= w1_k; w2_e <= w1_e; w2_j <= w1_j; w2_o <= w1_o; w2_d <= lb_rd;
2108 end
2109 // The fold's and the flush's log writes: a value beat with the key beat, decay exponent and gate before it.
2110 reg [31:0] ldec_h, beta_h;
2111 reg [511:0] key_h;
2112 reg f_log_wr, f_q_wr, fw_wr;
2113 reg [7:0] f_log_at, f_q_slot;
2114 reg [15:0] f_log_beat, f_q_beat, fw_beat;
2115 reg [511:0] f_log_k, f_log_v, f_q_data, lo_k, lo_v;
2116 reg [1023:0] fw_k, fw_v;
2117 reg [31:0] f_log_decay, f_log_beta;
2118 reg fold_bank;
2119 // The fold's run: its batch's tokens and the older ones it keeps a fold past, held through the run.
2120 reg [7:0] f_entries, f_older;
2121 // ── The streamer ────────────────────────────────────────────────────────────────────────────────────────────────
2122 localparam [2:0] S_IDLE = 3'd0, S_COPY = 3'd1, S_ROWS = 3'd2, S_NEXT = 3'd3, S_FLUSH = 3'd4;
2123 reg [2:0] ss;
2124 reg [31:0] sh, s_h0;
2125 reg [7:0] sslot;
2126 // The streamer's row: its region, whether it replays, its share's first row, its parity.
2127 reg s_act, s_flush, s_par;
2128 reg [31:0] s_region;
2129 reg [15:0] s_from;
2130 wire [47:0] s_base = {16'd0, s_region} * 48'd64;
2131 wire batch_end = sslot + 8'd1 == bs || sh + STEP >= nv;
2132 reg [15:0] cb;
2133 wire [511:0] f_y, f_c;
2134 wire [31:0] f_decay;
2135 wire [511:0] f_yn, f_cn;
2136 logos_gdn_fold_m #(.N(HD), .T_MAX(TMAX), .TRAILING(1)) fold(.clk(clk), .rst(rst), .log_wr(f_log_wr), .log_slot(lslot), .log_at(f_log_at),
2137 .log_beat(f_log_beat), .log_k(f_log_k), .log_v(f_log_v), .log_decay(f_log_decay), .log_beta(f_log_beta), .q_wr(f_q_wr),
2138 .q_slot(f_q_slot), .q_beat(f_q_beat), .q_data(f_q_data), .start(f_start), .bank(fold_bank), .entries(f_entries), .older(f_older),
2139 .busy(f_busy), .rd_bank(copied[0]), .rd_slot(sslot), .rd_beat(cb), .y_beat(f_y), .carried_beat(f_c),
2140 .ynew_beat(f_yn), .cnew_beat(f_cn), .rd_token(8'd0), .decay_out(f_decay));
2141 // The streamed head's folded query and collected values: a beat of each asked a clock (cb), out of the fold's bank two
2142 // clocks after, copied in the clock after that; the copy over once its last beat is in.
2143 // Each head's folds into buffer sb, the buffer the head before did not take.
2144 reg sb;
2145 reg [32*HD-1:0] ybuf0, ybuf1, ybuf_n0, ybuf_n1;
2146 `LOGOS_RAMSTYLE_ASYNC(BEATS) reg [511:0] cbuf0 [0:BEATS-1];
2147 `LOGOS_RAMSTYLE_ASYNC(BEATS) reg [511:0] cbuf1 [0:BEATS-1];
2148 `LOGOS_RAMSTYLE_ASYNC(BEATS) reg [511:0] cbuf_n0 [0:BEATS-1];
2149 `LOGOS_RAMSTYLE_ASYNC(BEATS) reg [511:0] cbuf_n1 [0:BEATS-1];
2150 reg cp_a, cp_m, cp_v, cp_b;
2151 reg [15:0] cp_ab, cp_mb, cp_at;
2152 reg [511:0] cp_y, cp_c, cp_yn, cp_cn;
2153 always @(posedge clk) begin
2154 cp_a <= !rst && ss == S_COPY && cb < BEATS;
2155 cp_ab <= cb;
2156 cp_m <= !rst && cp_a;
2157 cp_mb <= cp_ab;
2158 cp_v <= !rst && cp_m;
2159 cp_at <= cp_mb;
2160 cp_b <= sb;
2161 cp_y <= f_y;
2162 cp_c <= f_c;
2163 cp_yn <= f_yn;
2164 cp_cn <= f_cn;
2165 if (cp_v && !cp_b) begin cbuf0[cp_at] <= cp_c; cbuf_n0[cp_at] <= cp_cn; end
2166 if (cp_v && cp_b) begin cbuf1[cp_at] <= cp_c; cbuf_n1[cp_at] <= cp_cn; end
2167 end
2168 genvar cq;
2169 generate
2170 for (cq = 0; cq < BEATS; cq = cq + 1) begin : copy_in
2171 always @(posedge clk) begin
2172 if (cp_v && !cp_b && cp_at == cq) begin ybuf0[512*cq +: 512] <= cp_y; ybuf_n0[512*cq +: 512] <= cp_yn; end
2173 if (cp_v && cp_b && cp_at == cq) begin ybuf1[512*cq +: 512] <= cp_y; ybuf_n1[512*cq +: 512] <= cp_yn; end
2174 end
2175 end
2176 endgenerate
2177 // The rows asked: each its row; lines answered in order, registered as they come, binary16 halves widened a clock
2178 // on and registered, then into the lines.
2179 localparam QB = OUTSTANDING > 9'd32 ? $clog2(OUTSTANDING) + 1 : 6;
2180 localparam QD = 1 << QB;
2181 reg [15:0] row_req, ri;
2182 reg [26:0] t_row [0:QD-1];
2183 reg [511:0] lo_q [0:QD-1];
2184 reg [511:0] hi_q [0:QD-1];
2185 reg [QB-1:0] t_wr, t_rd, l_in, l_wr;
2186 // The lines asked and not yet through the read-out, and of them those of each buffer's head.
2187 reg [9:0] owed, owed_b0, owed_b1;
2188 wire row_walk = ss == S_ROWS && row_req != HD_ROWS;
2189 assign rq_valid = row_walk && owed < {1'b0, OUTSTANDING};
2190 wire rq_take = rq_valid && rq_ready;
2191 // The walk: it starts at its copy's end, and each line taken steps it a line, its row's last to the next row's first.
2192 // A row's line tagged with where its row falls — below the share (read through the newer fold), in it (a share row the
2193 // replay takes, and its place there) — and its head's slot, buffer and row. An ask's line (r_byte) and its tag (t_new)
2194 // are registers the walk moves with: the line a line on with each line taken (a head's rows are whole lines, one after
2195 // another), the tag its row's or the next row's, each computed from registers alone. The batch's head, region, slot,
2196 // buffer and share hold from before its copy to the walk's end.
2197 wire walk_starts = ss == S_COPY && cb == BEATS + 16'd2;
2198 wire walk_takes = ss == S_ROWS && rq_take;
2199 wire row_ends = walk_takes && ri + 16'd1 == row_lines;
2200 wire [15:0] row_first = 16'd0, row_next = row_req + 16'd1;
2201 wire [26:0] tag_first = {row_first[7:0] - s_from[7:0], s_flush && row_first >= s_from && row_first < s_from + SHARE,
2202 row_first < s_from, sslot, sb, row_first[7:0]};
2203 wire [26:0] tag_here = {row_req[7:0] - s_from[7:0], s_flush && row_req >= s_from && row_req < s_from + SHARE,
2204 row_req < s_from, sslot, sb, row_req[7:0]};
2205 wire [26:0] tag_next = {row_next[7:0] - s_from[7:0], s_flush && row_next >= s_from && row_next < s_from + SHARE,
2206 row_next < s_from, sslot, sb, row_next[7:0]};
2207 wire [47:0] walk_first = s_base + mem_off + sh * HD * HD * eb;
2208 reg [47:0] r_byte;
2209 reg [26:0] t_new;
2210 always @(posedge clk) begin
2211 r_byte <= walk_starts ? walk_first : walk_takes ? r_byte + 48'd64 : r_byte;
2212 t_new <= walk_starts ? tag_first : row_ends ? tag_next : tag_here;
2213 end
2214 assign rq_line = r_byte[37:6];
2215 reg a1_v;
2216 reg [511:0] a1_d;
2217 wire [511:0] in_lo, in_hi;
2218 generate
2219 for (l = 0; l < 16; l = l + 1) begin : row_widen
2220 logos_f16_to_f32 lo(.h(a1_d[16*l +: 16]), .x(in_lo[32*l +: 32]));
2221 logos_f16_to_f32 hi(.h(a1_d[256 + 16*l +: 16]), .x(in_hi[32*l +: 32]));
2222 end
2223 endgenerate
2224 wire [511:0] in_lo_q = f16 ? in_lo : a1_d;
2225 // A line widened, registered (a2) before its queue and the head's bypass take it.
2226 (* maxfan = 64 *) reg a2_v;
2227 reg [511:0] a2_lo, a2_hi;
2228 always @(posedge clk) begin
2229 if (rq_take) t_row[t_wr] <= t_new;
2230 a1_v <= !rst && rs_valid;
2231 a1_d <= rs_data;
2232 a2_v <= !rst && a1_v;
2233 a2_lo <= in_lo_q;
2234 a2_hi <= in_hi;
2235 if (a2_v) begin lo_q[l_wr] <= a2_lo; hi_q[l_wr] <= a2_hi; end
2236 end
2237 wire have = l_wr != t_rd;
2238 reg [7:0] tr, tj;
2239 // The line's tag bits, the newer and the buffer choosing each of the read-out query's 8,192 bits, copied a copy each
2240 // 64 loads.
2241 (* maxfan = 64 *) reg tb, tn;
2242 reg tsh;
2243 reg [7:0] ts;
2244 wire [511:0] lo, hi;
2245 // A row's lines a beat a clock to the read-out and, flushing, the flush; its carried value with each beat.
2246 reg half;
2247 // A row below the share replayed reads through the fold over the newer tokens; the read-outs take a group's fold with
2248 // its first beat, and a share is whole groups.
2249 wire newer = tn;
2250 wire [511:0] c_beat = tb ? cbuf1[tr >> 4] : cbuf0[tr >> 4], c_beat_n = tb ? cbuf_n1[tr >> 4] : cbuf_n0[tr >> 4];
2251 wire [31:0] row_carried = newer ? c_beat_n[32 * tr[3:0] +: 32] : c_beat[32 * tr[3:0] +: 32];
2252 wire [32*HD-1:0] row_y = newer ? (tb ? ybuf_n1 : ybuf_n0) : (tb ? ybuf1 : ybuf0);
2253 wire r_ready, x_ready, r_ov, x_ov;
2254 wire [31:0] r_out;
2255 wire [512*W-1:0] x_out;
2256 wire [256*W-1:0] x_halves;
2257 wire held_line = W > 1 && !f16 && !half;
2258 wire feed = have && (held_line || r_ready);
2259 wire to_units = have && !held_line;
2260 wire last_half = W > 1 || !f16 || half;
2261 reg [511:0] row_lo;
2262 wire [512*W-1:0] row_beat;
2263 // The flush's out-stream asked as the side asks it, against a queue of sixteen lines: an ask holds room for the lines it
2264 // may bring (two at W > 1 in f32, else one) until it has passed the XD + 1 clocks after it (logos_gdn_flush_m's beat
2265 // comes XD clocks after its ask), and the stream is asked
2266 // next clock (x_ask, a register) only while the queue's lines and the room every ask out holds, this clock's ask's
2267 // included, leave room for one ask more. The room the queue and the asks before this clock's leave is kept a clock
2268 // ahead for each size of ask (x_free1, x_free2), from the queue's next count, so the ask waits on a compare alone.
2269 localparam XD = 2 + @F16@;
2270 reg x_pend, x_tog, x_hi, x_ask;
2271 reg [255:0] x_low;
2272 reg [511:0] x_out_hi;
2273 reg [XD:0] x_hist;
2274 reg [4:0] w_n;
2275 reg signed [6:0] x_free1, x_free2;
2276 // The asks out next clock but the one it makes: this clock's and the history's but its oldest.
2277 reg [2:0] x_held;
2278 integer xi;
2279 always @* begin
2280 x_held = {2'd0, x_ask};
2281 for (xi = 0; xi < XD; xi = xi + 1) x_held = x_held + {2'd0, x_hist[xi]};
2282 end
2283 wire x_two = W > 1 && !f16;
2284 wire [511:0] x_line_data;
2285 wire x_line_push;
2286 // The share's rows: each beat the read-out takes of a row the token replays, written the clock after into the slot of
2287 // its head in the streamed batch's flush bank at its place in the share (its line's tag).
2288 localparam [15:0] UB = HD / (16 * W);
2289 reg [15:0] u_bt;
2290 reg fm_wr, fm_start;
2291 reg [1:0] fm_wbank, fm_sbank;
2292 reg [7:0] fm_slot, fm_slots;
2293 reg [15:0] fm_j, fm_beat;
2294 reg [512*W-1:0] fm_data;
2295 wire [2:0] fm_free;
2296 wire u_take = (W > 1 ? to_units : have) && r_ready;
2297 wire in_share = tsh;
2298 always @(posedge clk) begin
2299 fm_wr <= !rst && u_take && in_share;
2300 fm_slot <= ts; fm_j <= {8'd0, tj}; fm_beat <= u_bt; fm_data <= row_beat; fm_wbank <= cbank;
2301 if (rst) u_bt <= 16'd0;
2302 else if (u_take) u_bt <= (u_bt == UB - 16'd1) ? 16'd0 : u_bt + 16'd1;
2303 end
2304 // The flush's log: the log's first WINDOW tokens of each head the loader walks of a row that replays, into its slot of
2305 // the loaded batch's bank, at the flush's beat (two of the fold's at W > 1).
2306 wire fl_wr = l_flush && f_log_at < WINDOW8 && (W > 1 ? fw_wr : f_log_wr);
2307 reg [15:0] fm_first;
2308 logos_gdn_flush_m #(.N(HD), .W(W), .T_MAX(WINDOW), .SHARE(SHARE), .SLOTS(H)) fl(.clk(clk), .rst(rst),
2309 .log_wr(fl_wr), .log_bank(lbank), .log_slot(lslot), .log_at(f_log_at), .log_beat(W > 1 ? fw_beat : f_log_beat),
2310 .log_k(W > 1 ? fw_k[512*W-1:0] : f_log_k[512*W-1:0]), .log_v(W > 1 ? fw_v[512*W-1:0] : f_log_v[512*W-1:0]),
2311 .log_decay(f_log_decay), .log_beta(f_log_beta),
2312 .row_wr(fm_wr), .row_bank(fm_wbank), .row_slot(fm_slot), .row_j(fm_j), .row_beat(fm_beat), .row_data(fm_data),
2313 .start(fm_start), .bank(fm_sbank), .entries(WINDOW8), .slots(fm_slots), .first(fm_first), .f16(f16), .free(fm_free),
2314 .out_valid(x_ov), .out_ready(x_ask), .out_beat(x_out), .out_half(x_halves));
2315 generate
2316 if (W == 1) begin : narrow
2317 assign row_beat = f16 ? (half ? hi : lo) : lo;
2318 logos_gdn_read_p #(.N(HD)) rd(.clk(clk), .rst(rst), .y(row_y), .scale(scale), .in_valid(have),
2319 .in_ready(r_ready), .in_beat(row_beat), .in_carried(row_carried), .out_valid(r_ov), .out_read(r_out));
2320 assign x_line_data = f16 ? {x_halves, x_low} : x_out;
2321 assign x_line_push = x_ov && (!f16 || x_hi);
2322 end else begin : wide
2323 assign row_beat = f16 ? {hi, lo} : {lo, row_lo};
2324 logos_gdn_read_w #(.N(HD), .W(W)) rd(.clk(clk), .rst(rst), .y(row_y), .scale(scale), .in_valid(to_units),
2325 .in_ready(r_ready), .in_beat(row_beat), .in_carried(row_carried), .out_valid(r_ov), .out_read(r_out));
2326 assign x_line_data = f16 ? x_halves[511:0] : (x_pend ? x_out_hi : x_out[511:0]);
2327 assign x_line_push = x_ov || x_pend;
2328 end
2329 endgenerate
2330 wire pop = have && feed && last_half;
2331 wire [QB-1:0] t_next = rst ? {QB{1'b0}} : t_rd + {{(QB-1){1'b0}}, pop};
2332 wire landing = a2_v && l_wr == t_next;
2333 wire [26:0] tr_n = (rq_take && t_wr == t_next) ? t_new : t_row[t_next];
2334 // The head's line: the queue's word at the next head, registered as block RAM reads it, or the line landing there
2335 // that clock, registered beside it.
2336 reg [511:0] lo_m, hi_m, lo_b, hi_b;
2337 reg land_d;
2338 always @(posedge clk) begin
2339 tr <= tr_n[7:0];
2340 tb <= tr_n[8];
2341 ts <= tr_n[16:9];
2342 tn <= tr_n[17];
2343 tsh <= tr_n[18];
2344 tj <= tr_n[26:19];
2345 lo_m <= lo_q[t_next];
2346 hi_m <= hi_q[t_next];
2347 lo_b <= a2_lo;
2348 hi_b <= a2_hi;
2349 land_d <= landing;
2350 end
2351 assign lo = land_d ? lo_b : lo_m;
2352 assign hi = land_d ? hi_b : hi_m;
2353 // The flushed rows packed into lines and queued for memory, the flushed head's rows in order, each line with its row's
2354 // parity (w_np: the lines queued of each).
2355 `LOGOS_RAMSTYLE_ASYNC(16) reg [511:0] wfifo_d [0:15];
2356 `LOGOS_RAMSTYLE_ASYNC(16) reg [31:0] wfifo_l [0:15];
2357 `LOGOS_RAMSTYLE_ASYNC(16) reg wfifo_p [0:15];
2358 reg [3:0] w_wr, w_rd;
2359 reg [4:0] w_np0, w_np1;
2360 // The replayed batches, in the order replayed (xq: each one's region, share's first row, flush bank, first head, heads
2361 // and row parity), and of each row parity those whose lines are not all queued (xr0, xr1); the flush writer's batch:
2362 // the head and row its next line is of.
2363 `LOGOS_RAMSTYLE(4) reg [31:0] xq_region [0:3];
2364 `LOGOS_RAMSTYLE_ASYNC(4) reg [15:0] xq_from [0:3];
2365 `LOGOS_RAMSTYLE(4) reg [1:0] xq_bank [0:3];
2366 `LOGOS_RAMSTYLE_ASYNC(4) reg [31:0] xq_h0 [0:3];
2367 `LOGOS_RAMSTYLE(4) reg [7:0] xq_slots [0:3];
2368 `LOGOS_RAMSTYLE(4) reg xq_par [0:3];
2369 reg [1:0] xq_wr, xq_rd;
2370 reg [2:0] xq_n;
2371 reg [3:0] xr0, xr1;
2372 reg x_act, x_par;
2373 reg [31:0] x_region;
2374 reg [15:0] x_from;
2375 reg [1:0] x_bank;
2376 reg [7:0] x_slots;
2377 reg [31:0] xh;
2378 reg [15:0] x_row, x_line;
2379 reg [7:0] x_hb;
2380 wire [47:0] x_base = {16'd0, x_region} * 48'd64;
2381 wire [47:0] x_byte = x_base + mem_off + (xh * HD + x_row) * HD * eb + 64 * x_line;
2382 assign wq_valid = w_n != 5'd0;
2383 assign wq_line = wfifo_l[w_rd];
2384 assign wq_data = wfifo_d[w_rd];
2385 wire w_pop = wq_valid && wq_ready;
2386 wire [4:0] w_next = w_n + (x_line_push ? 5'd1 : 5'd0) - (w_pop ? 5'd1 : 5'd0);
2387 // The read-outs, head by head behind the rows, row after row: the head and row the next is of, and its row's read-outs'
2388 // beats (o_set once the row's are taken up).
2389 reg [511:0] o_beat;
2390 reg [31:0] oh;
2391 reg [15:0] o_row;
2392 reg o_set;
2393 reg [SA-1:0] o_out;
2394 wire [31:0] o_wa = o_out + ((oh * HD + o_row) >> 4);
2395 // Row dn_n done once it is streamed and read out, none of its batches waits to replay, and none of its lines is queued.
2396 wire dn_past = o_n != dn_n && st_n != dn_n && (dn_n[0] ? xr1 == 4'd0 && w_np1 == 5'd0 : xr0 == 4'd0 && w_np0 == 5'd0);
2397 assign busy = in_n != dn_n || w_n != 5'd0;
2398 always @(posedge clk) begin
2399 f_log_wr <= 1'b0; f_q_wr <= 1'b0; f_start <= 1'b0; st_we <= 1'b0; q_pending <= 1'b0; fw_wr <= 1'b0; fm_start <= 1'b0;
2400 x_tog <= !x_tog;
2401 x_hist <= {x_hist[XD-1:0], x_ask};
2402 x_ask <= !rst && (!x_two || !x_tog) && (x_two ? x_free2 >= (x_ask ? 7'sd4 : 7'sd2) : x_free1 >= (x_ask ? 7'sd2 : 7'sd1));
2403 x_free1 <= 7'sd16 - $signed({2'b0, w_next}) - $signed({4'b0, x_held});
2404 x_free2 <= 7'sd16 - $signed({2'b0, w_next}) - $signed({3'b0, x_held, 1'b0});
2405 if (rq_take) t_wr <= t_wr + 1'b1;
2406 if (rs_valid) l_in <= l_in + 1'b1;
2407 if (a2_v) l_wr <= l_wr + 1'b1;
2408 owed <= owed + (rq_take ? 10'd1 : 10'd0) - (pop ? 10'd1 : 10'd0);
2409 owed_b0 <= owed_b0 + ((rq_take && !sb) ? 10'd1 : 10'd0) - ((pop && !tb) ? 10'd1 : 10'd0);
2410 owed_b1 <= owed_b1 + ((rq_take && sb) ? 10'd1 : 10'd0) - ((pop && tb) ? 10'd1 : 10'd0);
2411 if (start) in_n <= in_n + 8'd1;
2412 done <= !rst && dn_past;
2413 if (dn_past) dn_n <= dn_n + 8'd1;
2414 // The fold's run: started by the loader, finished once it falls idle after its start.
2415 if (f_start) f_run <= 1'b1;
2416 else if (f_run && !f_busy) begin f_run <= 1'b0; folded <= folded + 8'd1; end
2417 // ── the loader ──
2418 if (q_lo) begin f_q_wr <= 1'b1; f_q_slot <= q_lo_slot; f_q_beat <= q_lo_at; f_q_data <= st_rd; end
2419 if (w2_v) case (w2_k)
2420 K_DEC: ldec_h <= w_elem;
2421 K_BETA: beta_h <= w_elem;
2422 K_KEY: key_h <= w_beat;
2423 default: begin
2424 f_log_wr <= 1'b1; f_log_at <= w2_e; f_log_beat <= w2_j; f_log_k <= key_h; f_log_v <= w_beat;
2425 f_log_decay <= ldec_h; f_log_beta <= beta_h;
2426 if (!w2_j[0]) begin lo_k <= key_h; lo_v <= w_beat; end
2427 else begin fw_wr <= W > 1; fw_beat <= w2_j >> 1; fw_k <= {key_h, lo_k}; fw_v <= {w_beat, lo_v}; end
2428 end
2429 endcase
2430 case (ls)
2431 // The loader's row taken up once taken, its heads loaded a batch at a time, then the next row.
2432 L_IDLE: if (l_act && lh < nv && can_load) begin
2433 lslot <= 8'd0; qj <= 16'd0; ls <= L_Q;
2434 if (l_flush) fb_busy[lbank] <= 1'b1;
2435 end else if (l_act && lh >= nv) begin ld_n <= ld_n + 8'd1; l_act <= 1'b0; end
2436 else if (!l_act && ld_n != in_n) begin
2437 l_count <= e_count[ld_n[0]]; l_flush <= e_flush[ld_n[0]]; l_par <= e_par[ld_n[0]]; l_q <= e_q[ld_n[0]];
2438 lh <= first; lg <= first_g; l_act <= 1'b1;
2439 end
2440 L_Q: if (qj == BEATS) begin ge <= 8'd0; gi <= 16'd0; e_at <= l_par ? COPY_LINES * 32'd64 : 32'd0; ls <= L_LOG; end
2441 else if (st_gnt) begin q_pending <= 1'b1; q_at <= qj; qj <= qj + 16'd1; end
2442 L_LOG: if (walking) begin
2443 if (gi == 16'd1 + 2 * BEATS) begin gi <= 16'd0; ge <= ge + 8'd1; e_at <= e_at + entry; end else gi <= gi + 16'd1;
2444 end else ls <= L_TAIL;
2445 // The walk's last lines through the copy and the widen, then the batch's next head or its fold.
2446 L_TAIL: if (!w1_v && !w2_v && !f_log_wr && (SRL == 1 || (!q_pending && !q_flight))) begin
2447 lh <= lh + STEP;
2448 lg <= (lg + g_step >= nk) ? lg + g_step - nk : lg + g_step;
2449 if (lslot + 8'd1 == bs || lh + STEP >= nv) ls <= L_FOLD;
2450 else begin lslot <= lslot + 8'd1; qj <= 16'd0; ls <= L_Q; end
2451 end
2452 L_FOLD: begin
2453 f_start <= 1'b1; fold_bank <= loaded[0]; loaded <= loaded + 8'd1; lbank <= (lbank == 2'd2) ? 2'd0 : lbank + 2'd1;
2454 f_entries <= l_count; f_older <= l_flush ? WINDOW8 : 8'd0;
2455 ls <= L_IDLE;
2456 end
2457 default: ;
2458 endcase
2459 // ── the streamer ──
2460 if (feed) begin
2461 if (W > 1) begin
2462 t_rd <= t_rd + 1'b1;
2463 if (!f16) begin half <= !half; if (held_line) row_lo <= lo; end
2464 end else if (last_half) begin half <= 1'b0; t_rd <= t_rd + 1'b1; end else half <= 1'b1;
2465 end
2466 // The flush writer: a replayed batch's lines queued head by head through its share's rows; its bank free and the
2467 // next batch taken up with its last line, or once one is queued.
2468 if (x_line_push) begin
2469 wfifo_d[w_wr] <= x_line_data;
2470 wfifo_l[w_wr] <= x_byte[37:6];
2471 wfifo_p[w_wr] <= x_par;
2472 w_wr <= w_wr + 4'd1;
2473 if (x_line + 16'd1 == row_lines) begin
2474 x_line <= 16'd0;
2475 if (x_row + 16'd1 == x_from + SHARE) begin
2476 x_row <= x_from; xh <= xh + STEP;
2477 if (x_hb + 8'd1 == x_slots) begin
2478 x_hb <= 8'd0; x_act <= 1'b0; fb_busy[x_bank] <= 1'b0;
2479 if (x_par) xr1 <= xr1 - 4'd1; else xr0 <= xr0 - 4'd1;
2480 end else x_hb <= x_hb + 8'd1;
2481 end else x_row <= x_row + 16'd1;
2482 end else x_line <= x_line + 16'd1;
2483 end
2484 if (xq_n != 3'd0 && (!x_act || (x_line_push && x_line + 16'd1 == row_lines && x_row + 16'd1 == x_from + SHARE && x_hb + 8'd1 == x_slots))) begin
2485 x_region <= xq_region[xq_rd]; x_from <= xq_from[xq_rd]; x_row <= xq_from[xq_rd]; x_bank <= xq_bank[xq_rd];
2486 xh <= xq_h0[xq_rd]; x_slots <= xq_slots[xq_rd]; x_par <= xq_par[xq_rd]; x_hb <= 8'd0; x_line <= 16'd0; x_act <= 1'b1;
2487 xq_rd <= xq_rd + 2'd1;
2488 end
2489 if (W == 1 && x_ov) begin
2490 if (f16 && !x_hi) begin x_low <= x_halves[255:0]; x_hi <= 1'b1; end else x_hi <= 1'b0;
2491 end
2492 x_pend <= W > 1 && x_ov && !f16;
2493 if (x_ov) x_out_hi <= x_out[512*W-1:512*W-512];
2494 if (w_pop) w_rd <= w_rd + 4'd1;
2495 w_n <= w_next;
2496 w_np0 <= w_np0 + ((x_line_push && !x_par) ? 5'd1 : 5'd0) - ((w_pop && !wfifo_p[w_rd]) ? 5'd1 : 5'd0);
2497 w_np1 <= w_np1 + ((x_line_push && x_par) ? 5'd1 : 5'd0) - ((w_pop && wfifo_p[w_rd]) ? 5'd1 : 5'd0);
2498 // The read-outs into the store; past a row's last head, the next row's read-out beats taken up at once when the row
2499 // is already taken, or once it is.
2500 if (r_ov) begin
2501 o_beat[32 * o_row[3:0] +: 32] <= r_out;
2502 if (o_row[3:0] == 4'd15) begin st_we <= 1'b1; st_wa <= o_wa[SA-1:0]; st_wd <= {r_out, o_beat[479:0]}; end
2503 if (o_row + 16'd1 == HD_ROWS) begin
2504 o_row <= 16'd0;
2505 if (oh + STEP >= nv) begin
2506 o_n <= o_n + 8'd1; oh <= first; o_out <= e_out[~o_n[0]]; o_set <= o_n + 8'd1 != in_n;
2507 end else oh <= oh + STEP;
2508 end else o_row <= o_row + 16'd1;
2509 end else if (!o_set && o_n != in_n) begin o_out <= e_out[o_n[0]]; oh <= first; o_set <= 1'b1; end
2510 else if (o_set && oh >= nv) begin o_n <= o_n + 8'd1; o_set <= 1'b0; end
2511 case (ss)
2512 // A folded batch's next head, once every line of the head two back, the last to take its buffer, is through the
2513 // units.
2514 // The streamer's row taken up once taken, its batches streamed, then the next row.
2515 S_IDLE: if (s_act && sh < nv && folded != copied && (sb ? owed_b1 : owed_b0) == 10'd0) begin
2516 cb <= 16'd0; ss <= S_COPY;
2517 if (sslot == 8'd0) s_h0 <= sh;
2518 end else if (s_act && sh >= nv) begin st_n <= st_n + 8'd1; s_act <= 1'b0; end
2519 else if (!s_act && st_n != in_n) begin
2520 s_region <= e_region[st_n[0]]; s_flush <= e_flush[st_n[0]]; s_from <= e_from[st_n[0]]; s_par <= st_n[0];
2521 sh <= first; s_act <= 1'b1;
2522 end
2523 S_COPY: if (cb == BEATS + 16'd2) begin cb <= 16'd0; row_req <= 16'd0; ri <= 16'd0; ss <= S_ROWS; end else cb <= cb + 16'd1;
2524 S_ROWS: begin
2525 if (rq_take) begin
2526 if (ri + 16'd1 == row_lines) begin ri <= 16'd0; row_req <= row_req + 16'd1; end else ri <= ri + 16'd1;
2527 end
2528 if (row_req == HD_ROWS) ss <= S_NEXT;
2529 end
2530 // The batch's next head, its rows asked while the last head's are still on their way, each line carrying its head's
2531 // slot; a token that replays waits at the batch's end until every line is through the read-out, so each share row
2532 // went into its head's slot, then replays the share.
2533 S_NEXT: if (!(s_flush && batch_end) || owed == 10'd0) begin
2534 sh <= sh + STEP;
2535 sb <= !sb;
2536 if (batch_end) begin
2537 if (s_flush) ss <= S_FLUSH;
2538 else begin sslot <= 8'd0; copied <= copied + 8'd1; cbank <= (cbank == 2'd2) ? 2'd0 : cbank + 2'd1; ss <= S_IDLE; end
2539 end else begin sslot <= sslot + 8'd1; ss <= S_IDLE; end
2540 end
2541 // The batch's bank replayed once its last share row is written; the batch queued for the flush writer.
2542 S_FLUSH: if (!fm_wr) begin
2543 fm_start <= 1'b1; fm_sbank <= cbank; fm_slots <= sslot + 8'd1; fm_first <= s_from; sslot <= 8'd0;
2544 copied <= copied + 8'd1; cbank <= (cbank == 2'd2) ? 2'd0 : cbank + 2'd1; ss <= S_IDLE;
2545 xq_region[xq_wr] <= s_region; xq_from[xq_wr] <= s_from; xq_bank[xq_wr] <= cbank; xq_h0[xq_wr] <= s_h0;
2546 xq_slots[xq_wr] <= sslot + 8'd1; xq_par[xq_wr] <= s_par; xq_wr <= xq_wr + 2'd1;
2547 if (s_par) xr1 <= xr1 + 4'd1; else xr0 <= xr0 + 4'd1;
2548 end
2549 default: ;
2550 endcase
2551 xq_n <= xq_n + (ss == S_FLUSH && !fm_wr ? 3'd1 : 3'd0)
2552 - ((xq_n != 3'd0 && (!x_act || (x_line_push && x_line + 16'd1 == row_lines && x_row + 16'd1 == x_from + SHARE && x_hb + 8'd1 == x_slots))) ? 3'd1 : 3'd0);
2553 if (rst) begin
2554 in_n <= 8'd0; ld_n <= 8'd0; st_n <= 8'd0; o_n <= 8'd0; dn_n <= 8'd0; l_act <= 1'b0; s_act <= 1'b0; o_set <= 1'b0;
2555 x_act <= 1'b0; xq_wr <= 2'd0; xq_rd <= 2'd0; xq_n <= 3'd0; xr0 <= 4'd0; xr1 <= 4'd0; fb_busy <= 3'd0; w_np0 <= 5'd0;
2556 w_np1 <= 5'd0; loaded <= 8'd0; folded <= 8'd0; copied <= 8'd0; lbank <= 2'd0; cbank <= 2'd0; sslot <= 8'd0; sb <= 1'b0;
2557 o_row <= 16'd0; x_hb <= 8'd0; x_line <= 16'd0; x_hi <= 1'b0; done <= 1'b0;
2558 ls <= L_IDLE; ss <= S_IDLE; f_run <= 1'b0; t_wr <= {QB{1'b0}}; t_rd <= {QB{1'b0}}; l_in <= {QB{1'b0}};
2559 l_wr <= {QB{1'b0}}; w_wr <= 4'd0; w_rd <= 4'd0; w_n <= 5'd0; half <= 1'b0; owed <= 10'd0; owed_b0 <= 10'd0;
2560 owed_b1 <= 10'd0; x_pend <= 1'b0;
2561 x_hist <= {(XD+1){1'b0}}; x_tog <= 1'b0; x_free1 <= 7'sd16; x_free2 <= 7'sd16;
2562 end
2563 end
2564endmodule
2565"#;
2566
2567const GDN_ROWS_VERILOG: &str = r#"
2568// A recurrence job's token rows given on in order as descriptors (row_valid, taken when row_ready): the job (start) names
2569// its rows (`rows`) and the slots of its vectors (the query's, the read-outs', the key heads', the value heads' — the
2570// values from element v_at of theirs — the decay exponents' and the gates'); row k's region is the rows table's entry k
2571// and each vector's beat its slot's first plus k times the slot's beats a row, as
2572// the core's element_beat lays them. The tables are copies the host's writes keep: the rows table (table 1, an entry's
2573// region its low word) and the slots table (table 2, a slot's first beat and its beats a row). Each row's fields are
2574// registers, the next row's laid the clock its row is taken.
2575module logos_gdn_rows #(parameter SA = 10, ROW_MAX = 1)
2576 (input clk, input rst, input start, input [15:0] rows,
2577 input [4:0] q_slot, input [4:0] out_slot, input [4:0] k_slot, input [4:0] v_slot, input [19:0] v_at, input [4:0] ld_slot,
2578 input [4:0] gt_slot,
2579 input host_we, input [2:0] host_table, input [31:0] host_addr, input [511:0] host_wdata,
2580 output reg row_valid, input row_ready, output reg [31:0] region, output reg [SA-1:0] q_beat, output reg [SA-1:0] out_beat,
2581 output reg [SA-1:0] k_beat, output reg [SA-1:0] v_beat, output reg [SA-1:0] ld_beat, output reg [SA-1:0] gt_beat);
2582 localparam RB = ROW_MAX > 1 ? $clog2(ROW_MAX) : 1;
2583 `LOGOS_RAMSTYLE_ASYNC(ROW_MAX) reg [31:0] rowt [0:ROW_MAX-1];
2584 `LOGOS_RAMSTYLE_ASYNC(32) reg [63:0] slott [0:31];
2585 always @(posedge clk)
2586 if (host_we) case (host_table)
2587 3'd1: rowt[host_addr[RB-1:0]] <= host_wdata[31:0];
2588 3'd2: slott[host_addr[4:0]] <= host_wdata[63:0];
2589 default: ;
2590 endcase
2591 // The job's rows given (k of n) and the next row's entry in the rows table (r_at); each vector's beats a row.
2592 reg [15:0] k, n;
2593 reg [RB-1:0] r_at;
2594 reg [SA-1:0] q_st, out_st, k_st, v_st, ld_st, gt_st;
2595 reg laying;
2596 wire [63:0] sq = slott[q_slot], so = slott[out_slot], sk = slott[k_slot], sv = slott[v_slot], sl = slott[ld_slot],
2597 sg = slott[gt_slot];
2598 always @(posedge clk) begin
2599 if (laying) begin
2600 q_beat <= sq[SA-1:0]; out_beat <= so[SA-1:0]; k_beat <= sk[SA-1:0]; v_beat <= sv[SA-1:0] + v_at[SA+3:4];
2601 ld_beat <= sl[SA-1:0]; gt_beat <= sg[SA-1:0];
2602 q_st <= sq[32+SA-1:32]; out_st <= so[32+SA-1:32]; k_st <= sk[32+SA-1:32]; v_st <= sv[32+SA-1:32];
2603 ld_st <= sl[32+SA-1:32]; gt_st <= sg[32+SA-1:32];
2604 region <= rowt[r_at]; r_at <= r_at + 1'b1; row_valid <= 1'b1; laying <= 1'b0;
2605 end else if (row_valid && row_ready) begin
2606 if (k + 16'd1 == n) row_valid <= 1'b0;
2607 k <= k + 16'd1;
2608 region <= rowt[r_at]; r_at <= r_at + 1'b1;
2609 q_beat <= q_beat + q_st; out_beat <= out_beat + out_st; k_beat <= k_beat + k_st; v_beat <= v_beat + v_st;
2610 ld_beat <= ld_beat + ld_st; gt_beat <= gt_beat + gt_st;
2611 end
2612 if (start) begin
2613 k <= 16'd0; n <= rows; row_valid <= 1'b0; laying <= rows != 16'd0;
2614 r_at <= {RB{1'b0}};
2615 end
2616 if (rst) begin row_valid <= 1'b0; laying <= 1'b0; end
2617 end
2618endmodule
2619"#;
2620
2621const GDN_LANES_VERILOG: &str = r#"
2622// A recurrence job's token rows, each a lazily flushed Gated-DeltaNet block, on L head lanes. The job (start) runs `rows`
2623// rows, each given as a descriptor while row_valid and taken when row_ready: its region and the store beats of its
2624// query, read-outs, key heads, value heads, decay exponents and gates. The coordinator (logos_gdn_side in MODE 1) takes
2625// a row once the copy of the log its parity names is free — every lane done with the row two before it, which wrote that
2626// copy — and a row of the region of the row before it (two positions of one sequence) only once that row is done and
2627// every write written. It reads the log's count and appends the row's entry on the memory port (rq_*, wq_*, written) as
2628// the side does, reads the log's entries once into that copy of every lane's (LOG_LINES lines each, addressed by LBA
2629// bits), hands the row to each lane as the lane has room, writes the count back, and takes the next row while the lanes
2630// run this one. Lane k (logos_gdn_lane) takes value heads k, k + L, … of each row, its log from its copy and its rows
2631// from its own memory port (l_*, lane k's at k), its queries from the store on the clocks the store's read is its — the
2632// coordinator's appending reads first, then the lanes asking in turn, each keeping the read for a query's beats — and
2633// its read-outs into the store through a queue of its own, the queues written a beat a clock in turn. A lane's memory
2634// port is logical as the side's: lines back in the order asked, no wait taken on them. Value heads one granule each, on
2635// four channels, with L = 4 put all of lane k's heads on one channel. Busy from the job's start until every lane has done
2636// every row, every read-out is in the store, the coordinator is idle and every write is written. A job whose log, at
2637// `tokens` entries, would take more lines than a copy holds takes its rows and runs none: `fault` from its start until
2638// the next.
2639module logos_gdn_lanes #(parameter HD = 128, TMAX = 16, SA = 10, parameter [8:0] OUTSTANDING = 32, parameter W = 1,
2640 parameter L = 4, LBA = 13, LOG_LINES = 8192, FLUSHES = 1, SRL = 1)
2641 (input clk, input rst,
2642 input start, output busy, input [15:0] rows, input row_valid, output row_ready,
2643 input [31:0] region, input [31:0] mem_off, input [31:0] log_off, input [31:0] entry, input [31:0] nk, input [31:0] nv,
2644 input [31:0] fval, input [31:0] fdec, input [31:0] fgate, input [31:0] scale, input [7:0] tokens, input f16,
2645 input [SA-1:0] q_beat, input [SA-1:0] out_beat, input [SA-1:0] k_beat, input [SA-1:0] v_beat, input [SA-1:0] ld_beat,
2646 input [SA-1:0] gt_beat,
2647 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,
2648 output rq_valid, input rq_ready, output [31:0] rq_line, input rs_valid, input [511:0] rs_data,
2649 output wq_valid, input wq_ready, output [31:0] wq_line, output [511:0] wq_data, input written, output reg fault,
2650 output [L-1:0] l_rq_valid, input [L-1:0] l_rq_ready, output [32*L-1:0] l_rq_line, input [L-1:0] l_rs_valid,
2651 input [512*L-1:0] l_rs_data, output [L-1:0] l_wq_valid, input [L-1:0] l_wq_ready, output [32*L-1:0] l_wq_line,
2652 output [512*L-1:0] l_wq_data, input [L-1:0] l_written);
2653 localparam LB = L > 1 ? $clog2(L) : 1;
2654 localparam [LB-1:0] LAST = L - 1;
2655 localparam [39:0] HOLDS = LOG_LINES;
2656 localparam [LBA:0] SECOND = LOG_LINES;
2657 wire co_ready, lb_we, co_flush, co_busy, co_idle, co_reading;
2658 wire [LBA-1:0] lb_wa;
2659 wire [511:0] lb_wd;
2660 wire [7:0] co_count;
2661 wire [31:0] log_first, log_lines;
2662 wire [SA-1:0] co_ra;
2663 // What the coordinator does not drive: store writes.
2664 wire co_we;
2665 wire [SA-1:0] co_wa;
2666 wire [511:0] co_wd;
2667 wire [39:0] log_need = {32'd0, tokens} * {14'd0, entry[31:6]};
2668 wire too_long = log_need > HOLDS;
2669 // The job: its rows, those the coordinator has taken, and those every lane has done (lane k's count in l_fin).
2670 wire [L-1:0] l_busy, l_room, l_done, want, s_have;
2671 reg job;
2672 reg [15:0] n_rows, taken, done_all;
2673 reg [16*L-1:0] l_fin;
2674 integer di;
2675 always @* begin
2676 done_all = l_fin[15:0];
2677 for (di = 1; di < L; di = di + 1) if (l_fin[16*di +: 16] < done_all) done_all = l_fin[16*di +: 16];
2678 end
2679 // The row the coordinator runs: the copy it writes (its parity) and the fields each lane takes with it; and the region
2680 // of the row before.
2681 reg c_par;
2682 reg [31:0] c_region, last_region;
2683 reg [SA-1:0] c_q, c_out;
2684 wire copy_free = taken < 16'd2 || done_all + 16'd1 >= taken;
2685 wire same = taken != 16'd0 && region == last_region;
2686 wire quiet = l_busy == {L{1'b0}} && s_have == {L{1'b0}} && &l_written && written && !co_busy;
2687 wire co_go = job && !fault && row_valid && taken != n_rows && co_idle && copy_free && (!same || (done_all == taken && quiet));
2688 wire skip = job && fault && row_valid && taken != n_rows;
2689 assign row_ready = co_go || skip;
2690 // The row handed to each lane as the lane has room, once the log's last line is in every copy (co_ready a clock
2691 // old); taken once every lane has it.
2692 reg handing;
2693 reg [L-1:0] took;
2694 wire [L-1:0] l_go = {L{co_ready && handing}} & ~took & l_room;
2695 wire co_taken = co_ready && handing && &(took | l_go);
2696 assign busy = job;
2697 integer fi;
2698 always @(posedge clk) begin
2699 handing <= !rst && co_ready && !co_taken;
2700 took <= co_taken ? {L{1'b0}} : took | l_go;
2701 for (fi = 0; fi < L; fi = fi + 1) if (l_done[fi]) l_fin[16*fi +: 16] <= l_fin[16*fi +: 16] + 16'd1;
2702 if (co_go) begin c_par <= taken[0]; c_region <= region; c_q <= q_beat; c_out <= out_beat; last_region <= region; end
2703 if (row_ready) taken <= taken + 16'd1;
2704 if (job && (fault ? taken == n_rows : done_all == n_rows) && quiet) job <= 1'b0;
2705 if (start) begin job <= 1'b1; n_rows <= rows; taken <= 16'd0; fault <= too_long; l_fin <= {16*L{1'b0}}; end
2706 if (rst) begin job <= 1'b0; fault <= 1'b0; took <= {L{1'b0}}; taken <= 16'd0; n_rows <= 16'd0; l_fin <= {16*L{1'b0}}; end
2707 end
2708 logos_gdn_side #(.SRL(SRL), .HD(HD), .TMAX(TMAX), .SA(SA), .OUTSTANDING(OUTSTANDING), .W(W), .MODE(1), .LBA(LBA)) co(.clk(clk),
2709 .rst(rst), .start(co_go), .busy(co_busy), .region(region), .mem_off(mem_off), .log_off(log_off), .entry(entry), .nk(nk),
2710 .nv(nv), .fval(fval), .fdec(fdec), .fgate(fgate), .scale(scale), .tokens(tokens), .f16(f16), .q_beat(q_beat),
2711 .out_beat(out_beat), .k_beat(k_beat), .v_beat(v_beat), .ld_beat(ld_beat), .gt_beat(gt_beat), .st_ra(co_ra),
2712 .st_rd(st_rd), .st_we(co_we), .st_wa(co_wa), .st_wd(co_wd), .rq_valid(rq_valid), .rq_ready(rq_ready), .rq_line(rq_line),
2713 .rs_valid(rs_valid), .rs_data(rs_data), .wq_valid(wq_valid), .wq_ready(wq_ready), .wq_line(wq_line), .wq_data(wq_data),
2714 .written(written), .co_ready(co_ready),
2715 .co_taken(co_taken), .lb_we(lb_we), .lb_wa(lb_wa), .lb_wd(lb_wd), .co_count(co_count), .co_flush(co_flush),
2716 .log_first(log_first), .log_lines(log_lines), .co_reading(co_reading), .co_idle(co_idle));
2717 // The coordinator's copy writes, a clock on, into the copy its row's parity names (the second LOG_LINES lines in).
2718 reg cw_we;
2719 reg [LBA:0] cw_wa;
2720 reg [511:0] cw_wd;
2721 always @(posedge clk) begin
2722 cw_we <= !rst && lb_we;
2723 cw_wa <= c_par ? {1'b0, lb_wa} + SECOND : {1'b0, lb_wa};
2724 cw_wd <= lb_wd;
2725 end
2726 // The store's read: the coordinator's while it appends; otherwise a lane's while it holds it (held, holder — taken by
2727 // the first lane asking the clock after the last holder stops), a lane's read granted on the clocks it holds it and
2728 // asks and the coordinator does not read.
2729 wire [SA*L-1:0] l_ra;
2730 reg [LB-1:0] holder, pick;
2731 reg held, any;
2732 integer pi;
2733 always @* begin
2734 any = 1'b0;
2735 pick = {LB{1'b0}};
2736 for (pi = 0; pi < L; pi = pi + 1) if (!any && want[pi]) begin any = 1'b1; pick = pi[LB-1:0]; end
2737 end
2738 wire keeps = held && want[holder];
2739 assign st_ra = co_reading ? co_ra : held ? l_ra[SA*holder +: SA] : co_ra;
2740 always @(posedge clk) begin
2741 if (!keeps) begin held <= !rst && any; holder <= pick; end
2742 if (rst) held <= 1'b0;
2743 end
2744 // Each lane's read-outs queued a beat at a time and written into the store on its own clocks, lane k's when the slot
2745 // counter is k: a lane makes a beat at most every sixteen clocks (a beat is sixteen rows' read-outs, a read-out a
2746 // clock) and has a slot every L ≤ 4, so no queue holds more than one.
2747 wire [(SA+512)*L-1:0] s_head;
2748 reg [LB-1:0] s_turn;
2749 always @(posedge clk) begin
2750 st_we <= !rst && s_have[s_turn];
2751 {st_wa, st_wd} <= s_head[(SA+512)*s_turn +: SA+512];
2752 s_turn <= (s_turn == LAST) ? {LB{1'b0}} : s_turn + 1'b1;
2753 if (rst) s_turn <= {LB{1'b0}};
2754 end
2755 genvar k;
2756 generate
2757 for (k = 0; k < L; k = k + 1) begin : lane
2758 localparam [31:0] FIRST = k;
2759 localparam [LB-1:0] LANE = k;
2760 wire we;
2761 wire [SA-1:0] wa;
2762 wire [511:0] wd;
2763 wire [LBA:0] b_ra;
2764 wire [511:0] b_rd;
2765 logos_gdn_lane #(.SRL(SRL), .HD(HD), .TMAX(TMAX), .SA(SA), .OUTSTANDING(OUTSTANDING), .W(W), .STEP(L), .LBA(LBA), .FLUSHES(FLUSHES), .COPY_LINES(LOG_LINES))
2766 lane(.clk(clk),
2767 .rst(rst), .start(l_go[k]), .busy(l_busy[k]), .room(l_room[k]), .done(l_done[k]), .in_region(c_region), .mem_off(mem_off),
2768 .entry(entry), .nk(nk), .nv(nv), .fval(fval), .fdec(fdec), .fgate(fgate), .scale(scale), .f16(f16), .in_q_beat(c_q),
2769 .in_out_beat(c_out), .first(FIRST), .in_count(co_count), .in_flush(co_flush), .in_par(c_par), .st_rq(want[k]),
2770 .st_gnt(keeps && holder == LANE && !co_reading),
2771 .st_ra(l_ra[SA*k +: SA]), .st_rd(st_rd), .st_we(we), .st_wa(wa), .st_wd(wd), .lb_ra(b_ra), .lb_rd(b_rd),
2772 .rq_valid(l_rq_valid[k]), .rq_ready(l_rq_ready[k]), .rq_line(l_rq_line[32*k +: 32]), .rs_valid(l_rs_valid[k]),
2773 .rs_data(l_rs_data[512*k +: 512]), .wq_valid(l_wq_valid[k]), .wq_ready(l_wq_ready[k]), .wq_line(l_wq_line[32*k +: 32]),
2774 .wq_data(l_wq_data[512*k +: 512]));
2775 // The lane's two copies of the log, written as the coordinator reads it, read a clock after their address.
2776 logos_bram_w512 #(.D(2 * LOG_LINES), .A(LBA + 1)) copy(.clk(clk), .ra(b_ra), .rd(b_rd), .we(cw_we), .wa(cw_wa), .wd(cw_wd),
2777 .wm({64{1'b1}}));
2778 `LOGOS_RAMSTYLE_ASYNC(4) reg [SA+511:0] q [0:3];
2779 reg [1:0] q_wr, q_rd;
2780 reg [2:0] q_n;
2781 wire pop = s_turn == LANE && s_have[k];
2782 assign s_have[k] = q_n != 3'd0;
2783 assign s_head[(SA+512)*k +: SA+512] = q[q_rd];
2784 always @(posedge clk) begin
2785 if (we) begin q[q_wr] <= {wa, wd}; q_wr <= q_wr + 2'd1; end
2786 if (pop) q_rd <= q_rd + 2'd1;
2787 q_n <= q_n + (we ? 3'd1 : 3'd0) - (pop ? 3'd1 : 3'd0);
2788 if (rst) begin q_wr <= 2'd0; q_rd <= 2'd0; q_n <= 3'd0; end
2789 end
2790 end
2791 endgenerate
2792endmodule
2793"#;
2794
2795const GDN_FOLD_M_VERILOG: &str = r#"
2797// H heads' queries folded back through their logs at once, each as gdn_fold folds it: the heads in lockstep, a beat of
2798// each in turn, so with H the add's clocks a lane's running dot of a head comes back to its add as the head's next beat
2799// enters, and one head's dot, tree, gate and update are the other heads' issues rather than gaps. Each head's log (its
2800// tokens' keys, values, log decays and gates) and its query are written into its slot; a fold of `entries` tokens,
2801// newest first, folds every slot into one of two banks (`bank`, taken at its start), the first token's update taking
2802// the query and no collected values; the folded queries and collected values are read back from either bank, by slot
2803// (a bank not being folded into), two clocks after their address, and the decays of the last fold by slot. Every memory
2804// is written a word a clock and read into a register, as block RAM reads; a bank's address is a register beside it. TRAILING keeps, beside each bank, the fold
2805// once it has passed every token after the log's oldest `older` (taken at its start): the fold over the newer tokens
2806// alone, read back as the bank is (ynew_beat, cnew_beat); without it those are the bank's own.
2807module logos_gdn_fold_m #(parameter N = 128, T_MAX = 16, TRAILING = 0)
2808 (input clk, input rst,
2809 input log_wr, input [7:0] log_slot, input [7:0] log_at, input [15:0] log_beat, input [511:0] log_k, input [511:0] log_v,
2810 input [31:0] log_decay, input [31:0] log_beta,
2811 input q_wr, input [7:0] q_slot, input [15:0] q_beat, input [511:0] q_data,
2812 input start, input bank, input [7:0] entries, input [7:0] older, output busy,
2813 input rd_bank, input [7:0] rd_slot, input [15:0] rd_beat, output [511:0] y_beat, output [511:0] carried_beat,
2814 output [511:0] ynew_beat, output [511:0] cnew_beat,
2815 input [7:0] rd_token, output [31:0] decay_out);
2816 localparam H = @FADD@;
2817 localparam [7:0] HS = H;
2818 localparam [15:0] HW = H;
2819 localparam [15:0] BEATS = N / 16, ISSUES = BEATS * HW;
2820 localparam IDLE = 3'd0, EXPS = 3'd1, DOT = 3'd2, BCW = 3'd3, UPD = 3'd4, FILL = 3'd5, BARE = 3'd6;
2821 `LOGOS_RAMSTYLE(H*T_MAX*BEATS) reg [511:0] kmem [0:H*T_MAX*BEATS-1];
2822 `LOGOS_RAMSTYLE(H*T_MAX*BEATS) reg [511:0] vmem [0:H*T_MAX*BEATS-1];
2823 `LOGOS_RAMSTYLE(H*T_MAX) reg [31:0] log_decays [0:H*T_MAX-1];
2824 `LOGOS_RAMSTYLE(H*T_MAX) reg [31:0] betas [0:H*T_MAX-1];
2825 `LOGOS_RAMSTYLE(H*BEATS) reg [511:0] qmem [0:H*BEATS-1];
2826 always @(posedge clk) begin
2827 if (log_wr) begin
2828 kmem[(log_slot * T_MAX + log_at) * BEATS + log_beat] <= log_k;
2829 vmem[(log_slot * T_MAX + log_at) * BEATS + log_beat] <= log_v;
2830 if (log_beat == 16'd0) begin log_decays[log_slot * T_MAX + log_at] <= log_decay; betas[log_slot * T_MAX + log_at] <= log_beta; end
2831 end
2832 if (q_wr) qmem[q_slot * BEATS + q_beat] <= q_data;
2833 end
2834 // The two banks of folded queries and collected values.
2835 `LOGOS_RAMSTYLE(H*BEATS) reg [511:0] y0 [0:H*BEATS-1];
2836 `LOGOS_RAMSTYLE(H*BEATS) reg [511:0] y1 [0:H*BEATS-1];
2837 `LOGOS_RAMSTYLE(H*BEATS) reg [511:0] c0 [0:H*BEATS-1];
2838 `LOGOS_RAMSTYLE(H*BEATS) reg [511:0] c1 [0:H*BEATS-1];
2839 wire [15:0] rd_at = rd_slot * BEATS + rd_beat;
2840 `LOGOS_RAMSTYLE_ASYNC(H*T_MAX) reg [31:0] decays [0:H*T_MAX-1];
2841 assign decay_out = decays[rd_slot * T_MAX + rd_token];
2842 reg [2:0] state;
2843 reg fb;
2844 reg [7:0] e, xs, xt, got, fo;
2845 reg [15:0] i, written;
2846 reg [7:0] caught;
2847 `LOGOS_RAMSTYLE(H) reg [31:0] bc [0:H-1];
2848 reg bare_p, bare_r;
2849 assign busy = state != IDLE || bare_p || bare_r;
2850 // The decays: every slot's tokens' log decays through the exponential, back to back, slot by slot, each read a clock
2851 // before it enters.
2852 wire [31:0] ex;
2853 reg [@EXP@:1] ex_v;
2854 reg [15:0] ex_at [1:@EXP@];
2855 reg [31:0] x_r;
2856 reg x_v;
2857 reg [15:0] x_at;
2858 logos_exp_p xp(.clk(clk), .x_in(x_r), .y(ex));
2859 integer k;
2860 wire exp_issue = state == EXPS && xs != HS;
2861 always @(posedge clk) begin
2862 x_r <= log_decays[xs * T_MAX + xt];
2863 x_v <= !rst && exp_issue;
2864 x_at <= xs * T_MAX + xt;
2865 ex_v <= rst ? {@EXP@{1'b0}} : {ex_v[@EXP@-1:1], x_v};
2866 ex_at[1] <= x_at;
2867 for (k = 2; k <= @EXP@; k = k + 1) ex_at[k] <= ex_at[k-1];
2868 if (ex_v[@EXP@]) decays[ex_at[@EXP@]] <= ex;
2869 end
2870 // An issue's slot and beat: beat-major, so a slot's beats are H issues apart. The newest token's issues take the
2871 // query and no collected values; later ones the bank's.
2872 wire [15:0] s_w = i % HW;
2873 wire [7:0] s = s_w[7:0];
2874 wire [15:0] b = i / HW;
2875 wire [15:0] at = s * BEATS + b;
2876 wire fresh = e + 8'd1 == entries;
2877 // Each bank read at one address, the fold's while it folds into the bank, the read-back's otherwise, the query beside
2878 // them at the fold's: the address taken into a register copied near the banks it reaches (_p), the word registered the
2879 // clock after. An issue's marks take the same register stage below, so each of its reads and writes lands a clock
2880 // later than its choice, every one in the order it was.
2881 wire [15:0] a0 = (state != IDLE && !fb) ? at : rd_at, a1 = (state != IDLE && fb) ? at : rd_at;
2882 (* maxfan = 64 *) reg [15:0] a0_p, a1_p, q_ap, rd_at_p;
2883 reg rb_p;
2884 reg [511:0] y0_r, y1_r, c0_r, c1_r, q_r;
2885 reg rb_r;
2886 always @(posedge clk) begin
2887 a0_p <= a0;
2888 a1_p <= a1;
2889 q_ap <= at;
2890 rd_at_p <= rd_at;
2891 rb_p <= rd_bank;
2892 y0_r <= y0[a0_p];
2893 y1_r <= y1[a1_p];
2894 c0_r <= c0[a0_p];
2895 c1_r <= c1[a1_p];
2896 q_r <= qmem[q_ap];
2897 rb_r <= rb_p;
2898 end
2899 assign y_beat = rb_r ? y1_r : y0_r;
2900 assign carried_beat = rb_r ? c1_r : c0_r;
2901 wire dot_issue = state == DOT;
2902 wire upd_issue = state == UPD;
2903 wire bare = state == BARE;
2904 // An issue the clock its address is registered (_p), the clock its words are read (_r), then its operands.
2905 reg d_p, u_p, fresh_p, first_p, last_p;
2906 reg [15:0] at_p, b_p;
2907 reg [7:0] s_p;
2908 reg d_r, u_r, fresh_r, first_r, last_r;
2909 reg [15:0] at_r, b_r;
2910 reg [7:0] s_r;
2911 always @(posedge clk) begin
2912 d_p <= !rst && dot_issue;
2913 u_p <= !rst && upd_issue;
2914 bare_p <= !rst && bare;
2915 fresh_p <= fresh;
2916 first_p <= b == 16'd0;
2917 last_p <= b == BEATS - 1;
2918 at_p <= at;
2919 b_p <= b;
2920 s_p <= s;
2921 d_r <= !rst && d_p;
2922 u_r <= !rst && u_p;
2923 bare_r <= !rst && bare_p;
2924 fresh_r <= fresh_p;
2925 first_r <= first_p;
2926 last_r <= last_p;
2927 at_r <= at_p;
2928 b_r <= b_p;
2929 s_r <= s_p;
2930 end
2931 reg [511:0] y_q, k_q, v_q, c_q;
2932 reg d_q, u_q, first_q, last_q;
2933 reg [15:0] at_q;
2934 reg [7:0] s_q;
2935 reg [31:0] bc_q, decay_q;
2936 always @(posedge clk) begin
2937 y_q <= fresh_r ? q_r : fb ? y1_r : y0_r;
2938 k_q <= kmem[(s_r * T_MAX + e) * BEATS + b_r];
2939 v_q <= vmem[(s_r * T_MAX + e) * BEATS + b_r];
2940 c_q <= fresh_r ? 512'd0 : fb ? c1_r : c0_r;
2941 d_q <= !rst && d_r;
2942 u_q <= !rst && u_r;
2943 first_q <= first_r;
2944 last_q <= last_r;
2945 at_q <= at_r;
2946 s_q <= s_r;
2947 bc_q <= bc[s_r];
2948 decay_q <= decays[s_r * T_MAX + e];
2949 end
2950 reg [@BC@:1] d_d, last_d, first_d;
2951 reg [7:0] s_d [1:@BC@];
2952 reg [@FOLDED@:1] u_d;
2953 reg [15:0] at_d [1:@FOLDED@];
2954 reg [511:0] c_d [1:@FMUL@];
2955 reg [511:0] y_d [1:@FMUL@];
2956 // An update's decay beside its y − bc·k, which reaches the last multiply a multiply and an add after the issue.
2957 localparam DY = @FMUL@ + @FADD@;
2958 reg [31:0] decay_d [1:DY];
2959 always @(posedge clk) begin
2960 decay_d[1] <= decay_q;
2961 for (k = 2; k <= DY; k = k + 1) decay_d[k] <= decay_d[k-1];
2962 d_d <= rst ? {@BC@{1'b0}} : {d_d[@BC@-1:1], d_q};
2963 last_d <= {last_d[@BC@-1:1], last_q};
2964 first_d <= {first_d[@BC@-1:1], first_q};
2965 s_d[1] <= s_q;
2966 for (k = 2; k <= @BC@; k = k + 1) s_d[k] <= s_d[k-1];
2967 u_d <= rst ? {@FOLDED@{1'b0}} : {u_d[@FOLDED@-1:1], u_q};
2968 at_d[1] <= at_q;
2969 for (k = 2; k <= @FOLDED@; k = k + 1) at_d[k] <= at_d[k-1];
2970 c_d[1] <= c_q;
2971 y_d[1] <= y_q;
2972 for (k = 2; k <= @FMUL@; k = k + 1) begin c_d[k] <= c_d[k-1]; y_d[k] <= y_d[k-1]; end
2973 end
2974 // The lanes: each slot's key's dot with its folded query, the slot's running sum back at its add as its next term
2975 // enters; and the update, carried + bc·v and decay·(y − bc·k).
2976 wire [511:0] sums, cnew, ynew;
2977 genvar l;
2978 generate
2979 for (l = 0; l < 16; l = l + 1) begin : lane
2980 wire [31:0] p, cv, bk, t;
2981 logos_fmul_p m(.clk(clk), .a(k_q[32*l +: 32]), .b(y_q[32*l +: 32]), .y(p));
2982 logos_fadd_p a(.clk(clk), .a(first_d[@PRODUCTS@] ? 32'd0 : sums[32*l +: 32]), .b(p), .y(sums[32*l +: 32]));
2983 logos_fmul_p mv(.clk(clk), .a(bc_q), .b(v_q[32*l +: 32]), .y(cv));
2984 logos_fadd_p ac(.clk(clk), .a(c_d[@FMUL@][32*l +: 32]), .b(cv), .y(cnew[32*l +: 32]));
2985 logos_fmul_p mk(.clk(clk), .a(bc_q), .b(k_q[32*l +: 32]), .y(bk));
2986 logos_fadd_p sy(.clk(clk), .a(y_d[@FMUL@][32*l +: 32]), .b({~bk[31], bk[30:0]}), .y(t));
2987 logos_fmul_p md(.clk(clk), .a(decay_d[DY]), .b(t), .y(ynew[32*l +: 32]));
2988 end
2989 endgenerate
2990 // Each slot's dot through the tree and its gate's product, its slot beside it.
2991 wire [31:0] dot, bcw;
2992 reg [31:0] beta_r;
2993 always @(posedge clk) beta_r <= betas[s_d[@TREE@-1] * T_MAX + e];
2994 logos_tree16_p tr(.clk(clk), .lanes(sums), .sum(dot));
2995 logos_fmul_p mb(.clk(clk), .a(beta_r), .b(dot), .y(bcw));
2996 wire bc_in = d_d[@BC@] && last_d[@BC@];
2997 wire c_back = u_d[@UPDATED@];
2998 wire y_back = u_d[@FOLDED@];
2999 // A fold of no tokens: the queries into the bank as they are and no collected values, a beat a clock, each the
3000 // clock after its query is read.
3001 always @(posedge clk) begin
3002 if (bare_r ? !fb : c_back && !fb) c0[bare_r ? at_r : at_d[@UPDATED@]] <= bare_r ? 512'd0 : cnew;
3003 if (bare_r ? fb : c_back && fb) c1[bare_r ? at_r : at_d[@UPDATED@]] <= bare_r ? 512'd0 : cnew;
3004 if (bare_r ? !fb : y_back && !fb) y0[bare_r ? at_r : at_d[@FOLDED@]] <= bare_r ? q_r : ynew;
3005 if (bare_r ? fb : y_back && fb) y1[bare_r ? at_r : at_d[@FOLDED@]] <= bare_r ? q_r : ynew;
3006 if (bc_in) bc[s_d[@BC@]] <= bcw;
3007 end
3008 // The fold over the newer tokens: the bank's writes of the token at `older` (each landing while that token is the
3009 // fold's, its last before the walk moves on) and of a fold of no tokens, kept beside the bank.
3010 generate
3011 if (TRAILING) begin : behind
3012 `LOGOS_RAMSTYLE(H*BEATS) reg [511:0] ys0 [0:H*BEATS-1];
3013 `LOGOS_RAMSTYLE(H*BEATS) reg [511:0] ys1 [0:H*BEATS-1];
3014 `LOGOS_RAMSTYLE(H*BEATS) reg [511:0] cs0 [0:H*BEATS-1];
3015 `LOGOS_RAMSTYLE(H*BEATS) reg [511:0] cs1 [0:H*BEATS-1];
3016 reg [511:0] ys0_r, ys1_r, cs0_r, cs1_r;
3017 wire past = e == fo;
3018 always @(posedge clk) begin
3019 if (bare_r ? !fb : c_back && !fb && past) cs0[bare_r ? at_r : at_d[@UPDATED@]] <= bare_r ? 512'd0 : cnew;
3020 if (bare_r ? fb : c_back && fb && past) cs1[bare_r ? at_r : at_d[@UPDATED@]] <= bare_r ? 512'd0 : cnew;
3021 if (bare_r ? !fb : y_back && !fb && past) ys0[bare_r ? at_r : at_d[@FOLDED@]] <= bare_r ? q_r : ynew;
3022 if (bare_r ? fb : y_back && fb && past) ys1[bare_r ? at_r : at_d[@FOLDED@]] <= bare_r ? q_r : ynew;
3023 ys0_r <= ys0[rd_at_p];
3024 ys1_r <= ys1[rd_at_p];
3025 cs0_r <= cs0[rd_at_p];
3026 cs1_r <= cs1[rd_at_p];
3027 end
3028 assign ynew_beat = rb_r ? ys1_r : ys0_r;
3029 assign cnew_beat = rb_r ? cs1_r : cs0_r;
3030 end else begin : whole
3031 assign ynew_beat = y_beat;
3032 assign cnew_beat = carried_beat;
3033 end
3034 endgenerate
3035 always @(posedge clk) begin
3036 if (rst) begin state <= IDLE; e <= 8'd0; xs <= 8'd0; xt <= 8'd0; got <= 8'd0; i <= 16'd0; written <= 16'd0; caught <= 8'd0; end
3037 else begin
3038 case (state)
3039 IDLE: if (start) begin fb <= bank; fo <= older; xs <= 8'd0; xt <= 8'd0; got <= 8'd0; i <= 16'd0; state <= EXPS; end
3040 EXPS: begin
3041 if (exp_issue) begin
3042 if (xt + 8'd1 >= entries) begin xt <= 8'd0; xs <= xs + 8'd1; end else xt <= xt + 8'd1;
3043 end
3044 if ({8'd0, got} == HW * {8'd0, entries}) begin
3045 if (entries == 8'd0) state <= BARE; else begin e <= entries - 8'd1; i <= 16'd0; state <= DOT; end
3046 end
3047 end
3048 BARE: if (i + 16'd1 == ISSUES) begin i <= 16'd0; state <= IDLE; end else i <= i + 16'd1;
3049 DOT: if (i + 16'd1 == ISSUES) begin i <= 16'd0; caught <= 8'd0; state <= BCW; end else i <= i + 16'd1;
3050 BCW: if (caught == HS) begin written <= 16'd0; state <= UPD; end
3051 UPD: if (i + 16'd1 == ISSUES) begin i <= 16'd0; state <= FILL; end else i <= i + 16'd1;
3052 FILL: if (written == ISSUES) begin
3053 if (e == 8'd0) state <= IDLE; else begin e <= e - 8'd1; state <= DOT; end
3054 end
3055 default: state <= IDLE;
3056 endcase
3057 if (ex_v[@EXP@]) got <= got + 8'd1;
3058 if (y_back) written <= written + 16'd1;
3059 if (bc_in) caught <= caught + 8'd1;
3060 end
3061 end
3062endmodule
3063"#;
3064
3065const GDN_FLUSH_M_VERILOG: &str = r#"
3066// A trailing flush's shares of up to SLOTS heads at once, each replayed as gdn_flush replays its log. Each of three banks
3067// holds, per slot, a head's log — its tokens' keys and values a beat of 16·W elements at a time, each token's decay
3068// exponent and gate with its beat 0, the decay through the pipelined exponential as it is written — and its share of
3069// rows: SHARE rows of N elements in beats of 16·W f32, written by row and beat. `start` replays a bank's first
3070// `entries` tokens over the shares of its first `slots` slots, slot s's rows the head's rows from `first` (a row's
3071// value of a token its own element of the token's values), once every decay written to it is in and the bank started
3072// before it is replayed. The shares are groups of W·@FADD@ rows, slot-major, a group's rows one slot's: for every token
3073// in order each group has a dot pass (its rows' dots with the token's key, beat-major so a lane's chain sum comes back
3074// as the row's next beat enters), its rows' coefficients beta·(v − decay·dot), and an update pass decay·m + coef·k, no
3075// rounding between; the dot passes run back to back on their lanes and the update passes on theirs, a group's next dot
3076// once its update is written, so the other slots' groups fill each group's wait for its coefficients. A replayed
3077// bank's rows stream out slot by slot, row-major, each element stored once (rounded to binary16 and back when f16), the
3078// banks replayed in the order started and streamed in the order replayed, whichever bank each is: a beat leaves the
3079// clock after each clock out_ready is high. A bank is free from its last beat out until it is started again; the lane
3080// fills only a free bank, so one bank takes a batch's log while another takes the batch before's rows and the third
3081// replays the batch before that.
3082module logos_gdn_flush_m #(parameter N = 128, W = 2, T_MAX = 8, SHARE = 16, SLOTS = 4)
3083 (input clk, input rst,
3084 input log_wr, input [1:0] log_bank, input [7:0] log_slot, input [7:0] log_at, input [15:0] log_beat,
3085 input [512*W-1:0] log_k, input [512*W-1:0] log_v, input [31:0] log_decay, input [31:0] log_beta,
3086 input row_wr, input [1:0] row_bank, input [7:0] row_slot, input [15:0] row_j, input [15:0] row_beat, input [512*W-1:0] row_data,
3087 input start, input [1:0] bank, input [7:0] entries, input [7:0] slots, input [15:0] first, input f16,
3088 output [2:0] free,
3089 output out_valid, input out_ready, output [512*W-1:0] out_beat, output [256*W-1:0] out_half);
3090 localparam [15:0] BEATS = N / (16 * W);
3091 localparam [15:0] G = W * @FADD@;
3092 localparam [15:0] GS = SHARE / G;
3093 localparam [15:0] PASS = G * BEATS;
3094 localparam [15:0] SB = SHARE * BEATS;
3095 localparam RW = SLOTS * SHARE * BEATS;
3096 localparam LW = SLOTS * T_MAX * BEATS;
3097 localparam CHAIN = W * @FADD@, TREE = @FMUL@ + CHAIN + 4 * @FADD@, VALUE = TREE + @FMUL@;
3098 localparam GATED = VALUE + @FADD@, COEF = GATED + @FMUL@, UPDATED = @FMUL@ + @FADD@;
3099 localparam EW = $clog2(16 * W);
3100 localparam [15:0] SHARE_W = SHARE, T_MAX_W = T_MAX;
3101 // The coefficients' ring: four slots, an item's at its index's two low bits.
3102 localparam [15:0] RING = 16'd4;
3103 // Each bank's logs: every slot's tokens' keys, twice (one copy for each pass), and values; their decays and gates.
3104 reg [512*W-1:0] ka0 [0:LW-1];
3105 reg [512*W-1:0] ka1 [0:LW-1];
3106 reg [512*W-1:0] ka2 [0:LW-1];
3107 reg [512*W-1:0] kb0 [0:LW-1];
3108 reg [512*W-1:0] kb1 [0:LW-1];
3109 reg [512*W-1:0] kb2 [0:LW-1];
3110 reg [512*W-1:0] va0 [0:LW-1];
3111 reg [512*W-1:0] va1 [0:LW-1];
3112 reg [512*W-1:0] va2 [0:LW-1];
3113 `LOGOS_RAMSTYLE_ASYNC(SLOTS*T_MAX) reg [31:0] dc0 [0:SLOTS*T_MAX-1];
3114 `LOGOS_RAMSTYLE_ASYNC(SLOTS*T_MAX) reg [31:0] dc1 [0:SLOTS*T_MAX-1];
3115 `LOGOS_RAMSTYLE_ASYNC(SLOTS*T_MAX) reg [31:0] dc2 [0:SLOTS*T_MAX-1];
3116 `LOGOS_RAMSTYLE_ASYNC(SLOTS*T_MAX) reg [31:0] bt0 [0:SLOTS*T_MAX-1];
3117 `LOGOS_RAMSTYLE_ASYNC(SLOTS*T_MAX) reg [31:0] bt1 [0:SLOTS*T_MAX-1];
3118 `LOGOS_RAMSTYLE_ASYNC(SLOTS*T_MAX) reg [31:0] bt2 [0:SLOTS*T_MAX-1];
3119 // Each bank's shares, slot-major and row-major, twice: the dot pass reads one copy and the update pass the other.
3120 reg [512*W-1:0] ga0 [0:RW-1];
3121 reg [512*W-1:0] ga1 [0:RW-1];
3122 reg [512*W-1:0] ga2 [0:RW-1];
3123 reg [512*W-1:0] gb0 [0:RW-1];
3124 reg [512*W-1:0] gb1 [0:RW-1];
3125 reg [512*W-1:0] gb2 [0:RW-1];
3126 // A log write's places as the logs' addresses hold them: a place past the last slot wraps as it does in block RAM.
3127 localparam LA = LW > 1 ? $clog2(LW) : 1, TA = SLOTS * T_MAX > 1 ? $clog2(SLOTS * T_MAX) : 1;
3128 wire [15:0] la_at = ({8'd0, log_slot} * T_MAX_W + {8'd0, log_at}) * BEATS + log_beat;
3129 wire [15:0] lt_at = {8'd0, log_slot} * T_MAX_W + {8'd0, log_at};
3130 wire [LA-1:0] la = la_at[LA-1:0];
3131 wire [TA-1:0] lt = lt_at[TA-1:0];
3132 // The decays: each token's log decay through the exponential as its beat 0 is written, its bank and place beside;
3133 // the decays of each bank still on their way.
3134 wire [31:0] ex;
3135 reg [@EXP@:1] ex_v;
3136 reg [1:0] ex_b [1:@EXP@];
3137 reg [TA-1:0] ex_at [1:@EXP@];
3138 reg [7:0] exps0, exps1, exps2;
3139 logos_exp_p xp(.clk(clk), .x_in(log_decay), .y(ex));
3140 integer i;
3141 wire ex_in = log_wr && log_beat == 16'd0;
3142 always @(posedge clk) begin
3143 if (log_wr && log_bank == 2'd0) begin ka0[la] <= log_k; kb0[la] <= log_k; va0[la] <= log_v; end
3144 if (log_wr && log_bank == 2'd1) begin ka1[la] <= log_k; kb1[la] <= log_k; va1[la] <= log_v; end
3145 if (log_wr && log_bank == 2'd2) begin ka2[la] <= log_k; kb2[la] <= log_k; va2[la] <= log_v; end
3146 if (ex_in && log_bank == 2'd0) bt0[lt] <= log_beta;
3147 if (ex_in && log_bank == 2'd1) bt1[lt] <= log_beta;
3148 if (ex_in && log_bank == 2'd2) bt2[lt] <= log_beta;
3149 ex_v <= rst ? {@EXP@{1'b0}} : {ex_v[@EXP@-1:1], ex_in};
3150 ex_b[1] <= log_bank;
3151 ex_at[1] <= lt;
3152 for (i = 2; i <= @EXP@; i = i + 1) begin ex_b[i] <= ex_b[i-1]; ex_at[i] <= ex_at[i-1]; end
3153 if (ex_v[@EXP@] && ex_b[@EXP@] == 2'd0) dc0[ex_at[@EXP@]] <= ex;
3154 if (ex_v[@EXP@] && ex_b[@EXP@] == 2'd1) dc1[ex_at[@EXP@]] <= ex;
3155 if (ex_v[@EXP@] && ex_b[@EXP@] == 2'd2) dc2[ex_at[@EXP@]] <= ex;
3156 if (rst) begin exps0 <= 8'd0; exps1 <= 8'd0; exps2 <= 8'd0; end
3157 else begin
3158 exps0 <= exps0 + ((ex_in && log_bank == 2'd0) ? 8'd1 : 8'd0) - ((ex_v[@EXP@] && ex_b[@EXP@] == 2'd0) ? 8'd1 : 8'd0);
3159 exps1 <= exps1 + ((ex_in && log_bank == 2'd1) ? 8'd1 : 8'd0) - ((ex_v[@EXP@] && ex_b[@EXP@] == 2'd1) ? 8'd1 : 8'd0);
3160 exps2 <= exps2 + ((ex_in && log_bank == 2'd2) ? 8'd1 : 8'd0) - ((ex_v[@EXP@] && ex_b[@EXP@] == 2'd2) ? 8'd1 : 8'd0);
3161 end
3162 end
3163 // The banks: started and waiting their turn (pend, in the order started: rq0 first, rqn of them), replayed and
3164 // waiting to stream out or streaming (outq, in the order replayed: oq0 first, oqn of them); the bank replaying (rb,
3165 // while running) and the bank streaming out (ob_bank, while outing); each bank's run as started.
3166 reg [2:0] pend, outq;
3167 reg [1:0] rqn, oqn;
3168 reg running, outing;
3169 reg [1:0] rb, rq0, rq1, rq2, ob_bank, oq0, oq1, oq2;
3170 `LOGOS_RAMSTYLE_ASYNC(3) reg [7:0] b_entries [0:2];
3171 `LOGOS_RAMSTYLE_ASYNC(3) reg [7:0] b_slots [0:2];
3172 `LOGOS_RAMSTYLE_ASYNC(3) reg [15:0] b_first [0:2];
3173 reg [7:0] r_slots;
3174 reg [15:0] r_first, ng, total, ob, o_words;
3175 assign free = ~(pend | outq | (running ? 3'b001 << rb : 3'b000) | (outing ? 3'b001 << ob_bank : 3'b000));
3176 wire [7:0] exps_next = (rq0 == 2'd0) ? exps0 : (rq0 == 2'd1) ? exps1 : exps2;
3177 wire run_go = !running && rqn != 2'd0 && exps_next == 8'd0;
3178 // The dot pass's item (a group and a token, the group the faster) and its beat; the update pass's; the items whose
3179 // updates are written.
3180 reg [15:0] di, dg, de, dp, ui, ug, ue, up, written;
3181 wire [15:0] dj = dp % G, dbt = dp / G, uj = up % G, ubt = up / G;
3182 wire [15:0] dslot = dg / GS, uslot = ug / GS;
3183 // Each of the ring's four item slots: its rows' coefficients and how many are in.
3184 `LOGOS_RAMSTYLE(4*G) reg [31:0] coefs [0:4*G-1];
3185 reg [15:0] have [0:3];
3186 wire dot_go = running && di < total && (de == 16'd0 || written + ng > di) && di < ui + RING;
3187 wire upd_go = running && ui < di && have[ui[1:0]] == G;
3188 // A dot's beat: the row's beat and the token's key beat out of the running bank, and its tags.
3189 wire [15:0] xa = (dg * G + dj) * BEATS + dbt, ka_a = (dslot * T_MAX_W + de) * BEATS + dbt;
3190 reg [512*W-1:0] xa0_r, xa1_r, xa2_r, ka0_r, ka1_r, ka2_r;
3191 reg [1:0] rb_q;
3192 reg v_q, first_q, last_q;
3193 reg [15:0] row_q, e_q, j_q, slot_q;
3194 reg [1:0] ring_q;
3195 always @(posedge clk) begin
3196 xa0_r <= ga0[xa];
3197 xa1_r <= ga1[xa];
3198 xa2_r <= ga2[xa];
3199 ka0_r <= ka0[ka_a];
3200 ka1_r <= ka1[ka_a];
3201 ka2_r <= ka2[ka_a];
3202 rb_q <= rb;
3203 v_q <= !rst && dot_go;
3204 first_q <= dbt == 16'd0;
3205 last_q <= dbt == BEATS - 1;
3206 row_q <= dg * G + dj;
3207 e_q <= de;
3208 j_q <= dj;
3209 slot_q <= dslot;
3210 ring_q <= di[1:0];
3211 end
3212 wire [512*W-1:0] x_q = (rb_q == 2'd0) ? xa0_r : (rb_q == 2'd1) ? xa1_r : xa2_r;
3213 wire [512*W-1:0] k_q = (rb_q == 2'd0) ? ka0_r : (rb_q == 2'd1) ? ka1_r : ka2_r;
3214 reg [COEF:1] v_d, first_d, last_d;
3215 reg [15:0] row_d [1:COEF];
3216 reg [15:0] e_d [1:COEF];
3217 reg [15:0] j_d [1:COEF];
3218 reg [15:0] slot_d [1:COEF];
3219 reg [1:0] ring_d [1:COEF];
3220 always @(posedge clk) begin
3221 v_d <= rst ? {COEF{1'b0}} : {v_d[COEF-1:1], v_q};
3222 first_d <= {first_d[COEF-1:1], first_q};
3223 last_d <= {last_d[COEF-1:1], last_q};
3224 row_d[1] <= row_q; e_d[1] <= e_q; j_d[1] <= j_q; slot_d[1] <= slot_q; ring_d[1] <= ring_q;
3225 for (i = 2; i <= COEF; i = i + 1) begin
3226 row_d[i] <= row_d[i-1]; e_d[i] <= e_d[i-1]; j_d[i] <= j_d[i-1]; slot_d[i] <= slot_d[i-1]; ring_d[i] <= ring_d[i-1];
3227 end
3228 end
3229 // The dot's lanes, as logos_gdn_flush_w's: a beat's W products through a chain of W adds a lane.
3230 wire [511:0] sums;
3231 genvar l, j;
3232 generate
3233 for (l = 0; l < 16; l = l + 1) begin : lane
3234 wire [31:0] s [0:W];
3235 assign s[0] = first_d[@FMUL@] ? 32'd0 : s[W];
3236 for (j = 0; j < W; j = j + 1) begin : term
3237 wire [31:0] p, pd;
3238 logos_fmul_p m(.clk(clk), .a(x_q[512*j + 32*l +: 32]), .b(k_q[512*j + 32*l +: 32]), .y(p));
3239 if (j == 0) begin : now
3240 assign pd = p;
3241 end else begin : later
3242 logos_delay #(.W(32), .D(j * @FADD@)) dl(.clk(clk), .d(p), .q(pd));
3243 end
3244 logos_fadd_p a(.clk(clk), .a(s[j]), .b(pd), .y(s[j+1]));
3245 end
3246 assign sums[32*l +: 32] = s[W];
3247 end
3248 endgenerate
3249 // A row's dot through the tree, decay·dot, the row's own value of the token less it, and the gate's product: the
3250 // row's coefficient, into its item's ring slot.
3251 wire [31:0] dot, u, vu, c;
3252 logos_tree16_p t(.clk(clk), .lanes(sums), .sum(dot));
3253 // The token's decay and gate where the dot and its difference reach their multiplies, read as logos_gdn_flush_w reads
3254 // them (a slot's few tokens each), each place registered the clock before.
3255 reg [15:0] dt_at, bt_at;
3256 always @(posedge clk) begin
3257 dt_at <= slot_d[TREE-1] * T_MAX_W + e_d[TREE-1];
3258 bt_at <= slot_d[GATED-1] * T_MAX_W + e_d[GATED-1];
3259 end
3260 wire [31:0] decay_t = (rb_q == 2'd0) ? dc0[dt_at] : (rb_q == 2'd1) ? dc1[dt_at] : dc2[dt_at];
3261 wire [31:0] beta_g = (rb_q == 2'd0) ? bt0[bt_at] : (rb_q == 2'd1) ? bt1[bt_at] : bt2[bt_at];
3262 logos_fmul_p mu(.clk(clk), .a(decay_t), .b(dot), .y(u));
3263 // The row's place in its head: its value's element, its line read (its place registered the clock before) and its
3264 // lane taken as logos_gdn_flush_w's.
3265 wire [15:0] v_row = r_first + (row_d[VALUE-2] % SHARE_W), v_row_e = r_first + (row_d[VALUE-3] % SHARE_W);
3266 reg [15:0] va_a;
3267 reg [512*W-1:0] va0_r, va1_r, va2_r;
3268 reg [EW-1:0] v_lane;
3269 reg [31:0] v_val;
3270 always @(posedge clk) begin
3271 va_a <= (slot_d[VALUE-3] * T_MAX_W + e_d[VALUE-3]) * BEATS + (v_row_e >> EW);
3272 va0_r <= va0[va_a];
3273 va1_r <= va1[va_a];
3274 va2_r <= va2[va_a];
3275 v_lane <= v_row[EW-1:0];
3276 v_val <= (rb_q == 2'd0) ? va0_r[32 * v_lane +: 32] : (rb_q == 2'd1) ? va1_r[32 * v_lane +: 32] : va2_r[32 * v_lane +: 32];
3277 end
3278 logos_fadd_p sv(.clk(clk), .a(v_val), .b({~u[31], u[30:0]}), .y(vu));
3279 logos_fmul_p mb(.clk(clk), .a(beta_g), .b(vu), .y(c));
3280 wire coef_in = v_d[COEF] && last_d[COEF];
3281 // An update's beat: the row's beat and the token's key beat out of the running bank, the row's coefficient and the
3282 // token's decay; its place.
3283 wire [15:0] ua = (ug * G + uj) * BEATS + ubt, kb_a = (uslot * T_MAX_W + ue) * BEATS + ubt, ud_at = uslot * T_MAX_W + ue;
3284 // The update's reads of the second copy, or a streamed bank's out of it, each bank's one address.
3285 wire [15:0] gb0_a = (running && rb == 2'd0) ? ua : ob, gb1_a = (running && rb == 2'd1) ? ua : ob;
3286 wire [15:0] gb2_a = (running && rb == 2'd2) ? ua : ob;
3287 reg [512*W-1:0] gb0_r, gb1_r, gb2_r, kb0_r, kb1_r, kb2_r;
3288 reg [31:0] uc_q, ud0_r, ud1_r, ud2_r;
3289 reg uv_q, ul_q;
3290 reg [1:0] urb_q;
3291 reg [15:0] ua_q;
3292 always @(posedge clk) begin
3293 gb0_r <= gb0[gb0_a];
3294 gb1_r <= gb1[gb1_a];
3295 gb2_r <= gb2[gb2_a];
3296 kb0_r <= kb0[kb_a];
3297 kb1_r <= kb1[kb_a];
3298 kb2_r <= kb2[kb_a];
3299 uc_q <= coefs[ui[1:0] * G + uj];
3300 ud0_r <= dc0[ud_at];
3301 ud1_r <= dc1[ud_at];
3302 ud2_r <= dc2[ud_at];
3303 urb_q <= rb;
3304 uv_q <= !rst && upd_go;
3305 ul_q <= !rst && upd_go && up == PASS - 1;
3306 ua_q <= ua;
3307 end
3308 wire [512*W-1:0] ux_q = (urb_q == 2'd0) ? gb0_r : (urb_q == 2'd1) ? gb1_r : gb2_r;
3309 wire [512*W-1:0] uk_q = (urb_q == 2'd0) ? kb0_r : (urb_q == 2'd1) ? kb1_r : kb2_r;
3310 wire [31:0] ud_q = (urb_q == 2'd0) ? ud0_r : (urb_q == 2'd1) ? ud1_r : ud2_r;
3311 wire [512*W-1:0] upd;
3312 generate
3313 for (l = 0; l < 16 * W; l = l + 1) begin : elem
3314 wire [31:0] dm, ck;
3315 logos_fmul_p md(.clk(clk), .a(ud_q), .b(ux_q[32*l +: 32]), .y(dm));
3316 logos_fmul_p mc(.clk(clk), .a(uc_q), .b(uk_q[32*l +: 32]), .y(ck));
3317 logos_fadd_p au(.clk(clk), .a(dm), .b(ck), .y(upd[32*l +: 32]));
3318 end
3319 endgenerate
3320 // The update's beats written back where they were read in the running bank, an item's last marking its update
3321 // written; a share's rows written by address into the bank being filled.
3322 reg [UPDATED:1] wv_d, wl_d;
3323 reg [1:0] wb_d [1:UPDATED];
3324 reg [15:0] wa_d [1:UPDATED];
3325 always @(posedge clk) begin
3326 wv_d <= rst ? {UPDATED{1'b0}} : {wv_d[UPDATED-1:1], uv_q};
3327 wl_d <= rst ? {UPDATED{1'b0}} : {wl_d[UPDATED-1:1], ul_q};
3328 wb_d[1] <= urb_q;
3329 wa_d[1] <= ua_q;
3330 for (i = 2; i <= UPDATED; i = i + 1) begin wb_d[i] <= wb_d[i-1]; wa_d[i] <= wa_d[i-1]; end
3331 end
3332 wire [15:0] ra = ({8'd0, row_slot} * SHARE_W + row_j) * BEATS + row_beat;
3333 wire wb = wv_d[UPDATED];
3334 always @(posedge clk) begin
3335 if (row_wr && row_bank == 2'd0) begin ga0[ra] <= row_data; gb0[ra] <= row_data; end
3336 else if (wb && wb_d[UPDATED] == 2'd0) begin ga0[wa_d[UPDATED]] <= upd; gb0[wa_d[UPDATED]] <= upd; end
3337 if (row_wr && row_bank == 2'd1) begin ga1[ra] <= row_data; gb1[ra] <= row_data; end
3338 else if (wb && wb_d[UPDATED] == 2'd1) begin ga1[wa_d[UPDATED]] <= upd; gb1[wa_d[UPDATED]] <= upd; end
3339 if (row_wr && row_bank == 2'd2) begin ga2[ra] <= row_data; gb2[ra] <= row_data; end
3340 else if (wb && wb_d[UPDATED] == 2'd2) begin ga2[wa_d[UPDATED]] <= upd; gb2[wa_d[UPDATED]] <= upd; end
3341 if (coef_in) coefs[ring_d[COEF] * G + j_d[COEF]] <= c;
3342 end
3343 // A run ends once its last update is written and nothing of it is left on the lanes; a replayed bank streams out
3344 // once none is streaming.
3345 wire run_end = running && written == total && wv_d == {UPDATED{1'b0}} && v_d == {COEF{1'b0}};
3346 wire out_go = !outing && oqn != 2'd0;
3347 integer r;
3348 always @(posedge clk) begin
3349 if (rst) begin
3350 pend <= 3'b000; outq <= 3'b000; rqn <= 2'd0; oqn <= 2'd0; running <= 1'b0; rb <= 2'd0; outing <= 1'b0; ob_bank <= 2'd0;
3351 ob <= 16'd0;
3352 di <= 16'd0; dg <= 16'd0; de <= 16'd0; dp <= 16'd0; ui <= 16'd0; ug <= 16'd0; ue <= 16'd0; up <= 16'd0; written <= 16'd0;
3353 for (r = 0; r < 4; r = r + 1) have[r] <= 16'd0;
3354 end else begin
3355 if (start) begin pend[bank] <= 1'b1; b_entries[bank] <= entries; b_slots[bank] <= slots; b_first[bank] <= first; end
3356 // The banks to replay, in the order started: a start joins the back, a run takes the front.
3357 case ({start, run_go})
3358 2'b10: begin
3359 if (rqn == 2'd0) rq0 <= bank; else if (rqn == 2'd1) rq1 <= bank; else rq2 <= bank;
3360 rqn <= rqn + 2'd1;
3361 end
3362 2'b01: begin rq0 <= rq1; rq1 <= rq2; rqn <= rqn - 2'd1; end
3363 2'b11:
3364 if (rqn == 2'd1) rq0 <= bank;
3365 else if (rqn == 2'd2) begin rq0 <= rq1; rq1 <= bank; end
3366 else begin rq0 <= rq1; rq1 <= rq2; rq2 <= bank; end
3367 default: ;
3368 endcase
3369 // The banks to stream out, in the order replayed: a run's end joins the back, a stream takes the front.
3370 case ({run_end, out_go})
3371 2'b10: begin
3372 if (oqn == 2'd0) oq0 <= rb; else if (oqn == 2'd1) oq1 <= rb; else oq2 <= rb;
3373 oqn <= oqn + 2'd1;
3374 end
3375 2'b01: begin oq0 <= oq1; oq1 <= oq2; oqn <= oqn - 2'd1; end
3376 2'b11:
3377 if (oqn == 2'd1) oq0 <= rb;
3378 else if (oqn == 2'd2) begin oq0 <= oq1; oq1 <= rb; end
3379 else begin oq0 <= oq1; oq1 <= oq2; oq2 <= rb; end
3380 default: ;
3381 endcase
3382 for (r = 0; r < 4; r = r + 1)
3383 have[r] <= have[r] + ((coef_in && ring_d[COEF] == r[1:0]) ? 16'd1 : 16'd0)
3384 - ((upd_go && up == PASS - 1 && ui[1:0] == r[1:0]) ? G : 16'd0);
3385 if (dot_go) begin
3386 if (dp == PASS - 1) begin
3387 dp <= 16'd0; di <= di + 16'd1;
3388 if (dg + 16'd1 == ng) begin dg <= 16'd0; de <= de + 16'd1; end else dg <= dg + 16'd1;
3389 end else dp <= dp + 16'd1;
3390 end
3391 if (upd_go) begin
3392 if (up == PASS - 1) begin
3393 up <= 16'd0; ui <= ui + 16'd1;
3394 if (ug + 16'd1 == ng) begin ug <= 16'd0; ue <= ue + 16'd1; end else ug <= ug + 16'd1;
3395 end else up <= up + 16'd1;
3396 end
3397 if (wl_d[UPDATED]) written <= written + 16'd1;
3398 if (run_go) begin
3399 running <= 1'b1; rb <= rq0; pend[rq0] <= 1'b0;
3400 r_slots <= b_slots[rq0]; r_first <= b_first[rq0];
3401 ng <= {8'd0, b_slots[rq0]} * GS;
3402 total <= {8'd0, b_entries[rq0]} * ({8'd0, b_slots[rq0]} * GS);
3403 di <= 16'd0; dg <= 16'd0; de <= 16'd0; dp <= 16'd0; ui <= 16'd0; ug <= 16'd0; ue <= 16'd0; up <= 16'd0; written <= 16'd0;
3404 end else if (run_end) begin
3405 running <= 1'b0; outq[rb] <= 1'b1;
3406 end
3407 if (out_go) begin
3408 outing <= 1'b1; ob_bank <= oq0; ob <= 16'd0; o_words <= {8'd0, b_slots[oq0]} * SB;
3409 if ({8'd0, b_slots[oq0]} * SB == 16'd0) begin outing <= 1'b0; outq[oq0] <= 1'b0; end
3410 end else if (outing && out_ready) begin
3411 if (ob + 16'd1 == o_words) begin outing <= 1'b0; outq[ob_bank] <= 1'b0; end else ob <= ob + 16'd1;
3412 end
3413 end
3414 end
3415 // The rows out, each element stored: a beat read, then its binary16 rounding (logos_f32_to_f16_p) with the beat held
3416 // beside it; out_half the stored binary16, out_beat the element as stored.
3417 reg o_v;
3418 reg [1:0] o_b;
3419 always @(posedge clk) begin
3420 o_v <= !rst && outing && out_ready;
3421 o_b <= ob_bank;
3422 end
3423 wire [512*W-1:0] o_beat = (o_b == 2'd0) ? gb0_r : (o_b == 2'd1) ? gb1_r : gb2_r;
3424 wire [512*W-1:0] o_raw;
3425 reg [512*W-1:0] o_hold;
3426 always @(posedge clk) o_hold <= o_beat;
3427 logos_delay #(.W(512*W), .D(@F16@)) dob(.clk(clk), .d(o_hold), .q(o_raw));
3428 reg ov_d [0:@F16@];
3429 integer ok;
3430 always @(posedge clk) begin
3431 ov_d[0] <= !rst && o_v;
3432 for (ok = 1; ok <= @F16@; ok = ok + 1) ov_d[ok] <= !rst && ov_d[ok-1];
3433 end
3434 assign out_valid = ov_d[@F16@] && !rst;
3435 genvar o;
3436 generate
3437 for (o = 0; o < 16 * W; o = o + 1) begin : st
3438 wire [31:0] back;
3439 logos_f32_to_f16_p enc(.clk(clk), .x(o_hold[32*o +: 32]), .h(out_half[16*o +: 16]));
3440 logos_f16_to_f32 dec(.h(out_half[16*o +: 16]), .x(back));
3441 assign out_beat[32*o +: 32] = f16 ? back : o_raw[32*o +: 32];
3442 end
3443 endgenerate
3444endmodule
3445"#;
3446
3447const GDN_WIDE_VERILOG: &str = r#"
3452// logos_gdn_read_p at 16·W elements a clock: rows of N elements in beats of 16·W f32 (element 16·W·b + 16·j + l in
3453// lane l of the beat's sixteen j), row-major, in groups of W·@FADD@, each row's carried value with its first beat and
3454// the head's folded query y and scale with each group's first beat; out, in the rows' order, (row·y + carried)·scale
3455// of each row.
3456module logos_gdn_read_w #(parameter N = 128, W = 2)
3457 (input clk, input rst,
3458 input [32*N-1:0] y, input [31:0] scale,
3459 input in_valid, output in_ready, input [512*W-1:0] in_beat, input [31:0] in_carried,
3460 output reg out_valid, output reg [31:0] out_read);
3461 localparam [15:0] BEATS = N / (16 * W);
3462 localparam [15:0] G = W * @FADD@;
3463 localparam [15:0] GB = G * BEATS;
3464 localparam CHAIN = W * @FADD@, TREE = @FMUL@ + CHAIN + 4 * @FADD@, CARRIED = TREE + @FADD@, LAG = CARRIED + @FMUL@;
3465 // Two groups of rows, row-major; each group's carried values, query and scale.
3466 `LOGOS_RAMSTYLE(2*GB) reg [512*W-1:0] gbuf [0:2*GB-1];
3467 `LOGOS_RAMSTYLE(2*G) reg [31:0] gcar [0:2*G-1];
3468 `LOGOS_RAMSTYLE_ASYNC(2) reg [32*N-1:0] gy [0:1];
3469 `LOGOS_RAMSTYLE(2) reg [31:0] gscale [0:1];
3470 reg [1:0] full;
3471 // The group being gathered and the group being sent.
3472 reg wr, rd, sending;
3473 reg [15:0] wrow, sj, wbeat, sb;
3474 assign in_ready = !full[wr];
3475 wire take = in_valid && in_ready;
3476 wire group_in = take && wrow == G - 1 && wbeat == BEATS - 1;
3477 wire group_out = sending && sj == G - 1 && sb == BEATS - 1;
3478 always @(posedge clk) begin
3479 if (take) begin
3480 gbuf[(wr * G + wrow) * BEATS + wbeat] <= in_beat;
3481 if (wbeat == 16'd0) gcar[wr * G + wrow] <= in_carried;
3482 if (wrow == 16'd0 && wbeat == 16'd0) begin gy[wr] <= y; gscale[wr] <= scale; end
3483 end
3484 if (rst) begin
3485 full <= 2'b00; wr <= 1'b0; rd <= 1'b0; sending <= 1'b0; wrow <= 16'd0; wbeat <= 16'd0; sj <= 16'd0; sb <= 16'd0;
3486 end else begin
3487 if (take) begin
3488 if (wbeat == BEATS - 1) begin wbeat <= 16'd0; wrow <= (wrow == G - 1) ? 16'd0 : wrow + 16'd1; end
3489 else wbeat <= wbeat + 16'd1;
3490 if (group_in) wr <= !wr;
3491 end
3492 if (group_in) full[wr] <= 1'b1;
3493 if (group_out) begin full[rd] <= 1'b0; rd <= !rd; sending <= 1'b0; end
3494 else if (!sending && full[rd]) begin sending <= 1'b1; sj <= 16'd0; sb <= 16'd0; end
3495 if (sending && !group_out) begin
3496 if (sj == G - 1) begin sj <= 16'd0; sb <= sb + 16'd1; end else sj <= sj + 16'd1;
3497 end
3498 end
3499 end
3500 // The beat at hand, its query beat, and its tags: whether it opens or closes its row, the row's carried and scale.
3501 reg [512*W-1:0] beat_q, yb_q;
3502 reg v_q, first_q, last_q;
3503 reg [31:0] car_q, scale_q;
3504 always @(posedge clk) begin
3505 beat_q <= gbuf[(rd * G + sj) * BEATS + sb];
3506 yb_q <= gy[rd][512*W*sb +: 512*W];
3507 v_q <= !rst && sending;
3508 first_q <= sb == 16'd0;
3509 last_q <= sb == BEATS - 1;
3510 car_q <= gcar[rd * G + sj];
3511 scale_q <= gscale[rd];
3512 end
3513 // The tags through the products, the chain, the tree, the carried value's add and the scale's multiply.
3514 reg [LAG:1] v_d, first_d, last_d;
3515 reg [31:0] car_d [1:LAG];
3516 reg [31:0] scale_d [1:LAG];
3517 integer i;
3518 always @(posedge clk) begin
3519 v_d <= rst ? {LAG{1'b0}} : {v_d[LAG-1:1], v_q};
3520 first_d <= {first_d[LAG-1:1], first_q};
3521 last_d <= {last_d[LAG-1:1], last_q};
3522 car_d[1] <= car_q;
3523 scale_d[1] <= scale_q;
3524 for (i = 2; i <= LAG; i = i + 1) begin car_d[i] <= car_d[i-1]; scale_d[i] <= scale_d[i-1]; end
3525 end
3526 // The lanes: each beat's W products, the j-th entering the chain's j-th add j·@FADD@ clocks after the first; the
3527 // chain's end is the lane's sum for the row, back at its first add as the row's next beat enters, a row's first
3528 // beat added to zero.
3529 wire [511:0] sums;
3530 genvar l, j;
3531 generate
3532 for (l = 0; l < 16; l = l + 1) begin : lane
3533 wire [31:0] s [0:W];
3534 assign s[0] = first_d[@FMUL@] ? 32'd0 : s[W];
3535 for (j = 0; j < W; j = j + 1) begin : term
3536 wire [31:0] p, pd;
3537 logos_fmul_p m(.clk(clk), .a(beat_q[512*j + 32*l +: 32]), .b(yb_q[512*j + 32*l +: 32]), .y(p));
3538 if (j == 0) begin : now
3539 assign pd = p;
3540 end else begin : later
3541 logos_delay #(.W(32), .D(j * @FADD@)) dl(.clk(clk), .d(p), .q(pd));
3542 end
3543 logos_fadd_p a(.clk(clk), .a(s[j]), .b(pd), .y(s[j+1]));
3544 end
3545 assign sums[32*l +: 32] = s[W];
3546 end
3547 endgenerate
3548 // A row's lanes leave the chain the clock after its last beat's last term entered it; the tree, the carried value,
3549 // the scale.
3550 wire [31:0] dot, with_carried, read;
3551 logos_tree16_p t(.clk(clk), .lanes(sums), .sum(dot));
3552 logos_fadd_p c(.clk(clk), .a(dot), .b(car_d[TREE]), .y(with_carried));
3553 logos_fmul_p sc(.clk(clk), .a(with_carried), .b(scale_d[CARRIED]), .y(read));
3554 always @(posedge clk) begin
3555 out_valid <= !rst && v_d[LAG] && last_d[LAG];
3556 out_read <= read;
3557 end
3558endmodule
3559
3560// logos_gdn_flush_p at 16·W elements a clock over a whole head: the head's log written beat by beat (token log_at,
3561// beat log_beat of 16·W elements: its key's beat and its values' beat), each token's decay exponent and gate with its
3562// beat 0, each decay through the pipelined exponential; then ROWS of the head's rows from its row FIRST (all N of them
3563// unless given), each N elements in beats of 16·W f32, row-major, all held, taken once every decay is in. The rows are ROWS / (W·@FADD@)
3564// groups of W·@FADD@, a row's update its own, so a head's rows split over several flushes flush as one flush's. For every logged token in order,
3565// each group has a dot pass — every row's dot with the token's key, the group's beats issued beat-major so a lane's
3566// chain sum comes back as the row's next beat enters — then each row's coefficient beta·(v − decay·dot), v the row's own
3567// value of the token, then an update pass decay·m + coef·k over the group's rows, no rounding between. The dot passes
3568// run back to back on their lanes and the update passes on theirs: a group's update once its coefficients are in, its
3569// next dot once that update is written. After the last token the rows stream out row-major, each element stored once
3570// (rounded to binary16 and back when f16): a beat leaves the clock after each clock out_ready is high.
3571module logos_gdn_flush_w #(parameter N = 128, W = 2, T_MAX = 16, ROWS = N, FIRST = 0)
3572 (input clk, input rst,
3573 input log_wr, input [7:0] log_at, input [15:0] log_beat, input [512*W-1:0] log_k, input [512*W-1:0] log_v,
3574 input [31:0] log_decay, input [31:0] log_beta, input [7:0] entries, input f16,
3575 input in_valid, output in_ready, input [512*W-1:0] in_beat,
3576 output out_valid, input out_ready, output [512*W-1:0] out_beat, output [256*W-1:0] out_half);
3577 localparam [15:0] BEATS = N / (16 * W);
3578 localparam [15:0] G = W * @FADD@;
3579 localparam [15:0] NG = ROWS / G;
3580 localparam [15:0] PASS = G * BEATS;
3581 localparam [15:0] RB = ROWS * BEATS;
3582 localparam CHAIN = W * @FADD@, TREE = @FMUL@ + CHAIN + 4 * @FADD@, VALUE = TREE + @FMUL@;
3583 localparam GATED = VALUE + @FADD@, COEF = GATED + @FMUL@, UPDATED = @FMUL@ + @FADD@;
3584 localparam EW = $clog2(16 * W);
3585 // The coefficients' ring: four slots, a token's at its index's two low bits.
3586 localparam [15:0] RING = 16'd4;
3587 localparam IN = 2'd0, RUN = 2'd1, OUT = 2'd2;
3588 // The log: each token's key, twice (one copy for each pass), and values, its decay and its gate.
3589 `LOGOS_RAMSTYLE(T_MAX*BEATS) reg [512*W-1:0] ka [0:T_MAX*BEATS-1];
3590 `LOGOS_RAMSTYLE(T_MAX*BEATS) reg [512*W-1:0] kb [0:T_MAX*BEATS-1];
3591 `LOGOS_RAMSTYLE(T_MAX*BEATS) reg [512*W-1:0] vmem [0:T_MAX*BEATS-1];
3592 `LOGOS_RAMSTYLE_ASYNC(T_MAX) reg [31:0] decays [0:T_MAX-1];
3593 `LOGOS_RAMSTYLE_ASYNC(T_MAX) reg [31:0] betas [0:T_MAX-1];
3594 wire [31:0] ex;
3595 reg [@EXP@:1] ex_v;
3596 reg [7:0] ex_at [1:@EXP@];
3597 reg [7:0] exps;
3598 logos_exp_p xp(.clk(clk), .x_in(log_decay), .y(ex));
3599 integer i;
3600 always @(posedge clk) begin
3601 if (log_wr) begin
3602 ka[log_at * BEATS + log_beat] <= log_k;
3603 kb[log_at * BEATS + log_beat] <= log_k;
3604 vmem[log_at * BEATS + log_beat] <= log_v;
3605 if (log_beat == 16'd0) betas[log_at] <= log_beta;
3606 end
3607 ex_v <= rst ? {@EXP@{1'b0}} : {ex_v[@EXP@-1:1], log_wr && log_beat == 16'd0};
3608 ex_at[1] <= log_at;
3609 for (i = 2; i <= @EXP@; i = i + 1) ex_at[i] <= ex_at[i-1];
3610 if (ex_v[@EXP@]) decays[ex_at[@EXP@]] <= ex;
3611 if (rst) exps <= 8'd0;
3612 else exps <= exps + ((log_wr && log_beat == 16'd0) ? 8'd1 : 8'd0) - (ex_v[@EXP@] ? 8'd1 : 8'd0);
3613 end
3614 // The head's rows, row-major, twice: the dot pass reads one copy and the update pass the other, and both are written.
3615 `LOGOS_RAMSTYLE(RB) reg [512*W-1:0] ga [0:RB-1];
3616 `LOGOS_RAMSTYLE(RB) reg [512*W-1:0] gb [0:RB-1];
3617 reg [1:0] state;
3618 reg [15:0] inb, ob;
3619 wire [15:0] total = {8'd0, entries} * NG;
3620 assign in_ready = state == IN && exps == 8'd0;
3621 wire take = in_valid && in_ready;
3622 // The dot pass's item (a group and a token) and its beat; the update pass's; the items whose updates are written.
3623 reg [15:0] di, dp, ui, up, written;
3624 wire [15:0] dg = di % NG, de = di / NG, ug = ui % NG, ue = ui / NG;
3625 wire [15:0] dj = dp % G, dbt = dp / G, uj = up % G, ubt = up / G;
3626 // Each of the ring's four item slots: its rows' coefficients and how many are in.
3627 `LOGOS_RAMSTYLE(4*G) reg [31:0] coefs [0:4*G-1];
3628 reg [15:0] have [0:3];
3629 wire dot_go = state == RUN && di < total && (de == 16'd0 || written + NG > di) && di < ui + RING;
3630 wire upd_go = state == RUN && ui < di && have[ui[1:0]] == G;
3631 // A dot's beat: the row's beat, the token's key beat, and its tags.
3632 reg [512*W-1:0] x_q, k_q;
3633 reg v_q, first_q, last_q;
3634 reg [15:0] row_q, e_q, j_q;
3635 reg [1:0] slot_q;
3636 always @(posedge clk) begin
3637 x_q <= ga[(dg * G + dj) * BEATS + dbt];
3638 k_q <= ka[de * BEATS + dbt];
3639 v_q <= !rst && dot_go;
3640 first_q <= dbt == 16'd0;
3641 last_q <= dbt == BEATS - 1;
3642 row_q <= dg * G + dj;
3643 e_q <= de;
3644 j_q <= dj;
3645 slot_q <= di[1:0];
3646 end
3647 reg [COEF:1] v_d, first_d, last_d;
3648 reg [15:0] row_d [1:COEF];
3649 reg [15:0] e_d [1:COEF];
3650 reg [15:0] j_d [1:COEF];
3651 reg [1:0] slot_d [1:COEF];
3652 always @(posedge clk) begin
3653 v_d <= rst ? {COEF{1'b0}} : {v_d[COEF-1:1], v_q};
3654 first_d <= {first_d[COEF-1:1], first_q};
3655 last_d <= {last_d[COEF-1:1], last_q};
3656 row_d[1] <= row_q; e_d[1] <= e_q; j_d[1] <= j_q; slot_d[1] <= slot_q;
3657 for (i = 2; i <= COEF; i = i + 1) begin row_d[i] <= row_d[i-1]; e_d[i] <= e_d[i-1]; j_d[i] <= j_d[i-1]; slot_d[i] <= slot_d[i-1]; end
3658 end
3659 // The dot's lanes, as logos_gdn_read_w's: a beat's W products through a chain of W adds a lane.
3660 wire [511:0] sums;
3661 genvar l, j;
3662 generate
3663 for (l = 0; l < 16; l = l + 1) begin : lane
3664 wire [31:0] s [0:W];
3665 assign s[0] = first_d[@FMUL@] ? 32'd0 : s[W];
3666 for (j = 0; j < W; j = j + 1) begin : term
3667 wire [31:0] p, pd;
3668 logos_fmul_p m(.clk(clk), .a(x_q[512*j + 32*l +: 32]), .b(k_q[512*j + 32*l +: 32]), .y(p));
3669 if (j == 0) begin : now
3670 assign pd = p;
3671 end else begin : later
3672 logos_delay #(.W(32), .D(j * @FADD@)) dl(.clk(clk), .d(p), .q(pd));
3673 end
3674 logos_fadd_p a(.clk(clk), .a(s[j]), .b(pd), .y(s[j+1]));
3675 end
3676 assign sums[32*l +: 32] = s[W];
3677 end
3678 endgenerate
3679 // A row's dot through the tree, decay·dot, the row's own value of the token less it, and the gate's product: the
3680 // row's coefficient, into its item's slot.
3681 wire [31:0] dot, u, vu, c;
3682 logos_tree16_p t(.clk(clk), .lanes(sums), .sum(dot));
3683 logos_fmul_p mu(.clk(clk), .a(decays[e_d[TREE]]), .b(dot), .y(u));
3684 reg [512*W-1:0] v_line;
3685 reg [EW-1:0] v_lane;
3686 // The row's place in the head, its value's element.
3687 localparam [15:0] FIRST_ROW = FIRST;
3688 wire [15:0] v_row = row_d[VALUE-2] + FIRST_ROW;
3689 reg [31:0] v_val;
3690 always @(posedge clk) begin
3691 v_line <= vmem[e_d[VALUE-2] * BEATS + (v_row >> EW)];
3692 v_lane <= v_row[EW-1:0];
3693 v_val <= v_line[32 * v_lane +: 32];
3694 end
3695 logos_fadd_p sv(.clk(clk), .a(v_val), .b({~u[31], u[30:0]}), .y(vu));
3696 logos_fmul_p mb(.clk(clk), .a(betas[e_d[GATED]]), .b(vu), .y(c));
3697 wire coef_in = v_d[COEF] && last_d[COEF];
3698 // An update's beat: the row's beat, the token's key beat, the row's coefficient and the token's decay; its place.
3699 reg [512*W-1:0] ux_q, uk_q;
3700 reg [31:0] uc_q, ud_q;
3701 reg uv_q, ul_q;
3702 reg [15:0] ua_q;
3703 always @(posedge clk) begin
3704 ux_q <= gb[(ug * G + uj) * BEATS + ubt];
3705 uk_q <= kb[ue * BEATS + ubt];
3706 uc_q <= coefs[ui[1:0] * G + uj];
3707 ud_q <= decays[ue];
3708 uv_q <= !rst && upd_go;
3709 ul_q <= !rst && upd_go && up == PASS - 1;
3710 ua_q <= (ug * G + uj) * BEATS + ubt;
3711 end
3712 wire [512*W-1:0] upd;
3713 generate
3714 for (l = 0; l < 16 * W; l = l + 1) begin : elem
3715 wire [31:0] dm, ck;
3716 logos_fmul_p md(.clk(clk), .a(ud_q), .b(ux_q[32*l +: 32]), .y(dm));
3717 logos_fmul_p mc(.clk(clk), .a(uc_q), .b(uk_q[32*l +: 32]), .y(ck));
3718 logos_fadd_p au(.clk(clk), .a(dm), .b(ck), .y(upd[32*l +: 32]));
3719 end
3720 endgenerate
3721 // The update's beats written back where they were read, an item's last marking its update written.
3722 reg [UPDATED:1] wv_d, wl_d;
3723 reg [15:0] wa_d [1:UPDATED];
3724 always @(posedge clk) begin
3725 wv_d <= {wv_d[UPDATED-1:1], uv_q};
3726 wl_d <= {wl_d[UPDATED-1:1], ul_q};
3727 wa_d[1] <= ua_q;
3728 for (i = 2; i <= UPDATED; i = i + 1) wa_d[i] <= wa_d[i-1];
3729 if (rst) begin wv_d <= {UPDATED{1'b0}}; wl_d <= {UPDATED{1'b0}}; end
3730 end
3731 always @(posedge clk) begin
3732 if (take) begin ga[inb] <= in_beat; gb[inb] <= in_beat; end
3733 else if (wv_d[UPDATED]) begin ga[wa_d[UPDATED]] <= upd; gb[wa_d[UPDATED]] <= upd; end
3734 if (coef_in) coefs[slot_d[COEF] * G + j_d[COEF]] <= c;
3735 end
3736 integer r;
3737 always @(posedge clk) begin
3738 if (rst) begin
3739 state <= IN; inb <= 16'd0; ob <= 16'd0; di <= 16'd0; dp <= 16'd0; ui <= 16'd0; up <= 16'd0; written <= 16'd0;
3740 for (r = 0; r < 4; r = r + 1) have[r] <= 16'd0;
3741 end else begin
3742 for (r = 0; r < 4; r = r + 1)
3743 have[r] <= have[r] + ((coef_in && slot_d[COEF] == r[1:0]) ? 16'd1 : 16'd0)
3744 - ((upd_go && up == PASS - 1 && ui[1:0] == r[1:0]) ? G : 16'd0);
3745 if (dot_go) begin
3746 if (dp == PASS - 1) begin dp <= 16'd0; di <= di + 16'd1; end else dp <= dp + 16'd1;
3747 end
3748 if (upd_go) begin
3749 if (up == PASS - 1) begin up <= 16'd0; ui <= ui + 16'd1; end else up <= up + 16'd1;
3750 end
3751 if (wl_d[UPDATED]) written <= written + 16'd1;
3752 case (state)
3753 IN: if (take) begin
3754 if (inb == RB - 1) begin inb <= 16'd0; di <= 16'd0; dp <= 16'd0; ui <= 16'd0; up <= 16'd0; written <= 16'd0; state <= RUN; end
3755 else inb <= inb + 16'd1;
3756 end
3757 RUN: if (written == total && !wv_d[UPDATED]) begin ob <= 16'd0; state <= OUT; end
3758 OUT: if (out_ready) begin
3759 if (ob == RB - 1) state <= IN; else ob <= ob + 16'd1;
3760 end
3761 default: state <= IN;
3762 endcase
3763 end
3764 end
3765 // The rows out, each element stored: a beat read, then its binary16 rounding (logos_f32_to_f16_p) with the beat held
3766 // beside it; out_half the stored binary16, out_beat the element as stored.
3767 reg [512*W-1:0] o_beat;
3768 reg o_v;
3769 always @(posedge clk) begin
3770 o_v <= !rst && state == OUT && out_ready;
3771 o_beat <= gb[ob];
3772 end
3773 // The beat held beside its rounding, its valid in registers the reset clears.
3774 wire [512*W-1:0] o_raw;
3775 logos_delay #(.W(512*W), .D(@F16@)) dob(.clk(clk), .d(o_beat), .q(o_raw));
3776 reg ov_d [1:@F16@];
3777 integer ok;
3778 always @(posedge clk) begin
3779 ov_d[1] <= !rst && o_v;
3780 for (ok = 2; ok <= @F16@; ok = ok + 1) ov_d[ok] <= !rst && ov_d[ok-1];
3781 end
3782 assign out_valid = ov_d[@F16@] && !rst;
3783 genvar o;
3784 generate
3785 for (o = 0; o < 16 * W; o = o + 1) begin : st
3786 wire [31:0] back;
3787 logos_f32_to_f16_p enc(.clk(clk), .x(o_beat[32*o +: 32]), .h(out_half[16*o +: 16]));
3788 logos_f16_to_f32 dec(.h(out_half[16*o +: 16]), .x(back));
3789 assign out_beat[32*o +: 32] = f16 ? back : o_raw[32*o +: 32];
3790 end
3791 endgenerate
3792endmodule
3793"#;
3794
3795const GDN_GATE_VERILOG: &str = r#"
3796// A lane's read-outs gated and normed head by head as the machine's gated norm makes them: each head RMS-normed by the
3797// norm's weight w (numerics::rmsnorm) and each element then times silu of the head's gate z. A head's HD read-outs come
3798// in a clock or more apart (e_v, e_o), the head's first with the store beat its read-outs fill from (e_at); its gate's
3799// beats are written beforehand into the slot the head takes (z_we, z_wa = {slot, beat}, the beat in BA bits; heads take
3800// slots 0..NZ-1 in turn), the weight's before the job's first head (w_we, its beat w_wa). Each read-out is kept and its
3801// square added into lane j mod 16 of the head's sum from +0, fdot's order, in one of two banks by the head's parity;
3802// once its last is in, the head's sum through the tree, times RN (recip(HD) as the build's numerics make it), plus eps
3803// and through the reciprocal square root is its r. Then each element's ((o·r)·w)·silu(z) a clock at a time, the heads
3804// in turn, gathered into beats written from the head's store beat in order (st_we). `gated` counts the heads whose
3805// every beat has been written; a head's slot is free again once it is gated.
3806module logos_gdn_gate_p #(parameter HD = 128, SA = 10, NZ = 16, parameter [31:0] RN = 32'h3c00_0000)
3807 (input clk, input rst,
3808 input [31:0] eps,
3809 input w_we, input [7:0] w_wa, input [511:0] w_wd,
3810 input z_we, input [15:0] z_wa, input [511:0] z_wd,
3811 input e_v, input [31:0] e_o, input [SA-1:0] e_at,
3812 output reg st_we, output reg [SA-1:0] st_wa, output reg [511:0] st_wd,
3813 output reg [15:0] gated, output busy);
3814 localparam BEATS = HD / 16, BA = BEATS > 1 ? $clog2(BEATS) : 1, ZB = $clog2(NZ), JB = $clog2(HD);
3815 localparam [31:0] HD_LAST = HD - 1;
3816 localparam [JB-1:0] J_LAST = HD_LAST[JB-1:0];
3817 localparam [31:0] ONE = 32'h3f80_0000;
3818 localparam FMUL = @FMUL@, FADD = @FADD@, TREE = @TREE16@, RSQRT = @RSQRT@, EXP = @EXP@, RECIP = @RECIP@;
3819 localparam SIL = EXP + FADD + RECIP + FMUL, SCALE = TREE + FMUL + FADD + RSQRT;
3820 // The weight, the gates and the read-outs kept, each read a clock after its address.
3821 `LOGOS_RAMSTYLE(BEATS) reg [511:0] wm [0:BEATS-1];
3822 `LOGOS_RAMSTYLE(NZ << BA) reg [511:0] zm [0:(NZ << BA)-1];
3823 `LOGOS_RAMSTYLE(NZ << JB) reg [31:0] om [0:(NZ << JB)-1];
3824 `LOGOS_RAMSTYLE_ASYNC(NZ) reg [SA-1:0] at [0:NZ-1];
3825 `LOGOS_RAMSTYLE_ASYNC(NZ) reg [31:0] rr [0:NZ-1];
3826 always @(posedge clk) begin
3827 if (w_we) wm[w_wa[BA-1:0]] <= w_wd;
3828 if (z_we) zm[z_wa[ZB+BA-1:0]] <= z_wd;
3829 end
3830
3831 // ── The sums ─────────────────────────────────────────────────────────────────────────────────────────────────────
3832 // The head coming in: its slot, its element; each element's square, then its lane's sum, tagged by lane, bank, first
3833 // beat and last element.
3834 reg [15:0] a_n;
3835 reg [JB-1:0] a_j;
3836 wire [ZB-1:0] a_s = a_n[ZB-1:0];
3837 always @(posedge clk) begin
3838 if (e_v) begin
3839 om[{a_s, a_j}] <= e_o;
3840 if (a_j == {JB{1'b0}}) at[a_s] <= e_at;
3841 a_j <= a_j + 1'b1;
3842 if (a_j == J_LAST) a_n <= a_n + 16'd1;
3843 end
3844 if (rst) begin a_n <= 16'd0; a_j <= {JB{1'b0}}; end
3845 end
3846 wire [31:0] sq;
3847 logos_fmul_p usq(.clk(clk), .a(e_o), .b(e_o), .y(sq));
3848 // Each tag's landing's flight is the gate's busy's own reckoning (its heads in and gated) and is not read.
3849 wire sq_flight, su_flight, r_flight, y_flight;
3850 wire [3:0] sq_lane;
3851 wire sq_v, sq_bank, sq_first, sq_last;
3852 wire [ZB-1:0] sq_slot;
3853 logos_landing #(.W(7 + ZB), .D(FMUL)) dsq(.clk(clk), .rst(rst), .in_v(e_v),
3854 .in_d({a_j[3:0], a_s[0], (a_j >> 4) == {JB{1'b0}}, a_j == J_LAST, a_s}), .out_v(sq_v), .out_d({sq_lane, sq_bank, sq_first, sq_last, sq_slot}),
3855 .flight(sq_flight));
3856 reg [511:0] acc0, acc1;
3857 wire [511:0] acc_q = sq_bank ? acc1 : acc0;
3858 wire [31:0] sum;
3859 logos_fadd_p uacc(.clk(clk), .a(sq_first ? 32'd0 : acc_q[32*sq_lane +: 32]), .b(sq), .y(sum));
3860 wire [3:0] su_lane;
3861 wire su_v, su_bank, su_last;
3862 wire [ZB-1:0] su_slot;
3863 logos_landing #(.W(6 + ZB), .D(FADD)) dsu(.clk(clk), .rst(rst), .in_v(sq_v),
3864 .in_d({sq_lane, sq_bank, sq_last, sq_slot}), .out_v(su_v), .out_d({su_lane, su_bank, su_last, su_slot}), .flight(su_flight));
3865 always @(posedge clk) begin
3866 if (su_v && !su_bank) acc0[32*su_lane +: 32] <= sum;
3867 if (su_v && su_bank) acc1[32*su_lane +: 32] <= sum;
3868 end
3869
3870 // ── The scale ────────────────────────────────────────────────────────────────────────────────────────────────────
3871 // A clock after a head's last sum: its bank through the tree, times recip(HD), plus eps, through rsqrt: its r.
3872 reg t_v, t_bank;
3873 reg [ZB-1:0] t_slot;
3874 always @(posedge clk) begin
3875 t_v <= !rst && su_v && su_last;
3876 t_bank <= su_bank;
3877 t_slot <= su_slot;
3878 end
3879 wire [31:0] tsum, tm, te, r;
3880 logos_tree16_p utr(.clk(clk), .lanes(t_bank ? acc1 : acc0), .sum(tsum));
3881 logos_fmul_p utm(.clk(clk), .a(tsum), .b(RN), .y(tm));
3882 logos_fadd_p ute(.clk(clk), .a(tm), .b(eps), .y(te));
3883 logos_rsqrt_p utq(.clk(clk), .x_in(te), .y(r));
3884 wire r_v;
3885 wire [ZB-1:0] r_slot;
3886 logos_landing #(.W(ZB), .D(SCALE)) dr(.clk(clk), .rst(rst), .in_v(t_v), .in_d(t_slot), .out_v(r_v), .out_d(r_slot), .flight(r_flight));
3887 reg [15:0] scaled;
3888 always @(posedge clk) begin
3889 if (r_v) rr[r_slot] <= r;
3890 scaled <= rst ? 16'd0 : scaled + (r_v ? 16'd1 : 16'd0);
3891 end
3892
3893 // ── The products ─────────────────────────────────────────────────────────────────────────────────────────────────
3894 // The head going out: its slot, its element; issued once the head's r is kept, element after element; each read a
3895 // clock after its address, chosen a clock after that.
3896 reg [15:0] b_n;
3897 reg [JB-1:0] b_j;
3898 wire [JB-1:0] b_hi = b_j >> 4;
3899 reg b_on;
3900 wire [ZB-1:0] b_s = b_n[ZB-1:0];
3901 wire b_go = b_on || scaled != b_n;
3902 reg [31:0] b_r;
3903 always @(posedge clk) begin
3904 if (b_go) begin
3905 b_j <= b_j + 1'b1;
3906 if (b_j == J_LAST) b_n <= b_n + 16'd1;
3907 b_on <= b_j != J_LAST;
3908 end
3909 if (!b_on) b_r <= rr[b_s];
3910 if (rst) begin b_n <= 16'd0; b_j <= {JB{1'b0}}; b_on <= 1'b0; end
3911 end
3912 reg [31:0] o1, r1;
3913 reg [511:0] w1, z1;
3914 reg [3:0] k1;
3915 reg v1;
3916 reg [31:0] o2, w2, z2, r2;
3917 reg [3:0] k2;
3918 reg v2, last1, last2;
3919 reg [BA-1:0] bt1, bt2;
3920 reg [ZB-1:0] s1, s2;
3921 always @(posedge clk) begin
3922 v1 <= !rst && b_go; k1 <= b_j[3:0]; bt1 <= b_hi[BA-1:0]; s1 <= b_s; last1 <= b_j == J_LAST;
3923 o1 <= om[{b_s, b_j}]; w1 <= wm[b_hi[BA-1:0]]; z1 <= zm[{b_s, b_hi[BA-1:0]}]; r1 <= b_on ? b_r : rr[b_s];
3924 v2 <= !rst && v1; k2 <= k1; bt2 <= bt1; s2 <= s1; last2 <= last1;
3925 o2 <= o1; r2 <= r1; w2 <= w1[32*k1 +: 32]; z2 <= z1[32*k1 +: 32];
3926 end
3927 // ((o·r)·w), and beside it silu(z) = z·recip(1 + exp(-ftz(z))), then their product.
3928 wire [31:0] zf = `LOGOS_FTZ(z2);
3929 wire [31:0] p1, p2, p2d, w2d, e, s, sig, z2d, g, y;
3930 logos_fmul_p up1(.clk(clk), .a(o2), .b(r2), .y(p1));
3931 logos_delay #(.W(32), .D(FMUL)) dw(.clk(clk), .d(w2), .q(w2d));
3932 logos_fmul_p up2(.clk(clk), .a(p1), .b(w2d), .y(p2));
3933 logos_exp_p ux(.clk(clk), .x_in({~zf[31], zf[30:0]}), .y(e));
3934 logos_fadd_p ua(.clk(clk), .a(ONE), .b(e), .y(s));
3935 logos_recip_p ur(.clk(clk), .x_in(s), .y(sig));
3936 logos_delay #(.W(32), .D(EXP + FADD + RECIP)) dz(.clk(clk), .d(z2), .q(z2d));
3937 logos_fmul_p ug(.clk(clk), .a(z2d), .b(sig), .y(g));
3938 logos_delay #(.W(32), .D(SIL - 2 * FMUL)) dp(.clk(clk), .d(p2), .q(p2d));
3939 logos_fmul_p uy(.clk(clk), .a(p2d), .b(g), .y(y));
3940 wire y_v, y_last;
3941 wire [3:0] y_k;
3942 wire [BA-1:0] y_bt;
3943 wire [ZB-1:0] y_s;
3944 logos_landing #(.W(5 + BA + ZB), .D(SIL + FMUL)) dy(.clk(clk), .rst(rst), .in_v(v2), .in_d({k2, last2, bt2, s2}),
3945 .out_v(y_v), .out_d({y_k, y_last, y_bt, y_s}), .flight(y_flight));
3946 // The beats out: sixteen elements gathered, written at the head's store beat plus the beat's place.
3947 reg [479:0] gath;
3948 reg st_last;
3949 always @(posedge clk) begin
3950 st_we <= 1'b0;
3951 if (y_v) begin
3952 if (y_k != 4'd15) gath[32*y_k +: 32] <= y;
3953 else begin st_we <= !rst; st_last <= y_last; st_wa <= at[y_s] + {{(SA-BA){1'b0}}, y_bt}; st_wd <= {y, gath}; end
3954 end
3955 gated <= rst ? 16'd0 : gated + ((st_we && st_last) ? 16'd1 : 16'd0);
3956 end
3957 assign busy = a_n != gated || a_j != {JB{1'b0}};
3958endmodule
3959"#;