1use std::fmt::Write as _;
52
53use super::array::{CHAIN, PE8_P_LATENCY};
54use super::fp32::FADD_LATENCY;
55use super::lane::LANE_VALUE_LATENCY;
56use crate::model::quant::GgmlType;
57
58use super::super::numerics::Words;
59use super::weights::{decoder_name, Decoder, DECODERS, UNIT};
60
61pub const PASS_LATENCY: usize = PE8_P_LATENCY;
63
64pub const COLUMN_VALUE_LATENCY: usize = LANE_VALUE_LATENCY + 2 + PASS_LATENCY;
67
68pub fn column_passes(ty: GgmlType, two: bool) -> usize {
70 let d = DECODERS.iter().find(|d| d.ty == ty).unwrap_or_else(|| panic!("no decoder reads {}", ty.name()));
71 (if d.code_bits > 4 { 2 } else { 1 }) * (if two { 2 } else { 1 })
72}
73
74pub(crate) const FMT_COPY: &str = " (* dont_merge, maxfan = 256 *) reg [3:0] fmt_q;\n always @(posedge clk) fmt_q <= fmt;\n";
78
79pub fn column_clocks(groups: usize, pairs: usize, blocks: usize, tokens: usize, passes: usize) -> usize {
85 let (steps, issues) = (groups * blocks, pairs * tokens);
86 (steps - 1) * (passes * issues).max(FADD_LATENCY + 1) + passes * issues + 1 + COLUMN_VALUE_LATENCY + FADD_LATENCY
87}
88
89pub fn column_array_clocks(stripes: usize, blocks: usize, tokens: usize, passes: usize) -> usize {
93 column_clocks(stripes, 1, blocks, tokens, passes)
94}
95
96pub fn column_array_octet_clocks(stripes: usize, octets: usize, tokens: usize, passes: usize) -> usize {
102 column_array_octet_lane_clocks(stripes, octets, tokens, passes, 1)
103}
104
105pub fn column_array_octet_lane_clocks(stripes: usize, octets: usize, tokens: usize, passes: usize, lanes: usize) -> usize {
109 let gap = lanes * FADD_LATENCY + 1;
110 let mut free = [0usize; 2];
111 let mut fin = 0usize;
112 let mut emitted = 0usize;
113 let mut last: Vec<Option<usize>> = vec![None; tokens];
114 for k in 0..stripes * octets {
115 let first = (fin + passes).max(free[k % 2]);
116 fin = first + passes * (tokens - 1);
117 let mut at = (fin + 4).max(emitted + 1);
118 for _group in 0..8 / lanes {
119 for sum in last.iter_mut() {
120 if let Some(before) = *sum {
121 at = at.max(before + gap);
122 }
123 *sum = Some(at);
124 emitted = at;
125 at += 1;
126 }
127 }
128 free[k % 2] = emitted + 1;
129 }
130 emitted + 1 + COLUMN_VALUE_LATENCY + lanes * FADD_LATENCY
131}
132
133#[derive(Clone, Debug, PartialEq, Eq)]
142pub struct ColumnSet {
143 pub name: String,
144 pub formats: Vec<GgmlType>,
145 pub words: Words,
146 pub octets: bool,
147 pub slot: usize,
148 pub lanes: usize,
149 pub pe_pairs: bool,
150 pub token_pairs: bool,
151}
152
153impl ColumnSet {
154 pub fn new(name: &str, formats: &[GgmlType], words: Words) -> ColumnSet {
156 ColumnSet { name: name.to_string(), formats: formats.to_vec(), words, octets: false, slot: 1, lanes: 1, pe_pairs: false, token_pairs: false }
157 }
158
159 pub fn with_octets(self) -> ColumnSet {
161 assert!(self.formats.contains(&GgmlType::Q8_0), "a set without Q8_0 has no octets to take");
162 ColumnSet { octets: true, ..self }
163 }
164
165 pub fn with_octet_lanes(self, lanes: usize) -> ColumnSet {
168 assert!(self.octets, "octet lanes take an octet's blocks: a set with octets");
169 assert!(matches!(lanes, 1 | 2 | 4 | 8), "{lanes} lanes a row: a divisor of an octet's eight blocks");
170 ColumnSet { lanes, ..self }
171 }
172
173 pub fn with_pe_pairs(self) -> ColumnSet {
180 ColumnSet { pe_pairs: true, ..self }
181 }
182
183 pub fn with_token_pairs(self) -> ColumnSet {
193 ColumnSet { token_pairs: true, ..self }
194 }
195
196 pub fn issue_rows(&self) -> usize {
198 if self.token_pairs { 2 } else { 1 }
199 }
200
201 pub fn issues(&self, tokens: usize) -> usize {
203 tokens.div_ceil(self.issue_rows())
204 }
205
206 pub fn with_slots(self, slot: usize) -> ColumnSet {
208 assert!(slot.is_power_of_two() && slot <= 64, "a slot of {slot} bytes: a power of two no wider than a line");
209 ColumnSet { slot, ..self }
210 }
211
212 pub fn octet(&self, ty: GgmlType) -> bool {
214 self.octets && ty == GgmlType::Q8_0
215 }
216
217 pub fn block_len(&self, ty: GgmlType) -> usize {
219 if self.octet(ty) { 8 * ty.block_len() } else { ty.block_len() }
220 }
221
222 pub fn block_bytes(&self, ty: GgmlType) -> usize {
224 if self.octet(ty) { 8 * ty.block_bytes() } else { ty.block_bytes() }
225 }
226
227 pub fn slot_bytes(&self, ty: GgmlType) -> usize {
229 self.block_bytes(ty).next_multiple_of(self.slot)
230 }
231
232 pub fn hybrid(formats: &[GgmlType], words: Words) -> ColumnSet {
235 let mut f = formats.to_vec();
236 f.sort_by_key(|t| t.id());
237 f.dedup();
238 ColumnSet::new("step", &f, words)
239 }
240
241 pub fn signature(&self) -> String {
244 let names: Vec<String> = self.formats.iter().map(|&t| if self.octet(t) { format!("{}x8", t.name()) } else { t.name().to_string() }).collect();
245 let slot = if self.slot > 1 { format!("@{}", self.slot) } else { String::new() };
246 format!("{}:{}{slot}", names.join("+"), self.word_count())
247 }
248
249 pub fn elems(&self) -> usize {
251 self.formats.iter().map(|&t| self.block_len(t)).max().expect("a set of formats")
252 }
253
254 pub fn word_count(&self) -> usize {
256 if self.words == Words::Two {
257 2
258 } else {
259 1
260 }
261 }
262
263 pub fn decoded_bits(&self) -> usize {
267 let e = self.elems();
268 8 * e + 28 * (e / UNIT) + 32 + if self.octets { 7 * 16 } else { 0 }
269 }
270
271 pub fn decoded_fields(&self) -> DecodedFields {
274 let (e, u) = (self.elems(), self.elems() / UNIT);
275 let q = 8 * e + 16 * u + 32;
276 DecodedFields { code: 0, c: 8 * e, n: 8 * e + 8 * u, a: 8 * e + 16 * u, b: 8 * e + 16 * u + 16, q, s7: q + 12 * u }
277 }
278
279 pub fn act_slots(&self) -> usize {
281 if self.octets { 8 } else { 1 }
282 }
283
284 pub fn act_bits(&self) -> usize {
287 let (e, w) = (self.elems(), self.word_count());
288 (w * (8 * e + 12 * (e / UNIT)) + 20 + 24 * (self.act_slots() - 1)).div_ceil(8) * 8
289 }
290
291 pub fn act_fields(&self) -> ActFields {
294 let (e, w) = (self.elems(), self.word_count());
295 let sums = w * 8 * e;
296 let m = sums + w * 12 * (e / UNIT);
297 ActFields { excess: 0, sums, m, e: m + 8 }
298 }
299}
300
301#[derive(Clone, Copy, Debug, PartialEq, Eq)]
303pub struct DecodedFields {
304 pub code: usize,
305 pub c: usize,
306 pub n: usize,
307 pub a: usize,
308 pub b: usize,
309 pub q: usize,
310 pub s7: usize,
311}
312
313#[derive(Clone, Copy, Debug, PartialEq, Eq)]
315pub struct ActFields {
316 pub excess: usize,
317 pub sums: usize,
318 pub m: usize,
319 pub e: usize,
320}
321
322pub fn column_verilog() -> String {
325 let sets: Vec<ColumnSet> = DECODERS.iter().map(|d| ColumnSet::new(&d.ty.name().to_lowercase(), &[d.ty], Words::Two)).collect();
326 columns_verilog(&sets)
327}
328
329pub fn columns_verilog(sets: &[ColumnSet]) -> String {
335 let mut v = String::from(super::ram::RAMSTYLE);
336 v.push_str(&super::matvec::matvec_verilog());
337 let mut widths: Vec<usize> = sets.iter().map(ColumnSet::act_bits).collect();
338 widths.extend(sets.iter().filter(|s| s.token_pairs).map(|s| 2 * s.act_bits()));
339 widths.sort();
340 widths.dedup();
341 v.push_str(&super::ram::bram_verilog(&widths));
342 v.push_str(super::ram::delay_verilog());
343 v.push_str(&MATVEC_PASS.replace("@PE@", &PASS_LATENCY.to_string()));
344 v.push_str(ACT_STAGGER);
345 v.push_str(ACT_WORD);
346 for set in sets {
347 let ColumnSet { name, formats, words, .. } = set;
348 let decs: Vec<Coded> = formats
349 .iter()
350 .map(|&ty| Coded {
351 dec: DECODERS.iter().find(|d| d.ty == ty).unwrap_or_else(|| panic!("no decoder reads {}", ty.name())),
352 octet: set.octet(ty),
353 slot: set.slot,
354 })
355 .collect();
356 let elems = set.elems();
357 let bits = 8 * decs.iter().map(Coded::bytes).max().unwrap();
358 let units = elems / UNIT;
359 let db = set.decoded_bits();
360 let f = set.decoded_fields();
361 let a = set.act_fields();
362 let multi = decs.len() > 1;
363 let wide = *words == Words::Two;
364 let fmt_in = if multi { ", input [3:0] fmt" } else { "" };
365 let fmt_pass = if multi { ".fmt(fmt), " } else { "" };
366 let fmt_copy = if multi { FMT_COPY } else { "" };
367 let fmt_dec = if multi { ".fmt(fmt_q), " } else { "" };
368 let two_in = if wide { ", input two" } else { "" };
369 let two_pass = if wide { ".two(two), " } else { "" };
370 let two_decl = if wide { "" } else { " wire two = 1'b0;\n" };
371 let what = decs.iter().map(Coded::name).collect::<Vec<_>>().join("/");
372 let (code_top, bus12, bus24) = (8 * elems - 1, 12 * units - 1, 24 * units - 1);
373 let oct = core_octets(set);
374 let lane = lane_fields(set);
375 let paired = set.pe_pairs && !set.token_pairs;
378 let arr: std::collections::HashMap<&str, String> = array_octets(set).into_iter().collect();
379 let (st_sel, s_sel) = if wide {
382 (
383 format!("word_a ? act_word[{}:{}] : act_word[{}:{}]", a.excess + 16 * elems - 1, a.excess + 8 * elems, a.excess + code_top, a.excess),
384 format!("s1_word ? act_word[{}:{}] : act_word[{}:{}]", a.sums + 24 * units - 1, a.sums + 12 * units, a.sums + bus12, a.sums),
385 )
386 } else {
387 (format!("act_word[{}:{}]", a.excess + code_top, a.excess), format!("act_word[{}:{}]", a.sums + bus12, a.sums))
388 };
389 writeln!(
390 v,
391 r#"
392// A {what} beat decoded: each block's codes, its units' multipliers and offset terms as bytes, its two scales and its
393// units' sums of codes (sixteen codes in a balanced tree of four adds), block one's fields below block two's.
394module logos_column_decode_{name}(input [{top}:0] blk1, input [{top}:0] blk2{fmt_in}, output [{beat_top}:0] beat);
395 function [11:0] sum16(input [127:0] c);
396 reg [8:0] a0, a1, a2, a3, a4, a5, a6, a7;
397 reg [9:0] b0, b1, b2, b3;
398 reg [10:0] d0, d1;
399 begin
400 a0 = c[7:0] + c[15:8]; a1 = c[23:16] + c[31:24]; a2 = c[39:32] + c[47:40]; a3 = c[55:48] + c[63:56];
401 a4 = c[71:64] + c[79:72]; a5 = c[87:80] + c[95:88]; a6 = c[103:96] + c[111:104]; a7 = c[119:112] + c[127:120];
402 b0 = a0 + a1; b1 = a2 + a3; b2 = a4 + a5; b3 = a6 + a7;
403 d0 = b0 + b1; d1 = b2 + b3;
404 sum16 = d0 + d1;
405 end
406 endfunction
407{decode} genvar u;
408 generate
409 for (u = 0; u < {units}; u = u + 1) begin : unit
410 wire [11:0] q1 = sum16(code1_d[128*u +: 128]);
411 wire [11:0] q2 = sum16(code2_d[128*u +: 128]);
412 assign beat[{fc} + 8*u +: 8] = c1_d[16*u +: 8];
413 assign beat[{fn} + 8*u +: 8] = n1_d[16*u +: 8];
414 assign beat[{fq} + 12*u +: 12] = q1;
415 assign beat[{db} + {fc} + 8*u +: 8] = c2_d[16*u +: 8];
416 assign beat[{db} + {fn} + 8*u +: 8] = n2_d[16*u +: 8];
417 assign beat[{db} + {fq} + 12*u +: 12] = q2;
418 end
419 endgenerate
420 assign beat[{code_top}:0] = code1_d;
421 assign beat[{db} + {code_top}:{db}] = code2_d;
422 assign beat[{fa} +: 16] = a1_d;
423 assign beat[{fb} +: 16] = b1_d;
424 assign beat[{db} + {fa} +: 16] = a2_d;
425 assign beat[{db} + {fb} +: 16] = b2_d;
426{beat_s7}endmodule
427
428// A {what} column's datapath: the decoded beats it rings (each put in on `put`, the beat at the ring's head the one it
429// holds, the next taken on `load` by moving the head), and each pass of an issue of the beat held, the beat and
430// the pass's word of the activations (`act_word`, the clock after `pass`: the word's excess codes and their unit sums;
431// its excess codes staggered for the elements in `act_st`) through the systolic processing elements of the pass's code
432// digit, its partials summed into its word's in their place
433// from the issue's `first` pass as they leave the elements PL clocks on, with the pass's flags and its lanes' fields
434// delayed to meet them. The issue's last pass (`issue`) hands the sums to the two rows' lanes and the pipelined add. The
435// value of the issue LAT clocks before lands on the sum `land` names; the sum leaves the add ADD clocks later as the sum
436// `done` names and is written back, or, its last block (`last`), emitted and its place reset to +0. `clear` resets every
437// sum to +0.
438module logos_column_core_{name} #(parameter SUMS = 4, SW = 2, RING = 2)
439 (input clk, input rst, input clear{two_in}{fmt_in},
440 input put, input load, input [{beat_top}:0] w_beat,
441 input pass, input digit, input word, input first, input issue, input [{act_top}:0] act_word, input [{code_top}:0] act_st,
442 input [SW-1:0] land, input [SW-1:0] done, input last{core_octet_ports},
443 output reg o_valid, output reg [31:0] o_val1, output reg [31:0] o_val2);
444 localparam LAT = {lat}, ADD = {add}, TAGS = LAT + ADD;
445{two_decl}{fmt_copy}{zsel} // The ring: RING places, the beat held at its head and the beats queued behind it, a beat put in at the place after the
446 // last; a step's next beat is the head's next place, so taking it moves the head and no beat.
447 localparam RA = (RING > 2) ? 2 : 1;
448 localparam [RA-1:0] R0 = 0, R1 = 1, RLAST = RING - 1;
449 `LOGOS_RAMSTYLE_ASYNC(RING) reg [{beat_top}:0] ring [0:RING-1];
450 // The ring's places address every MLAB of the beat's width: registers Quartus copies beside each group of them.
451 (* maxfan = 64 *) reg [RA-1:0] rp, wp;
452 always @(posedge clk) begin
453 if (put) begin ring[wp] <= w_beat; wp <= (wp == RLAST) ? R0 : wp + R1; end
454 if (load) rp <= (rp == RLAST) ? R0 : rp + R1;
455 if (rst) begin rp <= R0; wp <= R1; end
456 end
457 wire [{beat_top}:0] held = ring[rp];
458 // The pass's flags, registered beside its word of the activations; its codes go to the elements from the beat held,
459 // a clock before the word.
460 reg s1_pass, s1_digit, s1_word, s1_first;
461 always @(posedge clk) begin s1_pass <= pass; s1_digit <= digit; s1_word <= word; s1_first <= first; end
462 wire [{code_top}:0] code1 = held[{code_top}:0], code2 = held[{db} + {code_top}:{db}];
463 wire [{bus12}:0] s_act = {s_sel};
464 wire [{bus24}:0] w_p1, w_p2;
465 logos_matvec_pass_p #(.UNITS({units})) parts(.clk(clk), .code1(code1), .code2(code2), .w(act_st), .s(s_act), .digit(digit),
466 .p1(w_p1), .p2(w_p2));
467 // The pass's flags and the fields of its beat and activations the lanes and the slots take, PL clocks behind with its
468 // partials (the beat's taken from the beat held at the pass, a clock before the activations'); the pass flag in
469 // registers the reset clears.
470 localparam PL = {pl}, LB = {lb_bits}, LA = {la_bits};
471 reg [PL-1:0] d_pass, d_digit;
472 reg [PL-2:0] d_word, d_first;
473 // The word and first flags' last places drive every unit's sums: registers Quartus copies a copy each 256 loads.
474 (* maxfan = 256 *) reg p_word, p_first;
475 wire [LB-1:0] lb_in = {{{lb_in}}};
476 wire [LA-1:0] la_in = {{{la_in}}};
477 wire [LB-1:0] lfb;
478 wire [LA-1:0] lfa;
479 logos_delay #(.W(LB), .D(PL + 1)) lbd(.clk(clk), .d(lb_in), .q(lfb));
480 logos_delay #(.W(LA), .D(PL)) lad(.clk(clk), .d(la_in), .q(lfa));
481 always @(posedge clk) begin
482 d_pass <= rst ? {{PL{{1'b0}}}} : {{d_pass[PL-2:0], s1_pass}};
483 d_digit <= {{d_digit[PL-2:0], s1_digit}};
484 d_word <= (d_word << 1) | {{{{(PL-2){{1'b0}}}}, s1_word}}; p_word <= d_word[PL-2];
485 d_first <= (d_first << 1) | {{{{(PL-2){{1'b0}}}}, s1_first}}; p_first <= d_first[PL-2];
486 end
487 wire p_pass = d_pass[PL-1], p_digit = d_digit[PL-1];
488{lf_out} // The pass's partials, its digit's in their place, added to its word's sums so far (none at an issue's first pass).
489 reg [{bus24}:0] a1_hi, a2_hi, a1_lo, a2_lo, t1_hi, t2_hi, t1_lo, t2_lo;
490 reg [{bus12}:0] as_hi, as_lo, ts_hi, ts_lo;
491 reg [23:0] sh1, sh2, b1_hi, b2_hi, b1_lo, b2_lo;
492 integer u;
493 always @* begin
494 for (u = 0; u < {units}; u = u + 1) begin
495 sh1 = p_digit ? {{w_p1[24*u +: 20], 4'd0}} : w_p1[24*u +: 24];
496 sh2 = p_digit ? {{w_p2[24*u +: 20], 4'd0}} : w_p2[24*u +: 24];
497 b1_hi = p_first ? 24'd0 : a1_hi[24*u +: 24];
498 b2_hi = p_first ? 24'd0 : a2_hi[24*u +: 24];
499 b1_lo = p_first ? 24'd0 : a1_lo[24*u +: 24];
500 b2_lo = p_first ? 24'd0 : a2_lo[24*u +: 24];
501 t1_hi[24*u +: 24] = p_word ? b1_hi : b1_hi + sh1;
502 t2_hi[24*u +: 24] = p_word ? b2_hi : b2_hi + sh2;
503 t1_lo[24*u +: 24] = p_word ? b1_lo + sh1 : b1_lo;
504 t2_lo[24*u +: 24] = p_word ? b2_lo + sh2 : b2_lo;
505 end
506 ts_hi = p_word ? (p_first ? {{({units}*12){{1'b0}}}} : as_hi) : f_s;
507 ts_lo = p_word ? f_s : (p_first ? {{({units}*12){{1'b0}}}} : as_lo);
508 end
509 always @(posedge clk) if (p_pass) begin
510 a1_hi <= t1_hi; a2_hi <= t2_hi; a1_lo <= t1_lo; a2_lo <= t2_lo; as_hi <= ts_hi; as_lo <= ts_lo;
511 end
512{core_octets} // The lanes' inputs: the sums over the issue's passes, registered the clock after its last{lanes_from}.
513 reg [{bus24}:0] p1_hi, p2_hi, p1_lo, p2_lo;
514 reg [{bus12}:0] s_hi, s_lo, q1, q2;
515 reg [{unit_top}:0] lc1, ln1, lc2, ln2;
516 reg [15:0] la1, lb1, la2, lb2;
517 reg [7:0] lm;
518 reg signed [11:0] le;
519 always @(posedge clk) begin
520{octet_lanes} p1_hi <= t1_hi; p2_hi <= t2_hi; p1_lo <= t1_lo; p2_lo <= t2_lo;
521 s_hi <= ts_hi; s_lo <= ts_lo; q1 <= f_q1; q2 <= f_q2;
522 lc1 <= f_c1; ln1 <= f_n1; lc2 <= f_c2; ln2 <= f_n2;
523 la1 <= f_a1; lb1 <= f_b1; la2 <= f_a2; lb2 <= f_b2;
524 lm <= f_m;
525 le <= f_e;
526{octet_lanes_end} end
527 wire [31:0] value1, value2;
528 logos_lane_value_p #(.UNITS({units}), .WORDS({words_n})) l1(.clk(clk), .p_hi(p1_hi), .s_hi(s_hi), .p_lo(p1_lo), .s_lo(s_lo),
529 .q(q1), .c(lc1), .n(ln1), .z(z), .a(la1), .b(lb1), .m(lm), .e(le), .two(two), .value(value1));
530 logos_lane_value_p #(.UNITS({units}), .WORDS({words_n})) l2(.clk(clk), .p_hi(p2_hi), .s_hi(s_hi), .p_lo(p2_lo), .s_lo(s_lo),
531 .q(q2), .c(lc2), .n(ln2), .z(z), .a(la2), .b(lb2), .m(lm), .e(le), .two(two), .value(value2));
532 // The two rows' sums: a value added as it lands, the sum written as it leaves the add.
533 (* ramstyle = "logic" *) reg [31:0] acc1 [0:SUMS-1];
534 (* ramstyle = "logic" *) reg [31:0] acc2 [0:SUMS-1];
535 wire [31:0] sum1, sum2;
536 logos_fadd_p add1(.clk(clk), .a(acc1[land]), .b(value1), .y(sum1));
537 logos_fadd_p add2(.clk(clk), .a(acc2[land]), .b(value2), .y(sum2));
538 // This core's issues in flight.
539 reg alive [0:TAGS-1];
540 integer k;
541 always @(posedge clk) begin
542 o_valid <= 1'b0;
543 alive[0] <= issue;
544 for (k = 1; k < TAGS; k = k + 1) alive[k] <= alive[k-1];
545 if (alive[TAGS-1]) begin
546 acc1[done] <= last ? 32'd0 : sum1;
547 acc2[done] <= last ? 32'd0 : sum2;
548 if (last) begin o_valid <= 1'b1; o_val1 <= sum1; o_val2 <= sum2; end
549 end
550 if (rst) begin
551 for (k = 0; k < TAGS; k = k + 1) alive[k] <= 1'b0;
552 end else if (clear) begin
553 for (k = 0; k < SUMS; k = k + 1) begin acc1[k] <= 32'd0; acc2[k] <= 32'd0; end
554 end
555 end
556endmodule
557
558// A {what} matrix streamed block-major within groups of row pairs against up to T_MAX token-rows: for each group, every
559// pair's block 0, then every pair's block 1, ..., a beat a pair's two blocks, decoded as it is taken. The beat held is
560// issued against every token-row, a token-row a clock, an issue's coordinates riding beside it to its sum's emission, and
561// the next beat is taken the clock the held one issues its last token-row.
562module logos_column_{name} #(parameter T_MAX = 4, NB_MAX = 8, P_MAX = 4)
563 (input clk, input rst,
564 input start, input [15:0] groups, input [7:0] pairs, input [15:0] blocks, input [7:0] tokens{two_in}{fmt_in}, output busy,
565 input act_wr, input [7:0] act_t, input [15:0] act_b, input [{codes_top}:0] act_codes, input [7:0] act_m, input [11:0] act_e,
566 input w_valid, output w_ready, input [{top}:0] w_blk1, input [{top}:0] w_blk2,
567 output o_valid, output reg [15:0] o_group, output reg [7:0] o_pair, output reg [7:0] o_t, output [31:0] o_val1,
568 output [31:0] o_val2);
569{two_decl}{fmt_copy} localparam LAT = {lat}, ADD = {add}, TAGS = LAT + ADD;
570 localparam AC = P_MAX * T_MAX, AW = (AC > 1) ? $clog2(AC) : 1;
571 // The beat held, its group, block and pair, the token-row it issues next; the coordinates of the beat taken next.
572 reg have, more;
573 reg [15:0] hg, hb, ng, nb;
574 reg [7:0] hp, np, t;
575 wire [31:0] at_wide = hp * T_MAX + t;
576 wire [AW-1:0] at = at_wide[AW-1:0];
577 // Each issue in flight: whether it is one, its sum's place, its coordinates, and whether it is its sum's last block.
578 reg live [0:TAGS-1];
579 reg [AW-1:0] tag [0:TAGS-1];
580 reg [15:0] tg [0:TAGS-1];
581 reg [7:0] tp [0:TAGS-1];
582 reg [7:0] tt [0:TAGS-1];
583 reg last [0:TAGS-1];
584 // An issue waits while an earlier issue of its sum is among the last ADD: that one's sum is written as it leaves the
585 // add, ADD clocks after it lands, and this one, landing LAT clocks after it issues as that one did, must read it.
586 reg stall, flight;
587 integer h;
588 always @* begin
589 stall = 1'b0;
590 flight = 1'b0;
591 for (h = 0; h < ADD; h = h + 1) if (live[h] && tag[h] == at) stall = 1'b1;
592 for (h = 0; h < TAGS; h = h + 1) if (live[h]) flight = 1'b1;
593 end
594 // An issue's passes, a clock each: its format's code digits, for each activation word; the last waits on a stall.
595{digits} wire [2:0] passes = two ? {{digits, 1'b0}} : {{1'b0, digits}};
596 reg [2:0] pk;
597 wire last_pass = pk + 3'd1 == passes;
598 wire issue = have && last_pass && !stall;
599 wire pass = have && (!last_pass || !stall);
600 wire digit = digits[1] && pk[0];
601 wire word = digits[1] ? pk[1] : pk[0];
602 wire fin = issue && (t + 8'd1 == tokens);
603 assign w_ready = more && (!have || fin);
604 wire take = w_valid && w_ready;
605 assign busy = have || more || flight;
606 // The beat decoded as it is taken; each token-row's blocks of activations, as their words' excess codes and unit
607 // sums, in block RAM.
608 wire [{beat_top}:0] w_beat;
609 logos_column_decode_{name} dec(.blk1(w_blk1), .blk2(w_blk2), {fmt_dec}.beat(w_beat));
610 localparam AN = T_MAX * NB_MAX, AA = (AN > 1) ? $clog2(AN) : 1;
611 wire [{act_top}:0] act_wd, act_word;
612 logos_column_act_word #(.ELEMS({elems}), .WORDS({words_n}), .SLOTS({slots})) word_of(.codes(act_codes), .two(two), .m(act_m), .e(act_e),
613 .slot({act_slot}),
614 .word(act_wd));
615 wire [31:0] act_ra = t * NB_MAX + hb, act_wa = act_t * NB_MAX + act_b;
616 {act_ram} #(.D(AN), .A(AA)) act_ram(.clk(clk), .ra(act_ra[AA-1:0]), .rd(act_word), .we(act_wr),
617 .wa(act_wa[AA-1:0]), .wd(act_wd), .wm({{{act_bytes}{{1'b1}}}}));
618{stagger} logos_column_core_{name} #(.SUMS(AC), .SW(AW)) core(.clk(clk), .rst(rst), .clear(start && !busy), {two_pass}{fmt_pass}.put(take), .load(take),
619 .w_beat(w_beat), .pass(pass), .digit(digit), .word(word), .first(pk == 3'd0), .issue(issue), .act_word(act_word), .act_st(act_st),
620 .land(tag[LAT-1]), .done(tag[TAGS-1]), .last(last[TAGS-1]), .o_valid(o_valid), .o_val1(o_val1), .o_val2(o_val2));
621 integer k;
622 always @(posedge clk) begin
623 live[0] <= issue; tag[0] <= at; tg[0] <= hg; tp[0] <= hp; tt[0] <= t; last[0] <= (hb + 16'd1 == blocks);
624 for (k = 1; k < TAGS; k = k + 1) begin
625 live[k] <= live[k-1]; tag[k] <= tag[k-1]; tg[k] <= tg[k-1]; tp[k] <= tp[k-1]; tt[k] <= tt[k-1]; last[k] <= last[k-1];
626 end
627 if (live[TAGS-1] && last[TAGS-1]) begin o_group <= tg[TAGS-1]; o_pair <= tp[TAGS-1]; o_t <= tt[TAGS-1]; end
628 if (pass) pk <= last_pass ? 3'd0 : pk + 3'd1;
629 if (issue) t <= (t + 8'd1 == tokens) ? 8'd0 : t + 8'd1;
630 if (take) begin
631 hg <= ng; hb <= nb; hp <= np; have <= 1'b1;
632 if (np + 8'd1 < pairs) np <= np + 8'd1;
633 else begin
634 np <= 8'd0;
635 if (nb + 16'd1 < blocks) nb <= nb + 16'd1;
636 else begin
637 nb <= 16'd0;
638 if (ng + 16'd1 < groups) ng <= ng + 16'd1; else more <= 1'b0;
639 end
640 end
641 end else if (fin) have <= 1'b0;
642 if (rst) begin
643 have <= 1'b0; more <= 1'b0; t <= 8'd0; pk <= 3'd0;
644 for (k = 0; k < TAGS; k = k + 1) live[k] <= 1'b0;
645 end else if (start && !busy) begin
646 ng <= 16'd0; nb <= 16'd0; np <= 8'd0; t <= 8'd0; pk <= 3'd0; more <= 1'b1;
647 end
648 end
649endmodule
650
651// A {what} matrix laid in stripes, each channel's tile of a stripe block-major, through CH * N cores, N on each channel:
652// core c = ch * N + p takes row pair p of channel ch's tile of each stripe, its decoded beats in order. Stripe s's tile on
653// channel ch holds the tensor's rows from (CH * s + turns[ch]) * 2N, fewer than 2N only at the tensor's end; a core whose
654// pair's first row is past the tensor takes no beat that stripe. Every core runs the same pass of the same token-row of
655// the same block step each clock against one read of the activations; a step issues once every core the stripe holds a
656// pair for has its beat, and the cores take the next step's beats the clock the last token-row issues: core c's feed
657// puts each of its beats into the core's ring as it decodes it (w_put[c], the beat on its w_beat), w_valid[c] says the
658// ring holds one queued, and taking it moves the ring's head (its feed queues one beat a core for two cores or more, two
659// for a lone core, so the ring's places are one more). Each core's sums
660// emerge at the stripe's last block, a token-row a clock, all cores at once, with the stripe and the token-row beside
661// them. No pass runs while `hold`.
662module logos_column_array_{name} #(parameter CH = 4, N = 2, T_MAX = 4, NB_MAX = 8)
663 (input clk,{clk2_in} input rst,
664 input start, input [15:0] stripes, input [15:0] blocks, input [7:0] tokens{two_in}{fmt_in}, input [31:0] rows,
665 input [2*CH-1:0] turns, input hold, output busy,
666 input act_wr, input [7:0] act_t, input [15:0] act_b{arr_act_s}, input [{codes_top}:0] act_codes, input [7:0] act_m, input [11:0] act_e,
667 input [CH*N-1:0] w_valid, output [CH*N-1:0] w_ready, input [CH*N-1:0] w_put, input [CH*N*{beat}-1:0] w_beat,
668 output [CH*N-1:0] o_valid, output [CH*N*32-1:0] o_val1, output [CH*N*32-1:0] o_val2, output reg [15:0] o_stripe,
669 output reg [7:0] o_t);
670 localparam LAT = {lat}, ADD = {add}, TAGS = LAT + ADD, C = CH * N;
671{two_decl}{fmt_copy} localparam TW = (T_MAX > 1) ? $clog2(T_MAX) : 1;
672 // The step held: its stripe, its block, the token-row it issues next; whether a step is held. Beside them, each the value its wire would have from them, so an issue is a few levels
673 // from registers: whether the block is its stripe's last (step_end), the stripe the last (last_stripe), the token-row
674 // the step's last (t_last), and whether every core the step holds a pair for has its beat (ready).
675 reg held, step_end, last_stripe, t_last, ready;
676 reg [15:0] hs, hb;
677 reg [7:0] t;
678 reg [C-1:0] have;
679 wire next = !(step_end && last_stripe);
680 localparam [31:0] STRIPE_ROWS = CH * 2 * N;
681 // The matrix's rows from the next stripe's first on (left_after), and whether each core's pair's first row is among
682 // the rows from the held stripe's first on (need_cur) and from the next one's (need_adv), registers updated a stripe
683 // at a time; so the cores the held step and the next one hold a pair for are a select of registers. The first
684 // step's from the rows.
685 reg [31:0] left_after;
686 function [31:0] after_stripe(input [31:0] l);
687 after_stripe = (l > STRIPE_ROWS) ? l - STRIPE_ROWS : 32'd0;
688 endfunction
689 reg [C-1:0] need, need_next, need_start, need_cur, need_adv, adv_start, adv_next;
690 reg [31:0] first;
691 integer c;
692 always @* begin
693 for (c = 0; c < C; c = c + 1) begin
694 first = turns[2*(c/N) +: 2] * 2 * N + 2 * (c % N);
695 need[c] = held && need_cur[c];
696 need_next[c] = held && next && (step_end ? need_adv[c] : need_cur[c]);
697 need_start[c] = first < rows;
698 adv_start[c] = first < after_stripe(rows);
699 adv_next[c] = first < after_stripe(left_after);
700 end
701 end
702 // Each issue in flight: whether it is one, its token-row, its stripe, and whether it is its sums' last block.
703 reg live [0:TAGS-1];
704 reg [TW-1:0] tag [0:TAGS-1];
705 reg [15:0] ts [0:TAGS-1];
706 reg [7:0] tt [0:TAGS-1];
707 reg last [0:TAGS-1];
708 wire [TW-1:0] at = t[TW-1:0];
709 reg stall, flight;
710 integer h;
711 always @* begin
712 stall = 1'b0;
713 flight = 1'b0;
714 for (h = 0; h < ADD; h = h + 1) if (live[h] && tag[h] == at) stall = 1'b1;
715 for (h = 0; h < TAGS; h = h + 1) if (live[h]) flight = 1'b1;
716 end
717{arr_octets} // An issue's passes, a clock each: its format's code digits, for each activation word; the last waits on a stall.
718{digits} wire [2:0] passes = two ? {{digits, 1'b0}} : {{1'b0, digits}};
719 reg [2:0] pk;
720 // The pass counter one on (pk_up), a register beside it from its next value, so the last pass is a compare of registers.
721 reg [2:0] pk_up;
722 wire last_pass = pk_up == passes;
723 wire issue = held && ready && !hold && last_pass && {pe_ok};
724 wire pass = held && ready && !hold && (!last_pass || {pe_ok});
725 wire digit = digits[1] && pk[0];
726 wire word = digits[1] ? pk[1] : pk[0];
727 wire fin = issue && t_last;
728 wire [2:0] pk_n = rst || (start && !busy) ? 3'd0 : pass ? (last_pass ? 3'd0 : pk_up) : pk;
729 always @(posedge clk) pk_up <= pk_n + 3'd1;
730 assign w_ready = (need & ~have) | ({{C{{fin}}}} & need_next);
731 wire [C-1:0] take = w_valid & w_ready;
732 assign busy = held || flight{arr_busy};
733 // Each token-row's blocks of activations, as their words' excess codes and unit sums, in block RAM beside each
734 // channel: every copy written alike a clock after the word is made, each read a clock for its own channel's cores.
735 // Each token-row's blocks at a pitch of a power of two (NB_P, 2^NB_L), so an address is a token-row's place and a
736 // block's in bits of their own.
737 localparam NB_L = (NB_MAX > 1) ? $clog2(NB_MAX) : 0, NB_P = 1 << NB_L;
738 localparam AN = T_MAX * NB_P, AA = (AN > 1) ? $clog2(AN) : 1;
739 wire [{act_top}:0] act_wd;
740 logos_column_act_word #(.ELEMS({elems}), .WORDS({words_n}), .SLOTS({slots})) word_of(.codes(act_codes), .two(two), .m(act_m), .e(act_e),
741 .slot({act_slot}),
742 .word(act_wd));
743 wire [31:0] act_wa = ({{24'd0, act_t}} << NB_L) | (act_b & (NB_P - 1));
744 // The step's next token-row and block, as the clock's update leaves them, and the read address they make: each copy
745 // takes it into a register of its own beside its blocks, so a read sees the address of the step's token-row and block
746 // as before, from a register.
747 // The step's token-row and block one on (t_up, hb_up) are registers beside them, the next values plus their steps,
748 // so each next value is a choice of registers and the read address holds no carry chain.
749 reg [7:0] t_up;
750 reg [15:0] hb_up;
751 wire [7:0] t_n = rst || (start && !busy) ? 8'd0 : issue ? (t_last ? 8'd0 : t_up) : t;
752 wire [15:0] hb_n = !rst && start && !busy ? 16'd0 : fin && next ? (step_end ? 16'd0 : hb_up) : hb;
753 always @(posedge clk) begin t_up <= t_n + 8'd1; hb_up <= hb_n + 16'd1; end
754 wire [31:0] ra_n = ({{24'd0, t_n}} << NB_L) | ({{16'd0, hb_n}} & (NB_P - 1));
755 wire [CH*{act_w}-1:0] act_words;
756{ach_decls} genvar ch;
757 generate
758 for (ch = 0; ch < CH; ch = ch + 1) begin : ach
759 reg we;
760 reg [AA-1:0] wa;
761 reg [{act_top}:0] wd;
762 reg [{wm_top}:0] wm;
763 (* maxfan = 64 *) reg [AA-1:0] ra;
764 always @(posedge clk) begin we <= act_wr; wa <= act_wa[AA-1:0]; wd <= act_wd; wm <= {act_wm}; ra <= ra_n[AA-1:0]; end
765 wire [{act_top}:0] act_word;
766 {act_ram} #(.D(AN), .A(AA)) act_ram(.clk(clk), .ra(ra), .rd(act_word), .we(we), .wa(wa), .wd(wd), .wm(wm));
767 assign act_words[ch*{act_w} +: {act_w}] = act_word;
768{ach_body} end
769 endgenerate
770 genvar g;
771 generate
772 for (g = 0; g < C; g = g + 1) begin : core
773 logos_column_core_{core_mod} #(.SUMS(T_MAX), .SW(TW), .RING((N == 1) ? 3 : 2)) u(.clk(clk), .rst(rst), .clear(start && !busy),
774 {two_pass}{fmt_pass}.put(w_put[g]), .load(take[g]), .w_beat(w_beat[g*{beat} +: {beat}]), .pass(pass), .digit(digit), .word(word), .first(pk == 3'd0),
775 .issue({core_issue}){core_octet_conn}, .act_word(act_words[(g/N)*{act_w} +: {act_w}]), {core_pe}.land(tag[LAT-1]), .done(tag[TAGS-1]), .last(last[TAGS-1]),
776 .o_valid(o_valid[g]), .o_val1(o_val1[g*32 +: 32]), .o_val2(o_val2[g*32 +: 32]));
777 end
778{pairs} endgenerate
779 integer k;
780 always @(posedge clk) begin
781{tags_in}
782 for (k = 1; k < TAGS; k = k + 1) begin
783 live[k] <= live[k-1]; tag[k] <= tag[k-1]; ts[k] <= ts[k-1]; tt[k] <= tt[k-1]; last[k] <= last[k-1];
784 end
785 if (live[TAGS-1] && last[TAGS-1]) begin o_stripe <= ts[TAGS-1]; o_t <= tt[TAGS-1]; end
786 if (pass) pk <= last_pass ? 3'd0 : pk + 3'd1;
787 if (issue) begin t <= t_last ? 8'd0 : t + 8'd1; t_last <= t_last ? tokens == 8'd1 : t + 8'd2 == tokens; end
788 have <= (have & ~{{C{{fin}}}}) | take;
789 // Next clock's ready from this one's registers: past a step's last issue every core the next step needs is offered
790 // its beat, otherwise every core this step needs holds its beat or is offered it.
791 ready <= fin ? &(~need_next | w_valid) : &(have | w_valid | ~need);
792 if (fin) begin
793 if (!next) held <= 1'b0;
794 else if (step_end) begin
795 hb <= 16'd0; hs <= hs + 16'd1; left_after <= after_stripe(left_after); need_cur <= need_adv;
796 need_adv <= adv_next; step_end <= blocks == 16'd1; last_stripe <= hs + 16'd2 == stripes;
797 end else begin
798 hb <= hb + 16'd1; step_end <= hb + 16'd2 == blocks;
799 end
800 end
801{arr_octet_step} if (rst) begin
802 held <= 1'b0; have <= {{C{{1'b0}}}}; t <= 8'd0; pk <= 3'd0;
803 for (k = 0; k < TAGS; k = k + 1) live[k] <= 1'b0;{arr_octet_reset}
804 end else if (start && !busy) begin
805 held <= 1'b1; hs <= 16'd0; hb <= 16'd0; left_after <= after_stripe(rows); need_cur <= need_start;
806 need_adv <= adv_start; t <= 8'd0; pk <= 3'd0; have <= {{C{{1'b0}}}};
807 step_end <= blocks == 16'd1; last_stripe <= stripes == 16'd1; t_last <= tokens == 8'd1; ready <= &(~need_start);{arr_octet_reset}
808 end
809 end
810endmodule
811
812// One channel's feed of a {what} matrix laid in stripes to its N cores: the image stream's beats of the channel's tiles
813// (a beat at offset 0 starting a tile, whose first row it carries; zeros past its kept bytes) gathered into blocks, and
814// each block step's blocks dealt in pairs, decoded, the tile's rows 2p and 2p + 1 to core p, the second block zeros when
815// the tile's last row has no partner. A tile holds 2N rows, fewer at the tensor's `rows`. Each block comes in its slot
816// (its bytes, then its pad's zeros), so a beat lands in the gather register a whole number of slots in. The gather
817// register keeps less than a block past the last block it holds, so it never holds more than two tiles' bytes, and its
818// bytes past those it holds are zeros, a beat's bytes ORed in at its end (cleared with the reset, so whatever it powers
819// up holding never reaches a block); the beats queued for each core, in its ring behind the beat it holds
820// (logos_column_core_{name}), let the stream run a step ahead of the cores: two for a lone core, one a core for two or
821// more, whose beats come no sooner than 2N blocks apart. A pair's beat is decoded into a register and put into its
822// core's ring the clock after (w_put, the register on every core's w_beat), a core's room counting the beat on its way;
823// whether the row a block goes to is its tile's last is held in a register beside the row.
824module logos_column_feed_{name} #(parameter N = 2)
825 (input clk, input rst{fmt_in}, input [15:0] blocks, input [31:0] rows,
826 input in_valid, output in_ready, input [511:0] in_data, input [6:0] in_keep, input [31:0] in_row, input [23:0] in_offset,
827 output [N-1:0] w_valid, input [N-1:0] w_ready, output [N-1:0] w_put, output [N*{beat}-1:0] w_beat);
828{fmt_copy}{feed_bb} reg [8*GB-1:0] gather;
829 // Each core's put, a register copied a copy each 64 of the ring's write enables it reaches.
830 (* maxfan = 64 *) reg [N-1:0] put_q;
831 assign w_put = put_q;
832 reg [15:0] fill;
833 // The rows of the tile whose blocks leave next, and of the tile after it once its first beat is in.
834 reg [7:0] held, held_next;
835 reg has_next;
836 (* maxfan = 256 *) reg [7:0] j;
837 reg jl;
838 reg [15:0] b;
839 reg [{top}:0] stage;
840 // The beats queued in each core's ring, QD at most, and the beat decoded on its way there.
841 localparam QD = (N == 1) ? 2 : 1;
842 reg [1:0] count [0:N-1];
843 reg [{beat_top}:0] dq;
844 reg dq_v;
845 reg [7:0] dq_p;
846 wire [7:0] p = j >> 1;
847 wire odd = j[0];
848 wire alone = !odd && jl;
849 // A block leaves the gather register when its tile is known and, completing a beat, its core's queue has room for it
850 // beside any beat already on its way there.
851 wire pair = odd || alone;
852 wire on_way = dq_v && dq_p == p;
853 wire queue_room = (QD == 2) ? (count[p] == 2'd0 || (count[p] == 2'd1 && !on_way)) : (count[p] == 2'd0 && !on_way);
854 wire emit = fill >= BB && held != 8'd0 && (!pair || queue_room);
855 wire push = emit && pair;
856 wire [15:0] kept = emit ? fill - BB : fill;
857 assign in_ready = kept <= BB && !(in_offset == 24'd0 && has_next);
858 wire take = in_valid && in_ready;
859 localparam [31:0] TILE = 2 * N;
860 wire [31:0] room = rows - in_row;
861 wire [7:0] tile_rows = (room < TILE) ? room[7:0] : TILE[7:0];
862 wire tile_end = emit && jl && (b + 16'd1 == blocks);
863 wire [8*GB-1:0] shifted = emit ? {drop} : gather;
864 // The next row and the next tile's rows, and so whether the next row is its tile's last.
865 wire [7:0] j_next = emit ? (jl ? 8'd0 : j + 8'd1) : j;
866 wire tile_in = take && in_offset == 24'd0;
867 wire [7:0] held_now = tile_in ? ((held == 8'd0 || tile_end) ? tile_rows : held) : tile_end ? (has_next ? held_next : 8'd0) : held;
868 // The beat a push queues, decoded.
869 wire [{beat_top}:0] decoded;
870 logos_column_decode_{name} dec(.blk1(odd ? stage : gather[{top}:0]), .blk2(odd ? gather[{top}:0] : {{{bits}{{1'b0}}}}),
871 {fmt_dec}.beat(decoded));
872 genvar g;
873 generate
874 for (g = 0; g < N; g = g + 1) begin : core
875 assign w_valid[g] = count[g] != 2'd0;
876 assign w_beat[g*{beat} +: {beat}] = dq;
877 end
878 endgenerate
879 integer k;
880 always @(posedge clk) begin
881 if (take) gather <= shifted | ({{{{(8*GB-512){{1'b0}}}}, in_data}} << ({gather_shift}));
882 else gather <= shifted;
883 fill <= kept + (take ? in_keep : 7'd0);
884 if (emit) begin
885 if (!odd && !alone) stage <= gather[{top}:0];
886 if (jl) b <= (b + 16'd1 == blocks) ? 16'd0 : b + 16'd1;
887 end
888 j <= j_next;
889 held <= held_now;
890 jl <= j_next + 8'd1 == held_now;
891 dq_v <= push;
892 if (push) begin dq <= decoded; dq_p <= p; end
893 for (k = 0; k < N; k = k + 1) begin
894 put_q[k] <= push && p == k[7:0];
895 if (w_valid[k] && w_ready[k]) begin
896 if (!(dq_v && dq_p == k[7:0])) count[k] <= count[k] - 2'd1;
897 end else if (dq_v && dq_p == k[7:0]) count[k] <= count[k] + 2'd1;
898 end
899 if (tile_in) begin
900 if (!(held == 8'd0 || tile_end)) begin held_next <= tile_rows; has_next <= 1'b1; end
901 end else if (tile_end) has_next <= 1'b0;
902 if (rst) begin
903 gather <= {{(8*GB){{1'b0}}}}; fill <= 16'd0; held <= 8'd0; has_next <= 1'b0; j <= 8'd0; jl <= 1'b0; b <= 16'd0;
904 dq_v <= 1'b0;
905 for (k = 0; k < N; k = k + 1) begin count[k] <= 2'd0; put_q[k] <= 1'b0; end
906 end
907 end
908endmodule"#,
909 top = bits - 1,
910 beat = 2 * db,
911 beat_top = 2 * db - 1,
912 act_top = set.act_bits() - 1,
913 act_bytes = set.act_bits() / 8,
914 codes_top = 16 * elems - 1,
915 unit_top = 8 * units - 1,
916 fc = f.c,
917 fn = f.n,
918 fa = f.a,
919 fb = f.b,
920 fq = f.q,
921 decode = decode(&decs, elems),
922 zsel = zsel(&decs),
923 digits = digits(&decs),
924 words_n = set.word_count(),
925 feed_bb = feed_block_bytes(&decs),
926 gather_shift = if set.slot > 1 { format!("{} * kept[15:{}]", 8 * set.slot, set.slot.trailing_zeros()) } else { "8 * kept".to_string() },
927 drop = feed_drop(&decs),
928 lat = COLUMN_VALUE_LATENCY,
929 add = FADD_LATENCY,
930 pl = PASS_LATENCY,
931 lb_bits = lane.beat_bits,
932 lb_in = lane.beat_packed,
933 la_bits = lane.act_bits,
934 la_in = lane.act_packed,
935 lf_out = lane.unpacked,
936 act_ram = super::ram::bram_name(set.act_bits()),
937 stagger = if paired {
938 format!(
939 " // The pass's word for the pairs' processing elements, its excess codes chosen at the read and its units' sums,\n \
940 // once for every core; each core's codes out to its pair and its partials back.\n reg word_a;\n \
941 always @(posedge clk) word_a <= word;\n wire [{code_top}:0] act_un = {st_sel};\n \
942 wire [{bus12}:0] s_un = {};\n localparam CODE = {}, PART = {};\n wire [C*CODE-1:0] pc1, pc2;\n \
943 wire [C*PART-1:0] pp1, pp2;\n",
944 s_sel.replace("s1_word", "word_a"),
945 code_top + 1,
946 bus24 + 1
947 )
948 } else {
949 format!(
950 " // The pass's excess codes for the systolic elements, the word chosen at the read and each element's block\n \
951 // delayed for it, once for every core.\n reg word_a;\n always @(posedge clk) word_a <= word;\n \
952 wire [{code_top}:0] act_st;\n logos_column_act_stagger #(.ELEMS({elems})) st(.clk(clk), .codes({st_sel}), .st(act_st));\n"
953 )
954 },
955 clk2_in = if set.pe_pairs { " input clk2," } else { "" },
956 core_mod = if paired { format!("bare_{name}") } else { name.to_string() },
957 core_pe = if paired {
958 ".pe_code1(pc1[g*CODE +: CODE]), .pe_code2(pc2[g*CODE +: CODE]), .pe_p1(pp1[g*PART +: PART]), .pe_p2(pp2[g*PART +: PART]),\n ".to_string()
959 } else {
960 format!(".act_st(act_sts[(g/N)*{w} +: {w}]), ", w = code_top + 1)
961 },
962 act_w = set.act_bits(),
963 wm_top = set.act_bits() / 8 - 1,
964 ach_decls = if paired {
965 format!(
966 " // Each channel's word for its pairs' processing elements, its excess codes chosen at the read and its units'\n \
967 // sums; each core's codes out to its pair and its partials back.\n \
968 wire [CH*{cw}-1:0] act_uns;\n wire [CH*{sw}-1:0] s_uns;\n localparam CODE = {cw}, PART = {pw};\n \
969 wire [C*CODE-1:0] pc1, pc2;\n wire [C*PART-1:0] pp1, pp2;\n",
970 cw = code_top + 1,
971 sw = bus12 + 1,
972 pw = bus24 + 1
973 )
974 } else {
975 format!(" // Each channel's excess codes for its cores' systolic elements, each element's block delayed for it.\n wire [CH*{}-1:0] act_sts;\n", code_top + 1)
976 },
977 ach_body = if paired {
978 format!(
979 " reg word_a;\n always @(posedge clk) word_a <= word;\n \
980 assign act_uns[ch*{cw} +: {cw}] = {st_sel};\n assign s_uns[ch*{sw} +: {sw}] = {};\n",
981 s_sel.replace("s1_word", "word_a"),
982 cw = code_top + 1,
983 sw = bus12 + 1
984 )
985 } else {
986 format!(
987 " reg word_a;\n always @(posedge clk) word_a <= word;\n wire [{code_top}:0] act_st;\n \
988 logos_column_act_stagger #(.ELEMS({elems})) st(.clk(clk), .codes({st_sel}), .st(act_st));\n \
989 assign act_sts[ch*{cw} +: {cw}] = act_st;\n",
990 cw = code_top + 1
991 )
992 },
993 pairs = if paired {
994 format!(
995 " // Each pair of cores on one set of processing elements in clk2, core 2g's codes at its first turn.\n \
996 for (g = 0; g < C / 2; g = g + 1) begin : pair\n \
997 logos_matvec_pass_x2 #(.UNITS({units})) pe(.clk(clk), .clk2(clk2), .code1a(pc1[2*g*CODE +: CODE]),\n \
998 .code2a(pc2[2*g*CODE +: CODE]), .code1b(pc1[(2*g+1)*CODE +: CODE]), .code2b(pc2[(2*g+1)*CODE +: CODE]),\n \
999 .w(act_uns[((2*g)/N)*CODE +: CODE]), .s(s_uns[((2*g)/N)*{sw} +: {sw}]), .digit(digit), .p1a(pp1[2*g*PART +: PART]), .p2a(pp2[2*g*PART +: PART]), .p1b(pp1[(2*g+1)*PART +: PART]),\n \
1000 .p2b(pp2[(2*g+1)*PART +: PART]));\n end\n",
1001 sw = bus12 + 1
1002 )
1003 } else {
1004 String::new()
1005 },
1006 beat_s7 = if set.octets { format!(" assign beat[{s} +: 112] = s71_d;\n assign beat[{} +: 112] = s72_d;\n", db + f.s7, s = f.s7) } else { String::new() },
1007 core_octet_ports = oct.ports,
1008 core_octets = oct.body,
1009 octet_lanes = oct.lanes,
1010 octet_lanes_end = oct.lanes_end,
1011 lanes_from = oct.from,
1012 slots = set.act_slots(),
1013 act_slot = if set.octets { "act_s" } else { "3'd0" },
1014 arr_act_s = arr["arr_act_s"],
1015 arr_octets = arr["arr_octets"],
1016 pe_ok = arr["pe_ok"],
1017 arr_busy = arr["arr_busy"],
1018 act_wm = arr["act_wm"],
1019 core_issue = arr["core_issue"],
1020 core_octet_conn = arr["core_octet_conn"],
1021 tags_in = arr["tags_in"],
1022 arr_octet_step = arr["arr_octet_step"],
1023 arr_octet_reset = arr["arr_octet_reset"],
1024 )
1025 .unwrap();
1026 if set.octets || set.token_pairs {
1027 let at = v.rfind(&format!("module logos_column_{name} ")).expect("the set's lone column");
1029 let start = v[..at].rfind("\n\n").map_or(at, |i| i + 1);
1030 let end = at + v[at..].find("endmodule").expect("its end") + "endmodule".len();
1031 v.replace_range(start..end, "");
1032 }
1033 if set.lanes > 1 {
1034 v = octet_lanes(&v, set);
1035 }
1036 if paired {
1037 v = bare_core(&v, set, units, code_top, bus24);
1038 }
1039 if set.token_pairs {
1040 v = token_pairs(&v, set);
1041 }
1042 }
1043 if sets.iter().any(|s| s.octets) {
1044 v.push_str(OCTET_DECODER);
1045 }
1046 if sets.iter().any(|s| s.pe_pairs && !s.token_pairs) {
1047 v.push_str(&matvec_pass_x2_verilog());
1048 }
1049 if sets.iter().any(|s| s.pe_pairs && s.token_pairs) {
1050 v.push_str(&matvec_pass_x2t_verilog());
1051 }
1052 v
1053}
1054
1055fn bare_core(v: &str, set: &ColumnSet, units: usize, code_top: usize, bus24: usize) -> String {
1059 let name = &set.name;
1060 let head = format!("module logos_column_core_{name} ");
1061 let at = v.find(&head).unwrap_or_else(|| panic!("`{head}`"));
1062 let end = at + v[at..].find("endmodule").expect("its end") + "endmodule".len();
1063 let bare = replaced(
1064 &v[at..end],
1065 &[
1066 (&head, &format!("module logos_column_core_bare_{name} ")),
1067 (
1068 &format!("input [{code_top}:0] act_st,"),
1069 &format!("output [{code_top}:0] pe_code1, output [{code_top}:0] pe_code2,\n input [{bus24}:0] pe_p1, input [{bus24}:0] pe_p2,"),
1070 ),
1071 (
1072 &format!(
1073 " logos_matvec_pass_p #(.UNITS({units})) parts(.clk(clk), .code1(code1), .code2(code2), .w(act_st), .s(s_act), .digit(digit),\n .p1(w_p1), .p2(w_p2));\n"
1074 ),
1075 " assign pe_code1 = code1;\n assign pe_code2 = code2;\n assign w_p1 = pe_p1;\n assign w_p2 = pe_p2;\n",
1076 ),
1077 ],
1078 );
1079 let mut out = v.to_string();
1080 out.insert_str(
1081 end,
1082 &format!(
1083 "\n\n// logos_column_core_{name} with its processing elements outside it, shared with another core: its pass's codes out\n\
1084 // (`pe_code1`, `pe_code2`) and their partials in (`pe_p1`, `pe_p2`), as its own elements would give them.\n{bare}"
1085 ),
1086 );
1087 out
1088}
1089
1090struct LaneFields {
1096 beat_bits: usize,
1097 beat_packed: String,
1098 act_bits: usize,
1099 act_packed: String,
1100 unpacked: String,
1101}
1102
1103fn lane_fields(set: &ColumnSet) -> LaneFields {
1104 let (f, a, db, u) = (set.decoded_fields(), set.act_fields(), set.decoded_bits(), set.elems() / UNIT);
1105 let beat = |name: &str, at: usize, w: usize| (name.to_string(), format!("held[{at} +: {w}]"), w);
1106 let mut fields = vec![
1107 beat("f_q1", f.q, 12 * u),
1108 beat("f_q2", db + f.q, 12 * u),
1109 beat("f_c1", f.c, 8 * u),
1110 beat("f_n1", f.n, 8 * u),
1111 beat("f_c2", db + f.c, 8 * u),
1112 beat("f_n2", db + f.n, 8 * u),
1113 beat("f_a1", f.a, 16),
1114 beat("f_b1", f.b, 16),
1115 beat("f_a2", db + f.a, 16),
1116 beat("f_b2", db + f.b, 16),
1117 ("f_m".to_string(), format!("act_word[{} +: 8]", a.m), 8),
1118 ("f_e".to_string(), format!("act_word[{} +: 12]", a.e), 12),
1119 ("f_s".to_string(), "s_act".to_string(), 12 * u),
1120 ];
1121 if set.octets {
1122 fields.push(beat("f_s71", f.s7, 112));
1123 fields.push(beat("f_s72", db + f.s7, 112));
1124 for k in 1..8 {
1125 fields.push((format!("f_ms{k}"), format!("{{act_word[{} +: 12], act_word[{} +: 8]}}", a.e + 24 * k, a.m + 24 * k), 20));
1126 }
1127 }
1128 let (beats, acts): (Vec<_>, Vec<_>) = fields.iter().partition(|(_, e, _)| e.starts_with("held["));
1129 let mut unpacked = String::new();
1130 let mut pack = |part: &[&(String, String, usize)], out: &str| {
1131 let mut at = 0;
1132 for (name, _, w) in part {
1133 writeln!(unpacked, " wire [{}:0] {name} = {out}[{at} +: {w}];", w - 1).unwrap();
1134 at += w;
1135 }
1136 let packed: Vec<&str> = part.iter().rev().map(|(_, e, _)| e.as_str()).collect();
1137 (at, packed.join(", "))
1138 };
1139 let (beat_bits, beat_packed) = pack(&beats, "lfb");
1140 let (act_bits, act_packed) = pack(&acts, "lfa");
1141 LaneFields { beat_bits, beat_packed, act_bits, act_packed, unpacked }
1142}
1143
1144fn octet_lanes(v: &str, set: &ColumnSet) -> String {
1149 let (name, l) = (&set.name, set.lanes);
1150 let words = set.word_count();
1151 let core_head = format!("module logos_column_core_{name} ");
1152 let array_head = format!("module logos_column_array_{name} ");
1153 let span = |v: &str, head: &str| {
1154 let at = v.find(head).unwrap_or_else(|| panic!("`{head}`"));
1155 (at, at + v[at..].find("endmodule").expect("its end"))
1156 };
1157 let mut out = v.to_string();
1158 let (a, b) = span(&out, &core_head);
1159 let mut chain = String::from(
1160 " // The octet lanes past the first: lane j's value of a token-row's block o_k + j added into the row's sum j adds after\n \
1161 // the first add, the value delayed j adds to meet it; an octet job's sum is the chain's last.\n",
1162 );
1163 for r in 1..=2 {
1164 writeln!(chain, " wire [31:0] ch{r}_0 = sum{r};").unwrap();
1165 for j in 1..l {
1166 let d = j * FADD_LATENCY;
1167 writeln!(
1168 chain,
1169 " wire [31:0] sv{r}_{j};\n logos_lane_slim_p #(.UNITS(2), .WORDS({words})) slim{r}_{j}(.clk(clk), .p_hi(sl{j}_p{r}h), .s_hi(sl{j}_sh), .p_lo(sl{j}_p{r}l),\n \
1170 .s_lo(sl{j}_sl), .q(sl{j}_q{r}), .z(z), .a(sl{j}_a{r}), .m(sl{j}_me[7:0]), .e(sl{j}_me[19:8]), .two(two), .value(sv{r}_{j}));\n \
1171 reg [31:0] dv{r}_{j} [0:{dt}];\n integer dk{r}_{j};\n always @(posedge clk) begin\n dv{r}_{j}[0] <= sv{r}_{j};\n \
1172 for (dk{r}_{j} = 1; dk{r}_{j} < {d}; dk{r}_{j} = dk{r}_{j} + 1) dv{r}_{j}[dk{r}_{j}] <= dv{r}_{j}[dk{r}_{j} - 1];\n end\n \
1173 wire [31:0] ch{r}_{j};\n logos_fadd_p chain{r}_{j}(.clk(clk), .a(ch{r}_{p}), .b(dv{r}_{j}[{dt}]), .y(ch{r}_{j}));",
1174 dt = d - 1,
1175 p = j - 1,
1176 )
1177 .unwrap();
1178 }
1179 writeln!(chain, " wire [31:0] chain{r} = ch{r}_{};", l - 1).unwrap();
1180 }
1181 let core = replaced(
1182 &out[a..b],
1183 &[
1184 (
1185 &format!(" localparam LAT = {}, ADD = {}, TAGS = LAT + ADD;\n", COLUMN_VALUE_LATENCY, FADD_LATENCY),
1186 &format!(" localparam LAT = {}, ADD = {}, TAGS = LAT + {l} * ADD, KT = LAT + ADD;\n", COLUMN_VALUE_LATENCY, FADD_LATENCY),
1187 ),
1188 (" // This core's issues in flight.\n", &format!("{chain} // This core's issues in flight.\n")),
1189 (
1190 " if (alive[TAGS-1]) begin\n acc1[done] <= last ? 32'd0 : sum1;\n acc2[done] <= last ? 32'd0 : sum2;\n \
1191 if (last) begin o_valid <= 1'b1; o_val1 <= sum1; o_val2 <= sum2; end\n end\n",
1192 " if (oct ? alive[TAGS-1] : alive[KT-1]) begin\n acc1[done] <= last ? 32'd0 : (oct ? chain1 : sum1);\n \
1193 acc2[done] <= last ? 32'd0 : (oct ? chain2 : sum2);\n \
1194 if (last) begin o_valid <= 1'b1; o_val1 <= oct ? chain1 : sum1; o_val2 <= oct ? chain2 : sum2; end\n end\n",
1195 ),
1196 ],
1197 );
1198 out.replace_range(a..b, &core);
1199 let (a, b) = span(&out, &array_head);
1200 let array = replaced(
1201 &out[a..b],
1202 &[
1203 (
1204 &format!(" localparam LAT = {}, ADD = {}, TAGS = LAT + ADD, C = CH * N;\n", COLUMN_VALUE_LATENCY, FADD_LATENCY),
1205 &format!(" localparam LAT = {}, ADD = {}, TAGS = LAT + {l} * ADD, KT = LAT + ADD, C = CH * N;\n", COLUMN_VALUE_LATENCY, FADD_LATENCY),
1206 ),
1207 (".land(tag[LAT-1]), .done(tag[TAGS-1]), .last(last[TAGS-1]),", ".land(tag[LAT-1]), .done(oct ? tag[TAGS-1] : tag[KT-1]), .last(oct ? last[TAGS-1] : last[KT-1]),"),
1208 (
1209 " if (live[TAGS-1] && last[TAGS-1]) begin o_stripe <= ts[TAGS-1]; o_t <= tt[TAGS-1]; end\n",
1210 " if (oct ? live[TAGS-1] && last[TAGS-1] : live[KT-1] && last[KT-1]) begin\n \
1211 o_stripe <= oct ? ts[TAGS-1] : ts[KT-1]; o_t <= oct ? tt[TAGS-1] : tt[KT-1];\n end\n",
1212 ),
1213 ],
1214 );
1215 out.replace_range(a..b, &array);
1216 out
1217}
1218
1219fn suffixed(text: &str, names: &[&str], suffix: &str) -> String {
1222 let ident = |c: char| c.is_ascii_alphanumeric() || c == '_';
1223 let mut out = String::with_capacity(text.len() + 64);
1224 let mut word = String::new();
1225 let flush = |word: &mut String, out: &mut String| {
1226 if names.contains(&word.as_str()) && !out.ends_with('.') {
1227 out.push_str(word);
1228 out.push_str(suffix);
1229 } else {
1230 out.push_str(word);
1231 }
1232 word.clear();
1233 };
1234 for c in text.chars() {
1235 if ident(c) {
1236 word.push(c);
1237 } else {
1238 flush(&mut word, &mut out);
1239 out.push(c);
1240 }
1241 }
1242 flush(&mut word, &mut out);
1243 out
1244}
1245
1246fn line_with<'a>(text: &'a str, needle: &str) -> &'a str {
1248 let lines: Vec<&str> = text.split_inclusive('\n').filter(|l| l.contains(needle)).collect();
1249 assert_eq!(lines.len(), 1, "one line holding `{needle}`");
1250 lines[0]
1251}
1252
1253fn from_line<'a>(text: &'a str, needle: &str, to: &str) -> &'a str {
1255 between(text, line_with(text, needle), to)
1256}
1257
1258fn between<'a>(text: &'a str, from: &str, to: &str) -> &'a str {
1260 assert_eq!(text.matches(from).count(), 1, "`{from}`");
1261 let a = text.find(from).unwrap();
1262 let b = a + text[a..].find(to).unwrap_or_else(|| panic!("`{to}` after `{from}`")) + to.len();
1263 &text[a..b]
1264}
1265
1266fn token_pairs(v: &str, set: &ColumnSet) -> String {
1272 let name = &set.name;
1273 let (u, elems) = (set.elems() / UNIT, set.elems());
1274 let (aw, cw) = (set.act_bits(), 8 * elems);
1275 let span = |v: &str, head: &str| {
1276 let at = v.find(head).unwrap_or_else(|| panic!("`{head}`"));
1277 (at, at + v[at..].find("endmodule").expect("its end"))
1278 };
1279 let mut out = v.to_string();
1280
1281 let (a, b) = span(&out, &format!("module logos_column_core_{name} "));
1282 let core = out[a..b].to_string();
1283 let s_act_line = line_with(&core, "] s_act = ");
1285 let parts = from_line(&core, "] w_p1, w_p2;", ".p1(w_p1), .p2(w_p2));\n");
1286 let b_word = format!(
1287 " // Token-row b's word of the pass: its unit sums, and the pass's partials of the same codes through elements of its own.\n{}{}",
1288 suffixed(s_act_line, &["s_act", "act_word"], "_b"),
1289 suffixed(parts, &["s_act", "act_st", "w_p1", "w_p2", "parts"], "_b"),
1290 );
1291 let x2t = format!(
1293 " // Token-row b's unit sums, and both token-rows' partials of the beat's codes on one set of elements in clk2, token-row\n \
1294 // a's word at its edge between clk's and b's at clk's next.\n{} wire [{t24}:0] w_p1, w_p2, w_p1_b, w_p2_b;\n \
1295 logos_matvec_pass_x2t #(.UNITS({u})) parts(.clk(clk), .clk2(clk2), .code1(code1), .code2(code2), .wa(act_st), .wb(act_st_b),\n \
1296 .sa(s_act), .sb(s_act_b), .digit(digit), .p1a(w_p1), .p2a(w_p2), .p1b(w_p1_b), .p2b(w_p2_b));\n",
1297 suffixed(s_act_line, &["s_act", "act_word"], "_b"),
1298 t24 = 24 * u - 1,
1299 );
1300 let la_in = between(&core, " wire [LA-1:0] la_in = ", ";\n");
1301 let lf_b: String = core.lines().filter(|l| l.contains("= lfa[")).map(|l| format!("{}\n", suffixed(l, &["lfa", "f_m", "f_e", "f_s", "f_ms1", "f_ms2", "f_ms3", "f_ms4", "f_ms5", "f_ms6", "f_ms7"], "_b"))).collect();
1302 let last_lfa = core.lines().filter(|l| l.contains("= lfa[")).last().expect("the word's lane fields").to_string();
1303 let sums_names = ["a1_hi", "a2_hi", "a1_lo", "a2_lo", "t1_hi", "t2_hi", "t1_lo", "t2_lo", "as_hi", "as_lo", "ts_hi", "ts_lo", "sh1", "sh2", "b1_hi", "b2_hi", "b1_lo", "b2_lo", "u", "w_p1", "w_p2", "f_s"];
1304 let sums = between(&core, " // The pass's partials, its digit's in their place", "as_hi <= ts_hi; as_lo <= ts_lo;\n end\n");
1305 let sums_b = suffixed(&sums.replacen(" // The pass's partials, its digit's in their place, added to its word's sums so far (none at an issue's first pass).\n", " // Token-row b's partials and sums, as token-row a's.\n", 1), &sums_names, "_b");
1306 let lanes = between(&core, " wire [31:0] value1, value2;\n", ".value(value2));\n");
1307 let lane_names = ["p1_hi", "p2_hi", "p1_lo", "p2_lo", "s_hi", "s_lo", "lm", "le", "value1", "value2", "l1", "l2"];
1308 let accs = between(&core, " (* ramstyle = \"logic\" *) reg [31:0] acc1 [0:SUMS-1];\n", "logos_fadd_p add2(.clk(clk), .a(acc2[land]), .b(value2), .y(sum2));\n");
1309 let acc_names = ["acc1", "acc2", "sum1", "sum2", "add1", "add2", "value1", "value2"];
1310 let wide = set.words == Words::Two;
1311 let zero = |w: usize| if w > 0 { format!("{{{w}{{1'b0}}}}, ") } else { String::new() };
1312 let (pad24, pad12) = (zero(24 * u - 48), zero(12 * u - 24));
1313 let mut lane_in = format!(
1314 " // Token-row b's lanes' inputs: its sums over the issue's passes{}; the beat's fields are the issue's.\n \
1315 reg [{t24}:0] p1_hi_b, p2_hi_b, p1_lo_b, p2_lo_b;\n reg [{t12}:0] s_hi_b, s_lo_b;\n reg [7:0] lm_b;\n reg signed [11:0] le_b;\n \
1316 always @(posedge clk) begin\n",
1317 if set.octets { ", or for an octet job from its slot" } else { "" },
1318 t24 = 24 * u - 1,
1319 t12 = 12 * u - 1,
1320 );
1321 if set.octets {
1322 lane_in.push_str(&format!(" if (oct) begin\n p1_hi_b <= {{{pad24}ob_p1h_b}};\n p2_hi_b <= {{{pad24}ob_p2h_b}};\n"));
1323 if wide {
1324 lane_in.push_str(&format!(" p1_lo_b <= {{{pad24}ob_p1l_b}};\n p2_lo_b <= {{{pad24}ob_p2l_b}};\n s_lo_b <= {{{pad12}ob_sl_b}};\n"));
1325 } else {
1326 lane_in.push_str(" p1_lo_b <= 0; p2_lo_b <= 0; s_lo_b <= 0;\n");
1327 }
1328 lane_in.push_str(&format!(" s_hi_b <= {{{pad12}ob_sh_b}};\n lm_b <= ob_me_b[7:0];\n le_b <= ob_me_b[19:8];\n"));
1329 for j in 1..set.lanes {
1330 lane_in.push_str(&format!(
1331 " sl{j}_p1h_b <= sb{j}_p1h_b; sl{j}_p2h_b <= sb{j}_p2h_b; sl{j}_p1l_b <= sb{j}_p1l_b; sl{j}_p2l_b <= sb{j}_p2l_b;\n \
1332 sl{j}_sh_b <= sb{j}_sh_b; sl{j}_sl_b <= sb{j}_sl_b; sl{j}_me_b <= sb{j}_me_b;\n"
1333 ));
1334 }
1335 lane_in.push_str(" end else begin\n");
1336 }
1337 lane_in.push_str(
1338 " p1_hi_b <= t1_hi_b; p2_hi_b <= t2_hi_b; p1_lo_b <= t1_lo_b; p2_lo_b <= t2_lo_b;\n s_hi_b <= ts_hi_b; s_lo_b <= ts_lo_b;\n lm_b <= f_m_b;\n le_b <= f_e_b;\n",
1339 );
1340 if set.octets {
1341 lane_in.push_str(" end\n");
1342 }
1343 lane_in.push_str(" end\n");
1344 let mut edits: Vec<(String, String)> = vec![
1345 (
1346 format!("input [{}:0] act_word, input [{}:0] act_st,", aw - 1, cw - 1),
1347 format!("input [{a}:0] act_word, input [{c}:0] act_st,\n input [{a}:0] act_word_b, input [{c}:0] act_st_b,", a = aw - 1, c = cw - 1),
1348 ),
1349 (
1350 "output reg o_valid, output reg [31:0] o_val1, output reg [31:0] o_val2);".to_string(),
1351 "output reg o_valid, output reg [31:0] o_val1, output reg [31:0] o_val2,\n output reg [31:0] o_val1_b, output reg [31:0] o_val2_b);".to_string(),
1352 ),
1353 if set.pe_pairs { (parts.to_string(), x2t) } else { (parts.to_string(), format!("{parts}{b_word}")) },
1354 (la_in.to_string(), format!("{la_in}{}", suffixed(la_in, &["la_in", "act_word", "s_act"], "_b"))),
1355 (" wire [LA-1:0] lfa;\n".to_string(), " wire [LA-1:0] lfa, lfa_b;\n".to_string()),
1356 (
1357 " logos_delay #(.W(LA), .D(PL)) lad(.clk(clk), .d(la_in), .q(lfa));\n".to_string(),
1358 " logos_delay #(.W(LA), .D(PL)) lad(.clk(clk), .d(la_in), .q(lfa));\n logos_delay #(.W(LA), .D(PL)) lad_b(.clk(clk), .d(la_in_b), .q(lfa_b));\n".to_string(),
1359 ),
1360 (format!("{last_lfa}\n"), format!("{last_lfa}\n{lf_b}")),
1361 (sums.to_string(), format!("{sums}{sums_b}")),
1362 (" wire [31:0] value1, value2;\n".to_string(), format!("{lane_in} wire [31:0] value1, value2;\n")),
1363 (lanes.to_string(), format!("{lanes}{}", suffixed(lanes, &lane_names, "_b"))),
1364 (accs.to_string(), format!("{accs}{}", suffixed(accs, &acc_names, "_b"))),
1365 if set.lanes > 1 {
1366 (
1367 " acc2[done] <= last ? 32'd0 : (oct ? chain2 : sum2);\n \
1368 if (last) begin o_valid <= 1'b1; o_val1 <= oct ? chain1 : sum1; o_val2 <= oct ? chain2 : sum2; end\n"
1369 .to_string(),
1370 " acc2[done] <= last ? 32'd0 : (oct ? chain2 : sum2);\n acc1_b[done] <= last ? 32'd0 : (oct ? chain1_b : sum1_b);\n \
1371 acc2_b[done] <= last ? 32'd0 : (oct ? chain2_b : sum2_b);\n \
1372 if (last) begin\n o_valid <= 1'b1; o_val1 <= oct ? chain1 : sum1; o_val2 <= oct ? chain2 : sum2;\n \
1373 o_val1_b <= oct ? chain1_b : sum1_b; o_val2_b <= oct ? chain2_b : sum2_b;\n end\n"
1374 .to_string(),
1375 )
1376 } else {
1377 (
1378 " acc2[done] <= last ? 32'd0 : sum2;\n if (last) begin o_valid <= 1'b1; o_val1 <= sum1; o_val2 <= sum2; end\n".to_string(),
1379 " acc2[done] <= last ? 32'd0 : sum2;\n acc1_b[done] <= last ? 32'd0 : sum1_b;\n acc2_b[done] <= last ? 32'd0 : sum2_b;\n \
1380 if (last) begin o_valid <= 1'b1; o_val1 <= sum1; o_val2 <= sum2; o_val1_b <= sum1_b; o_val2_b <= sum2_b; end\n"
1381 .to_string(),
1382 )
1383 },
1384 (
1385 "for (k = 0; k < SUMS; k = k + 1) begin acc1[k] <= 32'd0; acc2[k] <= 32'd0; end".to_string(),
1386 "for (k = 0; k < SUMS; k = k + 1) begin acc1[k] <= 32'd0; acc2[k] <= 32'd0; acc1_b[k] <= 32'd0; acc2_b[k] <= 32'd0; end".to_string(),
1387 ),
1388 ];
1389 if set.lanes > 1 {
1390 let SlotFields { p1h, p2h, p1l, p2l, sh, sl, me, .. } = slot_fields(set);
1391 let mut fields = String::from(" // Token-row b's octet lanes past the first: its next blocks' sums and scales out of its slot's read, the blocks' own\n // scales the pair's.\n");
1392 for j in 1..set.lanes {
1393 let lo = if wide {
1394 format!(" sb{j}_p1l_b <= o_rd_b[{p1l} + 48*o_k{j} +: 48]; sb{j}_p2l_b <= o_rd_b[{p2l} + 48*o_k{j} +: 48]; sb{j}_sl_b <= o_rd_b[{sl} + 24*o_k{j} +: 24];\n")
1395 } else {
1396 format!(" sb{j}_p1l_b <= 48'd0; sb{j}_p2l_b <= 48'd0; sb{j}_sl_b <= 24'd0;\n")
1397 };
1398 fields.push_str(&format!(
1399 " reg [47:0] sb{j}_p1h_b, sb{j}_p2h_b, sb{j}_p1l_b, sb{j}_p2l_b, sl{j}_p1h_b, sl{j}_p2h_b, sl{j}_p1l_b, sl{j}_p2l_b;\n \
1400 reg [23:0] sb{j}_sh_b, sb{j}_sl_b, sl{j}_sh_b, sl{j}_sl_b;\n reg [19:0] sb{j}_me_b, sl{j}_me_b;\n always @(posedge clk) begin\n \
1401 sb{j}_p1h_b <= o_rd_b[{p1h} + 48*o_k{j} +: 48]; sb{j}_p2h_b <= o_rd_b[{p2h} + 48*o_k{j} +: 48];\n \
1402 sb{j}_sh_b <= o_rd_b[{sh} + 24*o_k{j} +: 24]; sb{j}_me_b <= o_rd_b[{me} + 20*o_k{j} +: 20];\n{lo} end\n"
1403 ));
1404 }
1405 let chain = between(&core, " // The octet lanes past the first: lane j's value", &format!(" wire [31:0] chain2 = ch2_{};\n", set.lanes - 1));
1406 let mut names: Vec<String> = vec!["sum1".into(), "sum2".into(), "chain1".into(), "chain2".into()];
1407 for r in 1..=2 {
1408 for j in 0..set.lanes {
1409 for n in ["ch", "sv", "slim", "dv", "dk", "chain"] {
1410 names.push(format!("{n}{r}_{j}"));
1411 }
1412 }
1413 }
1414 for j in 1..set.lanes {
1415 for f in ["p1h", "p2h", "p1l", "p2l", "sh", "sl", "me"] {
1416 names.push(format!("sl{j}_{f}"));
1417 }
1418 }
1419 let names: Vec<&str> = names.iter().map(String::as_str).collect();
1420 let chain_b = suffixed(chain, &names, "_b").replacen(" // The octet lanes past the first: lane j's value", " // Token-row b's octet lanes past the first, as token-row a's: lane j's value", 1);
1421 edits.extend([
1422 (" // The lanes' inputs: the sums over the issue's passes".to_string(), format!("{fields} // The lanes' inputs: the sums over the issue's passes")),
1423 (chain.to_string(), format!("{chain}{chain_b}")),
1424 ]);
1425 }
1426 if set.octets {
1427 let SlotFields { p1h, p2h, p1l, p2l, sh, sl, me, .. } = slot_fields(set);
1428 let o_in: String = core.lines().filter(|l| l.starts_with(" assign o_in[")).map(|l| format!("{l}\n")).collect();
1429 let o_in_b = suffixed(&o_in, &["o_in", "t1_hi", "t2_hi", "t1_lo", "t2_lo", "ts_hi", "ts_lo", "f_e", "f_m", "f_ms1", "f_ms2", "f_ms3", "f_ms4", "f_ms5", "f_ms6", "f_ms7"], "_b");
1430 let read_b = if wide {
1431 format!(" ob_p1l_b <= o_rd_b[{p1l} + 48*o_k +: 48]; ob_p2l_b <= o_rd_b[{p2l} + 48*o_k +: 48]; ob_sl_b <= o_rd_b[{sl} + 24*o_k +: 24];\n")
1432 } else {
1433 String::new()
1434 };
1435 edits.extend([
1436 (" wire [OW-1:0] o_in;\n".to_string(), " wire [OW-1:0] o_in, o_in_b;\n".to_string()),
1437 (o_in.clone(), format!("{o_in}{o_in_b}")),
1438 (
1439 " `LOGOS_RAMSTYLE_ASYNC((2 << SW)) reg [OW-1:0] obuf [0:(2 << SW)-1];\n".to_string(),
1440 " `LOGOS_RAMSTYLE_ASYNC((2 << SW)) reg [OW-1:0] obuf [0:(2 << SW)-1];\n `LOGOS_RAMSTYLE_ASYNC((2 << SW)) reg [OW-1:0] obuf_b [0:(2 << SW)-1];\n".to_string(),
1441 ),
1442 (
1443 " wire [OW-1:0] o_rd = obuf[eslot_d[PL-1]];\n".to_string(),
1444 " wire [OW-1:0] o_rd = obuf[eslot_d[PL-1]], o_rd_b = obuf_b[eslot_d[PL-1]];\n".to_string(),
1445 ),
1446 (
1447 " reg [19:0] ob_me;\n".to_string(),
1448 " reg [19:0] ob_me;\n reg [47:0] ob_p1h_b, ob_p2h_b, ob_p1l_b, ob_p2l_b;\n reg [23:0] ob_sh_b, ob_sl_b;\n reg [19:0] ob_me_b;\n".to_string(),
1449 ),
1450 (
1451 " if (cap_d[PL]) obuf[wslot_d[PL]] <= o_in;\n".to_string(),
1452 " if (cap_d[PL]) begin obuf[wslot_d[PL]] <= o_in; obuf_b[wslot_d[PL]] <= o_in_b; end\n".to_string(),
1453 ),
1454 (
1455 format!("ob_me <= o_rd[{me} + 20*o_k +: 20];\n"),
1456 format!(
1457 "ob_me <= o_rd[{me} + 20*o_k +: 20];\n ob_p1h_b <= o_rd_b[{p1h} + 48*o_k +: 48]; ob_p2h_b <= o_rd_b[{p2h} + 48*o_k +: 48];\n \
1458 ob_sh_b <= o_rd_b[{sh} + 24*o_k +: 24]; ob_me_b <= o_rd_b[{me} + 20*o_k +: 20];\n{read_b}"
1459 ),
1460 ),
1461 ]);
1462 }
1463 if set.pe_pairs {
1464 edits.push(("(input clk, input rst, input clear".to_string(), "(input clk, input clk2, input rst, input clear".to_string()));
1465 }
1466 let pairs_note = "// With token pairs (ColumnSet::token_pairs) each issue runs the beat held against two token-rows, a and b (the `_b`\n\
1467 // signals): a word, processing elements, lane fields, sums over the passes, lanes and sums of each, the beat's fields\n\
1468 // and the issue's flags shared, a sum's place a token pair's.\n";
1469 let mut core = replaced(&core, &edits.iter().map(|(a, b)| (a.as_str(), b.as_str())).collect::<Vec<_>>());
1470 core.insert_str(0, pairs_note);
1471 out.replace_range(a..b, &core);
1472
1473 let (a, b) = span(&out, &format!("module logos_column_array_{name} "));
1474 let array = out[a..b].to_string();
1475 let st_full = between(&array, &format!(" wire [{}:0] act_st;\n", cw - 1), &format!(" assign act_sts[ch*{cw} +: {cw}] = act_st;\n"));
1476 let st_new = if set.pe_pairs {
1478 let at = st_full.find(".codes(").expect("the stagger's codes") + ".codes(".len();
1479 let codes = &st_full[at..at + st_full[at..].find("), .st(act_st));").expect("the stagger's codes' end")];
1480 format!(
1481 " // The channel's words for its cores' elements in clk2, unstaggered.\n wire [{c}:0] act_st = {codes};\n \
1482 assign act_sts[ch*{cw} +: {cw}] = act_st;\n",
1483 c = cw - 1
1484 )
1485 } else {
1486 st_full.to_string()
1487 };
1488 let wm = between(&array, "wd <= act_wd; wm <= ", "; ra <= ra_n[AA-1:0]; end");
1489 let wm_expr = &wm["wd <= act_wd; wm <= ".len()..wm.len() - "; ra <= ra_n[AA-1:0]; end".len()];
1490 let wb = aw / 8;
1491 let mut edits: Vec<(String, String)> = vec![
1492 (
1493 "output [CH*N*32-1:0] o_val2, output reg [15:0] o_stripe,".to_string(),
1494 "output [CH*N*32-1:0] o_val2,\n output [CH*N*32-1:0] o_val1_b, output [CH*N*32-1:0] o_val2_b, output reg [15:0] o_stripe,".to_string(),
1495 ),
1496 (" localparam TW = (T_MAX > 1) ? $clog2(T_MAX) : 1;\n".to_string(), " localparam TW = (T_MAX > 2) ? $clog2(T_MAX) - 1 : 1;\n".to_string()),
1497 (" wire [TW-1:0] at = t[TW-1:0];\n".to_string(), " wire [TW-1:0] at = t[TW:1];\n".to_string()),
1498 ("t_up <= t_n + 8'd1;".to_string(), "t_up <= t_n + 8'd2;".to_string()),
1499 (" wire [31:0] ra_n = ({24'd0, t_n} << NB_L) | ({16'd0, hb_n} & (NB_P - 1));\n".to_string(), " wire [31:0] ra_n = ({24'd0, t_n >> 1} << NB_L) | ({16'd0, hb_n} & (NB_P - 1));\n".to_string()),
1500 (" localparam AN = T_MAX * NB_P,".to_string(), " localparam AN = ((T_MAX + 1) / 2) * NB_P,".to_string()),
1501 (" wire [31:0] act_wa = ({24'd0, act_t} << NB_L) | (act_b & (NB_P - 1));\n".to_string(), " wire [31:0] act_wa = ({24'd0, act_t >> 1} << NB_L) | (act_b & (NB_P - 1));\n".to_string()),
1502 (
1503 format!(" reg [{}:0] wd;\n reg [{}:0] wm;\n", aw - 1, wb - 1),
1504 format!(" reg [{}:0] wd;\n reg [{}:0] wm;\n", 2 * aw - 1, 2 * wb - 1),
1505 ),
1506 (
1507 format!("wd <= act_wd; wm <= {wm_expr}; ra <= ra_n[AA-1:0]; end"),
1508 format!("wd <= {{act_wd, act_wd}}; wm <= act_t[0] ? {{{wm_expr}, {{{wb}{{1'b0}}}}}} : {{{{{wb}{{1'b0}}}}, {wm_expr}}}; ra <= ra_n[AA-1:0]; end"),
1509 ),
1510 (
1511 format!(" wire [{}:0] act_word;\n {} #(.D(AN), .A(AA)) act_ram(.clk(clk), .ra(ra), .rd(act_word),", aw - 1, super::ram::bram_name(aw)),
1512 format!(
1513 " wire [{p}:0] act_pair;\n {} #(.D(AN), .A(AA)) act_ram(.clk(clk), .ra(ra), .rd(act_pair),",
1514 super::ram::bram_name(2 * aw),
1515 p = 2 * aw - 1
1516 ),
1517 ),
1518 (
1519 format!(" assign act_words[ch*{aw} +: {aw}] = act_word;\n"),
1520 format!(
1521 " wire [{t}:0] act_word = act_pair[{t}:0], act_word_b = act_pair[{}:{aw}];\n assign act_words[ch*{aw} +: {aw}] = act_word;\n \
1522 assign act_words_b[ch*{aw} +: {aw}] = act_word_b;\n",
1523 2 * aw - 1,
1524 t = aw - 1
1525 ),
1526 ),
1527 (st_full.to_string(), format!("{st_new}{}", suffixed(&st_new, &["act_st", "st", "act_word", "act_sts"], "_b"))),
1528 (
1529 format!(" wire [CH*{cw}-1:0] act_sts;\n"),
1530 format!(" wire [CH*{cw}-1:0] act_sts, act_sts_b;\n wire [CH*{aw}-1:0] act_words_b;\n"),
1531 ),
1532 ("#(.SUMS(T_MAX), .SW(TW),".to_string(), "#(.SUMS((T_MAX + 1) / 2), .SW(TW),".to_string()),
1533 (
1534 format!(".act_word(act_words[(g/N)*{aw} +: {aw}]), "),
1535 format!(".act_word(act_words[(g/N)*{aw} +: {aw}]), .act_word_b(act_words_b[(g/N)*{aw} +: {aw}]),\n "),
1536 ),
1537 (
1538 format!(".act_st(act_sts[(g/N)*{cw} +: {cw}]), "),
1539 format!(".act_st(act_sts[(g/N)*{cw} +: {cw}]), .act_st_b(act_sts_b[(g/N)*{cw} +: {cw}]), "),
1540 ),
1541 (
1542 ".o_val1(o_val1[g*32 +: 32]), .o_val2(o_val2[g*32 +: 32]));".to_string(),
1543 ".o_val1(o_val1[g*32 +: 32]), .o_val2(o_val2[g*32 +: 32]),\n .o_val1_b(o_val1_b[g*32 +: 32]), .o_val2_b(o_val2_b[g*32 +: 32]));".to_string(),
1544 ),
1545 (
1546 " if (issue) begin t <= t_last ? 8'd0 : t + 8'd1; t_last <= t_last ? tokens == 8'd1 : t + 8'd2 == tokens; end\n".to_string(),
1547 " if (issue) begin t <= t_last ? 8'd0 : t + 8'd2; t_last <= t_last ? tokens <= 8'd2 : t + 8'd4 >= tokens; end\n".to_string(),
1548 ),
1549 ("t_last <= tokens == 8'd1; ready <=".to_string(), "t_last <= tokens <= 8'd2; ready <=".to_string()),
1550 ];
1551 if set.octets {
1552 edits.extend([
1553 (" wire [TW-1:0] eat = et[TW-1:0];\n".to_string(), " wire [TW-1:0] eat = et[TW:1];\n".to_string()),
1554 ("&& et + 8'd1 == tokens;\n".to_string(), "&& et + 8'd2 >= tokens;\n".to_string()),
1555 (
1556 format!(" if (et + 8'd1 == tokens) begin et <= 8'd0; eb <= eb + 3'd{l}; end else et <= et + 8'd1;\n", l = set.lanes),
1557 format!(" if (et + 8'd2 >= tokens) begin et <= 8'd0; eb <= eb + 3'd{l}; end else et <= et + 8'd2;\n", l = set.lanes),
1558 ),
1559 ]);
1560 }
1561 if set.pe_pairs {
1562 edits.push(("u(.clk(clk), .rst(rst), .clear(start && !busy),".to_string(), "u(.clk(clk), .clk2(clk2), .rst(rst), .clear(start && !busy),".to_string()));
1563 }
1564 let note = "// With token pairs (ColumnSet::token_pairs) an issue is a token pair's, t even, and each core takes the pair's two words\n\
1565 // from its channel's copy of the activations, a word a pair (the first token-row's in its low half), with each half's\n\
1566 // excess codes staggered for its elements; a sum's place is the pair's, and each core's sums emerge a pair a clock,\n\
1567 // the second token-row's (o_val1_b, o_val2_b) the one after o_t, a job's only when it has it.\n";
1568 let mut array = replaced(&array, &edits.iter().map(|(a, b)| (a.as_str(), b.as_str())).collect::<Vec<_>>());
1569 array.insert_str(0, note);
1570 out.replace_range(a..b, &array);
1571 out
1572}
1573
1574fn replaced(text: &str, edits: &[(&str, &str)]) -> String {
1576 let mut out = text.to_string();
1577 for (from, to) in edits {
1578 assert_eq!(out.matches(from).count(), 1, "`{from}`");
1579 out = out.replace(from, to);
1580 }
1581 out
1582}
1583
1584struct CoreOctets {
1587 ports: String,
1588 body: String,
1589 lanes: String,
1590 lanes_end: String,
1591 from: &'static str,
1592}
1593
1594struct SlotFields {
1598 p1h: usize,
1599 p2h: usize,
1600 p1l: usize,
1601 p2l: usize,
1602 sh: usize,
1603 sl: usize,
1604 q1: usize,
1605 q2: usize,
1606 sc1: usize,
1607 sc2: usize,
1608 me: usize,
1609 ow: usize,
1610}
1611
1612fn slot_fields(set: &ColumnSet) -> SlotFields {
1613 let u = set.elems() / UNIT;
1614 let words = if set.words == Words::Two { 2 } else { 1 };
1615 let p1h = 0;
1616 let p2h = p1h + 24 * u;
1617 let p1l = p2h + 24 * u;
1618 let p2l = p1l + 24 * u * (words - 1);
1619 let sh = p2l + 24 * u * (words - 1);
1620 let sl = sh + 12 * u;
1621 let q1 = sl + 12 * u * (words - 1);
1622 let q2 = q1 + 12 * u;
1623 let sc1 = q2 + 12 * u;
1624 let sc2 = sc1 + 128;
1625 let me = sc2 + 128;
1626 SlotFields { p1h, p2h, p1l, p2l, sh, sl, q1, q2, sc1, sc2, me, ow: me + 160 }
1627}
1628
1629fn core_octets(set: &ColumnSet) -> CoreOctets {
1630 if !set.octets {
1631 return CoreOctets { ports: String::new(), body: String::new(), lanes: String::new(), lanes_end: String::new(), from: "" };
1632 }
1633 let u = set.elems() / UNIT;
1634 let wide = set.words == Words::Two;
1635 let SlotFields { p1h, p2h, p1l, p2l, sh, sl, q1, q2, sc1, sc2, me, ow } = slot_fields(set);
1636 let mut body = format!(
1637 " // An octet job (oct): each issue's sums over its passes, its blocks' eight scales and its activation's eight kept in\n \
1638 // its slot (wslot) the clock after its last pass (capture); a slot's block (eslot, eblk) into the lanes the clock\n \
1639 // after it is asked, its two units as a block's first two, the rest zero, with its scale and its activation's.\n \
1640 localparam OW = {ow};\n reg [PL:0] cap_d;\n reg [SW:0] wslot_d [0:PL];\n integer cw;\n \
1641 always @(posedge clk) begin\n cap_d <= rst ? {{(PL+1){{1'b0}}}} : {{cap_d[PL-1:0], capture}};\n wslot_d[0] <= wslot;\n \
1642 for (cw = 1; cw <= PL; cw = cw + 1) wslot_d[cw] <= wslot_d[cw-1];\n end\n wire [OW-1:0] o_in;\n \
1643 assign o_in[{p1h} +: {w24}] = t1_hi;\n assign o_in[{p2h} +: {w24}] = t2_hi;\n",
1644 w24 = 24 * u
1645 );
1646 if wide {
1647 body.push_str(&format!(" assign o_in[{p1l} +: {w24}] = t1_lo;\n assign o_in[{p2l} +: {w24}] = t2_lo;\n", w24 = 24 * u));
1648 }
1649 body.push_str(&format!(" assign o_in[{sh} +: {w12}] = ts_hi;\n", w12 = 12 * u));
1650 if wide {
1651 body.push_str(&format!(" assign o_in[{sl} +: {w12}] = ts_lo;\n", w12 = 12 * u));
1652 }
1653 body.push_str(&format!(
1654 " assign o_in[{q1} +: {w12}] = f_q1;\n assign o_in[{q2} +: {w12}] = f_q2;\n \
1655 assign o_in[{sc1} +: 128] = {{f_s71, f_a1}};\n assign o_in[{sc2} +: 128] = {{f_s72, f_a2}};\n \
1656 assign o_in[{me} +: 20] = {{f_e, f_m}};\n",
1657 w12 = 12 * u,
1658 ));
1659 for k in 1..8 {
1660 body.push_str(&format!(" assign o_in[{} +: 20] = f_ms{k};\n", me + 20 * k));
1661 }
1662 let wide_sel = if wide {
1663 format!(" ob_p1l <= o_rd[{p1l} + 48*o_k +: 48]; ob_p2l <= o_rd[{p2l} + 48*o_k +: 48]; ob_sl <= o_rd[{sl} + 24*o_k +: 24];\n")
1664 } else {
1665 String::new()
1666 };
1667 body.push_str(&format!(
1668 " // A block asked for leaves the slots PL clocks on, as an issue's value meets the lanes PL clocks after its pass,\n \
1669 // its fields taken out of its slot as the slot is read.\n \
1670 `LOGOS_RAMSTYLE_ASYNC((2 << SW)) reg [OW-1:0] obuf [0:(2 << SW)-1];\n reg [SW:0] eslot_d [0:PL-1];\n reg [2:0] eblk_d [0:PL-1];\n \
1671 wire [OW-1:0] o_rd = obuf[eslot_d[PL-1]];\n wire [2:0] o_k = eblk_d[PL-1];\n \
1672 reg [47:0] ob_p1h, ob_p2h, ob_p1l, ob_p2l;\n reg [23:0] ob_sh, ob_sl, ob_q1, ob_q2;\n reg [15:0] ob_a1, ob_a2;\n \
1673 reg [19:0] ob_me;\n integer ce;\n always @(posedge clk) begin\n \
1674 if (cap_d[PL]) obuf[wslot_d[PL]] <= o_in;\n eslot_d[0] <= eslot; eblk_d[0] <= eblk;\n \
1675 for (ce = 1; ce < PL; ce = ce + 1) begin eslot_d[ce] <= eslot_d[ce-1]; eblk_d[ce] <= eblk_d[ce-1]; end\n \
1676 ob_p1h <= o_rd[{p1h} + 48*o_k +: 48]; ob_p2h <= o_rd[{p2h} + 48*o_k +: 48];\n \
1677 ob_sh <= o_rd[{sh} + 24*o_k +: 24]; ob_q1 <= o_rd[{q1} + 24*o_k +: 24]; ob_q2 <= o_rd[{q2} + 24*o_k +: 24];\n \
1678 ob_a1 <= o_rd[{sc1} + 16*o_k +: 16]; ob_a2 <= o_rd[{sc2} + 16*o_k +: 16]; ob_me <= o_rd[{me} + 20*o_k +: 20];\n{wide_sel} end\n",
1679 ));
1680 let mut takes = String::new();
1681 if set.lanes > 1 {
1682 let mut fields = String::from(
1683 " // The octet lanes past the first: a token-row's next blocks of its group, o_k + 1 on, out of the same slot's read.\n",
1684 );
1685 for j in 1..set.lanes {
1686 writeln!(fields, " reg [47:0] sb{j}_p1h, sb{j}_p2h, sb{j}_p1l, sb{j}_p2l;\n reg [23:0] sb{j}_sh, sb{j}_sl, sb{j}_q1, sb{j}_q2;\n reg [15:0] sb{j}_a1, sb{j}_a2;\n reg [19:0] sb{j}_me;").unwrap();
1687 writeln!(fields, " reg [47:0] sl{j}_p1h, sl{j}_p2h, sl{j}_p1l, sl{j}_p2l;\n reg [23:0] sl{j}_sh, sl{j}_sl, sl{j}_q1, sl{j}_q2;\n reg [15:0] sl{j}_a1, sl{j}_a2;\n reg [19:0] sl{j}_me;").unwrap();
1688 writeln!(fields, " reg [2:0] o_k{j};\n always @(posedge clk) o_k{j} <= eblk_d[PL-2] + 3'd{j};").unwrap();
1689 let lo = if wide {
1690 format!(
1691 " sb{j}_p1l <= o_rd[{p1l} + 48*o_k{j} +: 48]; sb{j}_p2l <= o_rd[{p2l} + 48*o_k{j} +: 48]; sb{j}_sl <= o_rd[{sl} + 24*o_k{j} +: 24];\n"
1692 )
1693 } else {
1694 format!(" sb{j}_p1l <= 48'd0; sb{j}_p2l <= 48'd0; sb{j}_sl <= 24'd0;\n")
1695 };
1696 writeln!(
1697 fields,
1698 " always @(posedge clk) begin\n sb{j}_p1h <= o_rd[{p1h} + 48*o_k{j} +: 48]; sb{j}_p2h <= o_rd[{p2h} + 48*o_k{j} +: 48];\n \
1699 sb{j}_sh <= o_rd[{sh} + 24*o_k{j} +: 24]; sb{j}_q1 <= o_rd[{q1} + 24*o_k{j} +: 24]; sb{j}_q2 <= o_rd[{q2} + 24*o_k{j} +: 24];\n \
1700 sb{j}_a1 <= o_rd[{sc1} + 16*o_k{j} +: 16]; sb{j}_a2 <= o_rd[{sc2} + 16*o_k{j} +: 16]; sb{j}_me <= o_rd[{me} + 20*o_k{j} +: 20];\n{lo} end"
1701 )
1702 .unwrap();
1703 writeln!(
1704 takes,
1705 " sl{j}_p1h <= sb{j}_p1h; sl{j}_p2h <= sb{j}_p2h; sl{j}_p1l <= sb{j}_p1l; sl{j}_p2l <= sb{j}_p2l; sl{j}_sh <= sb{j}_sh; sl{j}_sl <= sb{j}_sl;\n \
1706 sl{j}_q1 <= sb{j}_q1; sl{j}_q2 <= sb{j}_q2; sl{j}_a1 <= sb{j}_a1; sl{j}_a2 <= sb{j}_a2; sl{j}_me <= sb{j}_me;"
1707 )
1708 .unwrap();
1709 }
1710 body.push_str(&fields);
1711 }
1712 let zero = |w: usize| if w > 0 { format!("{{{w}{{1'b0}}}}, ") } else { String::new() };
1713 let pad24 = zero(24 * u - 48);
1714 let pad12 = zero(12 * u - 24);
1715 let mut lanes = format!(
1716 " if (oct) begin\n \
1717 p1_hi <= {{{pad24}ob_p1h}};\n p2_hi <= {{{pad24}ob_p2h}};\n"
1718 );
1719 if wide {
1720 lanes.push_str(&format!(" p1_lo <= {{{pad24}ob_p1l}};\n p2_lo <= {{{pad24}ob_p2l}};\n"));
1721 lanes.push_str(&format!(" s_lo <= {{{pad12}ob_sl}};\n"));
1722 } else {
1723 lanes.push_str(" p1_lo <= 0; p2_lo <= 0; s_lo <= 0;\n");
1724 }
1725 lanes.push_str(&format!(
1726 " s_hi <= {{{pad12}ob_sh}};\n q1 <= {{{pad12}ob_q1}};\n q2 <= {{{pad12}ob_q2}};\n \
1727 lc1 <= {{{pad8}16'h0101}}; ln1 <= 0; lc2 <= {{{pad8}16'h0101}}; ln2 <= 0;\n \
1728 la1 <= ob_a1; lb1 <= 16'd0; la2 <= ob_a2; lb2 <= 16'd0;\n \
1729 lm <= ob_me[7:0];\n le <= ob_me[19:8];\n{takes} end else begin\n",
1730 pad8 = zero(8 * u - 16)
1731 ));
1732 CoreOctets {
1733 ports: ", input oct, input capture, input [SW:0] wslot, input [SW:0] eslot, input [2:0] eblk".to_string(),
1734 body,
1735 lanes,
1736 lanes_end: " end\n".to_string(),
1737 from: ", or for an octet job from the slot its array asks for",
1738 }
1739}
1740
1741fn array_octets(set: &ColumnSet) -> Vec<(&'static str, String)> {
1744 let all = format!("{{{}{{1'b1}}}}", set.act_bits() / 8);
1745 if !set.octets {
1746 return vec![
1747 ("arr_act_s", String::new()),
1748 ("arr_octets", String::new()),
1749 ("pe_ok", "!stall".to_string()),
1750 ("arr_busy", String::new()),
1751 ("act_wm", all),
1752 ("core_issue", "issue && need[g]".to_string()),
1753 ("core_octet_conn", String::new()),
1754 ("tags_in", " live[0] <= issue; tag[0] <= at; ts[0] <= hs; tt[0] <= t; last[0] <= step_end;".to_string()),
1755 ("arr_octet_step", String::new()),
1756 ("arr_octet_reset", String::new()),
1757 ];
1758 }
1759 let place = set.formats.iter().position(|&t| set.octet(t)).expect("a set with octets takes Q8_0");
1760 let oct = if set.formats.len() > 1 { format!("fmt_q == 4'd{place}") } else { "1'b1".to_string() };
1761 let (e, u, w) = (set.elems(), set.elems() / UNIT, set.word_count());
1764 let (a, ab) = (set.act_fields(), set.act_bits() / 8);
1765 let mask = |k: usize| -> String {
1766 let mut bytes = vec![false; ab];
1767 for word in 0..w {
1768 for b in word * e + 32 * k..word * e + 32 * k + 32 {
1769 bytes[b] = true;
1770 }
1771 let sums = (a.sums + word * 12 * u) / 8 + 3 * k;
1772 bytes[sums..sums + 3].iter_mut().for_each(|b| *b = true);
1773 }
1774 let scale = (a.m + 24 * k) / 8;
1775 bytes[scale..(scale + 3).min(ab)].iter_mut().for_each(|b| *b = true);
1776 let bits: String = bytes.iter().rev().map(|&b| if b { '1' } else { '0' }).collect();
1777 format!("{ab}'b{bits}")
1778 };
1779 let mut masks = String::new();
1780 for k in 0..8 {
1781 writeln!(masks, " 3'd{k}: slot_wm = {};", mask(k)).unwrap();
1782 }
1783 let octets = format!(
1784 r#" // An octet job (oct): a step's issues fill one of two buffers, a slot a token-row, each core keeping its own sums; a
1785 // full buffer's blocks leave for the lanes a block a clock — every token-row's block 0, then every token-row's block
1786 // 1, ... — each the clock no add holds its sum, as an issue of the step's would; a step issues into a buffer once its
1787 // blocks have all left, and a buffer's first block leaves three clocks after its last issue, its slots written. A
1788 // block's activations are written into its octet's word under the mask of its bytes.
1789 wire oct = {oct};
1790 reg pbuf, ebuf;
1791 reg [1:0] full;
1792 `LOGOS_RAMSTYLE_ASYNC(2) reg [1:0] settle [0:1];
1793 `LOGOS_RAMSTYLE_ASYNC(2) reg [15:0] bs [0:1];
1794 `LOGOS_RAMSTYLE_ASYNC(2) reg bl [0:1];
1795 `LOGOS_RAMSTYLE_ASYNC(2) reg [C-1:0] bneed [0:1];
1796 reg [2:0] eb;
1797 reg [7:0] et;
1798 wire [TW-1:0] eat = et[TW-1:0];
1799 reg e_stall;
1800 integer h2;
1801 always @* begin
1802 e_stall = 1'b0;
1803 for (h2 = 0; h2 < {window}; h2 = h2 + 1) if (live[h2] && tag[h2] == eat) e_stall = 1'b1;
1804 end
1805 wire emit = oct && full[ebuf] && settle[ebuf] == 2'd0 && !hold && !e_stall;
1806 wire e_end = emit && eb == 3'd{last_group} && et + 8'd1 == tokens;
1807 reg [{abt}:0] slot_wm;
1808 always @*
1809 case (act_s)
1810{masks} default: slot_wm = {{{ab}{{1'b0}}}};
1811 endcase
1812 wire [{abt}:0] act_wm = oct ? slot_wm : {all};
1813"#,
1814 abt = ab - 1,
1815 window = if set.lanes == 1 { "ADD".to_string() } else { format!("{} * ADD", set.lanes) },
1816 last_group = 8 - set.lanes,
1817 );
1818 vec![
1819 ("arr_act_s", ", input [2:0] act_s".to_string()),
1820 ("arr_octets", octets),
1821 ("pe_ok", "(oct ? !full[pbuf] : !stall)".to_string()),
1822 ("arr_busy", " || (|full)".to_string()),
1823 ("act_wm", "act_wm".to_string()),
1824 ("core_issue", "oct ? emit && bneed[ebuf][g] : issue && need[g]".to_string()),
1825 ("core_octet_conn", ", .oct(oct), .capture(oct && issue && need[g]), .wslot({pbuf, at}), .eslot({ebuf, eat}), .eblk(eb)".to_string()),
1826 (
1827 "tags_in",
1828 format!(" live[0] <= oct ? emit : issue;\n tag[0] <= oct ? eat : at;\n ts[0] <= oct ? bs[ebuf] : hs;\n \
1829 tt[0] <= oct ? et : t;\n last[0] <= oct ? bl[ebuf] && eb == 3'd{} : step_end;",
1830 8 - set.lanes
1831 ),
1832 ),
1833 (
1834 "arr_octet_step",
1835 format!(" if (settle[0] != 2'd0) settle[0] <= settle[0] - 2'd1;\n if (settle[1] != 2'd0) settle[1] <= settle[1] - 2'd1;\n \
1836 if (oct && fin) begin\n full[pbuf] <= 1'b1; settle[pbuf] <= 2'd3; bs[pbuf] <= hs; bl[pbuf] <= step_end; bneed[pbuf] <= need;\n \
1837 pbuf <= ~pbuf;\n end\n if (emit) begin\n if (et + 8'd1 == tokens) begin et <= 8'd0; eb <= eb + 3'd{}; end else et <= et + 8'd1;\n \
1838 if (e_end) begin full[ebuf] <= 1'b0; ebuf <= ~ebuf; end\n end\n",
1839 set.lanes
1840 ),
1841 ),
1842 ("arr_octet_reset", " pbuf <= 1'b0; ebuf <= 1'b0; full <= 2'b00; eb <= 3'd0; et <= 8'd0;".to_string()),
1843 ]
1844}
1845
1846const OCTET_DECODER: &str = r#"
1850module logos_dec_q8_0x8(input [2175:0] blk, output [2047:0] code, output [255:0] c, output [255:0] n, output [15:0] a,
1851 output [15:0] b, output [111:0] s7);
1852 genvar k;
1853 generate
1854 for (k = 0; k < 8; k = k + 1) begin : block
1855 wire [255:0] ck;
1856 wire [31:0] cc, nn;
1857 wire [15:0] ak, bk;
1858 logos_dec_q8_0 d(.blk(blk[272*k +: 272]), .code(ck), .c(cc), .n(nn), .a(ak), .b(bk));
1859 assign code[256*k +: 256] = ck;
1860 assign c[32*k +: 32] = cc;
1861 assign n[32*k +: 32] = nn;
1862 if (k == 0) begin : first
1863 assign a = ak;
1864 end else begin : later
1865 assign s7[16*(k-1) +: 16] = ak;
1866 end
1867 end
1868 endgenerate
1869 assign b = 16'd0;
1870endmodule
1871"#;
1872
1873#[derive(Clone, Copy)]
1875struct Coded {
1876 dec: &'static Decoder,
1877 octet: bool,
1878 slot: usize,
1879}
1880
1881impl Coded {
1882 fn len(&self) -> usize {
1884 if self.octet { 8 * self.dec.ty.block_len() } else { self.dec.ty.block_len() }
1885 }
1886
1887 fn bytes(&self) -> usize {
1889 if self.octet { 8 * self.dec.ty.block_bytes() } else { self.dec.ty.block_bytes() }
1890 }
1891
1892 fn slot_bytes(&self) -> usize {
1894 self.bytes().next_multiple_of(self.slot)
1895 }
1896
1897 fn module(&self) -> String {
1899 if self.octet { format!("{}x8", decoder_name(self.dec.ty)) } else { decoder_name(self.dec.ty) }
1900 }
1901
1902 fn name(&self) -> String {
1904 if self.octet { format!("{}x8", self.dec.ty.name()) } else { self.dec.ty.name().to_string() }
1905 }
1906}
1907
1908fn decode(decs: &[Coded], elems: usize) -> String {
1911 let (code_top, unit_top) = (8 * elems - 1, 16 * (elems / UNIT) - 1);
1912 let s7 = |d: &Coded, k: &str| if d.octet { format!(", .s7({k})") } else { String::new() };
1913 let octets = decs.iter().any(|d| d.octet);
1914 if let [d] = decs {
1915 let dec = d.module();
1916 return format!(
1917 " wire [{code_top}:0] code1_d, code2_d;\n wire [{unit_top}:0] c1_d, n1_d, c2_d, n2_d;\n wire [15:0] a1_d, b1_d, a2_d, b2_d;\n{} \
1918 {dec} d1(.blk(blk1), .code(code1_d), .c(c1_d), .n(n1_d), .a(a1_d), .b(b1_d){});\n \
1919 {dec} d2(.blk(blk2), .code(code2_d), .c(c2_d), .n(n2_d), .a(a2_d), .b(b2_d){});\n",
1920 if octets { " wire [111:0] s71_d, s72_d;\n" } else { "" },
1921 s7(d, "s71_d"),
1922 s7(d, "s72_d"),
1923 );
1924 }
1925 let mut v = String::new();
1926 for (i, d) in decs.iter().enumerate() {
1927 let (len, bits) = (d.len(), 8 * d.bytes());
1928 let (ct, ut) = (8 * len - 1, 16 * (len / UNIT) - 1);
1929 let dec = d.module();
1930 writeln!(v, " wire [{ct}:0] code1_{i}, code2_{i};\n wire [{ut}:0] c1_{i}, n1_{i}, c2_{i}, n2_{i};\n wire [15:0] a1_{i}, b1_{i}, a2_{i}, b2_{i};").unwrap();
1931 if d.octet {
1932 writeln!(v, " wire [111:0] s71_{i}, s72_{i};").unwrap();
1933 }
1934 writeln!(v, " {dec} d1_{i}(.blk(blk1[{}:0]), .code(code1_{i}), .c(c1_{i}), .n(n1_{i}), .a(a1_{i}), .b(b1_{i}){});", bits - 1, s7(d, &format!("s71_{i}"))).unwrap();
1935 writeln!(v, " {dec} d2_{i}(.blk(blk2[{}:0]), .code(code2_{i}), .c(c2_{i}), .n(n2_{i}), .a(a2_{i}), .b(b2_{i}){});", bits - 1, s7(d, &format!("s72_{i}"))).unwrap();
1936 }
1937 writeln!(v, " reg [{code_top}:0] code1_d, code2_d;\n reg [{unit_top}:0] c1_d, n1_d, c2_d, n2_d;\n reg [15:0] a1_d, b1_d, a2_d, b2_d;").unwrap();
1938 if octets {
1939 v.push_str(" reg [111:0] s71_d, s72_d;\n");
1940 }
1941 v.push_str(" always @* begin\n code1_d = 0; code2_d = 0; c1_d = 0; n1_d = 0; c2_d = 0; n2_d = 0; a1_d = 0; b1_d = 0; a2_d = 0; b2_d = 0;\n");
1942 if octets {
1943 v.push_str(" s71_d = 0; s72_d = 0;\n");
1944 }
1945 v.push_str(" case (fmt)\n");
1946 for (i, d) in decs.iter().enumerate() {
1947 let scales = if d.octet { format!(" s71_d = s71_{i}; s72_d = s72_{i};") } else { String::new() };
1948 writeln!(
1949 v,
1950 " 4'd{i}: begin code1_d = code1_{i}; code2_d = code2_{i}; c1_d = c1_{i}; n1_d = n1_{i}; c2_d = c2_{i}; n2_d = n2_{i}; \
1951 a1_d = a1_{i}; b1_d = b1_{i}; a2_d = a2_{i}; b2_d = b2_{i};{scales} end"
1952 )
1953 .unwrap();
1954 }
1955 v.push_str(" default: ;\n endcase\n end\n");
1956 v
1957}
1958
1959fn zsel(decs: &[Coded]) -> String {
1961 if let [d] = decs {
1962 return format!(" wire [7:0] z = 8'd{};\n", d.dec.offset);
1963 }
1964 let mut v = String::from(" reg [7:0] z;\n always @*\n case (fmt_q)\n");
1965 for (i, d) in decs.iter().enumerate() {
1966 writeln!(v, " 4'd{i}: z = 8'd{};", d.dec.offset).unwrap();
1967 }
1968 writeln!(v, " default: z = 8'd{};\n endcase", decs[0].dec.offset).unwrap();
1969 v
1970}
1971
1972fn feed_block_bytes(decs: &[Coded]) -> String {
1975 if let [d] = decs {
1976 return format!(" localparam [15:0] BB = 16'd{b};\n localparam GB = {b} + 64;\n", b = d.slot_bytes());
1977 }
1978 let widest = decs.iter().map(Coded::slot_bytes).max().unwrap();
1979 let mut v = format!(" localparam GB = {widest} + 64;\n reg [15:0] BB;\n always @(posedge clk)\n case (fmt_q)\n");
1982 for (i, d) in decs.iter().enumerate() {
1983 writeln!(v, " 4'd{i}: BB <= 16'd{};", d.slot_bytes()).unwrap();
1984 }
1985 writeln!(v, " default: BB <= 16'd{};\n endcase", decs[0].slot_bytes()).unwrap();
1986 v
1987}
1988
1989fn feed_drop(decs: &[Coded]) -> String {
1991 if let [_] = decs {
1992 return "gather >> (8 * BB)".to_string();
1993 }
1994 let mut v = String::new();
1995 for (i, d) in decs.iter().enumerate().take(decs.len() - 1) {
1996 write!(v, "(fmt_q == 4'd{i}) ? gather >> {} : ", 8 * d.slot_bytes()).unwrap();
1997 }
1998 write!(v, "gather >> {}", 8 * decs[decs.len() - 1].slot_bytes()).unwrap();
1999 v
2000}
2001
2002fn digits(decs: &[Coded]) -> String {
2004 let of = |d: &Coded| if d.dec.code_bits > 4 { 2 } else { 1 };
2005 if let [d] = decs {
2006 return format!(" wire [1:0] digits = 2'd{};\n", of(d));
2007 }
2008 let mut v = String::from(" reg [1:0] digits;\n always @*\n case (fmt_q)\n");
2009 for (i, d) in decs.iter().enumerate() {
2010 writeln!(v, " 4'd{i}: digits = 2'd{};", of(d)).unwrap();
2011 }
2012 writeln!(v, " default: digits = 2'd{};\n endcase", of(&decs[0])).unwrap();
2013 v
2014}
2015
2016const ACT_STAGGER: &str = r#"
2018// A pass's excess codes for logos_pe8_p: code e of each eight given (e mod 8) / 2 clocks after it comes in, the element's
2019// block (e mod 8) / 2 taking its operands that many clocks after its block 0 does.
2020module logos_column_act_stagger #(parameter ELEMS = 256) (input clk, input [8*ELEMS-1:0] codes, output [8*ELEMS-1:0] st);
2021 reg [8*ELEMS-1:0] d1, d2, d3;
2022 always @(posedge clk) begin
2023 d1 <= codes;
2024 d2 <= d1;
2025 d3 <= d2;
2026 end
2027 genvar e;
2028 generate
2029 for (e = 0; e < ELEMS; e = e + 1) begin : code
2030 localparam K = (e % 8) / 2;
2031 assign st[8*e +: 8] = K == 0 ? codes[8*e +: 8] : K == 1 ? d1[8*e +: 8] : K == 2 ? d2[8*e +: 8] : d3[8*e +: 8];
2032 end
2033 endgenerate
2034endmodule
2035"#;
2036
2037const MATVEC_PASS: &str = r#"
2041// A pass of two rows' codes against one activation word's excess codes `w`, for the user clock: digit `digit` of every
2042// code, given with the codes a clock before the word and registered, through the systolic elements; each 16-element
2043// unit's partial sums for both rows, row one's from the low lanes and row two's from the high lanes with its offset
2044// restored by the unit's sum `s` of the word's excess codes, @PE@ clocks after the word (`s` delayed beside the
2045// elements).
2046module logos_matvec_pass_p #(parameter UNITS = 16)
2047 (input clk, input [128*UNITS-1:0] code1, input [128*UNITS-1:0] code2, input [128*UNITS-1:0] w, input [12*UNITS-1:0] s,
2048 input digit, output [24*UNITS-1:0] p1, output [24*UNITS-1:0] p2);
2049 wire [12*UNITS-1:0] s_pe;
2050 logos_delay #(.W(12*UNITS), .D(@PE@)) sd(.clk(clk), .d(s), .q(s_pe));
2051 genvar i, u;
2052 generate
2053 for (u = 0; u < UNITS; u = u + 1) begin : unit
2054 wire [31:0] q1a, q2a, q1b, q2b;
2055 for (i = 0; i < 8; i = i + 1) begin : nib
2056 assign q1a[4*i +: 4] = digit ? code1[8*(16*u + i) + 4 +: 4] : code1[8*(16*u + i) +: 4];
2057 assign q2a[4*i +: 4] = digit ? code2[8*(16*u + i) + 4 +: 4] : code2[8*(16*u + i) +: 4];
2058 assign q1b[4*i +: 4] = digit ? code1[8*(16*u + 8 + i) + 4 +: 4] : code1[8*(16*u + 8 + i) +: 4];
2059 assign q2b[4*i +: 4] = digit ? code2[8*(16*u + 8 + i) + 4 +: 4] : code2[8*(16*u + 8 + i) +: 4];
2060 end
2061 reg [31:0] q1a_r, q2a_r, q1b_r, q2b_r;
2062 always @(posedge clk) begin q1a_r <= q1a; q2a_r <= q2a; q1b_r <= q1b; q2b_r <= q2b; end
2063 wire [14:0] lo_a, lo_b;
2064 wire signed [16:0] hi_a, hi_b;
2065 logos_pe8_p pa(.clk(clk), .a(w[8*16*u +: 64]), .q1(q1a_r), .q2(q2a_r), .lo(lo_a), .hi(hi_a));
2066 logos_pe8_p pb(.clk(clk), .a(w[8*(16*u + 8) +: 64]), .q1(q1b_r), .q2(q2b_r), .lo(lo_b), .hi(hi_b));
2067 wire [23:0] one = {9'd0, lo_a} + {9'd0, lo_b};
2068 wire signed [23:0] other = $signed(hi_a) + $signed(hi_b) + $signed({9'd0, s_pe[12*u +: 12], 3'd0});
2069 assign p1[24*u +: 24] = one;
2070 assign p2[24*u +: 24] = other;
2071 end
2072 endgenerate
2073endmodule
2074"#;
2075
2076pub fn matvec_pass_x2_verilog() -> String {
2079 MATVEC_PASS_X2.replace("@PE@", &PASS_LATENCY.to_string())
2080}
2081
2082const _: () = assert!(CHAIN == 4 && PASS_LATENCY == CHAIN + 1);
2085
2086const MATVEC_PASS_X2: &str = r#"
2087// Two cores' passes of logos_matvec_pass_p on one set of systolic elements in a clock of twice theirs (clk2, its rising
2088// edges on clk's and between them): the word the cores share (`w`, unstaggered) and core a's code digits taken at
2089// clk2's edge between clk's, core b's at clk's next edge, the elements' blocks staggered in clk2; each core's partials
2090// as logos_matvec_pass_p gives them, @PE@ clocks of clk after its word, the codes and their digit given a clock before it.
2091module logos_matvec_pass_x2 #(parameter UNITS = 16)
2092 (input clk, input clk2,
2093 input [128*UNITS-1:0] code1a, input [128*UNITS-1:0] code2a, input [128*UNITS-1:0] code1b, input [128*UNITS-1:0] code2b,
2094 input [128*UNITS-1:0] w, input [12*UNITS-1:0] s, input digit,
2095 output [24*UNITS-1:0] p1a, output [24*UNITS-1:0] p2a, output [24*UNITS-1:0] p1b, output [24*UNITS-1:0] p2b);
2096 wire [12*UNITS-1:0] s_pe;
2097 logos_delay #(.W(12*UNITS), .D(@PE@)) sd(.clk(clk), .d(s), .q(s_pe));
2098 // Core a's turn on clk2: the clock after its edge between clk's, a toggle of clk's seen changed.
2099 reg tf = 1'b0, tf2 = 1'b0, a_turn = 1'b0;
2100 always @(posedge clk) tf <= ~tf;
2101 always @(posedge clk2) begin tf2 <= tf; a_turn <= tf != tf2; end
2102 // The word, held across both turns of its clock.
2103 reg [128*UNITS-1:0] w0;
2104 always @(posedge clk2) w0 <= w;
2105 genvar i, u;
2106 generate
2107 for (u = 0; u < UNITS; u = u + 1) begin : unit
2108 // Each core's digits of the two rows, registered on clk a clock before the word, then held on clk2 for its turn.
2109 wire [63:0] da, db, ea, eb;
2110 for (i = 0; i < 16; i = i + 1) begin : nib
2111 assign da[4*i +: 4] = digit ? code1a[8*(16*u + i) + 4 +: 4] : code1a[8*(16*u + i) +: 4];
2112 assign db[4*i +: 4] = digit ? code1b[8*(16*u + i) + 4 +: 4] : code1b[8*(16*u + i) +: 4];
2113 assign ea[4*i +: 4] = digit ? code2a[8*(16*u + i) + 4 +: 4] : code2a[8*(16*u + i) +: 4];
2114 assign eb[4*i +: 4] = digit ? code2b[8*(16*u + i) + 4 +: 4] : code2b[8*(16*u + i) +: 4];
2115 end
2116 reg [127:0] ra, rb, ha, hb;
2117 always @(posedge clk) begin ra <= {ea, da}; rb <= {eb, db}; end
2118 always @(posedge clk2) begin
2119 if (!a_turn) ha <= ra;
2120 if (a_turn) hb <= rb;
2121 end
2122 wire [127:0] q = a_turn ? ha : hb;
2123 // The word's codes for the unit's two elements, each block of two codes the clocks of clk2 its place in the chain.
2124 reg [127:0] w1, w2, w3;
2125 always @(posedge clk2) begin w1 <= w0[8*16*u +: 128]; w2 <= w1; w3 <= w2; end
2126 wire [127:0] st;
2127 for (i = 0; i < 16; i = i + 1) begin : stag
2128 localparam K = (i % 8) / 2;
2129 assign st[8*i +: 8] = K == 0 ? w0[8*(16*u + i) +: 8] : K == 1 ? w1[8*i +: 8] : K == 2 ? w2[8*i +: 8] : w3[8*i +: 8];
2130 end
2131 wire [14:0] lo_a, lo_b;
2132 wire signed [16:0] hi_a, hi_b;
2133 logos_pe8_p pa(.clk(clk2), .a(st[0 +: 64]), .q1(q[0 +: 32]), .q2(q[64 +: 32]), .lo(lo_a), .hi(hi_a));
2134 logos_pe8_p pb(.clk(clk2), .a(st[64 +: 64]), .q1(q[32 +: 32]), .q2(q[96 +: 32]), .lo(lo_b), .hi(hi_b));
2135 wire [23:0] one = {9'd0, lo_a} + {9'd0, lo_b};
2136 wire signed [23:0] other = $signed(hi_a) + $signed(hi_b);
2137 // Core a's sums caught at the edge between clk's after them, core b's at clk's edge after them, then on clk.
2138 reg [47:0] ca, cb, fa, fa2, fb;
2139 always @(posedge clk2) begin
2140 if (!a_turn) ca <= {other, one};
2141 if (a_turn) cb <= {other, one};
2142 end
2143 always @(posedge clk) begin fa <= ca; fa2 <= fa; fb <= cb; end
2144 assign p1a[24*u +: 24] = fa2[23:0];
2145 assign p2a[24*u +: 24] = $signed(fa2[47:24]) + $signed({9'd0, s_pe[12*u +: 12], 3'd0});
2146 assign p1b[24*u +: 24] = fb[23:0];
2147 assign p2b[24*u +: 24] = $signed(fb[47:24]) + $signed({9'd0, s_pe[12*u +: 12], 3'd0});
2148 end
2149 endgenerate
2150endmodule
2151"#;
2152
2153pub fn matvec_pass_x2t_verilog() -> String {
2156 MATVEC_PASS_X2T.replace("@PE@", &PASS_LATENCY.to_string())
2157}
2158const MATVEC_PASS_X2T: &str = r#"
2159// A core's two token-rows' passes of logos_matvec_pass_p on one set of systolic elements in a clock of twice theirs (clk2,
2160// its rising edges on clk's and between them): the beat's codes, the same for both, and token-row a's word (`wa`,
2161// unstaggered) taken at clk2's edge between clk's, token-row b's (`wb`) at clk's next edge, the elements' blocks staggered
2162// in clk2; each token-row's partials as logos_matvec_pass_p gives them, @PE@ clocks of clk after its word, the codes and
2163// their digit given a clock before it.
2164module logos_matvec_pass_x2t #(parameter UNITS = 16)
2165 (input clk, input clk2, input [128*UNITS-1:0] code1, input [128*UNITS-1:0] code2,
2166 input [128*UNITS-1:0] wa, input [128*UNITS-1:0] wb, input [12*UNITS-1:0] sa, input [12*UNITS-1:0] sb, input digit,
2167 output [24*UNITS-1:0] p1a, output [24*UNITS-1:0] p2a, output [24*UNITS-1:0] p1b, output [24*UNITS-1:0] p2b);
2168 wire [12*UNITS-1:0] sa_pe, sb_pe;
2169 logos_delay #(.W(12*UNITS), .D(@PE@)) sda(.clk(clk), .d(sa), .q(sa_pe));
2170 logos_delay #(.W(12*UNITS), .D(@PE@)) sdb(.clk(clk), .d(sb), .q(sb_pe));
2171 // Token-row a's turn on clk2: the clock after its edge between clk's, a toggle of clk's seen changed.
2172 reg tf = 1'b0, tf2 = 1'b0, a_turn = 1'b0;
2173 always @(posedge clk) tf <= ~tf;
2174 always @(posedge clk2) begin tf2 <= tf; a_turn <= tf != tf2; end
2175 // Each turn's word, taken at the edge before its turn.
2176 reg [128*UNITS-1:0] w0;
2177 always @(posedge clk2) w0 <= a_turn ? wb : wa;
2178 genvar i, u;
2179 generate
2180 for (u = 0; u < UNITS; u = u + 1) begin : unit
2181 // The beat's digits of the two rows, registered on clk a clock before the words, then held on clk2 for each turn.
2182 wire [63:0] d, e;
2183 for (i = 0; i < 16; i = i + 1) begin : nib
2184 assign d[4*i +: 4] = digit ? code1[8*(16*u + i) + 4 +: 4] : code1[8*(16*u + i) +: 4];
2185 assign e[4*i +: 4] = digit ? code2[8*(16*u + i) + 4 +: 4] : code2[8*(16*u + i) +: 4];
2186 end
2187 reg [127:0] r, ha, hb;
2188 always @(posedge clk) r <= {e, d};
2189 always @(posedge clk2) begin
2190 if (!a_turn) ha <= r;
2191 if (a_turn) hb <= r;
2192 end
2193 wire [127:0] q = a_turn ? ha : hb;
2194 // The turn's word's codes for the unit's two elements, each block of two codes the clocks of clk2 its place in the
2195 // chain.
2196 reg [127:0] w1, w2, w3;
2197 always @(posedge clk2) begin w1 <= w0[8*16*u +: 128]; w2 <= w1; w3 <= w2; end
2198 wire [127:0] st;
2199 for (i = 0; i < 16; i = i + 1) begin : stag
2200 localparam K = (i % 8) / 2;
2201 assign st[8*i +: 8] = K == 0 ? w0[8*(16*u + i) +: 8] : K == 1 ? w1[8*i +: 8] : K == 2 ? w2[8*i +: 8] : w3[8*i +: 8];
2202 end
2203 wire [14:0] lo_a, lo_b;
2204 wire signed [16:0] hi_a, hi_b;
2205 logos_pe8_p pa(.clk(clk2), .a(st[0 +: 64]), .q1(q[0 +: 32]), .q2(q[64 +: 32]), .lo(lo_a), .hi(hi_a));
2206 logos_pe8_p pb(.clk(clk2), .a(st[64 +: 64]), .q1(q[32 +: 32]), .q2(q[96 +: 32]), .lo(lo_b), .hi(hi_b));
2207 wire [23:0] one = {9'd0, lo_a} + {9'd0, lo_b};
2208 wire signed [23:0] other = $signed(hi_a) + $signed(hi_b);
2209 // Token-row a's sums caught at the edge between clk's after them, b's at clk's edge after them, then on clk.
2210 reg [47:0] ca, cb, fa, fa2, fb;
2211 always @(posedge clk2) begin
2212 if (!a_turn) ca <= {other, one};
2213 if (a_turn) cb <= {other, one};
2214 end
2215 always @(posedge clk) begin fa <= ca; fa2 <= fa; fb <= cb; end
2216 assign p1a[24*u +: 24] = fa2[23:0];
2217 assign p2a[24*u +: 24] = $signed(fa2[47:24]) + $signed({9'd0, sa_pe[12*u +: 12], 3'd0});
2218 assign p1b[24*u +: 24] = fb[23:0];
2219 assign p2b[24*u +: 24] = $signed(fb[47:24]) + $signed({9'd0, sb_pe[12*u +: 12], 3'd0});
2220 end
2221 endgenerate
2222endmodule
2223"#;
2224
2225const ACT_WORD: &str = r#"
2227// A token-row's activation block as block RAM holds it for the column: each activation word's excess codes — the high
2228// (or only) word in excess 128, the low byte of a two-word code as it is — and each 16-element unit's sum of them, then
2229// SLOTS scales' significands and exponents, 24 bits a scale, the scale `m`·2^`e` at `slot` and zeros at the others (a
2230// word of an octet's eight blocks is written a block at a time under a mask of its bytes); built for one word (WORDS 1),
2231// `two` is never set.
2232module logos_column_act_word #(parameter ELEMS = 256, WORDS = 1, SLOTS = 1)
2233 (input [16*ELEMS-1:0] codes, input two, input [7:0] m, input [11:0] e, input [2:0] slot,
2234 output [((WORDS*(8*ELEMS + 12*(ELEMS/16)) + 20 + 24*(SLOTS-1) + 7)/8)*8-1:0] word);
2235 localparam UNITS = ELEMS / 16, SUMS = WORDS * 8 * ELEMS, SCALE = SUMS + WORDS * 12 * UNITS;
2236 localparam TOP = ((WORDS*(8*ELEMS + 12*(ELEMS/16)) + 20 + 24*(SLOTS-1) + 7)/8)*8, SCALES = 20 + 24 * (SLOTS - 1);
2237 // A unit's sixteen excess codes summed in a balanced tree of four adds.
2238 function [11:0] sum16(input [127:0] c);
2239 reg [8:0] a0, a1, a2, a3, a4, a5, a6, a7;
2240 reg [9:0] b0, b1, b2, b3;
2241 reg [10:0] d0, d1;
2242 begin
2243 a0 = c[7:0] + c[15:8]; a1 = c[23:16] + c[31:24]; a2 = c[39:32] + c[47:40]; a3 = c[55:48] + c[63:56];
2244 a4 = c[71:64] + c[79:72]; a5 = c[87:80] + c[95:88]; a6 = c[103:96] + c[111:104]; a7 = c[119:112] + c[127:120];
2245 b0 = a0 + a1; b1 = a2 + a3; b2 = a4 + a5; b3 = a6 + a7;
2246 d0 = b0 + b1; d1 = b2 + b3;
2247 sum16 = d0 + d1;
2248 end
2249 endfunction
2250 wire [8*ELEMS-1:0] hi, lo;
2251 genvar i, u;
2252 generate
2253 for (i = 0; i < ELEMS; i = i + 1) begin : element
2254 wire [15:0] code = codes[16*i +: 16];
2255 assign hi[8*i +: 8] = (WORDS > 1 && two) ? (code[15:8] + 8'd128) : (code[7:0] + 8'd128);
2256 assign lo[8*i +: 8] = (WORDS > 1 && two) ? code[7:0] : 8'd0;
2257 end
2258 for (u = 0; u < UNITS; u = u + 1) begin : unit
2259 wire [11:0] sh = sum16(hi[128*u +: 128]), sl = sum16(lo[128*u +: 128]);
2260 assign word[SUMS + 12*u +: 12] = sh;
2261 if (WORDS > 1) begin : low_sum
2262 assign word[SUMS + 12*UNITS + 12*u +: 12] = sl;
2263 end
2264 end
2265 if (WORDS > 1) begin : low_word
2266 assign word[16*ELEMS-1:8*ELEMS] = lo;
2267 end
2268 for (u = 0; u < SLOTS; u = u + 1) begin : scale
2269 localparam [2:0] AT = u;
2270 assign word[SCALE + 24*u +: 8] = (slot == AT) ? m : 8'd0;
2271 assign word[SCALE + 24*u + 8 +: 12] = (slot == AT) ? e : 12'd0;
2272 if (u + 1 < SLOTS) begin : gap
2273 assign word[SCALE + 24*u + 20 +: 4] = 4'd0;
2274 end
2275 end
2276 if (TOP > SCALE + SCALES) begin : pad
2277 assign word[TOP-1:SCALE+SCALES] = {(TOP-SCALE-SCALES){1'b0}};
2278 end
2279 endgenerate
2280 assign word[8*ELEMS-1:0] = hi;
2281endmodule
2282"#;