1use std::fmt::Write as _;
17
18use crate::model::quant::GgmlType;
19
20use super::super::image::{Image, ImageLayout};
21use super::super::machine::Fold;
22use super::super::numerics::Words;
23use super::column::ColumnSet;
24use super::stream::{TileDescriptor, AW, BYW, LW, ROWW, TW};
25
26#[derive(Clone, Copy, Debug, PartialEq, Eq)]
29pub struct TileRun {
30 pub tensor: u32,
31 pub count: u64,
32 pub line: u64,
33 pub lines: u64,
34 pub bytes: u64,
35 pub last_lines: u64,
36 pub last_bytes: u64,
37 pub row: u64,
38 pub row_step: u64,
39}
40
41impl TileRun {
42 pub fn of(image: &Image, tensor: usize, channel: u32) -> TileRun {
44 let p = &image.tensors[tensor];
45 let c = p.channels.len() as u64;
46 let turn = p.channels.iter().position(|&ch| ch == channel).expect("a channel the tensor is dealt to") as u64;
47 let tiles = p.tile_count as u64;
48 let count = tiles.saturating_sub(turn).div_ceil(c);
49 let last = turn + c * count.saturating_sub(1);
50 let held = p.rows_per_tile.min(p.rows.saturating_sub(last * p.rows_per_tile));
51 TileRun {
52 tensor: tensor as u32,
53 count,
54 line: p.first_line[turn as usize],
55 lines: p.tile_lines,
56 bytes: p.rows_per_tile * p.image_row_bytes(),
57 last_lines: image.layout.lines_for(held * p.image_row_bytes()),
58 last_bytes: held * p.image_row_bytes(),
59 row: turn * p.rows_per_tile,
60 row_step: c * p.rows_per_tile,
61 }
62 }
63
64 pub fn descriptors(&self) -> Vec<TileDescriptor> {
66 (0..self.count)
67 .map(|k| {
68 let last = k + 1 == self.count;
69 TileDescriptor {
70 line: self.line + k * self.lines,
71 lines: if last { self.last_lines } else { self.lines },
72 bytes: if last { self.last_bytes } else { self.bytes },
73 tensor: self.tensor,
74 first_row: self.row + k * self.row_step,
75 }
76 })
77 .collect()
78 }
79}
80
81pub const STREAM_ENTRY_LINES: u64 = 3;
83
84#[derive(Clone, Copy, Debug, PartialEq, Eq)]
94pub struct StreamEntry {
95 pub fmt: u32,
96 pub blocks: u32,
97 pub rows: u32,
98 pub stripes: u32,
99 pub turns: u32,
100 pub tensor: u32,
101 pub greedy: bool,
102 pub fold: Fold,
103 pub runs: [TileRun; 4],
104}
105
106impl StreamEntry {
107 pub fn of(image: &Image, k: usize, set: &ColumnSet) -> Result<StreamEntry, String> {
111 let p = &image.tensors[k];
112 let pairs = image.layout.stripe.ok_or("an image not laid in stripes")?;
113 if !p.block_major || p.pieces != 1 {
114 return Err(format!("`{}` is not laid in stripes", p.name));
115 }
116 if image.layout.channels != 4 || p.channels.len() != 4 {
117 return Err(format!("`{}` over {} channels, the unit's four", p.name, p.channels.len()));
118 }
119 let fmt = set.formats.iter().position(|t| t.name() == p.ty).ok_or_else(|| format!("`{}` in {}, outside the set {:?}", p.name, p.ty, set.formats))?;
120 let ty: GgmlType = set.formats[fmt];
121 if p.block_bytes != set.block_bytes(ty) as u64 {
122 return Err(format!("`{}` laid in blocks of {} bytes, the unit's of {}", p.name, p.block_bytes, set.block_bytes(ty)));
123 }
124 if p.slot_bytes() != set.slot_bytes(ty) as u64 {
125 return Err(format!("`{}` laid in slots of {} bytes, the unit's of {}", p.name, p.slot_bytes(), set.slot_bytes(ty)));
126 }
127 let mut turns = 0u32;
128 for (turn, &ch) in p.channels.iter().enumerate() {
129 turns |= (turn as u32) << (2 * ch);
130 }
131 let runs = [0, 1, 2, 3].map(|ch| TileRun::of(image, k, ch));
132 let fits = |what: &str, v: u64, bits: u32| if v >> bits == 0 { Ok(v as u32) } else { Err(format!("`{}`'s {what} {v} past {bits} bits", p.name)) };
133 for r in &runs {
134 fits("tile count", r.count, 32)?;
135 fits("line", r.line + r.count * r.lines, AW)?;
136 fits("tile lines", r.lines.max(r.last_lines), LW)?;
137 fits("tile bytes", r.bytes.max(r.last_bytes), BYW)?;
138 fits("rows", r.row + r.count * r.row_step, ROWW)?;
139 }
140 Ok(StreamEntry {
141 fmt: fmt as u32,
142 blocks: fits("blocks a row", p.row_bytes / set.block_bytes(ty) as u64, 16)?,
143 rows: fits("rows", p.rows, 32)?,
144 stripes: fits("stripes", p.rows.div_ceil(8 * pairs), 16)?,
145 turns,
146 tensor: fits("tag", k as u64, TW)?,
147 greedy: false,
148 fold: Fold::Put,
149 runs,
150 })
151 }
152
153 pub fn lines(&self) -> [[u8; 64]; 3] {
155 let mut words = [0u32; 48];
156 let flags = self.greedy as u32 | (self.fold.code() as u32) << 1;
157 words[..7].copy_from_slice(&[self.fmt, self.blocks, self.rows, self.stripes, self.turns, self.tensor, flags]);
158 for (c, r) in self.runs.iter().enumerate() {
159 let at = 16 + 8 * c;
160 words[at..at + 8].copy_from_slice(&[r.count, r.line, r.lines, r.bytes, r.last_lines, r.last_bytes, r.row, r.row_step].map(|v| v as u32));
161 }
162 let mut lines = [[0u8; 64]; 3];
163 for (i, w) in words.iter().enumerate() {
164 lines[i / 16][4 * (i % 16)..4 * (i % 16) + 4].copy_from_slice(&w.to_le_bytes());
165 }
166 lines
167 }
168}
169
170pub fn tile_run_verilog() -> String {
172 format!(
173 r#"// One channel's tiles of one tensor, walked into tile descriptors: `count` tiles from line `line`, `lines` lines and
174// `bytes` row bytes each but the last's `last_lines` and `last_bytes`, the first row `row` and `row_step` more each.
175module logos_tile_run
176 (input clk, input rst, input start, input [31:0] count, input [{a}:0] line, input [{l}:0] lines, input [{b}:0] bytes,
177 input [{l}:0] last_lines, input [{b}:0] last_bytes, input [{r}:0] row, input [{r}:0] row_step, input [{t}:0] tensor,
178 output tile_valid, input tile_ready, output [{a}:0] tile_line, output [{l}:0] tile_lines, output [{b}:0] tile_bytes,
179 output [{t}:0] tile_tensor, output [{r}:0] tile_row, output busy);
180 reg on;
181 reg [31:0] k;
182 reg [{a}:0] at;
183 reg [{r}:0] first;
184 wire last = k + 32'd1 == count;
185 assign tile_valid = on;
186 assign tile_line = at;
187 assign tile_lines = last ? last_lines : lines;
188 assign tile_bytes = last ? last_bytes : bytes;
189 assign tile_tensor = tensor;
190 assign tile_row = first;
191 assign busy = on;
192 always @(posedge clk) begin
193 if (rst) on <= 1'b0;
194 else if (start) begin on <= count != 32'd0; k <= 32'd0; at <= line; first <= row; end
195 else if (on && tile_ready) begin
196 if (last) on <= 1'b0;
197 k <= k + 32'd1; at <= at + lines[{a}:0]; first <= first + row_step;
198 end
199 end
200endmodule
201"#,
202 a = AW - 1,
203 l = LW - 1,
204 b = BYW - 1,
205 r = ROWW - 1,
206 t = TW - 1
207 )
208}
209
210pub const ACT_CODE_LATENCY: usize = 20;
212
213pub const ACT_BLOCKS_READ_LATENCY: usize = 4 + super::act_quant::WIDE_SCALE_P_LATENCY;
216
217pub fn act_blocks_out(blocks: &[(usize, usize)]) -> Vec<usize> {
222 let mut free = 0;
223 blocks
224 .iter()
225 .map(|&(last, beats)| {
226 let start = (last + ACT_BLOCKS_READ_LATENCY).max(free);
227 free = start + beats;
228 start + beats + ACT_CODE_LATENCY + 1
229 })
230 .collect()
231}
232
233pub fn act_stream_p_verilog() -> String {
238 format!(
239 "{}\n{}\n{}",
240 super::act_quant::act_quant_verilog(),
241 super::ram::bram_verilog(&[512]),
242 ACT_STREAM_P
243 .replace("@LAT@", &ACT_CODE_LATENCY.to_string())
244 .replace("@WS@", &super::act_quant::WIDE_SCALE_P_LATENCY.to_string())
245 )
246}
247
248pub const GREEDY_LATENCY: usize = 5;
250
251pub fn greedy_verilog() -> String {
253 format!("{}{GREEDY}", super::ram::RAMSTYLE)
254}
255
256pub const FOLD_LATENCY: usize = super::simd::LANE_FUNC_P_LATENCY + super::fp32::FMUL_LATENCY;
259
260pub fn streaming_verilog(sets: &[ColumnSet], layout: &ImageLayout) -> String {
262 streaming_verilog_folding(sets, layout, 0)
263}
264
265pub fn streaming_verilog_folding(sets: &[ColumnSet], layout: &ImageLayout, folds: usize) -> String {
268 assert!(folds == 0 || [1, 2, 4, 8, 16].contains(&folds), "a fold over 1, 2, 4, 8 or 16 lanes, not {folds}");
269 let mut v = format!(
270 "{}\n{}\n{}\n{}\n{}",
271 super::column::columns_verilog(sets),
272 act_stream_p_verilog(),
273 super::stream::verilog(layout),
274 tile_run_verilog(),
275 greedy_verilog()
276 );
277 let dw = 8 * layout.line_bytes;
278 for set in sets {
279 let ColumnSet { name, formats, words, .. } = set;
280 let wide = *words == Words::Two;
281 let elems = formats.iter().map(|t| t.block_len()).max().expect("a set of formats");
282 let multi = formats.len() > 1;
283 let (octet_job, act_block, quant_blocks) = match formats.iter().position(|&t| set.octet(t)) {
285 Some(place) => (
286 format!(
287 " // An octet job's quantized blocks: eight to an octet, block b into its octet's word b / 8 at slot b mod 8, its\n \
288 // codes in the slot's 32 lanes.\n wire oct = {};\n wire [15:0] qblocks = oct ? {{blocks[12:0], 3'd0}} : blocks;\n",
289 if multi { format!("fmt_q == 4'd{place}") } else { "1'b1".to_string() }
290 ),
291 ".act_b(oct ? {3'd0, ob[15:3]} : ob), .act_s(oct ? ob[2:0] : 3'd0), .act_codes(oct ? (aq_codes & keep) << (512 * ob[2:0]) : aq_codes & keep)".to_string(),
292 "qblocks",
293 ),
294 None => (String::new(), ".act_b(ob), .act_codes(aq_codes & keep)".to_string(), "blocks"),
295 };
296 let mut lens = String::new();
297 if multi {
298 lens.push_str(" reg [15:0] blen;\n always @*\n case (fmt_q)\n");
299 for (i, t) in formats.iter().enumerate() {
300 writeln!(lens, " 4'd{i}: blen = 16'd{};", t.block_len()).unwrap();
301 }
302 writeln!(lens, " default: blen = 16'd{};\n endcase", formats[0].block_len()).unwrap();
303 } else {
304 writeln!(lens, " wire [15:0] blen = 16'd{};", formats[0].block_len()).unwrap();
305 }
306 writeln!(
307 v,
308 r#"
309// A {what} matvec job from the token-row store to the token-row store, its matrix's stripes from the four channels. The
310// input slot's row t holds token-row t's input from beat from_base + t * from_beats, the output slot's from to_base + t *
311// to_beats; the store's word for an address is out SRL clocks after it. Each weight block of each token-row's input is read a
312// beat a clock into the quantizer, block after block, and each block's codes, zero past the block, written to the
313// array's activations as the quantizer gives them; then each
314// channel's tile run (packed a field a channel) walks into its image stream, which reads the tiles' lines off the
315// channel's port, the array runs the stripes, and each clock's sums, one stripe's rows for one token-row, are written
316// into that token-row's output row under a mask of the rows the matrix has. A greedy job (a head's) writes no row:
317// each beat it would write goes to logos_greedy, which keeps every token-row's first largest number so far (a row's
318// sums arrive in row order), and once the last is through, each token-row's beat is its row and its value (lanes 0
319// and 1), numerics::argmax of the token-row's sums.
320module logos_streaming_matvec_{name} #(parameter N = 1, T_MAX = 4, NB_MAX = 16, SA = 12, SRL = 1)
321 (input clk,{clk2_in} input rst,
322 input start, input greedy{fmt_in}, input [15:0] blocks, input [7:0] tokens{two_in}, input [31:0] rows, input [15:0] stripes,
323 input [7:0] turns, input [SA-1:0] from_base, input [15:0] from_beats, input [SA-1:0] to_base, input [15:0] to_beats,
324 output busy,
325 output [SA-1:0] st_ra, input [511:0] st_rd,
326 output reg st_we, output reg [SA-1:0] st_wa, output reg [511:0] st_wd, output reg [63:0] st_wm,
327 input [127:0] tr_count, input [4*{aw}-1:0] tr_line, input [4*{lw}-1:0] tr_lines, input [4*{byw}-1:0] tr_bytes,
328 input [4*{lw}-1:0] tr_last_lines, input [4*{byw}-1:0] tr_last_bytes, input [4*{roww}-1:0] tr_row,
329 input [4*{roww}-1:0] tr_row_step, input [{tw}-1:0] tensor,
330 output [3:0] md_valid, input [3:0] md_ready, output [4*{mdw}-1:0] md_data,
331 input [3:0] mr_valid, output [3:0] mr_ready, input [4*{dw}-1:0] mr_data);
332 // The queue holds a burst of a stripe's token-rows beside every sum the array has in flight: the array holds its
333 // issues while the queue could not take them all.
334 localparam C = 4 * N, E = 8 * N, TAGS = {tags}, QD = T_MAX + TAGS + 1, QA = $clog2(QD + 1);
335{two_decl} localparam [31:0] ROWS_A_STRIPE = E;
336 localparam [QA-1:0] QLAST = QD - 1, HOLD_AT = T_MAX;
337 reg [QA-1:0] qh, qt, qn;
338 localparam IDLE = 3'd0, READ = 3'd1, QUANT = 3'd2, RUN = 3'd3, DRAIN = 3'd4, BEST = 3'd5;
339 reg greedy_r;
340{fmt_copy}{lens} wire [15:0] bpb = blen >> 4;
341 reg [2:0] state;
342 reg [7:0] t;
343 reg [15:0] b, j;
344 // The beat at hand: token-row t's beat b·bpb + j from its row's first, kept as offsets from the input slot's.
345 reg [31:0] in_row0, in_at;
346 assign st_ra = from_base + in_at[SA-1:0];
347 // A beat asked carries its block's marks and place (arriving, first, last, in_t, in_b, the clock after its address) to
348 // the clock its word is out of the store, SRL clocks after its address (landed_*).
349 reg arriving, first, last;
350 reg [7:0] in_t;
351 reg [15:0] in_b;
352 wire landed, landed_first, landed_last, landing;
353 wire [7:0] landed_t;
354 wire [15:0] landed_b;
355 logos_landing #(.W(26), .D(SRL - 1)) land(.clk(clk), .rst(rst), .in_v(arriving), .in_d({{first, last, in_t, in_b}}),
356 .out_v(landed), .out_d({{landed_first, landed_last, landed_t, landed_b}}), .flight(landing));
357 // The input's beats into the quantizer block after block, and each block's codes, scale and place out of it in order,
358 // written into the array's activations as they come.
359 wire aq_busy, aq_out;
360 wire [7:0] aq_m;
361 wire signed [11:0] aq_e;
362 wire [23:0] aq_tag;
363 wire [{codes_top}:0] aq_codes;
364 wire [7:0] ot = aq_tag[23:16];
365 wire [15:0] ob = aq_tag[15:0];
366 logos_act_blocks_p #(.LANES({elems}), .TW(24)) aq(.clk(clk), .rst(rst), .in_v(landed), .in_beat(st_rd),
367 .in_first(landed_first), .in_last(landed_last), .in_two(two), .in_tag({{landed_t, landed_b}}), .busy(aq_busy), .out_v(aq_out),
368 .out_m(aq_m), .out_e(aq_e), .out_tag(aq_tag), .codes(aq_codes));
369 // Which of the quantizer's codes are its block's, by the job's format, a register beside its loads: settled two clocks
370 // after the job starts, long before the job's first codes are out.
371 reg [{codes_top}:0] keep;
372 integer code;
373 always @(posedge clk) for (code = 0; code < {elems}; code = code + 1) keep[16*code +: 16] <= (code[15:0] < blen) ? 16'hffff : 16'h0000;
374 wire act_wr = aq_out;
375{octet_job}
376 // The array and the channels' feeds.
377 reg run;
378 wire array_busy;
379 wire [C-1:0] w_valid, w_ready, w_put, o_valid;
380 wire [C*{beat}-1:0] w_beat;
381 wire [C*32-1:0] o_val1, o_val2;
382 wire [15:0] o_stripe;
383 wire [7:0] o_t;
384 logos_column_array_{name} #(.CH(4), .N(N), .T_MAX(T_MAX), .NB_MAX(NB_MAX)) u(.clk(clk), {clk2_pass}.rst(rst), .start(run),
385 .stripes(stripes), .blocks(blocks), .tokens(tokens), {two_pass}{fmt_pass}.rows(rows), .turns(turns), .hold(qn > HOLD_AT),
386 .busy(array_busy),
387 .act_wr(act_wr), .act_t(ot), {act_block}, .act_m(aq_m), .act_e(aq_e), .w_valid(w_valid),
388 .w_ready(w_ready), .w_put(w_put), .w_beat(w_beat), .o_valid(o_valid), .o_val1(o_val1), .o_val2(o_val2),
389 .o_stripe(o_stripe), .o_t(o_t));
390 // Each channel: its tile run, its image stream, its feed.
391 wire [3:0] runs_busy;
392 genvar ch;
393 generate
394 for (ch = 0; ch < 4; ch = ch + 1) begin : chan
395 wire tile_valid, tile_ready, in_valid, in_ready, in_last, in_crc;
396 wire [{aw}-1:0] tile_line;
397 wire [{lw}-1:0] tile_lines;
398 wire [{byw}-1:0] tile_bytes, in_offset;
399 wire [{tw}-1:0] tile_tensor, in_tensor;
400 wire [{roww}-1:0] tile_row, in_row;
401 wire [511:0] in_data;
402 wire [6:0] in_keep;
403 logos_tile_run walk(.clk(clk), .rst(rst), .start(run), .count(tr_count[32*ch +: 32]), .line(tr_line[{aw}*ch +: {aw}]),
404 .lines(tr_lines[{lw}*ch +: {lw}]), .bytes(tr_bytes[{byw}*ch +: {byw}]), .last_lines(tr_last_lines[{lw}*ch +: {lw}]),
405 .last_bytes(tr_last_bytes[{byw}*ch +: {byw}]), .row(tr_row[{roww}*ch +: {roww}]), .row_step(tr_row_step[{roww}*ch +: {roww}]),
406 .tensor(tensor), .tile_valid(tile_valid), .tile_ready(tile_ready), .tile_line(tile_line), .tile_lines(tile_lines),
407 .tile_bytes(tile_bytes), .tile_tensor(tile_tensor), .tile_row(tile_row), .busy(runs_busy[ch]));
408 logos_image_stream stream(.clk(clk), .rst_n(!rst), .tile_valid(tile_valid), .tile_ready(tile_ready),
409 .tile_line(tile_line), .tile_lines(tile_lines), .tile_bytes(tile_bytes), .tile_tensor(tile_tensor),
410 .tile_row(tile_row), .md_valid(md_valid[ch]), .md_ready(md_ready[ch]), .md_data(md_data[{mdw}*ch +: {mdw}]),
411 .mr_valid(mr_valid[ch]), .mr_ready(mr_ready[ch]), .mr_data(mr_data[{dw}*ch +: {dw}]), .out_valid(in_valid),
412 .out_ready(in_ready), .out_data(in_data), .out_keep(in_keep), .out_last(in_last), .out_tensor(in_tensor),
413 .out_row(in_row), .out_offset(in_offset), .out_crc_error(in_crc));
414 logos_column_feed_{name} #(.N(N)) feed(.clk(clk), .rst(rst), {fmt_pass}.blocks(blocks), .rows(rows),
415 .in_valid(in_valid), .in_ready(in_ready), .in_data(in_data), .in_keep(in_keep), .in_row(in_row),
416 .in_offset(in_offset), .w_valid(w_valid[ch*N +: N]), .w_ready(w_ready[ch*N +: N]), .w_put(w_put[ch*N +: N]),
417 .w_beat(w_beat[ch*N*{beat} +: N*{beat}]));
418 end
419 endgenerate
420 // A clock's sums in the stripe's row order: core ch * N + p holds rows turn * 2N + 2p and the next of the stripe's 8N,
421 // a row past the matrix or of a core without a pair not one. The turns deal each block of 2N rows to one channel, so a
422 // block's sums are its channel's, chosen among the four by the turn alone; a row is the matrix's when it is among the
423 // rows the stripe leaves of it.
424 reg [32*E-1:0] ordered;
425 reg [E-1:0] held;
426 wire [31:0] base = o_stripe * E;
427 wire [31:0] rows_left = (rows > base) ? rows - base : 32'd0;
428 integer ob_blk, ob_ch, ob_row;
429 always @* begin
430 ordered = {{(32*E){{1'b0}}}};
431 held = {{E{{1'b0}}}};
432 for (ob_blk = 0; ob_blk < 4; ob_blk = ob_blk + 1)
433 for (ob_ch = 0; ob_ch < 4; ob_ch = ob_ch + 1)
434 if (turns[2*ob_ch +: 2] == ob_blk[1:0])
435 for (ob_row = 0; ob_row < 2 * N; ob_row = ob_row + 1) begin
436 ordered[32*(2*N*ob_blk + ob_row) +: 32] = ordered[32*(2*N*ob_blk + ob_row) +: 32]
437 | (ob_row[0] ? o_val2[32*(ob_ch*N + ob_row/2) +: 32] : o_val1[32*(ob_ch*N + ob_row/2) +: 32]);
438 held[2*N*ob_blk + ob_row] = held[2*N*ob_blk + ob_row] | (o_valid[ob_ch*N + ob_row/2] && $unsigned(2*N*ob_blk + ob_row) < rows_left);
439 end
440 end
441 // The queue of a token-row's stripe of sums, each written a store beat a clock: the beats from the one the stripe's
442 // first row lies in to the one its last does.
443 `LOGOS_RAMSTYLE_ASYNC(QD) reg [32*E-1:0] q_val [0:QD-1];
444 `LOGOS_RAMSTYLE_ASYNC(QD) reg [E-1:0] q_held [0:QD-1];
445 `LOGOS_RAMSTYLE_ASYNC(QD) reg [7:0] q_t [0:QD-1];
446 `LOGOS_RAMSTYLE_ASYNC(QD) reg [15:0] q_s [0:QD-1];
447 reg [15:0] beat;
448 wire [31:0] e0 = q_s[qh] * ROWS_A_STRIPE;
449 wire [31:0] skew = e0 % 32'd16;
450 wire [31:0] beats = (skew + ROWS_A_STRIPE + 32'd15) / 32'd16;
451 reg [511:0] lanes;
452 reg [63:0] mask;
453 reg [31:0] el, place;
454 integer l;
455 always @* begin
456 lanes = 512'd0;
457 mask = 64'd0;
458 for (l = 0; l < 16; l = l + 1) begin
459 place = {{16'd0, beat}} * 32'd16 + l[31:0];
460 el = place - skew;
461 if (place >= skew && el < ROWS_A_STRIPE && q_held[qh][el]) begin
462 lanes[32*l +: 32] = q_val[qh][32*el +: 32];
463 mask[4*l +: 4] = 4'hf;
464 end
465 end
466 end
467 wire writing = qn != {{QA{{1'b0}}}};
468 wire done_entry = writing && {{16'd0, beat}} + 32'd1 == beats;
469 wire [31:0] out_at = q_t[qh] * to_beats + e0 / 32'd16 + {{16'd0, beat}};
470 // The greedy head: each beat the writer would write, into the reduction to every token-row's first largest so far.
471 wire g_busy;
472 wire [31:0] b_row, b_val;
473 reg [15:0] present;
474 integer pl;
475 always @* for (pl = 0; pl < 16; pl = pl + 1) present[pl] = mask[4*pl];
476 logos_greedy #(.T_MAX(T_MAX)) head(.clk(clk), .rst(rst), .clear(state == IDLE && start), .in_valid(writing && greedy_r),
477 .in_t(q_t[qh]), .in_at(e0 / 32'd16 + {{16'd0, beat}}), .in_lanes(lanes), .in_present(present), .busy(g_busy), .rd_t(t),
478 .rd_row(b_row), .rd_val(b_val));
479 wire [31:0] best_at = t * to_beats;
480 assign busy = state != IDLE || array_busy || writing || st_we;
481 always @(posedge clk) begin
482 st_we <= (writing && !greedy_r) || state == BEST;
483 st_wa <= to_base + (state == BEST ? best_at[SA-1:0] : out_at[SA-1:0]);
484 st_wd <= state == BEST ? {{448'd0, b_val, b_row}} : lanes;
485 st_wm <= state == BEST ? 64'h0000_0000_0000_00ff : mask;
486 if (|o_valid) begin
487 q_val[qt] <= ordered; q_held[qt] <= held; q_t[qt] <= o_t; q_s[qt] <= o_stripe;
488 qt <= (qt == QLAST) ? {{QA{{1'b0}}}} : qt + 1'b1;
489 end
490 if (done_entry) begin beat <= 16'd0; qh <= (qh == QLAST) ? {{QA{{1'b0}}}} : qh + 1'b1; end
491 else if (writing) beat <= beat + 16'd1;
492 qn <= qn + (|o_valid) - done_entry;
493 if (rst) begin qh <= {{QA{{1'b0}}}}; qt <= {{QA{{1'b0}}}}; qn <= {{QA{{1'b0}}}}; beat <= 16'd0; end
494 run <= 1'b0;
495 arriving <= 1'b0;
496 if (rst) state <= IDLE;
497 else case (state)
498 IDLE: if (start) begin
499 t <= 8'd0; b <= 16'd0; j <= 16'd0; in_row0 <= 32'd0; in_at <= 32'd0; greedy_r <= greedy; state <= READ;
500 end
501 READ: begin
502 arriving <= 1'b1; first <= j == 16'd0; last <= j + 16'd1 == bpb; in_t <= t; in_b <= b;
503 in_at <= in_at + 32'd1;
504 if (j + 16'd1 != bpb) j <= j + 16'd1;
505 else begin
506 j <= 16'd0;
507 if (b + 16'd1 < {quant_blocks}) b <= b + 16'd1;
508 else begin
509 b <= 16'd0; in_row0 <= in_row0 + {{16'd0, from_beats}}; in_at <= in_row0 + {{16'd0, from_beats}};
510 if (t + 8'd1 < tokens) t <= t + 8'd1; else state <= QUANT;
511 end
512 end
513 end
514 QUANT: if (!arriving && !landing && !aq_busy) begin run <= 1'b1; state <= RUN; end
515 RUN: if (!run && !array_busy) state <= greedy_r ? DRAIN : IDLE;
516 DRAIN: if (!writing && !g_busy) begin t <= 8'd0; state <= BEST; end
517 BEST: begin
518 t <= t + 8'd1;
519 if (t + 8'd1 == tokens) state <= IDLE;
520 end
521 default: state <= IDLE;
522 endcase
523 end
524endmodule"#,
525 what = formats.iter().map(|t| t.name()).collect::<Vec<_>>().join("/"),
526 fmt_in = if multi { ", input [3:0] fmt" } else { "" },
527 fmt_pass = if multi { ".fmt(fmt), " } else { "" },
528 fmt_copy = if multi { super::column::FMT_COPY } else { "" },
529 clk2_in = if set.pe_pairs { " input clk2," } else { "" },
530 clk2_pass = if set.pe_pairs { ".clk2(clk2), " } else { "" },
531 two_in = if wide { ", input two" } else { "" },
532 two_pass = if wide { ".two(two), " } else { "" },
533 two_decl = if wide { "" } else { " wire two = 1'b0;\n" },
534 codes_top = 16 * elems - 1,
535 octet_job = octet_job,
536 act_block = act_block,
537 quant_blocks = quant_blocks,
538 beat = 2 * set.decoded_bits(),
539 aw = AW,
540 lw = LW,
541 byw = BYW,
542 roww = ROWW,
543 tw = TW,
544 mdw = AW + LW + 1,
545 dw = dw,
546 tags = super::column::COLUMN_VALUE_LATENCY + super::fp32::FADD_LATENCY,
547 )
548 .unwrap();
549 if set.token_pairs {
550 let head = format!("\nmodule logos_streaming_matvec_{name} ");
551 let at = v.rfind(&head).expect("the set's unit");
552 let end = at + v[at..].find("endmodule").expect("the unit's end") + "endmodule".len();
553 let unit = token_pair_unit(&v[at..end]);
554 v.replace_range(at..end, &unit);
555 }
556 }
557 if folds == 0 {
558 return v;
559 }
560 let units = v.matches("\nmodule logos_streaming_matvec_").count();
561 assert_eq!(units, sets.len(), "a unit a set");
562 let mut out = String::new();
563 let mut rest = v.as_str();
564 while let Some(at) = rest.find("\nmodule logos_streaming_matvec_") {
565 let end = at + rest[at..].find("endmodule").expect("the unit's end") + "endmodule".len();
566 out.push_str(&rest[..at]);
567 out.push_str(&fold_unit(&rest[at..end], folds));
568 rest = &rest[end..];
569 }
570 out.push_str(rest);
571 format!("{out}\n{}\n{}", super::simd::simd_p_verilog(), fold_lane_verilog())
572}
573
574fn token_pair_unit(unit: &str) -> String {
580 let ordered = {
581 let at = unit.find(" always @* begin\n ordered = ").expect("the stripe's order");
582 &unit[at..at + unit[at..].find("\n end\n").expect("its end") + "\n end\n".len()]
583 };
584 let ordered_b: String = ordered
585 .split_inclusive('\n')
586 .filter(|l| !l.contains("held"))
587 .collect::<String>()
588 .replace("ordered", "ordered_b")
589 .replace("o_val1[", "o_val1_b[")
590 .replace("o_val2[", "o_val2_b[")
591 .replace("ob_blk", "ob_blk_b")
592 .replace("ob_ch", "ob_ch_b")
593 .replace("ob_row", "ob_row_b");
594 let mut u = unit.to_string();
595 let mut swap = |a: &str, b: &str| {
596 assert_eq!(u.matches(a).count(), 1, "the unit's `{a}`");
597 u = u.replacen(a, b, 1);
598 };
599 swap(" wire [C*32-1:0] o_val1, o_val2;\n", " wire [C*32-1:0] o_val1, o_val2, o_val1_b, o_val2_b;\n");
600 swap(".o_val1(o_val1), .o_val2(o_val2),", ".o_val1(o_val1), .o_val2(o_val2), .o_val1_b(o_val1_b), .o_val2_b(o_val2_b),");
601 swap(
602 ordered,
603 &format!(
604 "{ordered} // The pair's second token-row's sums in the same order, its rows the first's.\n reg [32*E-1:0] ordered_b;\n \
605 integer ob_blk_b, ob_ch_b, ob_row_b;\n{ordered_b}"
606 ),
607 );
608 swap(
609 " `LOGOS_RAMSTYLE_ASYNC(QD) reg [32*E-1:0] q_val [0:QD-1];\n",
610 " `LOGOS_RAMSTYLE_ASYNC(QD) reg [32*E-1:0] q_val [0:QD-1];\n `LOGOS_RAMSTYLE_ASYNC(QD) reg [32*E-1:0] q_val_b [0:QD-1];\n \
611 `LOGOS_RAMSTYLE_ASYNC(QD) reg q_two [0:QD-1];\n",
612 );
613 swap(
614 "q_val[qt] <= ordered; q_held[qt] <= held; q_t[qt] <= o_t; q_s[qt] <= o_stripe;",
615 "q_val[qt] <= ordered; q_held[qt] <= held; q_t[qt] <= o_t; q_s[qt] <= o_stripe;\n \
616 q_val_b[qt] <= ordered_b; q_two[qt] <= o_t + 8'd1 < tokens;",
617 );
618 swap(
619 " wire [31:0] beats = (skew + ROWS_A_STRIPE + 32'd15) / 32'd16;\n",
620 " wire [31:0] tbeats = (skew + ROWS_A_STRIPE + 32'd15) / 32'd16;\n wire [31:0] beats = q_two[qh] ? 2 * tbeats : tbeats;\n \
621 // The entry's beat within its token-row, and whether it is its second token-row's.\n \
622 wire half = q_two[qh] && {16'd0, beat} >= tbeats;\n wire [15:0] tbeat = half ? beat - tbeats[15:0] : beat;\n",
623 );
624 swap(" place = {16'd0, beat} * 32'd16 + l[31:0];\n", " place = {16'd0, tbeat} * 32'd16 + l[31:0];\n");
625 swap(" lanes[32*l +: 32] = q_val[qh][32*el +: 32];\n", " lanes[32*l +: 32] = half ? q_val_b[qh][32*el +: 32] : q_val[qh][32*el +: 32];\n");
626 swap(
627 " wire [31:0] out_at = q_t[qh] * to_beats + e0 / 32'd16 + {16'd0, beat};\n",
628 " wire [31:0] out_at = (q_t[qh] + half) * to_beats + e0 / 32'd16 + {16'd0, tbeat};\n",
629 );
630 swap(".in_t(q_t[qh]), .in_at(e0 / 32'd16 + {16'd0, beat}),", ".in_t(q_t[qh] + half), .in_at(e0 / 32'd16 + {16'd0, tbeat}),");
631 u
632}
633
634pub fn fold_lane_verilog() -> String {
636 use super::fp32::FADD_LATENCY;
637 use super::simd::LANE_FUNC_P_LATENCY;
638 assert!(FADD_LATENCY <= FOLD_LATENCY, "the sum is held to the product");
639 FOLD_LANE
640 .replace("@LANE@", &LANE_FUNC_P_LATENCY.to_string())
641 .replace("@ADD_HOLD@", &(FOLD_LATENCY - FADD_LATENCY).to_string())
642 .replace("@FOLD@", &FOLD_LATENCY.to_string())
643}
644
645const FOLD_LANE: &str = r#"
646// A fold's lane (machine::Fold): the element a sum leaves where its slot held old, @FOLD@ clocks on — fc 1 old + sum,
647// fc 2 the SiLU of old times sum, the function unit's SiLU then the product, as the vector unit computes each; the
648// other fold's value is held beside it to the same clock and the job's fold chooses.
649module logos_fold_lane_p(input clk, input [1:0] fc, input [31:0] old, input [31:0] sum, output [31:0] y);
650 wire [31:0] gate, held, product, total, total_held;
651 wire [1:0] fc_held;
652 logos_lane_func_p silu(.clk(clk), .f(4'd4), .x(old), .y(gate));
653 logos_delay #(.W(32), .D(@LANE@)) hold_sum(.clk(clk), .d(sum), .q(held));
654 logos_fmul_p times(.clk(clk), .a(gate), .b(held), .y(product));
655 logos_fadd_p plus(.clk(clk), .a(old), .b(sum), .y(total));
656 logos_delay #(.W(32), .D(@ADD_HOLD@)) hold_total(.clk(clk), .d(total), .q(total_held));
657 logos_delay #(.W(2), .D(@FOLD@)) hold_fc(.clk(clk), .d(fc), .q(fc_held));
658 assign y = fc_held == 2'd2 ? product : total_held;
659endmodule
660"#;
661
662fn fold_unit(unit: &str, lanes: usize) -> String {
668 let slices = 16 / lanes;
669 let in_flight = (FOLD_LATENCY + slices).div_ceil(slices) + 3;
670 let depth = in_flight.next_power_of_two();
671 let qa = depth.trailing_zeros() as usize;
672 let mut u = unit.to_string();
673 let mut swap = |a: &str, b: &str| {
674 assert_eq!(u.matches(a).count(), 1, "the unit's `{a}`");
675 u = u.replacen(a, b, 1);
676 };
677 swap("input start, input greedy", "input start, input greedy, input [1:0] fold");
678 swap(" assign st_ra = from_base + in_at[SA-1:0];", " reg f0_v;\n reg [SA-1:0] f0_addr;\n assign st_ra = f0_v ? f0_addr : from_base + in_at[SA-1:0];");
679 swap(
680 " wire done_entry = writing && {16'd0, beat} + 32'd1 == beats;",
681 " reg fold_on;\n wire f_take;\n wire adv = writing && (!fold_on || f_take);\n wire done_entry = adv && {16'd0, beat} + 32'd1 == beats;",
682 );
683 let path = FOLD_PATH
684 .replace("@FL@", &lanes.to_string())
685 .replace("@S@", &slices.to_string())
686 .replace("@SLAST@", &(slices - 1).to_string())
687 .replace("@FOLD@", &FOLD_LATENCY.to_string())
688 .replace("@QD@", &depth.to_string())
689 .replace("@QA@", &qa.to_string())
690 .replace("@ONEXT@", &if lanes == 16 { "fy".to_string() } else { format!("{{fy, o_beat[511:{}]}}", 32 * lanes) });
691 swap(" // The greedy head: each beat the writer would write", &format!("{path} // The greedy head: each beat the writer would write"));
692 swap(
693 " assign busy = state != IDLE || array_busy || writing || st_we;",
694 " assign busy = state != IDLE || array_busy || writing || st_we || f_busy;",
695 );
696 swap(" st_we <= (writing && !greedy_r) || state == BEST;", " st_we <= (writing && !greedy_r && !fold_on) || state == BEST || fo_end;");
697 swap(
698 " st_wa <= to_base + (state == BEST ? best_at[SA-1:0] : out_at[SA-1:0]);",
699 " st_wa <= fo_end ? fq_head[SA-1:0] : to_base + (state == BEST ? best_at[SA-1:0] : out_at[SA-1:0]);",
700 );
701 swap(" st_wd <= state == BEST ? {448'd0, b_val, b_row} : lanes;", " st_wd <= fo_end ? o_next : state == BEST ? {448'd0, b_val, b_row} : lanes;");
702 swap(
703 " st_wm <= state == BEST ? 64'h0000_0000_0000_00ff : mask;",
704 " st_wm <= fo_end ? fq_head[SA+63:SA] : state == BEST ? 64'h0000_0000_0000_00ff : mask;",
705 );
706 swap(" else if (writing) beat <= beat + 16'd1;", " else if (adv) beat <= beat + 16'd1;");
707 swap("greedy_r <= greedy; state <= READ;", "greedy_r <= greedy; fold_on <= fold != 2'd0; fold_r <= fold; state <= READ;");
708 u
709}
710
711const FOLD_PATH: &str = r#" // The job's fold (machine::Fold, fold_r from its start): 0 writes each beat as it leaves the queue; 1 and 2 fold each
712 // sum into the element of the output slot it lands on. A beat leaves the queue for the fold each @S@ clocks, its
713 // address registered (f0), the store's beat there out SRL clocks on (f1); then @FL@ lanes of logos_fold_lane_p take it @FL@
714 // elements a clock, the old elements and the sums shifted down a slice each clock; each beat's folded slices come
715 // out in order @FOLD@ clocks on, and the beat is written under its lanes as its last comes out, its lanes and address
716 // held in order in fq. Each slice's mark rides reset registers beside the lanes.
717 reg [1:0] fold_r;
718 reg [7:0] f_gap;
719 assign f_take = fold_on && writing && f_gap == 8'd0 && state != READ;
720 reg f1_v, sl_busy;
721 reg [SA-1:0] f1_addr;
722 reg [511:0] f0_new, f1_new, sl_old, sl_new, o_beat;
723 reg [63:0] f0_mask, f1_mask;
724 reg [7:0] sl_k;
725 wire [32*@FL@-1:0] fy;
726 genvar fk;
727 generate
728 for (fk = 0; fk < @FL@; fk = fk + 1) begin : fold_lane
729 logos_fold_lane_p lane(.clk(clk), .fc(fold_r), .old(sl_old[32*fk +: 32]), .sum(sl_new[32*fk +: 32]), .y(fy[32*fk +: 32]));
730 end
731 endgenerate
732 reg [@FOLD@-1:0] fo_v, fo_last;
733 wire fo_out = fo_v[@FOLD@-1], fo_end = fo_last[@FOLD@-1];
734 wire [511:0] o_next = @ONEXT@;
735 `LOGOS_RAMSTYLE_ASYNC(@QD@) reg [SA+63:0] fq [0:@QD@-1];
736 reg [@QA@:0] fq_w, fq_r;
737 wire [SA+63:0] fq_head = fq[fq_r[@QA@-1:0]];
738 // A beat's sums, lanes and address from its asking to the clock before its old beat is out.
739 wire fl_v, fl_busy;
740 wire [511:0] fl_new;
741 wire [63:0] fl_mask;
742 wire [SA-1:0] fl_addr;
743 logos_landing #(.W(576 + SA), .D(SRL - 1)) fland(.clk(clk), .rst(rst), .in_v(f0_v), .in_d({f0_new, f0_mask, f0_addr}),
744 .out_v(fl_v), .out_d({fl_new, fl_mask, fl_addr}), .flight(fl_busy));
745 wire f_busy = f0_v || fl_busy || f1_v || sl_busy || fo_v != {@FOLD@{1'b0}} || fq_w != fq_r;
746 always @(posedge clk) begin
747 if (f_take) begin f0_new <= lanes; f0_mask <= mask; f0_addr <= to_base + out_at[SA-1:0]; end
748 f1_new <= fl_new; f1_mask <= fl_mask; f1_addr <= fl_addr;
749 if (f1_v) begin
750 sl_old <= st_rd; sl_new <= f1_new; sl_k <= 8'd0;
751 fq[fq_w[@QA@-1:0]] <= {f1_mask, f1_addr};
752 end else if (sl_busy) begin
753 sl_old <= sl_old >> 32*@FL@; sl_new <= sl_new >> 32*@FL@; sl_k <= sl_k + 8'd1;
754 end
755 if (fo_out) o_beat <= o_next;
756 if (rst) begin
757 f_gap <= 8'd0; f0_v <= 1'b0; f1_v <= 1'b0; sl_busy <= 1'b0;
758 fo_v <= {@FOLD@{1'b0}}; fo_last <= {@FOLD@{1'b0}}; fq_w <= {(@QA@+1){1'b0}}; fq_r <= {(@QA@+1){1'b0}};
759 end else begin
760 f_gap <= f_take ? 8'd@SLAST@ : (f_gap != 8'd0 ? f_gap - 8'd1 : 8'd0);
761 f0_v <= f_take;
762 f1_v <= fl_v;
763 sl_busy <= f1_v || (sl_busy && sl_k != 8'd@SLAST@);
764 fo_v <= {fo_v[@FOLD@-2:0], sl_busy};
765 fo_last <= {fo_last[@FOLD@-2:0], sl_busy && sl_k == 8'd@SLAST@};
766 if (f1_v) fq_w <= fq_w + 1'b1;
767 if (fo_end) fq_r <= fq_r + 1'b1;
768 end
769 end
770"#;
771
772const GREEDY: &str = r#"
773// A greedy head's reduction of a matvec job's sums: beats of sixteen lanes, each beat token-row in_t's rows from
774// 16 * in_at, a lane one of them when its present bit is set, every token-row's beats in row order. A beat passes
775// through a tree of four registered levels to its lanes' first largest number — a node takes its higher child only when
776// that is present and the lower is not, or is a number strictly larger than the lower or the lower is no number, so
777// the lowest lane of the largest wins and a NaN is passed over — which then updates its token-row's best: the
778// token-row's first candidate, then a candidate that is a number and strictly larger, or a number where the best is
779// none. So a token-row's best is numerics::argmax of its rows and that row's value, its first row when none is a
780// number. rd_row and rd_val are token-row rd_t's; busy while a beat is in the tree; clear forgets every token-row's.
781module logos_greedy #(parameter T_MAX = 4)
782 (input clk, input rst, input clear, input in_valid, input [7:0] in_t, input [31:0] in_at, input [511:0] in_lanes,
783 input [15:0] in_present, output busy, input [7:0] rd_t, output [31:0] rd_row, output [31:0] rd_val);
784 function [31:0] fkey(input [31:0] x);
785 fkey = (x[30:0] == 31'd0) ? 32'h8000_0000 : (x[31] ? ~x : {1'b1, x[30:0]});
786 endfunction
787 function is_nan(input [31:0] x);
788 is_nan = x[30:23] == 8'hff && x[22:0] != 23'd0;
789 endfunction
790 function higher(input lp, input ln, input [31:0] lv, input hp, input hn, input [31:0] hv);
791 higher = hp && (!lp || (hn && (!ln || fkey(hv) > fkey(lv))));
792 endfunction
793 // The tree: per level every node's presence, whether it is a number, its value and its lane; beside it the beat's
794 // token-row and place.
795 reg [15:0] gp [0:4];
796 reg [15:0] gn [0:4];
797 reg [31:0] gv [0:4][0:15];
798 reg [3:0] gl [0:4][0:15];
799 reg g_live [0:4];
800 reg [7:0] g_t [0:4];
801 reg [31:0] g_at [0:4];
802 integer gk, gi;
803 always @(posedge clk) begin
804 g_live[0] <= in_valid;
805 g_t[0] <= in_t;
806 g_at[0] <= in_at;
807 for (gi = 0; gi < 16; gi = gi + 1) begin
808 gp[0][gi] <= in_present[gi];
809 gn[0][gi] <= in_present[gi] && !is_nan(in_lanes[32*gi +: 32]);
810 gv[0][gi] <= in_lanes[32*gi +: 32];
811 gl[0][gi] <= gi[3:0];
812 end
813 for (gk = 0; gk < 4; gk = gk + 1) begin
814 g_live[gk+1] <= g_live[gk]; g_t[gk+1] <= g_t[gk]; g_at[gk+1] <= g_at[gk];
815 for (gi = 0; gi < (8 >> gk); gi = gi + 1)
816 if (higher(gp[gk][2*gi], gn[gk][2*gi], gv[gk][2*gi], gp[gk][2*gi+1], gn[gk][2*gi+1], gv[gk][2*gi+1])) begin
817 gp[gk+1][gi] <= 1'b1; gn[gk+1][gi] <= gn[gk][2*gi+1]; gv[gk+1][gi] <= gv[gk][2*gi+1]; gl[gk+1][gi] <= gl[gk][2*gi+1];
818 end else begin
819 gp[gk+1][gi] <= gp[gk][2*gi]; gn[gk+1][gi] <= gn[gk][2*gi]; gv[gk+1][gi] <= gv[gk][2*gi]; gl[gk+1][gi] <= gl[gk][2*gi];
820 end
821 end
822 if (rst) for (gk = 0; gk < 5; gk = gk + 1) g_live[gk] <= 1'b0;
823 end
824 assign busy = g_live[0] || g_live[1] || g_live[2] || g_live[3] || g_live[4];
825 // Each token-row's best: whether it has one, whether that is a number, its value and its row.
826 (* ramstyle = "logic" *) reg b_seen [0:T_MAX-1];
827 `LOGOS_RAMSTYLE_ASYNC(T_MAX) reg b_num [0:T_MAX-1];
828 `LOGOS_RAMSTYLE_ASYNC(T_MAX) reg [31:0] b_val [0:T_MAX-1];
829 `LOGOS_RAMSTYLE_ASYNC(T_MAX) reg [31:0] b_row [0:T_MAX-1];
830 wire [7:0] c_t = g_t[4];
831 wire c_take = g_live[4] && gp[4][0] && (!b_seen[c_t] || higher(1'b1, b_num[c_t], b_val[c_t], 1'b1, gn[4][0], gv[4][0]));
832 integer bk;
833 always @(posedge clk) begin
834 if (c_take) begin
835 b_seen[c_t] <= 1'b1; b_num[c_t] <= gn[4][0]; b_val[c_t] <= gv[4][0]; b_row[c_t] <= g_at[4] * 32'd16 + {28'd0, gl[4][0]};
836 end
837 if (rst || clear) for (bk = 0; bk < T_MAX; bk = bk + 1) b_seen[bk] <= 1'b0;
838 end
839 assign rd_row = b_row[rd_t];
840 assign rd_val = b_val[rd_t];
841endmodule
842"#;
843
844const ACT_STREAM_P: &str = r#"
845// logos_act_code pipelined, a value a clock: `code` is the code of the operands of @LAT@ clocks before. The first stage
846// takes the value's significand and the shift that places it against the scale; the second places it (the dividend of
847// logos_act_code, 32 places finer) and decides whether the quotient's whole part reaches 2^16 (the code then the word's
848// limit); the next seventeen take its sixteen whole bits and its half a bit a stage by restoring division by m; the last
849// rounds to nearest even, the dividend's bits below the half and the remainder its sticky bit, and clamps to the word.
850module logos_act_code_p(input clk, input [31:0] x, input [7:0] m, input signed [11:0] e, input two,
851 output reg signed [15:0] code);
852 wire [7:0] ex = x[30:23];
853 reg neg, two_a;
854 reg [7:0] m_a;
855 reg [23:0] sx;
856 reg signed [12:0] up32;
857 always @(posedge clk) begin
858 neg <= x[31]; two_a <= two; m_a <= m;
859 sx <= (ex == 8'd0) ? 24'd0 : {1'b1, x[22:0]};
860 up32 <= $signed({5'd0, ex}) - 13'sd150 - e + 13'sd32;
861 end
862 wire [63:0] num = (up32 < 13'sd0) ? 64'd0 : (up32 > 13'sd39) ? 64'hffff_ffff_ffff_ffff : ({40'd0, sx} << up32[5:0]);
863 reg [7:0] r [0:17];
864 reg [16:0] q [0:17];
865 reg [16:0] rest [0:17];
866 reg [7:0] mm [0:17];
867 reg over [0:17];
868 reg sign [0:17];
869 reg wide [0:17];
870 reg low [0:17];
871 always @(posedge clk) begin
872 r[0] <= num[55:48]; q[0] <= 17'd0; rest[0] <= num[47:31]; mm[0] <= m_a;
873 over[0] <= num[63:48] >= {8'd0, m_a}; sign[0] <= neg; wide[0] <= two_a; low[0] <= |num[30:0];
874 end
875 genvar i;
876 generate
877 for (i = 0; i < 17; i = i + 1) begin : step
878 wire [8:0] r2 = {r[i], rest[i][16]};
879 wire ge = r2 >= {1'b0, mm[i]};
880 wire [8:0] d = r2 - {1'b0, mm[i]};
881 always @(posedge clk) begin
882 r[i+1] <= ge ? d[7:0] : r2[7:0];
883 q[i+1] <= {q[i][15:0], ge};
884 rest[i+1] <= {rest[i][15:0], 1'b0};
885 mm[i+1] <= mm[i]; over[i+1] <= over[i]; sign[i+1] <= sign[i]; wide[i+1] <= wide[i]; low[i+1] <= low[i];
886 end
887 end
888 endgenerate
889 wire [15:0] whole = q[17][16:1];
890 wire up = q[17][0] && (r[17] != 8'd0 || low[17] || whole[0]);
891 wire [16:0] n = {1'b0, whole} + {16'd0, up};
892 wire [16:0] lim = wide[17] ? 17'd32767 : 17'd127;
893 wire [16:0] c = (over[17] || n > lim) ? lim : n;
894 always @(posedge clk) code <= sign[17] ? -$signed({1'b0, c[14:0]}) : $signed({1'b0, c[14:0]});
895endmodule
896
897// logos_act_stream with pipelined code units: the same ports and protocol, the block's beats in registered-read block
898// RAM, the block's largest magnitude a clock behind its beats and its scale (logos_wide_scale_p) @WS@ clocks behind that,
899// latched with the word count `go` brings LAG clocks after `go`; then a beat read a clock and its codes written @LAT@
900// clocks after it arrives, `busy` from `go` until the last's are; past the block's length its codes are zero.
901module logos_act_stream_p #(parameter LANES = 256)
902 (input clk, input rst, input clear, input in_v, input [511:0] in_beat, input go, input two,
903 output busy, output reg [7:0] m, output reg signed [11:0] e, output [16*LANES-1:0] codes);
904 localparam BEATS = LANES / 16;
905 localparam BA = (BEATS > 1) ? $clog2(BEATS) : 1;
906 localparam L = @LAT@ + 1;
907 // The beat's largest magnitude, a denormal read as zero, by a balanced tree: its first two levels here, its last two
908 // a clock on.
909 wire [30:0] mag [0:15];
910 wire [30:0] t1 [0:7];
911 wire [30:0] t2 [0:3];
912 genvar g;
913 generate
914 for (g = 0; g < 16; g = g + 1) begin : magnitude
915 assign mag[g] = (in_beat[32*g+30 -: 8] == 8'd0) ? 31'd0 : in_beat[32*g+30 -: 31];
916 end
917 for (g = 0; g < 8; g = g + 1) begin : max8
918 assign t1[g] = (mag[2*g] > mag[2*g+1]) ? mag[2*g] : mag[2*g+1];
919 end
920 for (g = 0; g < 4; g = g + 1) begin : max4
921 assign t2[g] = (t1[2*g] > t1[2*g+1]) ? t1[2*g] : t1[2*g+1];
922 end
923 endgenerate
924 // The tree's last two levels and the block's maximum a clock after the beat, the scale WS clocks after the maximum
925 // and the word count latched at `go`: the scale latched LAG clocks after `go` is its block's for a `go` any clock
926 // after the last beat.
927 localparam WS = @WS@;
928 localparam [3:0] LAG = 1 + WS;
929 reg [30:0] h0, h1, h2, h3;
930 reg hv, hclear;
931 always @(posedge clk) begin h0 <= t2[0]; h1 <= t2[1]; h2 <= t2[2]; h3 <= t2[3]; hv <= in_v && !rst; hclear <= clear; end
932 wire [30:0] u0 = (h0 > h1) ? h0 : h1, u1 = (h2 > h3) ? h2 : h3;
933 wire [30:0] beat_max = (u0 > u1) ? u0 : u1;
934 reg [30:0] amax;
935 reg [BA:0] n, c;
936 reg reading, two_r, scaling;
937 reg [3:0] lag;
938 wire [7:0] m_s;
939 wire signed [11:0] e_s;
940 logos_wide_scale_p sc(.clk(clk), .amax(amax), .two(two_r), .m(m_s), .e(e_s));
941 wire [511:0] xb;
942 logos_bram_w512 #(.D(BEATS), .A(BA)) beats(.clk(clk), .ra(c[BA-1:0]), .rd(xb), .we(in_v),
943 .wa(clear ? {BA{1'b0}} : n[BA-1:0]), .wd(in_beat), .wm({64{1'b1}}));
944 wire [255:0] cb;
945 generate
946 for (g = 0; g < 16; g = g + 1) begin : lane
947 wire signed [15:0] cg;
948 logos_act_code_p u(.clk(clk), .x(xb[32*g +: 32]), .m(m), .e(e), .two(two_r), .code(cg));
949 assign cb[16*g +: 16] = cg;
950 end
951 endgenerate
952 // Each beat read, beside its codes on their way: whether it is one and its place.
953 reg live [0:L-1];
954 reg [BA:0] at [0:L-1];
955 reg flight;
956 integer h;
957 always @* begin
958 flight = 1'b0;
959 for (h = 0; h < L; h = h + 1) if (live[h]) flight = 1'b1;
960 end
961 assign busy = scaling || reading || flight;
962 reg [256*BEATS-1:0] cr;
963 assign codes = cr[16*LANES-1:0];
964 always @(posedge clk) begin
965 live[0] <= reading; at[0] <= c;
966 for (h = 1; h < L; h = h + 1) begin live[h] <= live[h-1]; at[h] <= at[h-1]; end
967 // A block's first codes clear the beats past them: past its length a block's codes are zero.
968 if (live[L-1]) begin
969 if (at[L-1] == {(BA+1){1'b0}}) cr <= {{(256*BEATS-256){1'b0}}, cb};
970 else cr[256*at[L-1] +: 256] <= cb;
971 end
972 if (rst) begin
973 scaling <= 1'b0; reading <= 1'b0; n <= {(BA+1){1'b0}};
974 for (h = 0; h < L; h = h + 1) live[h] <= 1'b0;
975 end else begin
976 if (in_v) n <= (clear ? {(BA+1){1'b0}} : n) + 1'b1;
977 if (hv) amax <= (hclear || beat_max > amax) ? beat_max : amax;
978 if (go) begin
979 scaling <= 1'b1; lag <= LAG; two_r <= two;
980 end else if (scaling) begin
981 if (lag == 4'd1) begin
982 scaling <= 1'b0; reading <= n != {(BA+1){1'b0}}; c <= {(BA+1){1'b0}}; m <= m_s; e <= e_s;
983 end else lag <= lag - 4'd1;
984 end else if (reading) begin
985 if (c + 1'b1 == n) reading <= 1'b0; else c <= c + 1'b1;
986 end
987 end
988 end
989endmodule
990
991// logos_act_stream_p's blocks back to back: a block's beats arrive a beat a clock (`in_v`), any clocks apart, its first
992// marked `in_first`, its last `in_last` with its word count `in_two` and its tag `in_tag`, and the next block's first
993// may come the clock after its last. A block's largest magnitude is final two clocks after its last beat, its scale @WS@
994// clocks later, and the block then waits in a queue; the queue's blocks are read out in order, a beat a clock from a
995// ring of block RAM, through the code units with their block's scale and word count beside each beat. The clock after
996// a block's last codes land in `codes`, `out_v` holds with its scale and tag; the next block's first codes land at that
997// clock's end at the soonest, so `codes` are the block's while `out_v` holds, zero past its length. `busy` holds from a
998// block's first beat until the clock after its `out_v`.
999module logos_act_blocks_p #(parameter LANES = 256, TW = 24)
1000 (input clk, input rst, input in_v, input [511:0] in_beat, input in_first, input in_last, input in_two,
1001 input [TW-1:0] in_tag, output busy, output reg out_v, output reg [7:0] out_m, output reg signed [11:0] out_e,
1002 output reg [TW-1:0] out_tag, output [16*LANES-1:0] codes);
1003 localparam BEATS = LANES / 16;
1004 localparam BA = (BEATS > 1) ? $clog2(BEATS) : 1;
1005 localparam L = @LAT@ + 1;
1006 localparam WS = @WS@;
1007 // The ring holds each beat from its arrival to its read: at most a block's beats and those arriving in the clocks
1008 // from its last to its first read, and one more.
1009 localparam RA = $clog2(BEATS + WS + 6);
1010 // The queue holds each block from its scale to its codes: at most a block a beat the ring holds and a block a beat
1011 // in the code units.
1012 localparam QA = $clog2((1 << RA) + L + 4);
1013 // The beat's largest magnitude, a denormal read as zero, by a balanced tree: its first two levels here, its last two
1014 // a clock on with the beat's marks.
1015 wire [30:0] mag [0:15];
1016 wire [30:0] t1 [0:7];
1017 wire [30:0] t2 [0:3];
1018 genvar g;
1019 generate
1020 for (g = 0; g < 16; g = g + 1) begin : magnitude
1021 assign mag[g] = (in_beat[32*g+30 -: 8] == 8'd0) ? 31'd0 : in_beat[32*g+30 -: 31];
1022 end
1023 for (g = 0; g < 8; g = g + 1) begin : max8
1024 assign t1[g] = (mag[2*g] > mag[2*g+1]) ? mag[2*g] : mag[2*g+1];
1025 end
1026 for (g = 0; g < 4; g = g + 1) begin : max4
1027 assign t2[g] = (t1[2*g] > t1[2*g+1]) ? t1[2*g] : t1[2*g+1];
1028 end
1029 endgenerate
1030 reg [30:0] h0, h1, h2, h3;
1031 reg hv, hfirst, hlast, htwo;
1032 reg [BA:0] hn, bn;
1033 reg [TW-1:0] htag;
1034 always @(posedge clk) begin
1035 h0 <= t2[0]; h1 <= t2[1]; h2 <= t2[2]; h3 <= t2[3];
1036 hv <= in_v && !rst; hfirst <= in_first; hlast <= in_last; htwo <= in_two; htag <= in_tag;
1037 hn <= in_first ? {{BA{1'b0}}, 1'b1} : bn + 1'b1;
1038 if (in_v) bn <= in_first ? {{BA{1'b0}}, 1'b1} : bn + 1'b1;
1039 end
1040 wire [30:0] u0 = (h0 > h1) ? h0 : h1, u1 = (h2 > h3) ? h2 : h3;
1041 wire [30:0] beat_max = (u0 > u1) ? u0 : u1;
1042 // The block's maximum the clock after its last beat's marks, `fin` beside it; its scale WS clocks on, its word count,
1043 // beats and tag carried beside.
1044 reg [30:0] amax;
1045 reg fin, ftwo;
1046 reg [BA:0] fn;
1047 reg [TW-1:0] ftag;
1048 always @(posedge clk) begin
1049 if (hv) amax <= (hfirst || beat_max > amax) ? beat_max : amax;
1050 fin <= hv && hlast && !rst; ftwo <= htwo; fn <= hn; ftag <= htag;
1051 end
1052 wire [7:0] m_s;
1053 wire signed [11:0] e_s;
1054 logos_wide_scale_p sc(.clk(clk), .amax(amax), .two(ftwo), .m(m_s), .e(e_s));
1055 reg sv [1:WS];
1056 reg sw [1:WS];
1057 reg [BA:0] sn [1:WS];
1058 reg [TW-1:0] st [1:WS];
1059 integer k;
1060 always @(posedge clk) begin
1061 sv[1] <= fin && !rst; sw[1] <= ftwo; sn[1] <= fn; st[1] <= ftag;
1062 for (k = 2; k <= WS; k = k + 1) begin sv[k] <= sv[k-1] && !rst; sw[k] <= sw[k-1]; sn[k] <= sn[k-1]; st[k] <= st[k-1]; end
1063 end
1064 // The queue of scaled blocks: written as each scale is out, read at the reader's start of the block and again for
1065 // its codes.
1066 reg [7:0] qm [0:(1<<QA)-1];
1067 reg signed [11:0] qe [0:(1<<QA)-1];
1068 reg qtwo [0:(1<<QA)-1];
1069 reg [BA:0] qn [0:(1<<QA)-1];
1070 reg [TW-1:0] qtag [0:(1<<QA)-1];
1071 reg [QA-1:0] q_wr, q_rd, q_out;
1072 always @(posedge clk)
1073 if (sv[WS]) begin qm[q_wr] <= m_s; qe[q_wr] <= e_s; qtwo[q_wr] <= sw[WS]; qn[q_wr] <= sn[WS]; qtag[q_wr] <= st[WS]; end
1074 // The ring: each beat written as it arrives; read a beat a clock, block after block, as the queue has them.
1075 reg [RA-1:0] wp, rp;
1076 wire [511:0] xb;
1077 logos_bram_w512 #(.D(1 << RA), .A(RA)) ring(.clk(clk), .ra(rp), .rd(xb), .we(in_v), .wa(wp), .wd(in_beat),
1078 .wm({64{1'b1}}));
1079 reg reading;
1080 reg [BA:0] rleft, rj;
1081 reg [7:0] rm;
1082 reg signed [11:0] re;
1083 reg rtwo;
1084 wire queued = q_rd != q_wr;
1085 wire take = queued && (!reading || rleft == {{BA{1'b0}}, 1'b1});
1086 // The code units take each beat the clock after its read, its block's scale and word count beside it.
1087 reg [7:0] cm;
1088 reg signed [11:0] ce;
1089 reg ctwo;
1090 wire [255:0] cb;
1091 generate
1092 for (g = 0; g < 16; g = g + 1) begin : lane
1093 wire signed [15:0] cg;
1094 logos_act_code_p u(.clk(clk), .x(xb[32*g +: 32]), .m(cm), .e(ce), .two(ctwo), .code(cg));
1095 assign cb[16*g +: 16] = cg;
1096 end
1097 endgenerate
1098 // Each beat read, beside its codes on their way: whether it is one, its place and whether its block's last.
1099 reg live [0:L-1];
1100 reg lst [0:L-1];
1101 reg [BA:0] at [0:L-1];
1102 reg [256*BEATS-1:0] cr;
1103 assign codes = cr[16*LANES-1:0];
1104 reg filling;
1105 reg [QA:0] held_blocks;
1106 assign busy = filling || held_blocks != {(QA+1){1'b0}};
1107 integer h;
1108 always @(posedge clk) begin
1109 cm <= rm; ce <= re; ctwo <= rtwo;
1110 live[0] <= reading && !rst; lst[0] <= rleft == {{BA{1'b0}}, 1'b1}; at[0] <= rj;
1111 for (h = 1; h < L; h = h + 1) begin live[h] <= live[h-1] && !rst; lst[h] <= lst[h-1]; at[h] <= at[h-1]; end
1112 // A block's first codes clear the beats past them: past its length a block's codes are zero.
1113 if (live[L-1]) begin
1114 if (at[L-1] == {(BA+1){1'b0}}) cr <= {{(256*BEATS-256){1'b0}}, cb};
1115 else cr[256*at[L-1] +: 256] <= cb;
1116 end
1117 out_v <= live[L-1] && lst[L-1] && !rst;
1118 if (live[L-1] && lst[L-1]) begin out_m <= qm[q_out]; out_e <= qe[q_out]; out_tag <= qtag[q_out]; end
1119 if (rst) begin
1120 wp <= {RA{1'b0}}; rp <= {RA{1'b0}}; q_wr <= {QA{1'b0}}; q_rd <= {QA{1'b0}}; q_out <= {QA{1'b0}};
1121 reading <= 1'b0; filling <= 1'b0; held_blocks <= {(QA+1){1'b0}};
1122 end else begin
1123 if (in_v) begin wp <= wp + 1'b1; filling <= !in_last; end
1124 held_blocks <= held_blocks + ((in_v && in_last) ? 1'b1 : 1'b0) - (out_v ? 1'b1 : 1'b0);
1125 if (sv[WS]) q_wr <= q_wr + 1'b1;
1126 if (live[L-1] && lst[L-1]) q_out <= q_out + 1'b1;
1127 if (reading) begin rp <= rp + 1'b1; rleft <= rleft - 1'b1; rj <= rj + 1'b1; end
1128 if (take) begin
1129 reading <= 1'b1; rleft <= qn[q_rd]; rj <= {(BA+1){1'b0}}; rm <= qm[q_rd]; re <= qe[q_rd]; rtwo <= qtwo[q_rd];
1130 q_rd <= q_rd + 1'b1;
1131 end else if (reading && rleft == {{BA{1'b0}}, 1'b1}) reading <= 1'b0;
1132 end
1133 end
1134endmodule
1135"#;