Skip to main content

logicaffeine_compile/spatial/decode_engine/rtl/
gdn.rs

1//! The Gated-DeltaNet row engine as Verilog: one value row of a memory through one token
2//! ([`crate::spatial::decode_engine::numerics::gdn_row`]), sixteen elements a cycle.
3//!
4//! A row streams in as `N/16` beats of sixteen f32 elements, the row's value on its first beat. Each beat feeds
5//! sixteen lanes of two dots, element `16·beat + l` into lane `l` in index order — the key's dot and the query's — and
6//! is kept. After the last beat the lanes fold by the pairwise tree, the coefficient `beta·(v − decay·(row·k))` and the
7//! read-out `(decay·(row·q) + coef·(k·q))·scale` follow, and the kept row streams back out updated, sixteen elements
8//! a cycle, each `decay·m + coef·k` rounded to binary16 and back when the state is stored in f16; the read-out rides
9//! the last beat.
10//!
11//! The state's codec is [`super::fp32`]'s binary16 conversions.
12//!
13//! `logos_gdn_lazy_row` is the row under a lazily flushed memory ([`crate::spatial::decode_engine::numerics::gdn_read`],
14//! [`gdn_flush`](crate::spatial::decode_engine::numerics::gdn_flush)): the row streams in with its carried value and
15//! its logged values, its dot with the head's folded query in sixteen lanes and the tree gives the read-out
16//! `(row·y + carried)·scale`; when the log is full every logged update is replayed over the held row in order — the
17//! row's dot with the token's key, the coefficient, the rank-one update, no rounding between — and the row streams back
18//! out stored once.
19//!
20//! `logos_gdn_read_p` is the lazily flushed read-out for the user clock, on the pipelined units: rows stream in beat by
21//! beat, row-major, up to a beat a clock, and out comes each row's `(row·y + carried)·scale`, the rows in their order.
22//! It gathers rows four at a time into one of two buffers and sends each group through its sixteen lanes beat-major —
23//! the four rows' first beats, then their second — so the add's latency of four clocks is exactly the time between a
24//! lane's terms for one row, and each lane's sum feeds back into its own add; a row's lanes then fold through the
25//! pipelined tree. A group takes its query and scale with its first beat, so heads stream back to back.
26//!
27//! `logos_gdn_flush_p` is the flush for the user clock: a head's log written beat by beat, then its rows in fours, each
28//! with its logged values. For every logged token the group's beats pass through the same sixteen lanes to the tree for
29//! each row's dot with the token's key, the coefficient `beta·(v − decay·(row·k))` follows on the pipelined units, and a
30//! second pass writes `decay·m + coef·k` back over the held rows; after the last token the rows stream out, stored once.
31//!
32//! `logos_gdn_fold_p` folds a head's query back through its log for the user clock ([`gdn_fold`](
33//! crate::spatial::decode_engine::numerics::gdn_fold)): every logged token's decay through the pipelined exponential first,
34//! then the tokens newest first — the key's dot with the folded query a beat each four clocks, so each lane's sum comes
35//! back to its add as the next term enters, the tree, the gate's product, then a beat a clock the values collected and
36//! the query decayed past the token.
37
38/// The row engine with the f32 units and the codec it is built from.
39pub fn gdn_verilog() -> String {
40    format!("{}{}\n{}", super::ram::RAMSTYLE, super::fp32::fp32_verilog(), GDN_VERILOG)
41}
42
43/// The lines the recurrence's coordinator and each head lane ask ahead of those they have taken (`OUTSTANDING`) unless a
44/// build gives more ([`super::engine::Streaming::with_recurrence_credit`]): behind a memory that answers `latency` clocks
45/// after an ask, a credit of `c` lines reads at most `c / latency` lines a clock. Each line queue is the power of two past
46/// the credit, 64 lines at least.
47pub const LANE_CREDIT: usize = 32;
48
49/// The most lines [`LANE_CREDIT`] may be raised to: a channel's read FIFO holds 512 lines, and the channel multiplexer
50/// takes 30 runs a channel.
51pub const LANE_CREDIT_MAX: usize = 256;
52
53/// Clocks the head lanes' fold (`logos_gdn_fold_m`) takes from a read-back's address (`rd_bank`, `rd_slot`, `rd_beat`)
54/// to its words: the address registered beside the banks, then read.
55pub const FOLD_M_READ_LATENCY: usize = 2;
56
57/// The recurrence on head lanes (`logos_gdn_lanes`): a coordinator (the side's MODE 1) that counts, appends and reads
58/// the log once into each lane's copy of it, and L head lanes (`logos_gdn_lane`), lane k taking value heads k, k + L, …
59/// a batch at a time through the fold of several heads at once and its own memory port; emitted beside
60/// [`gdn_stream_verilog`]'s units, the side and the fold among them.
61pub 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
66/// [`lanes_verilog`] under a trailing flush of `window` tokens a window: the coordinator keeps the log as a ring of two
67/// windows and hands the lanes the share of rows each row replays; lane k is `logos_gdn_lane_t`, its window's flush
68/// `logos_gdn_flush_m`; each copy of the log holds two windows. The lanes' `TMAX` is the log's two windows.
69pub 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
89/// `logos_gdn_gate_p`, a head lane's read-outs gated and normed head by head as the machine's gated norm makes them
90/// ([`crate::spatial::decode_engine::machine::DeltaNorm`]): each head's sum of squares in sixteen lanes as it comes in,
91/// its r through the tree, the product with recip(HD), the sum with epsilon and the reciprocal square root, then each
92/// element's `((o·r)·w)·silu(z)` a clock at a time, on the pipelined units.
93pub 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
103/// [`lanes_verilog`]'s or [`lanes_trailing_verilog`]'s head lanes with each lane's read-outs gated and normed where they
104/// are made, as a recurrence job that carries a [`crate::spatial::decode_engine::machine::DeltaNorm`] asks
105/// (`Streaming::with_delta_norm`): the job's `norm`, its epsilon, the gate's slot (each row's beat of it, from the rows
106/// module, `z_beat`) and the norm's weight's first line (`w_line`) come in beside the job's other fields. The lanes fetch
107/// the weight's `hd / 16` lines on the coordinator's port at the job's start, before its first row, and give every lane
108/// each as it comes. A lane's loader reads each head's gate beats after its query's into the gate's slot of the head
109/// (`logos_gdn_gate_p`, heads taking its NZ slots in turn), loading a batch only while the heads loaded and not yet gated
110/// leave the batch room; its read-outs go through the gate, which writes the store; a row is done only once the gate has
111/// written its every head. A job without a norm runs as the lanes run it.
112pub 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    // A lane loads a batch (at most FADD heads) only while its heads loaded and not yet gated leave the batch room in the
118    // gate's 16 slots; two batches loaded ahead of the stream and one in the gate always fit, so the loader never waits on
119    // heads that wait on it.
120    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    // The lanes: the job's norm fields, the weight's fetch, each row's gate beat, each lane's norm ports.
126    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    // A lane: the norm's ports; each head's gate beats read after its query's into the head's slot of the gate; the
156    // read-outs through the gate.
157    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        // Rows in flight: each row's gate beat with its other fields; a row done only once the gate has written every head
200        // given it up to the row's last (gin, the heads given; o_mark0/1, the count once each row's last is given).
201        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        // One row at a time: its gate beat with its other fields; the row ends only once the gate has written every head.
216        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
228/// [`rows_verilog`] giving each row's beat of one more vector, the gate of a recurrence job's norm (`z_slot`, `z_beat`),
229/// for [`delta_normed_lanes`].
230pub 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
246/// `logos_gdn_rows`, a recurrence job's token rows given on one after another as the descriptors `logos_gdn_lanes` takes
247/// (and the side, a row at a time): each row's region from a copy of the rows table, each vector's store beat from a copy
248/// of the slots table, the copies written as the host writes the tables.
249pub fn rows_verilog() -> String {
250    GDN_ROWS_VERILOG.to_string()
251}
252
253/// The streaming units for the user clock with the f32 units and the pipelined exponential they are built from:
254/// `logos_gdn_read_p`, a lazily flushed head's read-out a beat a clock; `logos_gdn_flush_p`, its rows' flush;
255/// `logos_gdn_fold_p`, its query folded back through its log.
256pub 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
260/// `logos_gdn_flush_m`, a trailing flush's shares of several heads at once in two banks, emitted beside
261/// [`gdn_stream_verilog`]'s units, which it is built from.
262pub fn flush_m_verilog() -> String {
263    format!("{}{}", super::ram::RAMSTYLE, filled(GDN_FLUSH_M_VERILOG))
264}
265
266/// `text` with every unit's latency in its place.
267fn filled(text: &str) -> String {
268    // Clocks after a beat's operands: its products, its lanes' sums, a row's dot out of the tree; then the read-out's
269    // carried value and scale, or the flush's logged value and coefficient; an update's beat.
270    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
299// `logos_gdn_read_p` sends a group's rows in turn, each lane's sum coming back to its add as the row's next term
300// enters: a group is as many rows as the add's clocks.
301const _: () = 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
1469/// A head lane of the recurrence on head lanes (`logos_gdn_lane`).
1470const 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
1959/// The recurrence on head lanes (`logos_gdn_lanes`): a coordinator (`logos_gdn_side` in MODE 1) and L lanes
1960/// (`logos_gdn_lane`), the log read once into every lane's copy of it.
1961const 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
2795/// `logos_gdn_fold_m`: the fold of as many heads at once as the add takes clocks, a beat of each in turn.
2796const 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
3447// The read-out and the flush at 16·W elements a clock, a binary16 line a clock at W = 2. A lane takes a beat's W
3448// terms in order through W chained adds, so element i meets lane i mod 16 in index order exactly as numerics::fdot adds
3449// it, and a group is as many rows as the chain's clocks, so a lane's sum for a row comes back to the chain's first add
3450// as the row's next beat enters.
3451const 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"#;