1use std::collections::{BTreeMap, BTreeSet};
32
33use super::{
34 addr_v, bits_for, buffer_uses, fold_step, from_phase, gather_lanes, index_read, item_coord_names, lit64, log2_banks, loop_vars,
35 mcast_sends, mcast_valids, ph_less, resize_lanes, sem_incs, sems_local, tensor_width, tensors_read, tensors_written,
36 var_name, Ctx, Emit, EnginePlan, Fixed, GenUnit, Guard, Micro, UnaryKind,
37};
38use crate::codegen_sva::systolic::pipelined_latency;
39use crate::spatial::ir::*;
40use crate::spatial::lower::SpatialLowerError;
41use crate::spatial::passes::{LV_ITEM, LV_SUB_H, LV_SUB_W};
42use crate::spatial::target::precision_int_bits;
43use crate::spatial::value_range::{signed_bits, Interval};
44use crate::tensor_recognize::ElementwiseOp;
45
46fn log2(n: usize) -> u32 {
48 n.trailing_zeros()
49}
50
51fn fit(v: &str, width: u32, keep: u32) -> String {
54 if width > keep {
55 format!("$signed({v}[{}:0])", keep - 1)
56 } else {
57 format!("$signed({v})")
58 }
59}
60
61pub(super) fn acc_regions(micro: &[Micro]) -> Vec<Option<usize>> {
65 let mut out = vec![None; micro.len()];
66 let mut i = 0;
67 while i < micro.len() {
68 if let Micro::Fixed(Fixed::AcquireAcc { tiles, .. }) = micro[i] {
69 let end = (i..micro.len())
70 .find(|&k| matches!(micro[k], Micro::Fixed(Fixed::ReleaseAcc) | Micro::Then(Fixed::ReleaseAcc)))
71 .unwrap_or(micro.len());
72 let cols = micro[i..end]
73 .iter()
74 .find_map(|m| match m {
75 Micro::Fixed(Fixed::Matmul { block, .. }) => Some(block.osw),
76 _ => None,
77 })
78 .unwrap_or(tiles);
79 for slot in out.iter_mut().take(end).skip(i) {
80 *slot = Some(cols);
81 }
82 i = end;
83 }
84 i += 1;
85 }
86 out
87}
88
89struct BodyClass {
92 key: String,
93 unit: GenUnit,
95}
96
97fn body_shape(b: &TensorBody, role: &dyn Fn(LoopVar) -> &'static str, params: &mut Vec<i64>) -> Result<String, SpatialLowerError> {
99 let two = |tag: &str, x: &TensorBody, y: &TensorBody, params: &mut Vec<i64>| -> Result<String, SpatialLowerError> {
100 Ok(format!("{tag}({},{})", body_shape(x, role, params)?, body_shape(y, role, params)?))
101 };
102 Ok(match b {
103 TensorBody::Operand(i) => format!("o{i}"),
104 TensorBody::Const(bits) => {
105 params.push(f64::from_bits(*bits) as i64);
106 "k".into()
107 }
108 TensorBody::Add(x, y) => two("add", x, y, params)?,
109 TensorBody::Sub(x, y) => two("sub", x, y, params)?,
110 TensorBody::Mul(x, y) => two("mul", x, y, params)?,
111 TensorBody::Min(x, y) => two("min", x, y, params)?,
112 TensorBody::Max(x, y) => two("max", x, y, params)?,
113 TensorBody::FloorDiv(x, y) => two("fdiv", x, y, params)?,
114 TensorBody::Shr { arg, by } => {
115 let a = body_shape(arg, role, params)?;
116 params.push(*by as i64);
117 format!("shr({a},k)")
118 }
119 TensorBody::Select { cond, then, otherwise } => {
120 let rhs = match cond.rhs() {
121 PredRhs::Const(k) => {
122 params.push(k as i64);
123 "k".to_string()
124 }
125 PredRhs::Operand(i) => format!("o{i}"),
126 };
127 let cmp = match cond {
128 Pred::AxisLt(..) => "lt",
129 Pred::AxisGe(..) => "ge",
130 };
131 let t = body_shape(then, role, params)?;
132 let o = body_shape(otherwise, role, params)?;
133 format!("sel({cmp},{},{rhs},{t},{o})", role(cond.var()))
134 }
135 TensorBody::Unary { .. } => {
136 return Err(SpatialLowerError::Unsupported("a transcendental in an integer body"))
137 }
138 })
139}
140
141pub fn floor_divider_latency(wa: u32, wq: u32) -> u64 {
145 wa.min(wq) as u64 + 2
146}
147
148pub fn floor_divider_verilog(name: &str, wa: u32, wb: u32, wq: u32) -> String {
156 let n = wa.min(wq);
157 let mut e = Emit { s: String::new() };
158 e.l(format!(
159 "module {name}(input clk, input signed [{}:0] a, input signed [{}:0] b, output reg signed [{}:0] q);",
160 wa - 1,
161 wb - 1,
162 wq - 1
163 ));
164 e.l(format!(" wire [{}:0] am = a[{}] ? -a : a;", wa - 1, wa - 1));
165 e.l(format!(" wire [{}:0] bm = b[{}] ? -b : b;", wb - 1, wb - 1));
166 e.l(format!(" reg [{}:0] r0, d0; reg [{}:0] x0; reg s0;", wb - 1, n - 1));
167 let top = if wa > n { format!("am[{}:{n}]", wa - 1) } else { "0".to_string() };
168 e.l(format!(
169 " always @(posedge clk) begin r0 <= {top}; x0 <= am[{}:0]; d0 <= bm; s0 <= a[{}] ^ b[{}]; end",
170 n - 1,
171 wa - 1,
172 wb - 1
173 ));
174 for k in 1..=n {
175 let p = k - 1;
176 e.l(format!(" wire [{wb}:0] t{k} = {{r{p}, x{p}[{}]}};", n - 1));
177 e.l(format!(" wire g{k} = t{k} >= {{1'b0, d{p}}};"));
178 e.l(format!(" reg [{}:0] r{k}, d{k}; reg [{}:0] x{k}; reg s{k};", wb - 1, n - 1));
179 let shift = if n == 1 { format!("g{k}") } else { format!("{{x{p}[{}:0], g{k}}}", n - 2) };
180 e.l(format!(
181 " always @(posedge clk) begin r{k} <= g{k} ? t{k} - {{1'b0, d{p}}} : t{k}; x{k} <= {shift}; d{k} <= d{p}; s{k} <= s{p}; end"
182 ));
183 }
184 e.l(format!(
185 " always @(posedge clk) q <= s{n} ? ((r{n} != 0) ? -$signed({{1'b0, x{n}}}) - 1 : -$signed({{1'b0, x{n}}})) : $signed({{1'b0, x{n}}});"
186 ));
187 e.l("endmodule");
188 e.s
189}
190
191struct Staged {
194 wire: String,
195 width: u32,
196 ready: Option<u32>,
197}
198
199struct Pipe<'a> {
214 class: usize,
215 axis: &'a dyn Fn(LoopVar) -> String,
216 operand: &'a dyn Fn(usize) -> String,
217 suffix: &'a str,
218 widths: &'a [u32],
219 prefix: &'a str,
221 slice: Option<(u32, u32)>,
223 next_param: usize,
224 node: usize,
225 lines: Vec<String>,
226 modules: Vec<String>,
227}
228
229impl Pipe<'_> {
230 fn param(&mut self) -> String {
231 let n = self.next_param;
232 self.next_param += 1;
233 format!("gp{}_{n}", self.class)
234 }
235
236 fn take(&mut self) -> (usize, String, u32) {
237 let j = self.node;
238 self.node += 1;
239 (j, format!("gn{}_{j}{}", self.class, self.suffix), self.widths[j])
240 }
241
242 fn keep(&mut self, wire: &str) {
244 let decl = format!(" {wire}; always @(posedge clk) {wire} <= ");
245 let line = self.lines.iter_mut().find(|l| l.starts_with(" reg signed [") && l.contains(&decl)).expect("a node's register");
246 line.insert_str(2, "(* keep *) ");
247 }
248
249 fn at(&mut self, v: &Staged, to: u32) -> String {
251 let Some(from) = v.ready else { return v.wire.clone() };
252 let mut cur = v.wire.clone();
253 for s in from + 1..=to {
254 let d = format!("{}_d{s}", v.wire);
255 self.lines.push(format!(" reg signed [{}:0] {d}; always @(posedge clk) {d} <= {cur};", v.width - 1));
256 cur = d;
257 }
258 cur
259 }
260
261 fn walk(&mut self, b: &TensorBody) -> Staged {
262 match b {
263 TensorBody::Operand(i) => {
264 let (_, wire, width) = self.take();
265 self.lines.push(format!(" wire signed [{}:0] {wire} = $signed({});", width - 1, (self.operand)(*i)));
266 Staged { wire, width, ready: Some(0) }
267 }
268 TensorBody::Const(_) => {
269 let p = self.param();
270 let (_, wire, width) = self.take();
271 self.lines.push(format!(" wire signed [{}:0] {wire} = $signed({p});", width - 1));
272 Staged { wire, width, ready: None }
273 }
274 TensorBody::Add(xb, yb) | TensorBody::Sub(xb, yb) | TensorBody::Mul(xb, yb) | TensorBody::Min(xb, yb) | TensorBody::Max(xb, yb) => {
275 let (x, y) = (self.walk(xb), self.walk(yb));
276 let (j, wire, width) = self.take();
277 let s = x.ready.max(y.ready).unwrap_or(0);
278 let (a, c) = (self.at(&x, s), self.at(&y, s));
279 let w = width - 1;
280 let (expr, ready) = match b {
281 TensorBody::Add(..) => (format!("$signed({a}) + $signed({c})"), s + 1),
282 TensorBody::Sub(..) => (format!("$signed({a}) - $signed({c})"), s + 1),
283 TensorBody::Mul(..) => {
284 let (wa, wb) = (x.width.min(width), y.width.min(width));
285 if self.slice.is_some_and(|(wide, narrow)| wa.max(wb) > wide || wa.min(wb) > narrow) {
286 for (child, operand) in [(xb, &x), (yb, &y)] {
287 if matches!(**child, TensorBody::Add(..) | TensorBody::Sub(..)) {
288 self.keep(&operand.wire);
289 }
290 }
291 }
292 let m = format!("gm{}_{j}{}", self.class, self.suffix);
295 let (a, c) = (fit(&a, x.width, width), fit(&c, y.width, width));
296 self.lines.push(format!(" reg signed [{w}:0] {m}; always @(posedge clk) {m} <= {a} * {c};"));
297 (m, s + 2)
298 }
299 TensorBody::Min(..) => (format!("($signed({a}) < $signed({c})) ? $signed({a}) : $signed({c})"), s + 1),
300 _ => (format!("($signed({a}) > $signed({c})) ? $signed({a}) : $signed({c})"), s + 1),
301 };
302 self.lines.push(format!(" reg signed [{w}:0] {wire}; always @(posedge clk) {wire} <= {expr};"));
303 Staged { wire, width, ready: Some(ready) }
304 }
305 TensorBody::FloorDiv(x, y) => {
306 let (x, y) = (self.walk(x), self.walk(y));
307 let (j, wire, width) = self.take();
308 let s = x.ready.max(y.ready).unwrap_or(0);
309 let (a, c) = (self.at(&x, s), self.at(&y, s));
310 let module = format!("{}_fd{}_{j}", self.prefix, self.class);
311 self.modules.push(floor_divider_verilog(&module, x.width, y.width, width));
312 self.lines.push(format!(" wire signed [{}:0] {wire};", width - 1));
313 self.lines.push(format!(" {module} u_{module}(.clk(clk), .a({a}), .b({c}), .q({wire}));"));
314 Staged { wire, width, ready: Some(s + floor_divider_latency(x.width, width) as u32) }
315 }
316 TensorBody::Shr { arg, .. } => {
317 let x = self.walk(arg);
318 let p = self.param();
319 let (_, wire, width) = self.take();
320 let s = x.ready.unwrap_or(0);
321 let a = self.at(&x, s);
322 self.lines.push(format!(" reg signed [{}:0] {wire}; always @(posedge clk) {wire} <= $signed({a}) >>> {p};", width - 1));
323 Staged { wire, width, ready: Some(s + 1) }
324 }
325 TensorBody::Select { cond, then, otherwise } => {
326 let lhs = format!("$signed({{32'd0, {}}})", (self.axis)(cond.var()));
327 let rhs = match cond.rhs() {
328 PredRhs::Const(_) => format!("$signed({})", self.param()),
329 PredRhs::Operand(i) => format!("$signed({})", (self.operand)(i)),
330 };
331 let op = match cond {
332 Pred::AxisLt(..) => "<",
333 Pred::AxisGe(..) => ">=",
334 };
335 let (t, o) = (self.walk(then), self.walk(otherwise));
336 let (j, wire, width) = self.take();
337 let pred = Staged { wire: format!("gs{}_{j}{}", self.class, self.suffix), width: 1, ready: Some(0) };
338 self.lines.push(format!(" wire {} = {lhs} {op} {rhs};", pred.wire));
339 let s = t.ready.max(o.ready).unwrap_or(0);
340 let (p, a, c) = (self.at(&pred, s), self.at(&t, s), self.at(&o, s));
341 self.lines.push(format!(
342 " reg signed [{}:0] {wire}; always @(posedge clk) {wire} <= {p} ? $signed({a}) : $signed({c});",
343 width - 1
344 ));
345 Staged { wire, width, ready: Some(s + 1) }
346 }
347 TensorBody::Unary { .. } => unreachable!("refused by body_shape"),
348 }
349 }
350}
351
352fn scalar_operand(s: usize) -> String {
354 format!("g_op{s}")
355}
356
357#[allow(clippy::too_many_arguments)]
360fn class_pipeline(
361 b: &TensorBody,
362 class: usize,
363 axis: &dyn Fn(LoopVar) -> String,
364 operand: &dyn Fn(usize) -> String,
365 suffix: &str,
366 widths: &[u32],
367 prefix: &str,
368 slice: Option<(u32, u32)>,
369) -> (Vec<String>, Vec<String>, String, u64) {
370 let mut pipe = Pipe { class, axis, operand, suffix, widths, prefix, slice, next_param: 0, node: 0, lines: Vec::new(), modules: Vec::new() };
371 let root = pipe.walk(b);
372 (pipe.lines, pipe.modules, root.wire, root.ready.unwrap_or(0) as u64)
373}
374
375struct Classes {
377 classes: Vec<BodyClass>,
378 op_class: BTreeMap<usize, (usize, Vec<i64>)>,
380}
381
382impl Classes {
383 fn of(micro: &[Micro]) -> Result<Classes, SpatialLowerError> {
384 let mut out = Classes { classes: Vec::new(), op_class: BTreeMap::new() };
385 for (i, m) in micro.iter().enumerate() {
386 let Micro::Fixed(Fixed::Generic { unit, .. }) = m else { continue };
387 let role = axis_role(unit);
388 let mut params = Vec::new();
389 let key = body_shape(&unit.body, &role, &mut params)?;
390 let class = match out.classes.iter().position(|c| c.key == key) {
391 Some(c) => c,
392 None => {
393 out.classes.push(BodyClass { key, unit: (**unit).clone() });
394 out.classes.len() - 1
395 }
396 };
397 out.op_class.insert(i, (class, params));
398 }
399 Ok(out)
400 }
401
402 fn params_of(&self, class: usize) -> usize {
403 self.op_class.values().filter(|(c, _)| *c == class).map(|(_, p)| p.len()).max().unwrap_or(0)
404 }
405}
406
407pub(super) const FOLD_STAGES: u64 = 2;
410
411pub(super) fn set_generic_depths(ctx: &Ctx, plans: &mut [EnginePlan]) -> Result<(), SpatialLowerError> {
415 let engine_classes: BTreeSet<usize> = plans.iter().map(|p| p.class).collect();
416 for ec in engine_classes {
417 let rep = plans.iter().position(|p| p.class == ec).expect("a plan of the class");
418 let cls = Classes::of(&plans[rep].micro)?;
419 let depths: Vec<u64> = {
420 let class_plans: Vec<&EnginePlan> = plans.iter().filter(|p| p.class == ec).collect();
421 let wd = Widths::of(ctx, &class_plans, &cls);
422 cls.classes
423 .iter()
424 .enumerate()
425 .map(|(c, bc)| class_pipeline(&bc.unit.body, c, &|_| String::new(), &scalar_operand, "", &wd.nodes[c], "", ctx.multiplier).3)
426 .collect()
427 };
428 for plan in plans.iter_mut().filter(|p| p.class == ec) {
429 for (i, m) in plan.micro.iter_mut().enumerate() {
430 if let Micro::Fixed(Fixed::Generic { unit, depth, .. }) = m {
431 *depth = depths[cls.op_class[&i].0] + unit.tree_levels() + FOLD_STAGES;
432 }
433 }
434 }
435 }
436 Ok(())
437}
438
439struct Shape {
442 banks: usize,
443 rows: usize,
444 slots: usize,
445 axis_bits: usize,
447 issue_taps: Vec<usize>,
450 hold_taps: Vec<usize>,
451 body_tap: usize,
452 cls: Classes,
453 array: Option<(usize, usize, u32)>,
454 a_rows: usize,
455 gathers: BTreeSet<usize>,
456 lane_gathers: BTreeSet<usize>,
458 lanes: usize,
460 class_lanes: Vec<usize>,
462 tree_levels: usize,
464 lane_pools: [bool; 3],
467 lane_contracts: bool,
468 lane_writes_points: bool,
469 lane_writes_folds: bool,
470 pitched_fill: bool,
472}
473
474fn axis_role(g: &GenUnit) -> impl Fn(LoopVar) -> &'static str + '_ {
475 move |v: LoopVar| {
476 if v == g.row.var {
477 "r"
478 } else if v == g.col.var {
479 "c"
480 } else if g.k.is_some_and(|k| k.var == v) {
481 "x"
482 } else {
483 "0"
484 }
485 }
486}
487
488fn lane_axis_wire(role: &'static str, l: usize) -> String {
491 match role {
492 "r" | "c" | "x" => format!("gb_{role}_l{l}"),
493 _ => "32'd0".into(),
494 }
495}
496
497fn axis_wire(role: &'static str) -> String {
499 match role {
500 "r" => "gb_r".into(),
501 "c" => "gb_c".into(),
502 "x" => "gb_x".into(),
503 _ => "32'd0".into(),
504 }
505}
506
507impl Shape {
508 fn of(ctx: &Ctx, micro: &[Micro], regions: &[Option<usize>]) -> Result<Shape, SpatialLowerError> {
509 let (te, tr, tc) = (ctx.te, ctx.tr, ctx.tc);
510 let mut sh = Shape {
511 banks: 1,
512 rows: 1,
513 slots: 0,
514 axis_bits: 1,
515 issue_taps: Vec::new(),
516 hold_taps: Vec::new(),
517 body_tap: 0,
518 cls: Classes::of(micro)?,
519 array: None,
520 a_rows: 1,
521 gathers: BTreeSet::new(),
522 lane_gathers: BTreeSet::new(),
523 lanes: 1,
524 class_lanes: Vec::new(),
525 tree_levels: 0,
526 lane_pools: [false; 3],
527 lane_contracts: false,
528 lane_writes_points: false,
529 lane_writes_folds: false,
530 pitched_fill: micro.iter().any(|m| {
531 matches!(m, Micro::Fixed(Fixed::ReadTiles { into, .. }) if ctx.page_pitch.contains_key(&ctx.template(*into)))
532 }),
533 };
534 sh.class_lanes = vec![1; sh.cls.classes.len()];
535 let need = |cols_t: Option<usize>, elems: usize, sh: &mut Shape| -> Result<(), SpatialLowerError> {
536 let cols_t = cols_t.ok_or(SpatialLowerError::Unsupported("an accumulator op outside an acquired region"))?;
537 let width = cols_t.max(1) * tc;
538 sh.banks = sh.banks.max(width);
539 sh.rows = sh.rows.max(elems.div_ceil(cols_t.max(1) * te) * tr);
540 Ok(())
541 };
542 for (i, m) in micro.iter().enumerate() {
543 let Micro::Fixed(f) = m else { continue };
544 match f {
545 Fixed::AcquireAcc { tiles, .. } | Fixed::Unary { tiles, .. } => need(regions[i], tiles * te, &mut sh)?,
546 Fixed::PackAcc { tiles, .. } => need(regions[i], tiles * te, &mut sh)?,
547 Fixed::Matmul { block, operand, array, reload, .. } => {
548 need(regions[i], block.osh * block.osw * te, &mut sh)?;
549 sh.banks = sh.banks.max(array.1);
550 let _ = reload;
551 let bits = precision_int_bits(*operand).unwrap_or(64);
552 let widest = sh.array.map_or(bits, |a| a.2.max(bits));
553 sh.array = Some((array.0, array.1, widest));
554 sh.a_rows = sh.a_rows.max(block.osh * tr);
555 }
556 Fixed::Generic { unit, tiles, .. } => {
557 if tr != 1 || tc != 1 {
558 return Err(SpatialLowerError::Unsupported("a Generic on a tile of more than one element"));
559 }
560 need(regions[i], tiles * te, &mut sh)?;
561 sh.slots = sh.slots.max(unit.slots());
562 for s in 0..unit.slots() {
563 if unit.gathers(s) {
564 sh.gathers.insert(s);
565 if unit.lanes > 1 {
566 sh.lane_gathers.insert(s);
567 }
568 }
569 }
570 sh.lanes = sh.lanes.max(unit.lanes);
571 let class = sh.cls.op_class[&i].0;
572 sh.class_lanes[class] = sh.class_lanes[class].max(unit.lanes);
573 sh.tree_levels = sh.tree_levels.max(unit.tree_levels() as usize);
574 if unit.lanes > 1 && !unit.tree() {
575 sh.lane_contracts |= unit.k.is_some();
576 match (unit.row.kind, unit.col.kind) {
577 (IterKind::Reduce { pool }, IterKind::Parallel) => {
578 sh.lane_pools[pool_index(pool)] = true;
579 sh.lane_writes_folds = true;
580 }
581 _ => sh.lane_writes_points = true,
582 }
583 }
584 let is = &unit.issue;
585 let most = [unit.row.extent, unit.col.extent, unit.k_extent(), is.ii].into_iter().max().unwrap_or(1);
586 sh.axis_bits = sh.axis_bits.max(bits_for(most + 1));
587 sh.body_tap = sh.body_tap.max(is.body);
588 sh.issue_taps.resize(sh.slots, 0);
589 sh.hold_taps.resize(sh.slots, 0);
590 for (s, at) in is.at.iter().enumerate() {
591 sh.issue_taps[s] = sh.issue_taps[s].max(*at);
592 sh.hold_taps[s] = sh.hold_taps[s].max(is.body - at - ctx.lat as usize - 1);
593 }
594 }
595 _ => {}
596 }
597 }
598 Ok(sh)
599 }
600}
601
602struct SlotWalk {
606 d0: i64,
607 d1: i64,
608 d2: i64,
609 lim: i64,
610}
611
612impl SlotWalk {
613 fn of(ctx: &Ctx, unit: &GenUnit, ins: &[BufferId], sl: usize) -> SlotWalk {
615 let m = &unit.maps[sl];
616 let (vr, vc, vk) = (unit.row.var, unit.col.var, unit.k.map(|k| k.var));
617 let lv = unit.lane_var();
618 let stride = |v: Option<LoopVar>| -> i64 {
619 v.map_or(0, |v| m.terms.iter().filter(|(t, _)| *t == v).map(|(_, s)| *s).sum::<i64>() * if v == lv { unit.lanes as i64 } else { 1 })
620 };
621 let (sx, sr, scol) = (stride(vk), stride(Some(vr)), stride(Some(vc)));
622 let (smid, sout) = if unit.rows_inner() { (sr, scol) } else { (scol, sr) };
623 let nmid = if unit.rows_inner() { unit.points_along(vr) } else { unit.points_along(vc) };
624 let kx1 = vk.map_or(1, |v| unit.points_along(v)) as i64 - 1;
625 let width = (ctx.page_elems(ins[sl]) / ctx.te) * ctx.tr;
626 SlotWalk {
627 d0: sx,
628 d1: smid - sx * kx1,
629 d2: sout - smid * (nmid as i64 - 1) - sx * kx1,
630 lim: (ctx.tr * width) as i64,
631 }
632 }
633
634 fn offsets(unit: &GenUnit, sl: usize, index: Option<Interval>) -> (Interval, Interval) {
637 let m = &unit.maps[sl];
638 let extent = |v: LoopVar| {
639 if v == unit.row.var {
640 unit.row.extent
641 } else if v == unit.col.var {
642 unit.col.extent
643 } else if unit.k.is_some_and(|k| k.var == v) {
644 unit.k_extent()
645 } else {
646 1
647 }
648 };
649 let lv = unit.lane_var();
651 let (mut lo, mut hi) = (0i128, 0i128);
652 for (v, s) in &m.terms {
653 let reach = if *v == lv { unit.points_along(*v) * unit.lanes } else { extent(*v) };
654 let span = *s as i128 * (reach as i128 - 1);
655 lo += span.min(0);
656 hi += span.max(0);
657 }
658 let lin = (lo, hi);
659 let (mut lo, mut hi) = (lo + m.base as i128, hi + m.base as i128);
660 if let (Some(ind), true, Some(ix)) = (m.indirect, unit.gathers(sl), index) {
661 let (a, b) = (ind.stride as i128 * ix.0 as i128, ind.stride as i128 * ix.1 as i128);
662 lo += a.min(b);
663 hi += a.max(b);
664 }
665 let fit = |(lo, hi): (i128, i128)| -> Interval {
666 if lo < i64::MIN as i128 || hi > i64::MAX as i128 {
667 (i64::MIN, i64::MAX)
668 } else {
669 (lo as i64, hi as i64)
670 }
671 };
672 (fit(lin), fit((lo, hi)))
673 }
674}
675
676fn pool_index(p: ReducePool) -> usize {
678 match p {
679 ReducePool::Sum => 0,
680 ReducePool::Max => 1,
681 ReducePool::ArgMax => 2,
682 }
683}
684
685struct Widths {
688 acc: u32,
690 slot: u32,
692 value: u32,
694 nodes: Vec<Vec<u32>>,
696 params: Vec<Vec<u32>>,
698 un_in: u32,
701 un_b: u32,
702 un_mul: u32,
703 un_out: u32,
704 un_m: u32,
705 addr: u32,
707}
708
709impl Widths {
710 fn of(ctx: &Ctx, plans: &[&EnginePlan], cls: &Classes) -> Widths {
711 let r = &ctx.ranges;
712 let mut w = Widths {
713 acc: 1,
714 slot: 1,
715 value: 1,
716 nodes: cls.classes.iter().map(|c| vec![1; c.unit.body.post_order().len()]).collect(),
717 params: (0..cls.classes.len()).map(|c| vec![1; cls.params_of(c)]).collect(),
718 un_in: 1,
719 un_b: 1,
720 un_mul: 1,
721 un_out: 1,
722 un_m: 1,
723 addr: 1,
724 };
725 let grow = |slot: &mut u32, iv: Option<Interval>| {
726 if let Some(iv) = iv {
727 *slot = (*slot).max(signed_bits(iv));
728 }
729 };
730 let buf = |b: BufferId| r.buffers[b.idx()];
731 let held = |s: StageId| Some(r.stages[s.idx()].held.unwrap_or((i64::MIN, i64::MAX)));
733 let with_zero = |iv: Option<Interval>| iv.map(|(lo, hi)| (lo.min(0), hi.max(0)));
734 for plan in plans {
735 for (i, m) in plan.micro.iter().enumerate() {
736 let Micro::Fixed(f) = m else { continue };
737 match f {
738 Fixed::Matmul { stage, reload, .. } => {
739 grow(&mut w.acc, held(*stage));
740 grow(&mut w.acc, reload.and_then(buf));
741 }
742 Fixed::Unary { kind, ins, stage, .. } => {
743 let st = &r.stages[stage.idx()];
744 grow(&mut w.un_in, buf(ins[0]));
745 if let Some(b) = ins.get(1) {
746 grow(&mut w.un_b, buf(*b));
747 }
748 match kind {
749 UnaryKind::Requant { m, .. } => {
750 grow(&mut w.un_m, Some((*m, *m)));
751 grow(&mut w.un_mul, st.nodes.first().copied());
752 }
753 UnaryKind::Bias => grow(&mut w.un_mul, st.nodes.first().copied()),
754 UnaryKind::Elementwise(_) => grow(&mut w.un_mul, st.nodes.last().copied()),
755 UnaryKind::Relu => {}
756 UnaryKind::Scalar(_) => {
757 for x in [&mut w.un_in, &mut w.un_b, &mut w.un_mul, &mut w.un_out] {
758 *x = 64;
759 }
760 }
761 }
762 grow(&mut w.un_out, held(*stage));
763 grow(&mut w.acc, held(*stage));
764 }
765 Fixed::Generic { unit, ins, stage, .. } => {
766 let st = &r.stages[stage.idx()];
767 for b in ins {
768 grow(&mut w.slot, with_zero(buf(*b)));
769 }
770 for (sl, m) in unit.maps.iter().enumerate() {
771 let walk = SlotWalk::of(ctx, unit, ins, sl);
772 let index = m.indirect.and_then(|ind| with_zero(buf(ins[ind.operand])));
773 let (lin, off) = SlotWalk::offsets(unit, sl, index);
774 for iv in [lin, off, (m.base, m.base), (walk.d0, walk.d0), (walk.d1, walk.d1), (walk.d2, walk.d2), (walk.lim, walk.lim)] {
775 grow(&mut w.addr, Some(iv));
776 }
777 if let Some(ind) = m.indirect {
778 grow(&mut w.addr, Some((ind.stride, ind.stride)));
779 }
780 }
781 let (class, _) = &cls.op_class[&i];
782 for (j, iv) in st.nodes.iter().enumerate() {
783 grow(&mut w.nodes[*class][j], Some(*iv));
784 }
785 grow(&mut w.value, held(*stage));
786 grow(&mut w.value, st.nodes.last().copied());
787 if unit.tree() && unit.tree_sums() {
790 grow(&mut w.value, st.nodes.last().map(|iv| run_sum(*iv, unit.lanes)));
791 }
792 grow(&mut w.acc, held(*stage));
793 }
794 _ => {}
795 }
796 }
797 }
798 for (class, values) in cls.op_class.values() {
799 for (j, v) in values.iter().enumerate() {
800 grow(&mut w.params[*class][j], Some((*v, *v)));
801 }
802 }
803 w
804 }
805}
806
807fn run_sum((lo, hi): Interval, n: usize) -> Interval {
809 let fit = |v: i128| v.clamp(i64::MIN as i128, i64::MAX as i128) as i64;
810 let (lo, hi, n) = (lo as i128, hi as i128, n as i128);
811 (fit(lo.min(n * lo).min(0)), fit(hi.max(n * hi).max(0)))
812}
813
814const AW_WIDE: usize = 0;
816
817pub(super) fn print_engine(
819 ctx: &Ctx,
820 plan: &EnginePlan,
821 class_plans: &[&EnginePlan],
822 name: &str,
823 grid_names: &BTreeMap<usize, String>,
824) -> Result<String, SpatialLowerError> {
825 let p = ctx.p;
826 let (te, tr, tc, lat) = (ctx.te, ctx.tr, ctx.tc, ctx.lat);
827 if !te.is_power_of_two() || !tr.is_power_of_two() || !tc.is_power_of_two() {
828 return Err(SpatialLowerError::Unsupported("a fabric tile whose sides are not powers of two"));
829 }
830 ctx.phase_flags.borrow_mut().clear();
831 let micro = &plan.micro;
832 let uses = buffer_uses(micro);
833 let bufs: BTreeSet<BufferId> = uses.all();
834 let tp = |b: BufferId| ctx.template(b).idx();
835 let gathered = gather_lanes(ctx, micro);
836 let t_rd = tensors_read(micro);
837 let t_wr = tensors_written(micro);
838 let s_local = sems_local(micro);
839 let s_inc = sem_incs(micro);
840 let m_valid = mcast_valids(micro);
841 let m_send = mcast_sends(micro);
842 let lvs = loop_vars(micro);
843 let regions = acc_regions(micro);
844 let sh = Shape::of(ctx, micro, ®ions)?;
845 let wd = Widths::of(ctx, class_plans, &sh.cls);
846 let mut e = Emit { s: String::new() };
847 let vl = sh.lanes;
851 let vec = vl > 1;
852 let lane_pipes: Vec<Vec<(Vec<String>, Vec<String>, String, u64)>> = sh
853 .cls
854 .classes
855 .iter()
856 .enumerate()
857 .map(|(c, cl)| {
858 let role = axis_role(&cl.unit);
859 if vec {
860 (0..sh.class_lanes[c])
861 .map(|l| {
862 let axis = |v: LoopVar| lane_axis_wire(role(v), l);
863 let operand = |s: usize| format!("g_op{s}_l{l}");
864 class_pipeline(&cl.unit.body, c, &axis, &operand, &format!("_l{l}"), &wd.nodes[c], &format!("floor_div_{name}_l{l}"), ctx.multiplier)
865 })
866 .collect()
867 } else {
868 let axis = |v: LoopVar| axis_wire(role(v));
869 vec![class_pipeline(&cl.unit.body, c, &axis, &scalar_operand, "", &wd.nodes[c], &format!("floor_div_{name}"), ctx.multiplier)]
870 }
871 })
872 .collect();
873 let pipes: Vec<&(Vec<String>, Vec<String>, String, u64)> = lane_pipes.iter().map(|lanes| &lanes[0]).collect();
874 for (i, (c, _)) in &sh.cls.op_class {
875 if let Micro::Fixed(Fixed::Generic { unit, depth, .. }) = µ[*i] {
876 debug_assert_eq!(*depth, pipes[*c].3 + unit.tree_levels() + FOLD_STAGES, "op {i} is as deep as its class's pipeline, its lane tree and the folds");
877 }
878 }
879 for lanes in &lane_pipes {
880 for (_, modules, _, _) in lanes {
881 for m in modules {
882 e.s.push_str(m);
883 }
884 }
885 }
886
887 let mut ports: Vec<String> = vec!["input clk".into(), "input rst".into(), "input start".into(), "output reg done".into()];
889 for b in &bufs {
890 let pe = ctx.page_words(*b);
891 let (sw, ew, cw) = (bits_for(ctx.depth(*b)), bits_for(pe), bits_for(ctx.depth(*b) + 1));
892 let k = tp(*b);
893 if uses.produces.contains(b) {
894 ports.push(format!("output reg f{k}_reserve"));
895 ports.push(format!("output reg [{cw}:0] f{k}_reserve_n"));
896 ports.push(format!("output reg f{k}_push"));
897 ports.push(format!("output reg [{cw}:0] f{k}_push_n"));
898 ports.push(format!("input [{cw}:0] f{k}_free"));
899 }
900 if uses.consumes.contains(b) {
901 ports.push(format!("output reg f{k}_pop"));
902 ports.push(format!("output reg [{cw}:0] f{k}_pop_n"));
903 ports.push(format!("input [{cw}:0] f{k}_count"));
904 }
905 let hi = (ctx.banks as u32 - 1) * ctx.buffer_width(*b);
906 if uses.writes.contains(b) {
907 ports.push(format!("output reg f{k}_wr_en"));
908 ports.push(format!("output reg [{sw}:0] f{k}_wr_slot"));
909 ports.push(format!("output reg [{ew}:0] f{k}_wr_elem"));
910 ports.push(format!("output reg [63:0] f{k}_wr_data"));
911 if ctx.banks > 1 {
912 ports.push(format!("output reg [{}:0] f{k}_wr_n", log2_banks(ctx.banks)));
913 ports.push(format!("output reg [3:0] f{k}_wr_lw"));
914 ports.push(format!("output reg [{}:0] f{k}_wr_hi", hi - 1));
915 }
916 ports.push(format!("input [{sw}:0] f{k}_fill_slot0"));
917 }
918 if uses.reads.contains(b) {
919 ports.push(format!("output reg [{sw}:0] f{k}_rd_slot"));
920 ports.push(format!("output reg [{ew}:0] f{k}_rd_elem"));
921 ports.push(format!("input [63:0] f{k}_rd_data"));
922 for l in 1..gathered.get(&ctx.template(*b)).copied().unwrap_or(1) {
923 ports.push(format!("output reg [{sw}:0] f{k}_l{l}_rd_slot"));
924 ports.push(format!("output reg [{ew}:0] f{k}_l{l}_rd_elem"));
925 ports.push(format!("input [63:0] f{k}_l{l}_rd_data"));
926 }
927 if ctx.banks > 1 {
928 ports.push(format!("input [{}:0] f{k}_rd_hi", hi - 1));
929 }
930 ports.push(format!("input [{sw}:0] f{k}_rd_slot0"));
931 ports.push(format!("input [{sw}:0] f{k}_fill_slot0_r"));
932 }
933 }
934 let t_hi = |t: TensorId| (ctx.banks as u32 - 1) * tensor_width(p.tensor(t), &ctx.ranges);
935 for t in &t_rd {
936 ports.push(format!("output reg [31:0] t{}_rd_addr", t.idx()));
937 ports.push(format!("input [63:0] t{}_rd_data", t.idx()));
938 if ctx.banks > 1 {
939 ports.push(format!("input [{}:0] t{}_rd_hi", t_hi(*t) - 1, t.idx()));
940 }
941 }
942 for t in &t_wr {
943 ports.push(format!("output reg t{}_wr_en", t.idx()));
944 ports.push(format!("output reg [31:0] t{}_wr_addr", t.idx()));
945 ports.push(format!("output reg [63:0] t{}_wr_data", t.idx()));
946 if ctx.banks > 1 {
947 ports.push(format!("output reg [{}:0] t{}_wr_n", log2_banks(ctx.banks), t.idx()));
948 ports.push(format!("output reg [3:0] t{}_wr_lw", t.idx()));
949 ports.push(format!("output reg [{}:0] t{}_wr_hi", t_hi(*t) - 1, t.idx()));
950 }
951 }
952 for s in &s_local {
953 ports.push(format!("input [15:0] s{}_count", s.idx()));
954 ports.push(format!("output reg s{}_set", s.idx()));
955 ports.push(format!("output reg [15:0] s{}_set_val", s.idx()));
956 }
957 for s in &s_inc {
958 ports.push(format!("output reg s{}_inc_remote", s.idx()));
959 ports.push(format!("output reg [15:0] s{}_inc_n", s.idx()));
960 }
961 for (s, g) in &m_valid {
962 ports.push(format!("output reg mv{}_g{}", s.idx(), g.idx()));
963 }
964 for g in &m_send {
965 ports.push(format!("output reg mc{}_send", g.idx()));
966 ports.push(format!("output reg [79:0] mc{}_din", g.idx()));
967 }
968 let links: Vec<(usize, usize, usize)> = micro
970 .iter()
971 .enumerate()
972 .filter_map(|(i, m)| match m {
973 Micro::Guarded(Guard::Link { point, ordinal }) => Some((i, *point, *ordinal)),
974 _ => None,
975 })
976 .collect();
977 if !links.is_empty() {
978 ports.push("input [7:0] link_n".into());
979 ports.push("output halted".into());
980 ports.push("output [15:0] halt_at".into());
981 }
982 e.l(format!("module {name} #(parameter ITEM_START = 0, parameter ITEM_COUNT = 1) ("));
983 e.l(format!(" {}", ports.join(",\n ")));
984 e.l(");");
985 e.l(" localparam AW = __AW__;");
987
988 let pcw = bits_for(micro.len() + 1);
990 e.l(format!(" reg [{pcw}:0] pc, pc_n;"));
991 let longest = micro.iter().filter_map(|m| if let Micro::Fixed(f) = m { Some(ctx.duration(f)) } else { None }).max().unwrap_or(1);
993 let phw = bits_for(longest as usize + 2).max(16);
994 e.l(format!(" reg [{}:0] ph, ph_n;", phw - 1));
995 e.l(format!(" reg op_last; reg [{}:0] op_left;", phw - 1));
998 e.l(" reg running, running_n, done_n;");
999 if !links.is_empty() {
1000 let waits: Vec<String> = links.iter().map(|(i, _, o)| format!("(pc == {i} && !(link_n > 8'd{o}))")).collect();
1002 e.l(format!(" assign halted = running && ({});", waits.join(" || ")));
1003 let at: String = links.iter().map(|(i, point, _)| format!("(pc == {i}) ? 16'd{point} : ")).collect();
1004 e.l(format!(" assign halt_at = {at}16'd0;"));
1005 }
1006 for v in &lvs {
1007 e.l(format!(" reg [31:0] {0}, {0}_n;", var_name(*v)));
1008 }
1009 let or_zero = |v: LoopVar| if lvs.contains(&v) { var_name(v) } else { "32'd0".into() };
1010 e.l(format!(" wire [31:0] lv5_or_zero = {};", or_zero(LV_ITEM)));
1011 e.l(" wire [31:0] item_id = ITEM_START + lv5_or_zero;");
1012 e.l(format!(" wire [31:0] oy = item_id / {};", ctx.grid_nx.max(1)));
1013 e.l(format!(" wire [31:0] ox = item_id % {};", ctx.grid_nx.max(1)));
1014 let sections_addressed: BTreeSet<usize> = micro
1015 .iter()
1016 .filter_map(|m| match m {
1017 Micro::Fixed(Fixed::ReadTiles { section, .. } | Fixed::WriteTiles { section, .. }) => *section,
1018 _ => None,
1019 })
1020 .collect();
1021 if !sections_addressed.is_empty() {
1022 let items = p.site_items();
1023 for k in §ions_addressed {
1024 let coords: Vec<(usize, usize)> =
1025 items.at(Some(*k), plan.site).iter().map(|it| p.work.items[it.idx()].coord).collect();
1026 for (axis, pick) in [("y", 0usize), ("x", 1usize)] {
1027 e.l(format!(" function [31:0] item_{axis}_s{k}(input [31:0] lv);"));
1028 let mut c = String::from(" case (lv)");
1029 for (n, xy) in coords.iter().enumerate() {
1030 c.push_str(&format!(" {n}: item_{axis}_s{k} = {};", if pick == 0 { xy.0 } else { xy.1 }));
1031 }
1032 c.push_str(&format!(" default: item_{axis}_s{k} = 0; endcase"));
1033 e.l(c);
1034 e.l(" endfunction");
1035 }
1036 }
1037 }
1038
1039 let g = sh.slots;
1042 let ax = sh.axis_bits;
1043 let dmax = sh
1045 .cls
1046 .op_class
1047 .iter()
1048 .filter_map(|(i, (c, _))| match µ[*i] {
1049 Micro::Fixed(Fixed::Generic { unit, .. }) => Some(pipes[*c].3 + unit.tree_levels()),
1050 _ => None,
1051 })
1052 .chain(pipes.iter().map(|p| p.3))
1053 .max()
1054 .unwrap_or(0);
1055 let mut live: Vec<(String, usize, bool)> = vec![
1056 ("aw_step".into(), 1, false),
1057 ("sw_we".into(), 1, false),
1058 ("az_we".into(), 1, false),
1059 ("sw_d".into(), wd.acc as usize, true),
1060 ("unary_in".into(), wd.un_in as usize, false),
1061 ("unary_bias".into(), wd.un_b as usize, false),
1062 ("mu_ra".into(), sh.array.map_or(1, |a| a.2) as usize, false),
1063 ("mu_rb".into(), sh.array.map_or(1, |a| a.2) as usize, false),
1064 ("mu_rr".into(), wd.acc as usize, false),
1065 ];
1066 for s in 0..sh.slots {
1067 if vec {
1068 for l in 0..vl {
1069 live.push((format!("gu_ld{s}_l{l}"), wd.slot as usize, false));
1070 }
1071 } else {
1072 live.push((format!("gu_ld{s}"), wd.slot as usize, false));
1073 }
1074 live.push((format!("gu_pp{s}"), 1, false));
1075 }
1076 if let (Some((_, _, bits)), true) = (sh.array, ctx.runs()) {
1077 for j in 0..ctx.banks {
1078 live.push((format!("mu_rl{j}"), wd.acc as usize, true));
1079 live.push((format!("mu_al{j}"), bits as usize, true));
1080 live.push((format!("mu_bl{j}"), bits as usize, true));
1081 }
1082 }
1083 let mut table: Vec<(String, usize, bool)> = vec![
1086 ("fx".into(), 1, false),
1088 ("tw_on".into(), 1, false),
1090 ("tw_from".into(), AW_WIDE, false),
1091 ("tw_bw".into(), AW_WIDE, false),
1092 ("tw_d1".into(), AW_WIDE, true),
1093 ("tw_d2".into(), AW_WIDE, true),
1094 ("tw_vec".into(), 1, false),
1095 ("tw_rs".into(), AW_WIDE, false),
1096 ("tw_lw".into(), 4, false),
1097 ("tw_n0".into(), log2_banks(ctx.banks) as usize + 1, false),
1098 ];
1099 if sh.pitched_fill {
1100 table.push(("tw_pp".into(), AW_WIDE, false));
1102 }
1103 table.extend([
1104 ("aw_cols".into(), AW_WIDE, false),
1106 ("ar_on".into(), 1, false),
1107 ("ar_e".into(), AW_WIDE, false),
1108 ("ar_dly".into(), 1, false),
1109 ("ar_lw".into(), 4, false),
1110 ("ar_n0".into(), log2_banks(ctx.banks) as usize + 1, false),
1111 ("pk_tr".into(), 1, false),
1112 ("un_on".into(), 1, false),
1114 ("un_pe".into(), AW_WIDE, false),
1115 ("un_pb".into(), AW_WIDE, false),
1116 ("un_kind".into(), 4, false),
1117 ("un_m".into(), wd.un_m as usize, true),
1118 ("un_s".into(), 8, false),
1119 ("mc_on".into(), 1, false),
1121 ("mc_pe".into(), AW_WIDE, false),
1122 ("mu_on".into(), 1, false),
1124 ("mu_p0".into(), AW_WIDE, false),
1125 ("mu_p1".into(), AW_WIDE, false),
1126 ("mu_p2".into(), AW_WIDE, false),
1127 ("mu_p3".into(), AW_WIDE, false),
1128 ("mu_rows".into(), AW_WIDE, false),
1129 ("mu_kop".into(), AW_WIDE, false),
1130 ("mu_nop".into(), AW_WIDE, false),
1131 ("mu_osw".into(), AW_WIDE, false),
1132 ("mu_ibw".into(), AW_WIDE, false),
1133 ("mu_bwrap".into(), AW_WIDE, false),
1134 ("mu_bjump".into(), AW_WIDE, false),
1135 ("mu_pn".into(), AW_WIDE, false),
1136 ("mr_on".into(), 1, false),
1137 ("mu_mv".into(), 1, false),
1138 ("mr_lw".into(), 4, false),
1139 ("mr_n0o".into(), log2_banks(ctx.banks) as usize + 1, false),
1140 ("mr_n0i".into(), log2_banks(ctx.banks) as usize + 1, false),
1141 ("gu_on".into(), 1, false),
1143 ("gu_ii".into(), ax, false),
1144 ("gu_kx".into(), ax, false),
1145 ("gu_nmid".into(), ax, false),
1146 ("gu_nout".into(), ax, false),
1147 ("gu_rinner".into(), 1, false),
1148 ("gu_cls".into(), 8, false),
1149 ("gu_kind".into(), 2, false),
1150 ("gu_pool".into(), 2, false),
1151 ("gu_rpool".into(), 2, false),
1152 ("gu_rinner_cx".into(), ax, false),
1153 ("gu_rx".into(), ax, false),
1154 ("gu_dep".into(), bits_for(dmax as usize + 1), false),
1155 ("gu_iss".into(), AW_WIDE, false),
1156 ("gu_body".into(), bits_for(sh.body_tap + 1), false),
1157 ]);
1158 if vec {
1159 for (n, w) in [
1165 ("gu_lsh", 4),
1166 ("gu_lax", 2),
1167 ("gu_lanes", bits_for(vl + 1)),
1168 ("gu_llast", ax),
1169 ("gu_ltail", bits_for(vl + 1)),
1170 ("gu_tl", bits_for(sh.tree_levels + 1)),
1171 ("gu_tpool", 2),
1172 ("gu_cdep", bits_for(dmax as usize + 1)),
1173 ("gu_wsh", 4),
1174 ] {
1175 table.push((n.into(), w, false));
1176 }
1177 }
1178 for s in 0..g {
1179 let aw = wd.addr as usize;
1180 let (at, hold) = (bits_for(sh.issue_taps[s] + 1), bits_for(sh.hold_taps[s] + 1));
1181 for (n, w, sg) in [("base", aw, true), ("d0", aw, true), ("d1", aw, true), ("d2", aw, true), ("lim", aw, true), ("gon", 1, false), ("gst", aw, true), ("gop", 8, false), ("at", at, false), ("dl", hold, false)] {
1182 table.push((format!("gu_{n}{s}"), w, sg));
1183 }
1184 if vec {
1185 table.push((format!("gu_bc{s}"), 1, false));
1187 }
1188 }
1189 for c in 0..sh.cls.classes.len() {
1190 for j in 0..sh.cls.params_of(c) {
1191 table.push((format!("gp{c}_{j}"), wd.params[c][j] as usize, true));
1192 }
1193 }
1194 for (n, w, sg) in live.iter().chain(&table) {
1195 let s = if *sg { "signed " } else { "" };
1196 let range = if *w == AW_WIDE { "[AW-1:0]".to_string() } else { format!("[{}:0]", w - 1) };
1197 e.l(format!(" reg {s}{range} {n};"));
1198 }
1199
1200 e.l(" // tile walk: element offset of a tile read or write");
1202 e.l(" reg [AW-1:0] tw_lo, tw_w, tw_off;");
1203 e.l(" // accumulator walk: the element's row and column in the accumulator block");
1204 e.l(" reg [AW-1:0] aw_cc, aw_r, aw_j, aw_i;");
1205 e.l(format!(" wire [AW-1:0] aw_row = (aw_i << {}) + aw_r;", log2(tr)));
1206 e.l(format!(" wire [AW-1:0] aw_col = (aw_j << {}) + aw_cc;", log2(tc)));
1207 e.l(" // unary and multicast walks");
1208 e.l(" reg [AW-1:0] un_e, un_e2, un_bk, un_bt, mc_e, mc_pg;");
1209 e.l(format!(" reg [{}:0] unary_out;", wd.un_out - 1));
1210 e.l(" wire mu_reload;");
1211
1212 let (banks, rows) = (sh.banks, sh.rows);
1214 e.l(format!(" // accumulator: {banks} banks of {rows} rows"));
1215 for c in 0..banks {
1217 e.l(format!(" (* ram_style = \"registers\" *) reg signed [{}:0] acc_b{c} [0:{}];", wd.acc - 1, rows - 1));
1218 }
1219 e.l(format!(" reg signed [{}:0] sr_d;", wd.acc - 1));
1221 {
1222 let mut r = String::from(" always @* begin sr_d = 0; case (aw_col)");
1223 for c in 0..banks {
1224 r.push_str(&format!(" {c}: sr_d = acc_b{c}[aw_row];"));
1225 }
1226 r.push_str(" default: ; endcase end");
1227 e.l(r);
1228 }
1229 if ctx.runs() {
1232 let (l, lg) = (ctx.banks, log2_banks(ctx.banks));
1233 let banks = sh.banks;
1236 e.l(" reg [AW-1:0] ar_i, ar_j, ar_p, ar_r, ar_rr_r, ar_rp_r; reg ar_first;");
1237 e.l(format!(" reg [{lg}:0] ar_n_r; reg [{}:0] ar_z_r;", banks - 1));
1238 e.l(" wire [AW-1:0] ar_l = 32'd1 << ar_lw;");
1239 e.l(format!(" wire [{lg}:0] ar_n = ar_first ? ar_n0 : ar_n_r;"));
1240 let z0: Vec<String> = (0..banks).rev().map(|c| format!("(32'd{c} < ar_n0)")).collect();
1241 e.l(format!(" wire [{}:0] ar_z = ar_first ? {{{}}} : ar_z_r;", banks - 1, z0.join(", ")));
1242 e.l(" wire [AW-1:0] ar_rr = ar_first ? aw_cols : ar_rr_r, ar_rp = ar_first ? ar_e : ar_rp_r;");
1246 e.l(" wire ar_adv = (ar_l < ar_rr);");
1247 e.l(" wire [AW-1:0] ar_jn = ar_adv ? ar_j + ar_l : 32'd0, ar_pn = ar_p + ar_n;");
1248 e.l(" wire [AW-1:0] ar_rln = ar_adv ? ar_rr - ar_l : aw_cols, ar_ln = ar_rp - ar_n, ar_rn = ar_adv ? ar_r + 1 : 32'd0;");
1249 e.l(" wire [AW-1:0] ar_mn = (ar_rln < ar_ln) ? ar_rln : ar_ln;");
1250 e.l(format!(" wire [{lg}:0] ar_nn = (ar_mn < ar_l) ? ar_mn[{lg}:0] : ar_l[{lg}:0];"));
1251 let zn: Vec<String> = (0..banks)
1255 .rev()
1256 .map(|c| {
1257 let (word, lane) = (format!("(32'd{c} >> ar_lw)"), format!("(32'd{c} & (ar_l - 1))"));
1258 format!(
1259 "(ar_adv ? ({word} == ar_r + 1 && {lane} < ar_rr - ar_l && {lane} < ar_ln) : ({word} == 0 && {lane} < aw_cols && {lane} < ar_ln))"
1260 )
1261 })
1262 .collect();
1263 e.l(format!(" wire [{}:0] ar_zn = {{{}}};", banks - 1, zn.join(", ")));
1264 e.l(" always @(posedge clk)");
1265 e.l(" if (rst || !(running && ar_on) || op_last) begin ar_i <= 0; ar_j <= 0; ar_p <= 0; ar_r <= 0; ar_first <= 1'b1; end");
1266 e.l(" else begin ar_first <= 1'b0; ar_n_r <= ar_nn; ar_z_r <= ar_zn; ar_p <= ar_pn; ar_j <= ar_jn; ar_r <= ar_rn; ar_rr_r <= ar_rln; ar_rp_r <= ar_ln; if (!ar_adv) ar_i <= ar_i + 1; end");
1267 let own: Vec<String> = (0..banks)
1271 .rev()
1272 .map(|c| format!("((32'd{c} >> ar_lw) == (ar_j >> ar_lw) && ((32'd{c} & (ar_l - 1)) < ar_n))"))
1273 .collect();
1274 e.l(" // synthesis translate_off");
1275 e.l(" always @(posedge clk) if (!rst && running && ar_on && ar_p + ar_n > ar_e) begin $display(\"FATAL: a run of %0d at %0d passes its page of %0d\", ar_n, ar_p, ar_e); $finish; end");
1276 e.l(format!(
1277 " always @(posedge clk) if (!rst && running && ar_on && ar_z != {{{}}}) begin $display(\"FATAL: the banks a run zeroes are not its own: %b at column %0d, length %0d\", ar_z, ar_j, ar_n); $finish; end",
1278 own.join(", ")
1279 ));
1280 e.l(" // synthesis translate_on");
1281 if lat == 0 {
1282 e.l(format!(" wire [AW-1:0] ar_wi = ar_i, ar_wj = ar_j, ar_wp = ar_p; wire [{lg}:0] ar_wn = ar_n;"));
1283 } else {
1284 for d in 1..=lat {
1285 let src = |x: &str| if d == 1 { x.to_string() } else { format!("{x}{}", d - 1) };
1286 e.l(format!(
1287 " reg [AW-1:0] ar_i{d}, ar_j{d}, ar_p{d}; reg [{lg}:0] ar_n{d}; always @(posedge clk) begin ar_i{d} <= {}; ar_j{d} <= {}; ar_p{d} <= {}; ar_n{d} <= {}; end",
1288 src("ar_i"),
1289 src("ar_j"),
1290 src("ar_p"),
1291 src("ar_n")
1292 ));
1293 }
1294 e.l(format!(
1295 " wire [AW-1:0] ar_wi = ar_dly ? ar_i{lat} : ar_i, ar_wj = ar_dly ? ar_j{lat} : ar_j, ar_wp = ar_dly ? ar_p{lat} : ar_p; wire [{lg}:0] ar_wn = ar_dly ? ar_n{lat} : ar_n;"
1296 ));
1297 }
1298 let acc = wd.acc;
1301 for j in 0..l {
1302 let step = (j + 1).next_power_of_two();
1303 let mut r = format!(" reg signed [{}:0] pk_a{j}; always @* case (ar_wj >> {})", acc - 1, step.trailing_zeros());
1304 for m in 0..banks.div_ceil(step) {
1305 if m * step + j < banks {
1306 r.push_str(&format!(" {m}: pk_a{j} = acc_b{}[ar_wi];", m * step + j));
1307 }
1308 }
1309 r.push_str(&format!(" default: pk_a{j} = 0; endcase"));
1310 e.l(r);
1311 let t = if acc > 32 {
1312 format!("pk_tr ? {{{{{}{{pk_a{j}[31]}}}}, pk_a{j}[31:0]}} : pk_a{j}", acc - 32)
1313 } else {
1314 format!("pk_a{j}")
1315 };
1316 e.l(format!(" wire signed [{}:0] pk_t{j} = {t};", acc - 1));
1317 }
1318 let packed: BTreeSet<BufferId> = micro
1320 .iter()
1321 .filter_map(|m| match m {
1322 Micro::Fixed(Fixed::PackAcc { to, .. }) => Some(ctx.template(*to)),
1323 _ => None,
1324 })
1325 .collect();
1326 let read: BTreeSet<BufferId> = uses.reads.iter().map(|b| ctx.template(*b)).collect();
1327 for tpl in &packed {
1328 let (k, w) = (tpl.idx(), ctx.buffer_width(*tpl));
1329 for j in 0..l {
1330 let page = if !read.contains(tpl) {
1331 "0".to_string()
1332 } else if j == 0 {
1333 format!("(ar_dly ? $signed(f{k}_rd_data) : 0)")
1334 } else {
1335 format!("(ar_dly ? $signed(f{k}_rd_hi[{}:{}]) : 0)", j as u32 * w - 1, (j as u32 - 1) * w)
1336 };
1337 e.l(format!(" wire signed [{}:0] pk{k}_v{j} = $signed(pk_t{j}) + {page};", w - 1));
1338 }
1339 }
1340 }
1341
1342 let pe = sh.array;
1344 if let Some((k, n, bits)) = pe {
1345 e.l(" reg pe_w_load, pe_w_load_r;");
1346 e.l(format!(" reg [{}:0] pe_w_row;", bits_for(k) - 1));
1347 e.l(format!(" reg [{}:0] pe_w_data;", n as u32 * bits - 1));
1348 for r in 0..k {
1349 e.l(format!(" reg signed [63:0] pe_a{r};"));
1350 }
1351 for c in 0..n {
1352 e.l(format!(" wire signed [63:0] pe_o{c};"));
1353 }
1354 let mut conn = vec![
1355 ".clk(clk)".to_string(),
1356 ".rst(rst)".to_string(),
1357 ".w_load(pe_w_load)".to_string(),
1358 ".w_row(pe_w_row)".to_string(),
1359 ".w_data(pe_w_data)".to_string(),
1360 ];
1361 conn.extend((0..k).map(|r| format!(".a{r}(pe_a{r})")));
1362 conn.extend((0..n).map(|c| format!(".o{c}(pe_o{c})")));
1363 e.l(format!(" pe_{name} u_pe({});", conn.join(", ")));
1364 e.l(" always @* pe_w_load = pe_w_load_r;");
1365 let ar = sh.a_rows;
1367 for s in 0..ar {
1368 for x in 0..k {
1369 e.l(format!(" reg signed [{}:0] a_r{s}_{x};", bits - 1));
1370 }
1371 }
1372 for r in 0..k {
1373 e.l(format!(" reg [{}:0] b_r{r};", n as u32 * bits - 1));
1374 }
1375 e.l(" reg [AW-1:0] ma_xx, ma_r, ma_kk, ma_i, mb_cc, mb_x, mb_j, mb_kk, mbi_t, mbi_off;");
1376 }
1377
1378 if g > 0 {
1382 let axb = ax - 1;
1383 let aw = wd.addr;
1384 let delay = |e: &mut Emit, name: &str, width: usize, taps: usize, sel: &str, first: &str| {
1386 for d in 1..=taps {
1387 let prev = if d == 1 { first.to_string() } else { format!("{name}_{}", d - 1) };
1388 e.l(format!(" reg [{}:0] {name}_{d}; always @(posedge clk) {name}_{d} <= {prev};", width - 1));
1389 }
1390 let mut t = format!(" reg [{}:0] {name}; always @* case ({sel}) 0: {name} = {first};", width - 1);
1391 for d in 1..=taps {
1392 t.push_str(&format!(" {d}: {name} = {name}_{d};"));
1393 }
1394 t.push_str(&format!(" default: {name} = {first}; endcase"));
1395 e.l(t);
1396 };
1397 e.l(format!(" reg signed [{}:0] g_kacc, g_fold, g_ofold;", wd.value - 1));
1398 e.l(format!(" reg [{axb}:0] g_fidx, g_oidx;"));
1399 e.l(format!(" reg [{axb}:0] gu_sub, gu_x, gu_mid, gu_out;"));
1400 e.l(format!(" wire [{axb}:0] gu_r = gu_rinner ? gu_mid : gu_out;"));
1401 e.l(format!(" wire [{axb}:0] gu_c = gu_rinner ? gu_out : gu_mid;"));
1402 e.l(" wire gs_v = gu_on && (ph < gu_iss) && (gu_sub == 0);");
1404 let lane_start = if vec {
1408 e.l(format!(" wire [{axb}:0] gu_li = (gu_lax == 2'd0) ? gu_r : (gu_lax == 2'd1) ? gu_c : gu_x;"));
1409 e.l(" wire gu_lat_last = (gu_li == gu_llast);");
1410 let bits: Vec<String> = (0..vl).rev().map(|l| format!("(gu_lat_last ? ({l} < gu_ltail) : ({l} < gu_lanes))")).collect();
1411 e.l(format!(" wire [{}:0] gs_lbase = {{4'd0, gu_li}} << gu_lsh;", ax + 3));
1413 format!("gs_lbase, {}, ", bits.join(", "))
1414 } else {
1415 String::new()
1416 };
1417 let gbw = 3 * ax + 1 + if vec { vl + ax + 4 } else { 0 };
1418 e.l(format!(" wire [{}:0] gb0 = {{{lane_start}gu_c, gu_r, gu_x, gs_v}};", gbw - 1));
1419 delay(&mut e, "gbo", gbw, sh.body_tap, "gu_body", "gb0");
1420 e.l(" wire gb_v = gu_on && gbo[0] && (ph >= gu_body);");
1421 e.l(format!(" wire [{axb}:0] gb_x = gbo[{}:1], gb_r = gbo[{}:{}], gb_c = gbo[{}:{}];", ax, 2 * ax, ax + 1, 3 * ax, 2 * ax + 1));
1422 let cwd = ax + 4;
1426 if vec {
1427 e.l(format!(" wire [{}:0] gb_lbase = gbo[{}:{}];", cwd - 1, 3 * ax + vl + cwd, 3 * ax + 1 + vl));
1428 for l in 0..vl {
1429 for (role, code, src) in [("r", 0, "gb_r"), ("c", 1, "gb_c"), ("x", 2, "gb_x")] {
1430 e.l(format!(" wire [{0}:0] gb_{role}_l{l} = (gu_lax == 2'd{code}) ? (gb_lbase | {cwd}'d{l}) : {{4'd0, {src}}};", cwd - 1));
1431 }
1432 e.l(format!(" wire gb_lv{l} = gbo[{}];", 3 * ax + 1 + l));
1433 }
1434 }
1435 for s in 0..g {
1436 e.l(format!(" reg signed [{}:0] gu_lin{s};", aw - 1));
1437 if vec {
1438 for l in 0..vl {
1439 e.l(format!(" reg signed [{}:0] gv{s}_l{l};", wd.slot - 1));
1440 }
1441 } else {
1442 e.l(format!(" reg signed [{}:0] gv{s};", wd.slot - 1));
1443 }
1444 }
1445 for s in 0..g {
1446 e.l(format!(" wire [{}:0] gi{s}_0 = {{gs_v, gu_pp{s}, gu_lin{s}}};", aw + 1));
1449 delay(&mut e, &format!("gi{s}"), aw as usize + 2, sh.issue_taps[s], &format!("gu_at{s}"), &format!("gi{s}_0"));
1450 e.l(format!(" wire gi_v{s} = gu_on && gi{s}[{}] && (ph >= gu_at{s}), gi_pp{s} = gi{s}[{aw}];", aw + 1));
1451 let gather = if sh.gathers.contains(&s) {
1457 let mut idx = String::new();
1458 for j in 0..g {
1459 let gv = if vec { format!("gv{j}_l0") } else { format!("gv{j}") };
1460 idx.push_str(&format!("(gu_gop{s} == 8'd{j}) ? $signed({gv}) : "));
1461 }
1462 idx.push('0');
1463 e.l(format!(" wire signed [{0}:0] gu_gx{s} = {idx}; reg signed [{0}:0] gu_gm{s};", aw - 1));
1464 e.l(format!(" always @(posedge clk) gu_gm{s} <= gu_gst{s} * gu_gx{s};"));
1465 format!(" + (gu_gon{s} ? gu_gm{s} : 0)")
1466 } else {
1467 String::new()
1468 };
1469 e.l(format!(" wire signed [{}:0] gu_off{s} = gu_base{s} + $signed(gi{s}[{}:0]){gather};", aw - 1, aw - 1));
1470 e.l(format!(" wire gu_in{s} = (gu_off{s} >= 0) && (gu_off{s} < gu_lim{s});"));
1471 let el = if aw > 32 { format!("gu_off{s}[31:0]") } else { format!("gu_off{s}") };
1472 e.l(format!(" wire [31:0] gu_el{s} = {el};"));
1473 if !vec {
1475 if lat == 0 {
1476 e.l(format!(" wire gl_v{s} = gi_v{s}, gl_in{s} = gu_in{s};"));
1477 } else {
1478 for d in 1..=lat {
1479 let prev = if d == 1 { format!("gi_v{s}, gu_in{s}") } else { format!("gl_v{s}_{0}, gl_in{s}_{0}", d - 1) };
1480 e.l(format!(" reg gl_v{s}_{d}, gl_in{s}_{d}; always @(posedge clk) {{gl_v{s}_{d}, gl_in{s}_{d}}} <= {{{prev}}};"));
1481 }
1482 e.l(format!(" wire gl_v{s} = gl_v{s}_{lat}, gl_in{s} = gl_in{s}_{lat};"));
1483 }
1484 e.l(format!(" always @(posedge clk) if (gl_v{s}) gv{s} <= gl_in{s} ? $signed(gu_ld{s}) : 0;"));
1485 delay(&mut e, &format!("g_op{s}"), wd.slot as usize, sh.hold_taps[s], &format!("gu_dl{s}"), &format!("gv{s}"));
1487 continue;
1488 }
1489 let lane_gather = sh.lane_gathers.contains(&s);
1493 for l in 1..vl {
1494 if lane_gather {
1495 let mut idx = String::new();
1496 for j in 0..g {
1497 idx.push_str(&format!("(gu_gop{s} == 8'd{j}) ? $signed(gv{j}_l{l}) : "));
1498 }
1499 idx.push('0');
1500 e.l(format!(" wire signed [{0}:0] gu_gx{s}_l{l} = {idx}; reg signed [{0}:0] gu_gm{s}_l{l};", aw - 1));
1501 e.l(format!(" always @(posedge clk) gu_gm{s}_l{l} <= gu_gst{s} * gu_gx{s}_l{l};"));
1502 e.l(format!(
1503 " wire signed [{}:0] gu_off{s}_l{l} = gu_base{s} + $signed(gi{s}[{}:0]) + (gu_gon{s} ? gu_gm{s}_l{l} : 0);",
1504 aw - 1,
1505 aw - 1
1506 ));
1507 let el = if aw > 32 { format!("gu_off{s}_l{l}[31:0]") } else { format!("gu_off{s}_l{l}") };
1508 e.l(format!(" wire [31:0] gu_el{s}_l{l} = {el};"));
1509 e.l(format!(
1510 " wire gu_in{s}_l{l} = gu_gon{s} ? ((gu_off{s}_l{l} >= 0) && (gu_off{s}_l{l} < gu_lim{s})) : gu_bc{s} ? gu_in{s} : ((gu_off{s} + {l} >= 0) && (gu_off{s} + {l} < gu_lim{s}));"
1511 ));
1512 } else {
1513 e.l(format!(" wire gu_in{s}_l{l} = gu_bc{s} ? gu_in{s} : ((gu_off{s} + {l} >= 0) && (gu_off{s} + {l} < gu_lim{s}));"));
1514 }
1515 }
1516 let lane_in = |l: usize| if l == 0 { format!("gu_in{s}") } else { format!("gu_in{s}_l{l}") };
1517 let ins: Vec<String> = (0..vl).rev().map(lane_in).collect();
1518 e.l(format!(" wire [{}:0] gu_ins{s} = {{{}}};", vl - 1, ins.join(", ")));
1519 if lat == 0 {
1520 e.l(format!(" wire gl_v{s} = gi_v{s}; wire [{}:0] gl_in{s} = gu_ins{s};", vl - 1));
1521 } else {
1522 for d in 1..=lat {
1523 let prev = if d == 1 { format!("gi_v{s}, gu_ins{s}") } else { format!("gl_v{s}_{0}, gl_in{s}_{0}", d - 1) };
1524 e.l(format!(" reg gl_v{s}_{d}; reg [{}:0] gl_in{s}_{d}; always @(posedge clk) {{gl_v{s}_{d}, gl_in{s}_{d}}} <= {{{prev}}};", vl - 1));
1525 }
1526 e.l(format!(" wire gl_v{s} = gl_v{s}_{lat}; wire [{}:0] gl_in{s} = gl_in{s}_{lat};", vl - 1));
1527 }
1528 for l in 0..vl {
1529 e.l(format!(" always @(posedge clk) if (gl_v{s}) gv{s}_l{l} <= gl_in{s}[{l}] ? $signed(gu_ld{s}_l{l}) : 0;"));
1530 delay(&mut e, &format!("g_op{s}_l{l}"), wd.slot as usize, sh.hold_taps[s], &format!("gu_dl{s}"), &format!("gv{s}_l{l}"));
1531 }
1532 }
1533 for lanes in &lane_pipes {
1534 for (lines, _, _, _) in lanes {
1535 for l in lines {
1536 e.l(l.clone());
1537 }
1538 }
1539 }
1540 e.l(" wire gu_pd = (gu_kx == 1) || (gb_x == gu_kx - 1);");
1541 e.l(format!(" wire [{axb}:0] gu_ia = gu_kind[0] ? gb_c : gb_r;"));
1542 let cs = 7 + 3 * ax;
1546 let cw = if vec { cs + vl } else { cs };
1547 let lane_bits = if vec { format!("{}, ", (0..vl).rev().map(|l| format!("gb_lv{l}")).collect::<Vec<_>>().join(", ")) } else { String::new() };
1548 e.l(format!(
1549 " wire [{}:0] gc0 = {{{lane_bits}(gu_kind == 2'd0 || gu_kind == 2'd2) ? gb_c : {ax}'d0, gb_r, gu_ia, gu_pd && ((gu_kind == 2'd0) || (gu_kind == 2'd1 && gb_c == gu_rinner_cx) || (gu_kind == 2'd2 && gb_r == gu_rx - 1) || (gu_kind == 2'd3 && gb_c == gu_rinner_cx && gb_r == gu_rx - 1)), gu_kind == 2'd3 && gu_pd && gb_c == gu_rinner_cx, gb_r == 0, gu_ia == 0, gu_pd, gu_kx == 1 || gb_x == 0, gb_v}};",
1550 cw - 1
1551 ));
1552 for d in 1..=dmax {
1553 e.l(format!(" reg [{}:0] gc{d}; always @(posedge clk) gc{d} <= gc{};", cw - 1, d - 1));
1554 }
1555 let taps: BTreeSet<u64> = sh
1558 .cls
1559 .op_class
1560 .iter()
1561 .filter_map(|(i, (c, _))| match µ[*i] {
1562 Micro::Fixed(Fixed::Generic { unit, .. }) => Some(pipes[*c].3 + unit.tree_levels()),
1563 _ => None,
1564 })
1565 .collect();
1566 let mut tap = format!(" reg [{}:0] gco; always @* case (gu_dep)", cw - 1);
1567 for d in &taps {
1568 tap.push_str(&format!(" {d}: gco = gc{d};"));
1569 }
1570 tap.push_str(" default: gco = gc0; endcase");
1571 e.l(tap);
1572 if vec {
1573 let class_taps: BTreeSet<u64> = pipes.iter().map(|p| p.3).collect();
1576 let mut tap = format!(" reg [{}:0] gcd; always @* case (gu_cdep)", cw - 1);
1577 for d in &class_taps {
1578 tap.push_str(&format!(" {d}: gcd = gc{d};"));
1579 }
1580 tap.push_str(" default: gcd = gc0; endcase");
1581 e.l(tap);
1582 e.l(format!(" wire [{}:0] gq_lv = gco[{}:{cs}], gd_lv = gcd[{}:{cs}];", vl - 1, cw - 1, cw - 1));
1583 e.l(format!(" wire [{axb}:0] gd_ia = gcd[{}:7];", 6 + ax));
1584 }
1585 e.l(" wire gq_v = gu_on && gco[0] && (ph >= gu_dep);");
1586 e.l(" wire gq_kfirst = gco[1], gq_pd = gco[2], gq_ia0 = gco[3], gq_r0 = gco[4], gq_ofold = gco[5], gq_we = gco[6];");
1587 e.l(format!(
1588 " wire [{axb}:0] gq_ia = gco[{}:7], gq_r = gco[{}:{}], gq_wcol = gco[{}:{}];",
1589 6 + ax,
1590 6 + 2 * ax,
1591 7 + ax,
1592 6 + 3 * ax,
1593 7 + 2 * ax
1594 ));
1595 e.l(" // synthesis translate_off");
1597 e.l(" always @(posedge clk) if (gq_v && (gq_r >= gu_rx || gq_wcol > gu_rinner_cx)) begin $display(\"FATAL: a point of row %0d, column %0d leaves the pipeline of an op of %0d rows and %0d columns\", gq_r, gq_wcol, gu_rx, gu_rinner_cx + 1); $finish; end");
1598 e.l(" // synthesis translate_on");
1599 let vw = wd.value - 1;
1600 if vec {
1601 for l in 0..vl {
1603 let mut v = format!(" wire signed [{vw}:0] gu_v_l{l} = ");
1604 for (c, lanes) in lane_pipes.iter().enumerate() {
1605 if let Some((_, _, root, _)) = lanes.get(l) {
1606 v.push_str(&format!("(gu_cls == 8'd{c}) ? $signed({root}) : "));
1607 }
1608 }
1609 v.push_str("0;");
1610 e.l(v);
1611 }
1612 if sh.tree_levels > 0 {
1616 for j in 0..vl {
1617 e.l(format!(" wire signed [{vw}:0] tv0_{j} = gu_v_l{j}; wire tval0_{j} = gd_lv[{j}]; wire [{axb}:0] tix0_{j} = (gd_ia << gu_lsh) | {ax}'d{j};"));
1618 }
1619 for lv in 1..=sh.tree_levels {
1620 for j in 0..(vl >> lv) {
1621 let (a, b) = (2 * j, 2 * j + 1);
1622 let p = lv - 1;
1623 e.l(format!(
1624 " wire tpick{lv}_{j} = !tval{p}_{a} || (tval{p}_{b} && (gu_tpool != 2'd0) && ($signed(tv{p}_{b}) > $signed(tv{p}_{a})));"
1625 ));
1626 e.l(format!(" reg signed [{vw}:0] tv{lv}_{j}; reg tval{lv}_{j}; reg [{axb}:0] tix{lv}_{j};"));
1627 e.l(format!(
1628 " always @(posedge clk) begin tval{lv}_{j} <= tval{p}_{a} || tval{p}_{b}; tix{lv}_{j} <= tpick{lv}_{j} ? tix{p}_{b} : tix{p}_{a}; tv{lv}_{j} <= (gu_tpool == 2'd0 && tval{p}_{a} && tval{p}_{b}) ? $signed(tv{p}_{a}) + $signed(tv{p}_{b}) : tpick{lv}_{j} ? tv{p}_{b} : tv{p}_{a}; end"
1629 ));
1630 }
1631 }
1632 let mut v = format!(" reg signed [{vw}:0] gu_v; reg [{axb}:0] gu_tix; always @* case (gu_tl)");
1633 v.push_str(" 0: begin gu_v = gu_v_l0; gu_tix = gq_ia; end");
1634 for lv in 1..=sh.tree_levels {
1635 v.push_str(&format!(" {lv}: begin gu_v = tv{lv}_0; gu_tix = tix{lv}_0; end"));
1636 }
1637 v.push_str(" default: begin gu_v = gu_v_l0; gu_tix = gq_ia; end endcase");
1638 e.l(v);
1639 } else {
1640 e.l(format!(" wire signed [{vw}:0] gu_v = gu_v_l0; wire [{axb}:0] gu_tix = gq_ia;"));
1641 }
1642 } else {
1643 let mut v = format!(" wire signed [{}:0] gu_v = ", wd.value - 1);
1644 for (c, (_, _, root, _)) in pipes.iter().enumerate() {
1645 v.push_str(&format!("(gu_cls == 8'd{c}) ? $signed({root}) : "));
1646 }
1647 v.push_str("0;");
1648 e.l(v);
1649 }
1650 e.l(format!(" wire signed [{vw}:0] gu_pv = gq_kfirst ? gu_v : ($signed(g_kacc) + gu_v);"));
1652 e.l(format!(" reg fb_v, fb_pd, fb_ia0, fb_r0, fb_ofold, fb_we; reg [{axb}:0] fb_ia, fb_r, fb_wcol; reg signed [{vw}:0] fb_pv;"));
1655 let fb_ia = if vec { "gu_tix" } else { "gq_ia" };
1656 e.l(format!(" always @(posedge clk) begin fb_v <= gq_v && !op_last; fb_pd <= gq_pd; fb_ia0 <= gq_ia0; fb_r0 <= gq_r0; fb_ofold <= gq_ofold; fb_we <= gq_we; fb_ia <= {fb_ia}; fb_r <= gq_r; fb_wcol <= gq_wcol; fb_pv <= gu_pv; end"));
1657 for (pool, tag) in [(ReducePool::Sum, 0), (ReducePool::Max, 1), (ReducePool::ArgMax, 2)] {
1658 let (fv, fi) = fold_step(pool, "fb_ia0", "g_fold", "g_fidx", "fb_pv", "fb_ia");
1659 e.l(format!(" wire signed [{vw}:0] gu_fv{tag} = {fv}; wire [{axb}:0] gu_fi{tag} = {fi};"));
1660 }
1661 e.l(format!(" wire signed [{vw}:0] gu_fv = (gu_pool == 2'd2) ? gu_fv2 : (gu_pool == 2'd1) ? gu_fv1 : gu_fv0;"));
1662 e.l(format!(" wire [{axb}:0] gu_fi = (gu_pool == 2'd2) ? gu_fi2 : (gu_pool == 2'd1) ? gu_fi1 : gu_fi0;"));
1663 e.l(format!(" wire signed [{vw}:0] gu_ir = (gu_pool == 2'd2) ? $signed({{1'b0, gu_fi}}) : gu_fv;"));
1664 e.l(format!(" reg fc_v, fc_r0, fc_ofold, fc_we; reg [{axb}:0] fc_r, fc_wcol; reg signed [{vw}:0] fc_pv, fc_ir;"));
1666 e.l(" always @(posedge clk) begin fc_v <= fb_v && !op_last; fc_r0 <= fb_r0; fc_ofold <= fb_ofold; fc_we <= fb_we; fc_r <= fb_r; fc_wcol <= fb_wcol; fc_pv <= fb_pv; fc_ir <= gu_ir; end");
1667 for (pool, tag) in [(ReducePool::Sum, 0), (ReducePool::Max, 1), (ReducePool::ArgMax, 2)] {
1668 let (ov, oi) = fold_step(pool, "fc_r0", "g_ofold", "g_oidx", "fc_ir", "fc_r");
1669 e.l(format!(" wire signed [{vw}:0] gu_ov{tag} = {ov}; wire [{axb}:0] gu_oi{tag} = {oi};"));
1670 }
1671 e.l(format!(" wire signed [{vw}:0] gu_ov = (gu_rpool == 2'd2) ? gu_ov2 : (gu_rpool == 2'd1) ? gu_ov1 : gu_ov0;"));
1672 e.l(format!(" wire [{axb}:0] gu_oi = (gu_rpool == 2'd2) ? gu_oi2 : (gu_rpool == 2'd1) ? gu_oi1 : gu_oi0;"));
1673 e.l(format!(" wire signed [{vw}:0] gu_or = (gu_rpool == 2'd2) ? $signed({{1'b0, gu_oi}}) : gu_ov;"));
1674 if vec {
1675 e.l(format!(" reg [{}:0] fb_lv, fc_lv; always @(posedge clk) begin fb_lv <= gq_lv; fc_lv <= fb_lv; end", vl - 1));
1679 let pools: Vec<(ReducePool, u8)> =
1680 [(ReducePool::Sum, 0u8), (ReducePool::Max, 1), (ReducePool::ArgMax, 2)].into_iter().filter(|(p, _)| sh.lane_pools[pool_index(*p)]).collect();
1681 for l in 1..vl {
1682 e.l(format!(" reg signed [{vw}:0] fb_pv_l{l}, fc_pv_l{l};"));
1683 if sh.lane_contracts {
1684 e.l(format!(" reg signed [{vw}:0] g_kacc_l{l};"));
1685 e.l(format!(" wire signed [{vw}:0] gu_pv_l{l} = gq_kfirst ? gu_v_l{l} : ($signed(g_kacc_l{l}) + gu_v_l{l});"));
1686 } else {
1687 e.l(format!(" wire signed [{vw}:0] gu_pv_l{l} = gu_v_l{l};"));
1688 }
1689 e.l(format!(" always @(posedge clk) begin fb_pv_l{l} <= gu_pv_l{l}; fc_pv_l{l} <= fb_pv_l{l}; end"));
1690 if pools.is_empty() {
1691 continue;
1692 }
1693 e.l(format!(" reg signed [{vw}:0] g_fold_l{l}, fc_ir_l{l}; reg [{axb}:0] g_fidx_l{l};"));
1694 for (pool, tag) in &pools {
1695 let (fv, fi) = fold_step(*pool, "fb_ia0", &format!("g_fold_l{l}"), &format!("g_fidx_l{l}"), &format!("fb_pv_l{l}"), "fb_ia");
1696 e.l(format!(" wire signed [{vw}:0] gu_fv{tag}_l{l} = {fv}; wire [{axb}:0] gu_fi{tag}_l{l} = {fi};"));
1697 }
1698 let pick = |what: &str| -> String {
1699 let (last, rest) = pools.split_last().expect("a pool");
1700 let mut x = String::new();
1701 for (_, tag) in rest {
1702 x.push_str(&format!("(gu_pool == 2'd{tag}) ? {what}{tag}_l{l} : "));
1703 }
1704 format!("{x}{what}{}_l{l}", last.1)
1705 };
1706 e.l(format!(" wire signed [{vw}:0] gu_fv_l{l} = {};", pick("gu_fv")));
1707 e.l(format!(" wire [{axb}:0] gu_fi_l{l} = {};", pick("gu_fi")));
1708 let ir = if sh.lane_pools[pool_index(ReducePool::ArgMax)] {
1709 format!("(gu_pool == 2'd2) ? $signed({{1'b0, gu_fi_l{l}}}) : gu_fv_l{l}")
1710 } else {
1711 format!("gu_fv_l{l}")
1712 };
1713 e.l(format!(" wire signed [{vw}:0] gu_ir_l{l} = {ir};"));
1714 e.l(format!(" always @(posedge clk) fc_ir_l{l} <= gu_ir_l{l};"));
1715 }
1716 }
1717 }
1718 let needs_grid = micro.iter().any(|m| matches!(m, Micro::Fixed(Fixed::Unary { kind: UnaryKind::Scalar(_), .. })));
1720 if needs_grid {
1721 let gname = grid_names.get(&plan.class).cloned().unwrap_or_else(|| format!("g{}", plan.class));
1722 e.l(" wire [63:0] grid_result;");
1723 e.l(format!(" grid_{gname} u_grid(.in0(unary_in), .result(grid_result));"));
1724 } else {
1725 e.l(" wire grid_result = 1'b0;");
1726 }
1727
1728 let mut comb = Emit { s: String::new() };
1730 comb.l(" always @* begin");
1731 for b in &bufs {
1732 let k = tp(*b);
1733 if uses.produces.contains(b) {
1734 comb.l(format!(" f{k}_reserve = 1'b0; f{k}_reserve_n = 0; f{k}_push = 1'b0; f{k}_push_n = 0;"));
1735 }
1736 if uses.consumes.contains(b) {
1737 comb.l(format!(" f{k}_pop = 1'b0; f{k}_pop_n = 0;"));
1738 }
1739 if uses.writes.contains(b) {
1740 comb.l(format!(" f{k}_wr_en = 1'b0; f{k}_wr_slot = 0; f{k}_wr_elem = 0; f{k}_wr_data = 64'd0;"));
1741 if ctx.banks > 1 {
1742 comb.l(format!(" f{k}_wr_n = 1; f{k}_wr_lw = 0; f{k}_wr_hi = 0;"));
1743 }
1744 }
1745 if uses.reads.contains(b) {
1746 comb.l(format!(" f{k}_rd_slot = 0; f{k}_rd_elem = 0;"));
1747 for l in 1..gathered.get(&ctx.template(*b)).copied().unwrap_or(1) {
1748 comb.l(format!(" f{k}_l{l}_rd_slot = 0; f{k}_l{l}_rd_elem = 0;"));
1749 }
1750 }
1751 }
1752 for t in &t_rd {
1753 comb.l(format!(" t{}_rd_addr = 32'd0;", t.idx()));
1754 }
1755 for t in &t_wr {
1756 comb.l(format!(" t{0}_wr_en = 1'b0; t{0}_wr_addr = 32'd0; t{0}_wr_data = 64'd0;", t.idx()));
1757 if ctx.banks > 1 {
1758 comb.l(format!(" t{0}_wr_n = 1; t{0}_wr_lw = 0; t{0}_wr_hi = 0;", t.idx()));
1759 }
1760 }
1761 for s in &s_local {
1762 comb.l(format!(" s{0}_set = 1'b0; s{0}_set_val = 16'd0;", s.idx()));
1763 }
1764 for s in &s_inc {
1765 comb.l(format!(" s{0}_inc_remote = 1'b0; s{0}_inc_n = 16'd0;", s.idx()));
1766 }
1767 for (s, gr) in &m_valid {
1768 comb.l(format!(" mv{}_g{} = 1'b0;", s.idx(), gr.idx()));
1769 }
1770 for gr in &m_send {
1771 comb.l(format!(" mc{0}_send = 1'b0; mc{0}_din = 80'd0;", gr.idx()));
1772 }
1773 {
1774 let mut d = String::from(" ");
1775 for (n, _, _) in &live {
1776 d.push_str(&format!(" {n} = 0;"));
1777 }
1778 comb.l(d);
1779 }
1780 comb.l(" if (running) case (pc)");
1781
1782 let mut seq = Emit { s: String::new() };
1784 seq.l(" always @* begin");
1785 {
1786 let mut hold = String::from(" pc_n = pc; ph_n = ph; running_n = running; done_n = done;");
1787 for v in &lvs {
1788 hold.push_str(&format!(" {0}_n = {0};", var_name(*v)));
1789 }
1790 seq.l(hold);
1791 }
1792 seq.l(" if (!running && start && !done) begin running_n = 1'b1; pc_n = 0; ph_n = 0; end");
1793 seq.l(" else if (running) case (pc)");
1794 let mut nexts: Vec<(usize, Next)> = Vec::new();
1797 let mut rows: BTreeMap<usize, String> = BTreeMap::new();
1798 let mut counts: BTreeMap<usize, String> = BTreeMap::new();
1799
1800 let mut bound: BTreeSet<LoopVar> = BTreeSet::new();
1801 for (i, m) in micro.iter().enumerate() {
1802 let next = i + 1;
1803 match m {
1804 Micro::LoopStart { var, count, end } => {
1805 bound.insert(*var);
1806 let c = if *var == LV_ITEM { "ITEM_COUNT".to_string() } else { count.to_string() };
1807 let to = Next::when(format!("({c} == 0)"), Next::To(end + 1), Next::To(next));
1808 seq.l(format!(" {i}: begin {}_n = 0; pc_n = {}; end", var_name(*var), to.expr()));
1809 nexts.push((i, to));
1810 }
1811 Micro::LoopEnd { var, count, start } => {
1812 bound.remove(var);
1813 let last = if *var == LV_ITEM { "(ITEM_COUNT - 1)".to_string() } else { count.saturating_sub(1).to_string() };
1816 let v = var_name(*var);
1817 let to = Next::when(format!("({v} != {last})"), Next::To(start + 1), Next::To(next));
1818 seq.l(format!(" {i}: begin {v}_n = {v} + 1; pc_n = {}; end", to.expr()));
1819 nexts.push((i, to));
1820 }
1821 Micro::Then(_) => {}
1823 Micro::Guarded(gd) => {
1824 let cond = guard_cond(ctx, gd);
1825 if let Guard::Reserve { buf, pages } = gd {
1826 let k = ctx.template(*buf).idx();
1827 comb.l(format!(" {i}: begin f{k}_reserve = {cond}; f{k}_reserve_n = {pages}; end"));
1828 }
1829 let to = Next::when(cond, Next::To(next), Next::To(i));
1830 seq.l(format!(" {i}: pc_n = {};", to.expr()));
1831 nexts.push((i, to));
1832 }
1833 Micro::Fixed(f) => {
1834 let d = ctx.duration(f);
1835 let exit = super::exit_of(micro, i, plan.site);
1837 let check = |e: &super::Early| -> String {
1838 use super::Early;
1839 match *e {
1840 Early::Count(_, n) | Early::Free(_, n) | Early::AtLeast(_, n) if n <= 0 => "1'b1".into(),
1841 Early::Count(b, n) => format!("(f{}_count >= {n})", ctx.template(b).idx()),
1842 Early::Free(b, n) => format!("(f{}_free >= {n})", ctx.template(b).idx()),
1843 Early::AtLeast(sem, n) => format!("(s{}_count >= {n})", sem.idx()),
1844 Early::Equals(sem, n) => format!("(s{}_count == {n})", sem.idx()),
1845 }
1846 };
1847 let branch = |(at, checks): &(usize, Vec<(usize, super::Early)>)| -> (Next, Option<String>) {
1848 match checks.last() {
1849 Some((last, _)) => {
1850 let all = checks.iter().map(|(_, e)| check(e)).collect::<Vec<_>>().join(" && ");
1851 (Next::when(format!("({all})"), Next::To(last + 1), Next::To(*at)), Some(all))
1852 }
1853 None => (Next::To(*at), None),
1854 }
1855 };
1856 let count = |var: LoopVar, count: usize| if var == LV_ITEM { "ITEM_COUNT".to_string() } else { count.to_string() };
1857 let final_count = |var: LoopVar, count: usize| if var == LV_ITEM { "(ITEM_COUNT - 1)".to_string() } else { count.saturating_sub(1).to_string() };
1858 let (to, steps, taken): (Next, String, Vec<String>) = match exit.bound {
1859 None => (branch(&exit.branches[0]).0, String::new(), vec!["1'b1".into()]),
1860 Some(super::Bound::End { var, count: c, .. }) => {
1861 let v = var_name(var);
1862 let runs = format!("({v} != {})", final_count(var, c));
1863 (
1864 Next::when(runs.clone(), branch(&exit.branches[0]).0, branch(&exit.branches[1]).0),
1865 format!(" {v}_n = {v} + 1;"),
1866 vec![runs.clone(), format!("!{runs}")],
1867 )
1868 }
1869 Some(super::Bound::Start { var, count: c, .. }) => {
1870 let empty = format!("({} == 0)", count(var, c));
1871 (
1872 Next::when(empty.clone(), branch(&exit.branches[1]).0, branch(&exit.branches[0]).0),
1873 format!(" {}_n = 0;", var_name(var)),
1874 vec![format!("!{empty}"), empty.clone()],
1875 )
1876 }
1877 };
1878 seq.l(format!(" {i}: if (op_last) begin ph_n = 0;{steps} pc_n = {}; end else ph_n = ph + 1;", to.expr()));
1879 counts.insert(i, format!(" op_left <= {}; op_last <= {};", d.max(1) - 1, if d <= 1 { "1'b1" } else { "1'b0" }));
1880 let mut c = format!(" {i}: begin");
1881 for (b, taken) in exit.branches.iter().zip(&taken) {
1883 let Some(all) = branch(b).1 else { continue };
1884 let mut takes = String::new();
1885 for (g, _) in &b.1 {
1886 if let Micro::Guarded(Guard::Reserve { buf, pages }) = µ[*g] {
1887 let k = ctx.template(*buf).idx();
1888 takes.push_str(&format!(" f{k}_reserve = 1'b1; f{k}_reserve_n = {pages};"));
1889 }
1890 }
1891 if !takes.is_empty() {
1892 c.push_str(&format!(" if (op_last && {taken} && {all}) begin{takes} end"));
1893 }
1894 }
1895 let after = super::after_effects(micro, i);
1896 let mut entries: Vec<(String, String)> = vec![("fx".into(), "1'b1".into())];
1897 print_op(ctx, &sh, &wd, ®ions, i, f, &bound, &mut entries, &mut c)?;
1898 for (j, m) in micro.iter().enumerate().take(after).skip(i + 1) {
1900 if let Micro::Then(e) = m {
1901 let mut drives = String::new();
1902 let mut none = Vec::new();
1903 print_op(ctx, &sh, &wd, ®ions, j, e, &bound, &mut none, &mut drives)?;
1904 debug_assert!(none.is_empty(), "an effect sets no unit parameter");
1905 if !drives.is_empty() {
1906 c.push_str(&format!(" if (op_last) begin{drives} end"));
1907 }
1908 }
1909 }
1910 c.push_str(" end");
1911 comb.l(c);
1912 rows.insert(i, entries.iter().map(|(n, v)| format!(" {n} <= {v};")).collect());
1913 nexts.push((i, Next::when("(op_last)".into(), to, Next::To(i))));
1914 }
1915 }
1916 }
1917 let fin = micro.len();
1918 seq.l(format!(" {fin}: begin running_n = 1'b0; done_n = 1'b1; end"));
1919 seq.l(" default: pc_n = 0;");
1920 seq.l(" endcase");
1921 seq.l(" end");
1922 {
1923 let mut reset = String::from(" always @(posedge clk) if (rst) begin pc <= 0; ph <= 0; running <= 1'b0; done <= 1'b0;");
1924 let mut r = String::from(" end else begin pc <= pc_n; ph <= ph_n; running <= running_n; done <= done_n;");
1925 for v in &lvs {
1926 reset.push_str(&format!(" {} <= 0;", var_name(*v)));
1927 r.push_str(&format!(" {0} <= {0}_n;", var_name(*v)));
1928 }
1929 r.push_str(" end");
1930 seq.l(format!("{reset}{r}"));
1931 }
1932 comb.l(" default: ;");
1933 comb.l(" endcase");
1934 comb.l(" end");
1935
1936 let row = |s: usize| rows.get(&s).cloned().unwrap_or_default();
1941 let mut tab = Emit { s: String::new() };
1942 tab.l(" always @(posedge clk) begin");
1943 {
1944 let mut d = String::from(" ");
1945 for (n, _, _) in &table {
1946 d.push_str(&format!(" {n} <= 0;"));
1947 }
1948 tab.l(d);
1949 }
1950 tab.l(format!(" if (rst) ; else if (!running) begin if (start && !done) begin{} end end", row(0)));
1951 tab.l(" else case (pc)");
1952 for (i, nx) in &nexts {
1953 tab.l(format!(" {i}: begin{} end", nx.load(&row)));
1954 }
1955 tab.l(format!(" {fin}: ;"));
1956 tab.l(format!(" default: begin{} end", row(0)));
1957 tab.l(" endcase");
1958 tab.l(" end");
1959 let zero = " op_left <= 0; op_last <= 1'b0;".to_string();
1962 let count = |s: usize| counts.get(&s).cloned().unwrap_or_else(|| zero.clone());
1963 let mut last = Emit { s: String::new() };
1964 last.l(" wire fresh = !running || !fx || op_last;");
1965 last.l(" always @(posedge clk)");
1966 last.l(" if (rst) begin op_left <= 0; op_last <= 1'b0; end");
1967 last.l(format!(" else if (!running) begin if (start && !done) begin{} end else begin{zero} end end", count(0)));
1968 last.l(" else if (fresh) case (pc)");
1969 for (i, nx) in &nexts {
1970 last.l(format!(" {i}: begin{} end", nx.load(&count)));
1971 }
1972 last.l(format!(" {fin}: begin{zero} end"));
1973 last.l(format!(" default: begin{} end", count(0)));
1974 last.l(" endcase");
1975 last.l(" else begin op_left <= op_left - 1; op_last <= (op_left == 1); end");
1976
1977 let units = units(ctx, &sh, &wd, te, tr, tc, lat);
1978 let top = ctx.phase_flags.borrow().iter().copied().max().unwrap_or(0);
1984 let ks: Vec<u64> = (1..=top).collect();
1985 if !ks.is_empty() {
1986 let flags: Vec<String> = ks.iter().map(|k| format!("ph_ge{k}")).collect();
1987 e.l(format!(" reg {};", flags.join(", ")));
1988 }
1989 e.s.push_str(&comb.s);
1990 e.s.push_str(&tab.s);
1991 e.s.push_str(&last.s);
1992 e.s.push_str(&seq.s);
1993 if !ks.is_empty() {
1994 let clear: String = ks.iter().map(|k| format!(" ph_ge{k} <= 1'b0;")).collect();
1995 let shift: String = ks.iter().map(|k| if *k == 1 { " ph_ge1 <= 1'b1;".to_string() } else { format!(" ph_ge{k} <= ph_ge{};", k - 1) }).collect();
1996 e.l(format!(
1997 " always @(posedge clk) if (rst || (running && fx && op_last) || (!running && start && !done)) begin{clear} end else if (running && fx) begin{shift} end"
1998 ));
1999 }
2000 e.l(" // synthesis translate_off");
2004 for k in &ks {
2005 e.l(format!(" always @(posedge clk) if (!rst && ph_ge{k} != (ph >= {k})) begin $display(\"FATAL: ph_ge{k} is %0d at phase %0d\", ph_ge{k}, ph); $finish; end"));
2006 }
2007 let held: Vec<&str> = table.iter().map(|(n, _, _)| n.as_str()).collect();
2008 if !held.is_empty() {
2009 e.l(format!(
2010 " always @(posedge clk) if (!rst && !running && (|{{{}}})) begin $display(\"FATAL: an idle engine holds a row of its op table\"); $finish; end",
2011 held.join(", ")
2012 ));
2013 }
2014 e.l(" // synthesis translate_on");
2015 e.s.push_str(&units);
2016 e.l("endmodule");
2017 let at_aw: BTreeSet<&str> = table.iter().filter(|(_, w, _)| *w == AW_WIDE).map(|(n, _, _)| n.as_str()).collect();
2021 let mut bound = p.tensors.iter().map(|t| (t.elems.rows * t.elems.cols) as u64).max().unwrap_or(1).max(longest + 2);
2022 for row in rows.values() {
2023 for stmt in row.split(';') {
2024 if let Some((n, v)) = stmt.split_once("<=") {
2025 if let (true, Ok(v)) = (at_aw.contains(n.trim()), v.trim().parse::<i64>()) {
2026 bound = bound.max(v.unsigned_abs());
2027 }
2028 }
2029 }
2030 }
2031 let aw = 64 - bound.leading_zeros() + 1;
2032 Ok(e.s.replacen("__AW__", &aw.to_string(), 1))
2033}
2034
2035enum Next {
2039 To(usize),
2040 If(String, Box<Next>, Box<Next>),
2041}
2042
2043impl Next {
2044 fn when(cond: String, then: Next, other: Next) -> Next {
2045 Next::If(cond, Box::new(then), Box::new(other))
2046 }
2047
2048 fn expr(&self) -> String {
2050 match self {
2051 Next::To(s) => s.to_string(),
2052 Next::If(c, a, b) => format!("({c} ? {} : {})", a.expr(), b.expr()),
2053 }
2054 }
2055
2056 fn load(&self, row: &dyn Fn(usize) -> String) -> String {
2058 match self {
2059 Next::To(s) => row(*s),
2060 Next::If(c, a, b) => format!(" if {c} begin{} end else begin{} end", a.load(row), b.load(row)),
2061 }
2062 }
2063}
2064
2065fn guard_cond(ctx: &Ctx, g: &Guard) -> String {
2067 match g {
2068 Guard::Reserve { buf, pages } => format!("(f{}_free >= {pages})", ctx.template(*buf).idx()),
2069 Guard::Wait { buf, pages } => format!("(f{}_count >= {pages})", ctx.template(*buf).idx()),
2070 Guard::SemWait { sem, until } => match until {
2071 SemCond::Equals(n) => format!("(s{}_count == {n})", sem.idx()),
2072 SemCond::AtLeast(n) => format!("(s{}_count >= {n})", sem.idx()),
2073 },
2074 Guard::Link { ordinal, .. } => format!("(link_n > 8'd{ordinal})"),
2075 }
2076}
2077
2078#[allow(clippy::too_many_arguments)]
2081fn print_op(
2082 ctx: &Ctx,
2083 sh: &Shape,
2084 wd: &Widths,
2085 regions: &[Option<usize>],
2086 i: usize,
2087 f: &Fixed,
2088 bound: &BTreeSet<LoopVar>,
2089 table: &mut Vec<(String, String)>,
2090 c: &mut String,
2091) -> Result<(), SpatialLowerError> {
2092 let (te, tc, lat) = (ctx.te, ctx.tc, ctx.lat);
2093 let cols = regions[i].unwrap_or(1);
2094 let mut set = |n: &str, v: String| table.push((n.to_string(), v));
2095 match f {
2096 Fixed::Push { buf, pages } => {
2097 let k = ctx.template(*buf).idx();
2098 c.push_str(&format!(" f{k}_push = 1'b1; f{k}_push_n = {pages};"));
2099 }
2100 Fixed::Pop { buf, pages } => {
2101 let k = ctx.template(*buf).idx();
2102 c.push_str(&format!(" f{k}_pop = 1'b1; f{k}_pop_n = {pages};"));
2103 }
2104 Fixed::Barrier | Fixed::ReleaseAcc => {}
2105 Fixed::SemSet { sem, value } => {
2106 let v = match value {
2107 SemValue::Invalid => 0,
2108 SemValue::Valid => 1,
2109 SemValue::Count(n) => *n,
2110 };
2111 c.push_str(&format!(" s{0}_set = 1'b1; s{0}_set_val = {v};", sem.idx()));
2112 }
2113 Fixed::SemInc { sem, by, .. } => {
2114 c.push_str(&format!(" s{0}_inc_remote = 1'b1; s{0}_inc_n = {by};", sem.idx()));
2115 }
2116 Fixed::McastSetValid { sem, group } => {
2117 c.push_str(&format!(" mv{}_g{} = 1'b1;", sem.idx(), group.idx()));
2118 }
2119 Fixed::McastSend { buf, pages, group } => {
2120 let k = ctx.template(*buf).idx();
2121 let pe = ctx.page_elems(*buf);
2122 let total = pages * pe;
2123 let depth = ctx.depth(*buf);
2124 let gr = group.idx();
2125 set("mc_on", "1'b1".into());
2126 set("mc_pe", pe.to_string());
2127 let ph_lt_total = ctx.ph_lt(total as u64);
2129 c.push_str(&format!(
2130 " if ({ph_lt_total}) begin f{k}_rd_slot = (f{k}_fill_slot0_r + mc_pg >= {depth}) ? f{k}_fill_slot0_r + mc_pg - {depth} : f{k}_fill_slot0_r + mc_pg; f{k}_rd_elem = mc_e; end"
2131 ));
2132 let elem = if lat == 0 { "ph[15:0]".to_string() } else { format!("ph[15:0] - 16'd{lat}") };
2133 let live = format!("{} && {}", ctx.ph_ge(lat), ctx.ph_lt(total as u64 + lat));
2134 c.push_str(&format!(" if ({live}) begin mc{gr}_send = 1'b1; mc{gr}_din = {{{elem}, f{k}_rd_data}}; end"));
2135 }
2136 Fixed::ReadTiles { tensor, at, block_w, stride_w, stride_h, into, section, .. } => {
2137 let k = ctx.template(*into).idx();
2138 let addr = addr_v(at, bound, item_coord_names(*section).as_tuple(), ctx);
2139 let gather = index_read(ctx, at);
2140 let skew = ctx.gather_skew(at);
2143 let (d1, d2) = walk_deltas(te, *block_w, *stride_w, *stride_h);
2144 let t = tensor.idx();
2145 let cp = ph_less(skew + lat);
2146 let land = from_phase(ctx,
2147 skew + lat,
2148 &format!("f{k}_wr_en = 1'b1; f{k}_wr_slot = f{k}_fill_slot0; f{k}_wr_elem = {cp}; f{k}_wr_data = t{t}_rd_data;"),
2149 );
2150 set("tw_on", "1'b1".into());
2151 set("tw_from", skew.to_string());
2152 set("tw_bw", block_w.to_string());
2153 if let Some((_, pitch)) = ctx.page_pitch.get(&ctx.template(*into)) {
2154 set("tw_pp", (pitch - block_w).to_string());
2156 }
2157 if ctx.walks_runs(f) {
2158 let (wt, wf) = (tensor_width(ctx.p.tensor(*tensor), &ctx.ranges), ctx.buffer_width(*into));
2160 let hi = resize_lanes(&format!("t{t}_rd_hi"), wt, wf, ctx.banks - 1);
2161 let land = from_phase(ctx,
2162 skew + lat,
2163 &format!("f{k}_wr_en = 1'b1; f{k}_wr_slot = f{k}_fill_slot0; f{k}_wr_elem = tw_pd; f{k}_wr_n = tw_nd; f{k}_wr_lw = tw_lw; f{k}_wr_data = t{t}_rd_data; f{k}_wr_hi = {hi};"),
2164 );
2165 set("tw_vec", "1'b1".into());
2166 set("tw_lw", ctx.run(f).trailing_zeros().to_string());
2167 set("tw_n0", (*block_w).min(ctx.run(f)).to_string());
2168 set("tw_rs", stride_h.to_string());
2169 c.push_str(&format!("{gather} t{t}_rd_addr = ({addr}) + tw_vo; {land}"));
2170 } else {
2171 set("tw_d1", d1.to_string());
2172 set("tw_d2", d2.to_string());
2173 c.push_str(&format!("{gather} t{t}_rd_addr = ({addr}) * {te} + tw_off; {land}"));
2174 }
2175 }
2176 Fixed::WriteTiles { from, tensor, at, block_w, stride_w, stride_h, section, .. } => {
2177 let k = ctx.template(*from).idx();
2178 if at.indirect.is_some_and(|ind| ctx.template(ind.index) == ctx.template(*from)) {
2179 return Err(SpatialLowerError::Unsupported(
2180 "a tile scatter whose index sits in the buffer it drains — one read port cannot read both in a cycle",
2181 ));
2182 }
2183 let addr = addr_v(at, bound, item_coord_names(*section).as_tuple(), ctx);
2184 let gather = index_read(ctx, at);
2185 let (d1, d2) = walk_deltas(te, *block_w, *stride_w, *stride_h);
2188 let t = tensor.idx();
2189 set("tw_on", "1'b1".into());
2190 set("tw_bw", block_w.to_string());
2191 if ctx.run(f) > 1 {
2192 let (wt, wf) = (tensor_width(ctx.p.tensor(*tensor), &ctx.ranges), ctx.buffer_width(*from));
2194 let hi = resize_lanes(&format!("f{k}_rd_hi"), wf, wt, ctx.banks - 1);
2195 let store = from_phase(ctx,
2196 lat,
2197 &format!("t{t}_wr_en = 1'b1; t{t}_wr_addr = ({addr}) + tw_vod; t{t}_wr_n = tw_nd; t{t}_wr_lw = tw_lw; t{t}_wr_data = f{k}_rd_data; t{t}_wr_hi = {hi};"),
2198 );
2199 set("tw_from", "0".into());
2200 set("tw_vec", "1'b1".into());
2201 set("tw_lw", ctx.run(f).trailing_zeros().to_string());
2202 set("tw_n0", (*block_w).min(ctx.run(f)).to_string());
2203 set("tw_rs", stride_h.to_string());
2204 c.push_str(&format!("{gather} f{k}_rd_slot = f{k}_rd_slot0; f{k}_rd_elem = tw_p; {store}"));
2205 } else {
2206 let store = from_phase(ctx, lat, &format!("t{t}_wr_en = 1'b1; t{t}_wr_addr = ({addr}) * {te} + tw_off; t{t}_wr_data = f{k}_rd_data;"));
2207 set("tw_from", lat.to_string());
2208 set("tw_d1", d1.to_string());
2209 set("tw_d2", d2.to_string());
2210 c.push_str(&format!("{gather} f{k}_rd_slot = f{k}_rd_slot0; f{k}_rd_elem = ph; {store}"));
2211 }
2212 }
2213 Fixed::AcquireAcc { tiles, .. } => {
2214 set("aw_cols", cols.to_string());
2215 if ctx.acc_run(f) > 1 {
2216 set("ar_on", "1'b1".into());
2218 set("ar_lw", ctx.acc_run(f).trailing_zeros().to_string());
2219 set("ar_e", (tiles * te).to_string());
2220 set("ar_n0", (tiles * te).min(cols).min(ctx.acc_run(f)).to_string());
2221 c.push_str(" az_we = 1'b1;");
2222 } else {
2223 c.push_str(" aw_step = 1'b1; sw_we = 1'b1; sw_d = 0;");
2224 }
2225 }
2226 Fixed::PackAcc { to, tiles, trunc32, accumulate, .. } => {
2227 let k = ctx.template(*to).idx();
2228 let skew = if *accumulate { lat } else { 0 };
2230 set("aw_cols", cols.to_string());
2231 if ctx.acc_run(f) > 1 {
2232 set("ar_on", "1'b1".into());
2234 set("ar_lw", ctx.acc_run(f).trailing_zeros().to_string());
2235 set("ar_e", (tiles * te).to_string());
2236 set("ar_n0", (tiles * te).min(cols).min(ctx.acc_run(f)).to_string());
2237 set("ar_dly", (*accumulate as u8).to_string());
2238 set("pk_tr", (*trunc32 as u8).to_string());
2239 if *accumulate {
2240 c.push_str(&format!(" f{k}_rd_slot = f{k}_fill_slot0; f{k}_rd_elem = ar_p;"));
2241 }
2242 let lanes: Vec<String> = (1..ctx.banks).rev().map(|l| format!("pk{k}_v{l}")).collect();
2243 let store = from_phase(ctx,
2244 skew,
2245 &format!(
2246 "f{k}_wr_en = 1'b1; f{k}_wr_slot = f{k}_fill_slot0; f{k}_wr_elem = ar_wp; f{k}_wr_n = ar_wn; f{k}_wr_lw = ar_lw; f{k}_wr_data = $signed(pk{k}_v0); f{k}_wr_hi = {{{}}};",
2247 lanes.join(", ")
2248 ),
2249 );
2250 c.push_str(&format!(" {store}"));
2251 } else {
2252 let cp = ph_less(skew);
2253 let v = if *trunc32 && wd.acc > 32 { "{{32{sr_d[31]}}, sr_d[31:0]}".to_string() } else { "sr_d".to_string() };
2255 let base = if *accumulate { format!("$signed(f{k}_rd_data) + ") } else { String::new() };
2256 if *accumulate {
2257 c.push_str(&format!(" f{k}_rd_slot = f{k}_fill_slot0; f{k}_rd_elem = ph;"));
2258 }
2259 let store = from_phase(ctx,
2260 skew,
2261 &format!("aw_step = 1'b1; f{k}_wr_en = 1'b1; f{k}_wr_slot = f{k}_fill_slot0; f{k}_wr_elem = {cp}; f{k}_wr_data = {base}$signed({v});"),
2262 );
2263 c.push_str(&format!(" {store}"));
2264 }
2265 }
2266 Fixed::Unary { kind, ins, .. } => {
2267 let k = ctx.template(ins[0]).idx();
2268 let pe = ctx.page_elems(ins[0]);
2269 set("un_on", "1'b1".into());
2270 set("un_pe", pe.to_string());
2271 c.push_str(&format!(" f{k}_rd_slot = f{k}_rd_slot0; f{k}_rd_elem = un_e; unary_in = f{k}_rd_data;"));
2272 let kind_code = match kind {
2273 UnaryKind::Requant { m, s } => {
2274 set("un_m", lit64(*m));
2275 set("un_s", s.to_string());
2276 0
2277 }
2278 UnaryKind::Bias => {
2279 let kb = ctx.template(ins[1]).idx();
2280 set("un_pb", ctx.page_elems(ins[1]).to_string());
2281 c.push_str(&format!(
2282 " f{kb}_rd_slot = f{kb}_rd_slot0; f{kb}_rd_elem = un_bt + (un_bk & {}); unary_bias = f{kb}_rd_data;",
2283 tc - 1
2284 ));
2285 1
2286 }
2287 UnaryKind::Relu => 2,
2288 UnaryKind::Elementwise(op) => {
2289 let kb = ctx.template(ins[1]).idx();
2290 set("un_pb", ctx.page_elems(ins[1]).to_string());
2291 c.push_str(&format!(" f{kb}_rd_slot = f{kb}_rd_slot0; f{kb}_rd_elem = un_e2; unary_bias = f{kb}_rd_data;"));
2292 match op {
2293 ElementwiseOp::Add => 4,
2294 ElementwiseOp::Mul => 5,
2295 }
2296 }
2297 UnaryKind::Scalar(_) => 6,
2298 };
2299 let store = from_phase(ctx, lat, "aw_step = 1'b1; sw_we = 1'b1;");
2300 set("un_kind", kind_code.to_string());
2301 set("aw_cols", cols.to_string());
2302 c.push_str(&format!(" sw_d = unary_out; {store}"));
2303 }
2304 Fixed::Generic { unit, ins, depth, .. } => {
2305 let (class, values) = &sh.cls.op_class[&i];
2306 let is = &unit.issue;
2307 let (rx, cx, kx) = (unit.points_along(unit.row.var), unit.points_along(unit.col.var), unit.k.map_or(1, |k| unit.points_along(k.var)));
2309 let (nmid, nout) = if unit.rows_inner() { (rx, cx) } else { (cx, rx) };
2310 use IterKind::{Parallel, Reduce};
2311 let (kind, pool, rpool) = match (unit.row.kind, unit.col.kind) {
2312 (Parallel, Parallel) => (0, ReducePool::Sum, ReducePool::Sum),
2313 (Parallel, Reduce { pool }) => (1, pool, ReducePool::Sum),
2314 (Reduce { pool }, Parallel) => (2, pool, ReducePool::Sum),
2315 (Reduce { pool: rp }, Reduce { pool }) => (3, pool, rp),
2316 };
2317 let code = |p: ReducePool| match p {
2318 ReducePool::Sum => 0,
2319 ReducePool::Max => 1,
2320 ReducePool::ArgMax => 2,
2321 };
2322 for (n, v) in [
2323 ("gu_on", "1'b1".to_string()),
2324 ("gu_ii", is.ii.to_string()),
2325 ("gu_kx", kx.to_string()),
2326 ("gu_nmid", nmid.to_string()),
2327 ("gu_nout", nout.to_string()),
2328 ("gu_rinner", (unit.rows_inner() as u8).to_string()),
2329 ("gu_cls", class.to_string()),
2330 ("gu_kind", kind.to_string()),
2331 ("gu_pool", code(pool).to_string()),
2332 ("gu_rpool", code(rpool).to_string()),
2333 ("gu_rinner_cx", (cx - 1).to_string()),
2334 ("gu_rx", rx.to_string()),
2335 ("gu_dep", (depth - FOLD_STAGES).to_string()),
2336 ("gu_iss", (unit.points() * is.ii as u64).to_string()),
2337 ("gu_body", is.body.to_string()),
2338 ] {
2339 set(n, v);
2340 }
2341 for (j, v) in values.iter().enumerate() {
2342 set(&format!("gp{class}_{j}"), lit64(*v));
2343 }
2344 let vec = sh.lanes > 1;
2345 let lsh = unit.lanes.trailing_zeros();
2346 if vec {
2347 let lv = unit.lane_var();
2348 let lax = if lv == unit.row.var { 0 } else if lv == unit.col.var { 1 } else { 2 };
2349 set("gu_lsh", lsh.to_string());
2350 set("gu_lax", lax.to_string());
2351 let last = unit.points_along(lv) - 1;
2352 set("gu_lanes", unit.lanes.to_string());
2353 set("gu_llast", last.to_string());
2354 set("gu_ltail", (unit.lane_extent() - last * unit.lanes).to_string());
2355 set("gu_tl", unit.tree_levels().to_string());
2356 set("gu_tpool", code(unit.tree_pool()).to_string());
2357 set("gu_cdep", (depth - FOLD_STAGES - unit.tree_levels()).to_string());
2358 if unit.lanes > 1 && !unit.tree() {
2359 set("gu_wsh", lsh.to_string());
2360 }
2361 }
2362 for (sl, m) in unit.maps.iter().enumerate() {
2363 let w = SlotWalk::of(ctx, unit, ins, sl);
2364 set(&format!("gu_base{sl}"), lit64(m.base));
2365 set(&format!("gu_d0{sl}"), lit64(w.d0));
2366 set(&format!("gu_d1{sl}"), lit64(w.d1));
2367 set(&format!("gu_d2{sl}"), lit64(w.d2));
2368 set(&format!("gu_lim{sl}"), lit64(w.lim));
2369 if let Some(ind) = m.indirect.filter(|_| unit.gathers(sl)) {
2370 set(&format!("gu_gon{sl}"), "1'b1".into());
2371 set(&format!("gu_gst{sl}"), lit64(ind.stride));
2372 set(&format!("gu_gop{sl}"), ind.operand.to_string());
2373 }
2374 set(&format!("gu_at{sl}"), is.at[sl].to_string());
2375 set(&format!("gu_dl{sl}"), (is.body - is.at[sl] - lat as usize - 1).to_string());
2376 if vec && unit.broadcast(sl) {
2377 set(&format!("gu_bc{sl}"), "1'b1".into());
2378 }
2379 let k = ctx.template(ins[sl]).idx();
2382 let role = axis_role(unit);
2383 let start_axis = |v: LoopVar| -> String {
2385 let wire = match role(v) {
2386 "r" => "gu_r",
2387 "c" => "gu_c",
2388 "x" => "gu_x",
2389 _ => return "32'd0".into(),
2390 };
2391 if lsh > 0 && v == unit.lane_var() {
2392 format!("({wire} << {lsh})")
2393 } else {
2394 wire.into()
2395 }
2396 };
2397 let addr = match is.shares.iter().find(|sh| sh.then == sl || sh.otherwise == sl) {
2398 Some(share) if share.then == sl => {
2399 let PredRhs::Const(bound) = share.pred.rhs() else { unreachable!("a shared read's select has a constant bound") };
2400 let cmp = match share.pred {
2401 Pred::AxisLt(..) => "<",
2402 Pred::AxisGe(..) => ">=",
2403 };
2404 c.push_str(&format!(" gu_pp{sl} = ({{32'd0, {}}} {cmp} 32'd{bound});", start_axis(share.pred.var())));
2405 Some(format!("gi_pp{sl} ? gu_el{sl} : gu_el{}", share.otherwise))
2406 }
2407 Some(_) => None,
2408 None => Some(format!("gu_el{sl}")),
2409 };
2410 if let Some(addr) = addr {
2411 c.push_str(&format!(" if (gi_v{sl}) begin f{k}_rd_slot = f{k}_rd_slot0; f{k}_rd_elem = {addr}; end"));
2412 }
2413 let lane_gather = unit.lanes > 1 && unit.gathers(sl);
2415 if lane_gather {
2416 for l in 1..unit.lanes {
2417 c.push_str(&format!(" if (gi_v{sl}) begin f{k}_l{l}_rd_slot = f{k}_rd_slot0; f{k}_l{l}_rd_elem = gu_el{sl}_l{l}; end"));
2418 }
2419 }
2420 if vec {
2421 let w = ctx.buffer_width(ins[sl]);
2424 for l in 0..sh.lanes {
2425 let src = if lane_gather && l > 0 && l < unit.lanes {
2426 format!("f{k}_l{l}_rd_data")
2427 } else if l == 0 || unit.broadcast(sl) || l >= unit.lanes {
2428 format!("f{k}_rd_data")
2429 } else {
2430 format!("$signed(f{k}_rd_hi[{}:{}])", l as u32 * w - 1, (l as u32 - 1) * w)
2431 };
2432 c.push_str(&format!(" gu_ld{sl}_l{l} = {src};"));
2433 }
2434 } else {
2435 c.push_str(&format!(" gu_ld{sl} = f{k}_rd_data;"));
2436 }
2437 }
2438 }
2439 Fixed::Matmul { block, per_site_n, sub_rows, ins, reload, .. } => {
2440 let (osh, osw, ibw) = (block.osh, block.osw, block.ibw);
2441 let (k, n) = (ibw * tc, osw * tc);
2442 let b_row = ctx.page_row(ins[1], *per_site_n);
2443 let run = ctx.matmul_run(block, b_row, *per_site_n, *sub_rows);
2444 let (rl, la, lb) = ctx.matmul_loads(block, reload.is_some(), run);
2445 let (rl, la, lb) = (rl as usize, la as usize, lb as usize);
2446 let (p0, p1, p2, p3) = (rl, rl + la, rl + la + lb, rl + la + lb + k);
2447 let rows = osh * ctx.tr;
2448 let (ka, kb) = (ctx.template(ins[0]).idx(), ctx.template(ins[1]).idx());
2449 let sub = |v: LoopVar| if bound.contains(&v) { var_name(v) } else { "32'd0".to_string() };
2452 let (shv, swv) = (sub(LV_SUB_H), sub(LV_SUB_W));
2453 for (name, v) in [
2454 ("mu_on", "1'b1".to_string()),
2455 ("mu_mv", if ctx.matvec(block) { "1'b1" } else { "1'b0" }.to_string()),
2456 ("mu_p0", p0.to_string()),
2457 ("mu_p1", p1.to_string()),
2458 ("mu_p2", p2.to_string()),
2459 ("mu_p3", p3.to_string()),
2460 ("mu_rows", rows.to_string()),
2461 ("mu_kop", k.to_string()),
2462 ("mu_nop", n.to_string()),
2463 ("mu_osw", osw.to_string()),
2464 ("mu_ibw", ibw.to_string()),
2465 ("mu_bwrap", (osw * te).to_string()),
2466 ("mu_bjump", ((b_row as i64 - osw as i64) * te as i64 + 1).to_string()),
2467 ("aw_cols", cols.to_string()),
2468 ] {
2469 set(name, v);
2470 }
2471 if run > 1 {
2472 set("mr_on", "1'b1".into());
2474 set("mr_lw", run.trailing_zeros().to_string());
2475 set("mr_n0o", (osw * te).min(run).to_string());
2476 set("mr_n0i", (ibw * te).min(run).to_string());
2477 set("mu_pn", b_row.to_string());
2478 let lanes = |k: usize, b: BufferId, into: &str| -> String {
2479 let w = ctx.buffer_width(b);
2480 (0..ctx.banks)
2481 .map(|j| {
2482 let src = if j == 0 { format!("f{k}_rd_data") } else { format!("f{k}_rd_hi[{}:{}]", j as u32 * w - 1, (j as u32 - 1) * w) };
2483 format!(" {into}{j} = $signed({src});")
2484 })
2485 .collect()
2486 };
2487 if let Some(r) = reload {
2488 let kr = ctx.template(*r).idx();
2489 c.push_str(&format!(" if ({}) begin f{kr}_rd_slot = f{kr}_rd_slot0; f{kr}_rd_elem = mr_pos; end{}", ctx.ph_lt(p0 as u64), lanes(kr, *r, "mu_rl")));
2490 }
2491 c.push_str(&format!(
2492 " if ({} && {}) begin f{ka}_rd_slot = f{ka}_rd_slot0; f{ka}_rd_elem = {shv} * {} + mr_pos; end{}",
2493 ctx.ph_ge(p0 as u64),
2494 ctx.ph_lt(p1 as u64),
2495 osh * ibw,
2496 lanes(ka, ins[0], "mu_al")
2497 ));
2498 c.push_str(&format!(
2499 " if ({} && {}) begin f{kb}_rd_slot = f{kb}_rd_slot0; f{kb}_rd_elem = {swv} * {osw} + mr_pos; end{}",
2500 ctx.ph_ge(p1 as u64),
2501 ctx.ph_lt(p2 as u64),
2502 lanes(kb, ins[1], "mu_bl")
2503 ));
2504 return Ok(());
2505 }
2506 if let Some(r) = reload {
2507 let kr = ctx.template(*r).idx();
2508 c.push_str(&format!(" if ({}) begin f{kr}_rd_slot = f{kr}_rd_slot0; f{kr}_rd_elem = ph; end mu_rr = f{kr}_rd_data;", ctx.ph_lt(p0 as u64)));
2509 }
2510 c.push_str(&format!(
2512 " if ({} && {}) begin f{ka}_rd_slot = f{ka}_rd_slot0; f{ka}_rd_elem = {shv} * {} + (ph - {p0}); end mu_ra = f{ka}_rd_data;",
2513 ctx.ph_ge(p0 as u64),
2514 ctx.ph_lt(p1 as u64),
2515 osh * ibw * te
2516 ));
2517 c.push_str(&format!(
2518 " if ({} && {}) begin f{kb}_rd_slot = f{kb}_rd_slot0; f{kb}_rd_elem = {swv} * {} + mbi_off; end mu_rb = f{kb}_rd_data;",
2519 ctx.ph_ge(p1 as u64),
2520 ctx.ph_lt(p2 as u64),
2521 osw * te
2522 ));
2523 }
2524 }
2525 Ok(())
2526}
2527
2528fn walk_deltas(te: usize, block_w: usize, stride_w: usize, stride_h: usize) -> (i64, i64) {
2531 let (te, bw, sw, shh) = (te as i64, block_w as i64, stride_w as i64, stride_h as i64);
2532 (sw * te - (te - 1), shh * te - (bw - 1) * sw * te - (te - 1))
2533}
2534
2535fn units(ctx: &Ctx, sh: &Shape, wd: &Widths, te: usize, tr: usize, tc: usize, lat: u64) -> String {
2537 let mut e = Emit { s: String::new() };
2538 let lat32 = format!("32'd{lat}");
2539 e.l(" always @(posedge clk)");
2541 e.l(" if (rst || !(running && tw_on) || op_last) begin tw_lo <= 0; tw_w <= 0; tw_off <= 0; end");
2542 e.l(format!(
2543 " else if (ph >= tw_from) begin if (tw_lo != {}) begin tw_lo <= tw_lo + 1; tw_off <= tw_off + 1; end else begin tw_lo <= 0; if (tw_w != tw_bw - 1) begin tw_w <= tw_w + 1; tw_off <= tw_off + tw_d1; end else begin tw_w <= 0; tw_off <= tw_off + tw_d2; end end end",
2544 te - 1
2545 ));
2546 if ctx.banks > 1 {
2549 let lg = log2_banks(ctx.banks);
2550 e.l(" reg [AW-1:0] tw_c, tw_rb, tw_p, tw_vo_r; reg tw_first;");
2554 e.l(format!(" reg [{lg}:0] tw_n_r;"));
2555 e.l(" wire [AW-1:0] tw_l = 32'd1 << tw_lw;");
2556 e.l(format!(" wire [{lg}:0] tw_n = tw_first ? tw_n0 : tw_n_r;"));
2557 e.l(" wire [AW-1:0] tw_vo = tw_first ? 32'd0 : tw_vo_r;");
2558 e.l(" wire tw_adv = (tw_c + tw_l < tw_bw);");
2559 e.l(" wire [AW-1:0] tw_cn = tw_adv ? tw_c + tw_l : 32'd0, tw_rbn = tw_adv ? tw_rb : tw_rb + tw_rs, tw_leftn = tw_bw - tw_cn;");
2560 e.l(format!(" wire [{lg}:0] tw_nn = (tw_leftn < tw_l) ? tw_leftn[{lg}:0] : tw_l[{lg}:0];"));
2561 e.l(" always @(posedge clk)");
2562 e.l(" if (rst || !(running && tw_on && tw_vec) || op_last) begin tw_c <= 0; tw_rb <= 0; tw_p <= 0; tw_first <= 1'b1; end");
2563 let step = if sh.pitched_fill { "tw_p + tw_n + (tw_adv ? 32'd0 : tw_pp)" } else { "tw_p + tw_n" };
2564 e.l(format!(" else if (ph >= tw_from) begin tw_first <= 1'b0; tw_p <= {step}; tw_c <= tw_cn; tw_rb <= tw_rbn; tw_vo_r <= tw_rbn + tw_cn; tw_n_r <= tw_nn; end"));
2565 if lat == 0 {
2566 e.l(format!(" wire [AW-1:0] tw_vod = tw_vo, tw_pd = tw_p; wire [{lg}:0] tw_nd = tw_n;"));
2567 } else {
2568 for d in 1..=lat {
2569 let (vo, p, n) = if d == 1 { ("tw_vo".to_string(), "tw_p".to_string(), "tw_n".to_string()) } else { (format!("tw_vo{}", d - 1), format!("tw_p{}", d - 1), format!("tw_n{}", d - 1)) };
2570 e.l(format!(
2571 " reg [AW-1:0] tw_vo{d}, tw_p{d}; reg [{lg}:0] tw_n{d}; always @(posedge clk) begin tw_vo{d} <= {vo}; tw_p{d} <= {p}; tw_n{d} <= {n}; end"
2572 ));
2573 }
2574 e.l(format!(" wire [AW-1:0] tw_vod = tw_vo{lat}, tw_pd = tw_p{lat}; wire [{lg}:0] tw_nd = tw_n{lat};"));
2575 }
2576 }
2577 e.l(" always @(posedge clk)");
2579 e.l(" if (rst || !running || op_last) begin aw_cc <= 0; aw_r <= 0; aw_j <= 0; aw_i <= 0; end");
2580 e.l(format!(
2581 " else if (aw_step || mu_reload) begin if (aw_cc != {}) aw_cc <= aw_cc + 1; else begin aw_cc <= 0; if (aw_r != {}) aw_r <= aw_r + 1; else begin aw_r <= 0; if (aw_j != aw_cols - 1) aw_j <= aw_j + 1; else begin aw_j <= 0; aw_i <= aw_i + 1; end end end end",
2582 tc - 1,
2583 tr - 1
2584 ));
2585 e.l(" always @(posedge clk)");
2587 e.l(" if (rst || !(running && un_on) || op_last) begin un_e <= 0; un_e2 <= 0; un_bk <= 0; un_bt <= 0; end");
2588 e.l(format!(
2589 " else begin un_e <= (un_e == un_pe - 1) ? 0 : un_e + 1; un_e2 <= (un_e2 == un_pb - 1) ? 0 : un_e2 + 1; if (un_bk == {}) begin un_bk <= 0; un_bt <= (un_bt + {te} >= un_pb) ? un_bt + {te} - un_pb : un_bt + {te}; end else un_bk <= un_bk + 1; end",
2590 te - 1
2591 ));
2592 let um = wd.un_mul.max(wd.un_out);
2594 e.l(format!(
2595 " wire signed [{0}:0] un_y = (un_kind == 4'd5) ? $signed(unary_bias) : un_m; wire signed [{1}:0] un_mul = {2} * un_y;",
2596 wd.un_b.max(wd.un_m).min(um) - 1,
2597 um - 1,
2598 fit("unary_in", wd.un_in, um)
2599 ));
2600 e.l(" always @* case (un_kind)");
2601 e.l(" 4'd0: unary_out = un_mul >>> un_s;");
2602 e.l(" 4'd1: unary_out = $signed(unary_in) + $signed(unary_bias);");
2603 e.l(" 4'd2: unary_out = ($signed(unary_in) < 0) ? 0 : unary_in;");
2604 e.l(" 4'd4: unary_out = $signed(unary_in) + $signed(unary_bias);");
2605 e.l(" 4'd5: unary_out = un_mul;");
2606 e.l(" 4'd6: unary_out = grid_result;");
2607 e.l(" default: unary_out = 0;");
2608 e.l(" endcase");
2609 e.l(" always @(posedge clk)");
2611 e.l(" if (rst || !(running && mc_on) || op_last) begin mc_e <= 0; mc_pg <= 0; end");
2612 e.l(" else if (mc_e == mc_pe - 1) begin mc_e <= 0; mc_pg <= mc_pg + 1; end else mc_e <= mc_e + 1;");
2613
2614 if let Some((k, n, bits)) = sh.array {
2616 let l = sh.array.map(|(a, b, _)| pipelined_latency(a, b)).unwrap_or(0);
2617 e.l(format!(" wire mu_live = mu_on && {};", ctx.ph_ge(lat)));
2618 e.l(format!(" wire [AW-1:0] mu_q = ph - {lat32};"));
2619 e.l(" always @(posedge clk)");
2621 e.l(" if (rst || !(running && mu_on) || op_last) begin mbi_t <= 0; mbi_off <= 0; end");
2622 e.l(" else if (ph >= mu_p1 && ph < mu_p2) begin if (mbi_t == mu_bwrap - 1) begin mbi_t <= 0; mbi_off <= mbi_off + mu_bjump; end else begin mbi_t <= mbi_t + 1; mbi_off <= mbi_off + 1; end end");
2623 e.l(" always @(posedge clk)");
2625 e.l(" if (rst || !(running && mu_on) || op_last) begin ma_xx <= 0; ma_r <= 0; ma_kk <= 0; ma_i <= 0; mb_cc <= 0; mb_x <= 0; mb_j <= 0; mb_kk <= 0; end");
2626 e.l(" else if (mu_live) begin");
2627 e.l(format!(
2628 " if (mu_q >= mu_p0 && mu_q < mu_p1) begin if (ma_xx != {}) ma_xx <= ma_xx + 1; else begin ma_xx <= 0; if (ma_r != {}) ma_r <= ma_r + 1; else begin ma_r <= 0; if (ma_kk != mu_ibw - 1) ma_kk <= ma_kk + 1; else begin ma_kk <= 0; ma_i <= ma_i + 1; end end end end",
2629 tc - 1,
2630 tr - 1
2631 ));
2632 e.l(format!(
2633 " if (mu_q >= mu_p1 && mu_q < mu_p2) begin if (mb_cc != {}) mb_cc <= mb_cc + 1; else begin mb_cc <= 0; if (mb_x != {}) mb_x <= mb_x + 1; else begin mb_x <= 0; if (mb_j != mu_osw - 1) mb_j <= mb_j + 1; else begin mb_j <= 0; mb_kk <= mb_kk + 1; end end end end",
2634 tc - 1,
2635 tr - 1
2636 ));
2637 e.l(" end");
2638 e.l(format!(" wire [AW-1:0] ma_s = (ma_i << {}) + ma_r;", log2(tr)));
2639 e.l(format!(" wire [AW-1:0] ma_x = (ma_kk << {}) + ma_xx;", log2(tc)));
2640 e.l(format!(" wire [AW-1:0] mb_r = (mb_kk << {}) + mb_x;", log2(tc)));
2641 e.l(format!(" wire [AW-1:0] mb_c = (mb_j << {}) + mb_cc;", log2(tc)));
2642 let mut a_elem = String::new();
2645 for s in 0..sh.a_rows {
2646 for x in 0..k {
2647 a_elem.push_str(&format!(" if (ma_s == {s} && ma_x == {x}) a_r{s}_{x} <= mu_ra[{}:0];\n", bits - 1));
2648 }
2649 }
2650 let mut b_elem = String::new();
2651 for r in 0..k {
2652 for c in 0..n {
2653 b_elem.push_str(&format!(
2654 " if (mb_r == {r} && mb_c == {c}) b_r{r}[{}:{}] <= mu_rb[{}:0];\n",
2655 (c as u32 + 1) * bits - 1,
2656 c as u32 * bits,
2657 bits - 1
2658 ));
2659 }
2660 }
2661 if !ctx.runs() {
2662 e.l(" always @(posedge clk) if (mu_live && mu_q >= mu_p0 && mu_q < mu_p1) begin");
2663 e.s.push_str(&a_elem);
2664 e.l(" end");
2665 e.l(" always @(posedge clk) if (mu_live && mu_q >= mu_p1 && mu_q < mu_p2) begin");
2666 e.s.push_str(&b_elem);
2667 e.l(" end");
2668 }
2669 let mut mask = format!(" wire [{}:0] wmask = {{", n as u32 * bits - 1);
2671 let parts: Vec<String> =
2672 (0..n).rev().map(|c| format!("(({c} < mu_nop) ? {{{bits}{{1'b1}}}} : {{{bits}{{1'b0}}}})")).collect();
2673 mask.push_str(&parts.join(", "));
2674 mask.push_str("};");
2675 e.l(mask);
2676 e.l(" wire [AW-1:0] mu_w = mu_q - mu_p2;");
2678 e.l(" wire [AW-1:0] mu_s = mu_q - mu_p3;");
2679 e.l(format!(" wire [AW-1:0] mu_o = mu_q - mu_p3 - {};", l + 1));
2680 e.l(" always @(posedge clk) begin");
2681 e.l(" pe_w_load_r <= 1'b0;");
2682 e.l(" if (mu_live && !mu_mv && mu_q >= mu_p2 && mu_q < mu_p3) begin");
2683 e.l(" pe_w_load_r <= 1'b1; pe_w_row <= mu_w;");
2684 let mut wr = String::from(" case (mu_w)");
2685 for r in 0..k {
2686 wr.push_str(&format!(" {r}: pe_w_data <= b_r{r} & wmask;"));
2687 }
2688 wr.push_str(" default: pe_w_data <= 0; endcase");
2689 e.l(wr);
2690 e.l(" end");
2691 e.l(" if (mu_live && mu_q >= mu_p3) begin");
2692 for x in 0..k {
2693 let mut a = format!(" pe_a{x} <= (mu_s < mu_rows && {x} < mu_kop) ? (");
2694 for s in 0..sh.a_rows {
2695 a.push_str(&format!("(mu_s == {s}) ? $signed(a_r{s}_{x}) : "));
2696 }
2697 a.push_str("64'sd0) : 64'sd0;");
2698 e.l(a);
2699 }
2700 e.l(" end");
2701 e.l(" end");
2702 e.l(format!(" wire mw_we = mu_live && (mu_mv ? (mu_q == mu_p2 + 1) : (mu_q >= mu_p3 + {0} && mu_q < mu_p3 + {0} + mu_rows));", l + 1));
2704 e.l(" wire [AW-1:0] mw_row = mu_mv ? 32'd0 : mu_o, mw_n = mu_nop;");
2705 e.l(" assign mu_reload = mu_live && !mr_on && mu_q < mu_p0;");
2707 if ctx.runs() {
2708 let lg = log2_banks(ctx.banks);
2711 e.l(" reg [AW-1:0] mr_row, mr_cb, mr_base, mr_pos_r, mr_rem_r; reg mr_first; reg [1:0] mr_ph_r;");
2715 e.l(format!(" reg [{lg}:0] mr_n_r;"));
2716 e.l(" wire [AW-1:0] mr_l = 32'd1 << mr_lw;");
2717 e.l(" always @(posedge clk) mr_ph_r <= (ph + 1 < mu_p0) ? 2'd0 : (ph + 1 < mu_p1) ? 2'd1 : 2'd2;");
2720 e.l(format!(" wire [1:0] mr_ph = ({}) ? ((mu_p0 != 0) ? 2'd0 : (mu_p1 != 0) ? 2'd1 : 2'd2) : mr_ph_r;", ctx.ph_lt(1)));
2721 e.l(" wire [AW-1:0] mr_w = (mr_ph == 2'd1) ? mu_ibw : mu_osw;");
2722 e.l(" wire [AW-1:0] mr_rs = (mr_ph == 2'd0) ? mu_osw : (mr_ph == 2'd1) ? mu_ibw : mu_pn;");
2723 e.l(" wire [AW-1:0] mr_pos = mr_first ? 32'd0 : mr_pos_r;");
2724 e.l(format!(" wire [{lg}:0] mr_n = mr_first ? ((mr_ph == 2'd1) ? mr_n0i : mr_n0o) : mr_n_r;"));
2725 e.l(" wire [AW-1:0] mr_rem = mr_first ? mr_w : mr_rem_r;");
2729 e.l(" wire mr_adv = (mr_l < mr_rem);");
2730 e.l(" wire [AW-1:0] mr_cbn = mr_adv ? mr_cb + 1 : 32'd0, mr_basen = mr_adv ? mr_base : mr_base + mr_rs;");
2731 e.l(" wire [AW-1:0] mr_leftn = mr_adv ? mr_rem - mr_l : mr_w;");
2732 e.l(format!(" wire [{lg}:0] mr_nn = (mr_leftn < mr_l) ? mr_leftn[{lg}:0] : mr_l[{lg}:0];"));
2733 e.l(" always @(posedge clk)");
2734 e.l(" if (rst || !(running && mu_on && mr_on) || op_last || ph + 1 == mu_p0 || ph + 1 == mu_p1) begin mr_row <= 0; mr_cb <= 0; mr_base <= 0; mr_first <= 1'b1; end");
2735 e.l(" else begin mr_first <= 1'b0; mr_cb <= mr_cbn; mr_base <= mr_basen; if (!mr_adv) mr_row <= mr_row + 1; mr_pos_r <= mr_basen + (mr_cbn << mr_lw); mr_n_r <= mr_nn; mr_rem_r <= mr_leftn; end");
2736 if lat == 0 {
2737 e.l(format!(" wire [AW-1:0] mr_rowd = mr_row, mr_cbd = mr_cb; wire [{lg}:0] mr_nd = mr_n;"));
2738 } else {
2739 for d in 1..=lat {
2740 let src = |x: &str| if d == 1 { x.to_string() } else { format!("{x}{}", d - 1) };
2741 e.l(format!(
2742 " reg [AW-1:0] mr_row{d}, mr_cb{d}; reg [{lg}:0] mr_n{d}; always @(posedge clk) begin mr_row{d} <= {}; mr_cb{d} <= {}; mr_n{d} <= {}; end",
2743 src("mr_row"),
2744 src("mr_cb"),
2745 src("mr_n")
2746 ));
2747 }
2748 e.l(format!(" wire [AW-1:0] mr_rowd = mr_row{lat}, mr_cbd = mr_cb{lat}; wire [{lg}:0] mr_nd = mr_n{lat};"));
2749 }
2750 let landing = |x: usize, dst: &str, lane: &str| -> String {
2753 let mut r = String::from("case (mr_lw)");
2754 for w in 1..=lg {
2755 let j = x & ((1 << w) - 1);
2756 r.push_str(&format!(" {w}: if (mr_cbd == {} && {j} < mr_nd) {dst} <= {lane}{j}[{}:0];", x >> w, bits - 1));
2757 }
2758 r.push_str(" default: ; endcase");
2759 r
2760 };
2761 e.l(" always @(posedge clk) if (mu_live && mu_q >= mu_p0 && mu_q < mu_p1) begin");
2762 e.l(" if (mr_on) begin");
2763 for s in 0..sh.a_rows {
2764 for x in 0..k {
2765 e.l(format!(" if (mr_rowd == {s}) {}", landing(x, &format!("a_r{s}_{x}"), "mu_al")));
2766 }
2767 }
2768 e.l(" end else begin");
2769 e.s.push_str(&a_elem);
2770 e.l(" end");
2771 e.l(" end");
2772 e.l(" always @(posedge clk) if (mu_live && mu_q >= mu_p1 && mu_q < mu_p2) begin");
2773 e.l(" if (mr_on) begin");
2774 for r in 0..k {
2775 for c in 0..n {
2776 e.l(format!(" if (mr_rowd == {r}) {}", landing(c, &format!("b_r{r}[{}:{}]", (c as u32 + 1) * bits - 1, c as u32 * bits), "mu_bl")));
2777 }
2778 }
2779 e.l(" end else begin");
2780 e.s.push_str(&b_elem);
2781 e.l(" end");
2782 e.l(" end");
2783 e.l(" wire mr_reload = mu_live && mr_on && mu_q < mu_p0;");
2784 }
2785 let land_b = "mu_live && mu_q >= mu_p1 && mu_q < mu_p2";
2789 let kidx = if ctx.runs() { "(mr_on ? mr_rowd : mb_r)" } else { "mb_r" };
2790 let mut ak = format!(" reg signed [{}:0] mv_a; always @* case ({kidx})", bits - 1);
2791 for x in 0..k {
2792 ak.push_str(&format!(" {x}: mv_a = $signed(a_r0_{x});"));
2793 }
2794 ak.push_str(" default: mv_a = 0; endcase");
2795 e.l(ak);
2796 let lgb = log2_banks(ctx.banks) as usize;
2797 for c in 0..n {
2798 let element = format!("(!mr_on && mb_c == {c}) ? {{1'b1, mu_rb[{}:0]}} : ", bits - 1);
2799 let runs: String = if ctx.runs() {
2800 (1..=lgb)
2801 .map(|w| {
2802 let j = c & ((1 << w) - 1);
2803 format!("(mr_on && mr_lw == {w} && mr_cbd == {} && {j} < mr_nd) ? {{1'b1, mu_bl{j}[{}:0]}} : ", c >> w, bits - 1)
2804 })
2805 .collect()
2806 } else {
2807 String::new()
2808 };
2809 e.l(format!(" wire [{bits}:0] mv_b{c} = {runs}{element}{}'d0;", bits + 1));
2810 e.l(format!(
2813 " wire signed [{2}:0] mv_m{c} = mv_a * $signed(mv_b{c}[{1}:0]); reg mv_q{c}; reg signed [{0}:0] mv_p{c}, mv{c}; always @(posedge clk) begin mv_q{c} <= mu_mv && {land_b} && mv_b{c}[{bits}]; mv_p{c} <= mv_m{c}; end",
2814 wd.acc - 1,
2815 bits - 1,
2816 2 * bits - 1
2817 ));
2818 e.l(format!(" always @(posedge clk) if (rst || !(running && mu_on) || op_last) mv{c} <= 0; else if (mv_q{c}) mv{c} <= mv{c} + mv_p{c};"));
2819 }
2820 } else {
2821 e.l(" assign mu_reload = 1'b0;");
2822 }
2823
2824 if sh.slots > 0 {
2827 let g = sh.slots;
2828 e.l(" always @(posedge clk)");
2829 e.l(" if (rst || !(running && gu_on) || op_last) begin gu_sub <= 0; gu_x <= 0; gu_mid <= 0; gu_out <= 0;");
2830 for s in 0..g {
2831 e.l(format!(" gu_lin{s} <= 0;"));
2832 }
2833 e.l(" end else if (ph < gu_iss) begin");
2834 e.l(" if (gu_sub != gu_ii - 1) gu_sub <= gu_sub + 1;");
2835 e.l(" else begin gu_sub <= 0;");
2836 e.l(" if (gu_x != gu_kx - 1) begin gu_x <= gu_x + 1;");
2837 for s in 0..g {
2838 e.l(format!(" gu_lin{s} <= gu_lin{s} + gu_d0{s};"));
2839 }
2840 e.l(" end else begin gu_x <= 0;");
2841 e.l(" if (gu_mid != gu_nmid - 1) begin gu_mid <= gu_mid + 1;");
2842 for s in 0..g {
2843 e.l(format!(" gu_lin{s} <= gu_lin{s} + gu_d1{s};"));
2844 }
2845 e.l(" end else begin gu_mid <= 0;");
2846 e.l(" if (gu_out != gu_nout - 1) begin gu_out <= gu_out + 1;");
2847 for s in 0..g {
2848 e.l(format!(" gu_lin{s} <= gu_lin{s} + gu_d2{s};"));
2849 }
2850 e.l(" end else begin gu_out <= 0;");
2851 for s in 0..g {
2852 e.l(format!(" gu_lin{s} <= 0;"));
2853 }
2854 e.l(" end");
2855 e.l(" end");
2856 e.l(" end");
2857 e.l(" end");
2858 e.l(" end");
2859 e.l(" always @(posedge clk) if (gq_v && gu_kx != 1) g_kacc <= gu_pv;");
2860 e.l(" always @(posedge clk) if (fb_v && gu_kind != 2'd0 && fb_pd) begin g_fold <= gu_fv; g_fidx <= gu_fi; end");
2861 e.l(" always @(posedge clk) if (fc_v && fc_ofold) begin g_ofold <= gu_ov; g_oidx <= gu_oi; end");
2862 let vl = sh.lanes;
2863 for l in 1..vl {
2864 if sh.lane_contracts {
2865 e.l(format!(" always @(posedge clk) if (gq_v && gu_kx != 1) g_kacc_l{l} <= gu_pv_l{l};"));
2866 }
2867 if sh.lane_pools.contains(&true) {
2868 e.l(format!(" always @(posedge clk) if (fb_v && gu_kind != 2'd0 && fb_pd) begin g_fold_l{l} <= gu_fv_l{l}; g_fidx_l{l} <= gu_fi_l{l}; end"));
2869 }
2870 }
2871 if vl > 1 {
2875 e.l(" wire gu_we = fc_v && fc_we && (gu_wsh == 4'd0);");
2876 e.l(" wire gv_we = fc_v && fc_we && (gu_wsh != 4'd0);");
2877 } else {
2878 e.l(" wire gu_we = fc_v && fc_we;");
2879 }
2880 e.l(" wire [31:0] gu_wcol = fc_wcol;");
2881 e.l(format!(" wire signed [{}:0] gu_wd = (gu_kind == 2'd0) ? fc_pv : (gu_kind == 2'd3) ? gu_or : fc_ir;", wd.value - 1));
2882 if vl > 1 {
2883 let lg = vl.trailing_zeros();
2888 let mvw = wd.value - 1;
2889 let pick = |pv: &str, ir: &str| -> String {
2890 match (sh.lane_writes_points, sh.lane_writes_folds) {
2891 (true, true) => format!("((gu_kind == 2'd0) ? {pv} : {ir})"),
2892 (false, true) => ir.to_string(),
2893 _ => pv.to_string(),
2894 }
2895 };
2896 for j in 0..vl {
2897 let (pv, ir) = if j == 0 { ("fc_pv".to_string(), "fc_ir".to_string()) } else { (format!("fc_pv_l{j}"), format!("fc_ir_l{j}")) };
2898 e.l(format!(" wire signed [{mvw}:0] gw_lane{j} = {};", pick(&pv, &ir)));
2899 }
2900 let ax = sh.axis_bits;
2901 e.l(format!(" wire [{}:0] gw_first = {{4'd0, fc_wcol}} << gu_wsh;", ax + 3));
2902 e.l(format!(" wire [{}:0] gw_rot = gw_first[{}:0];", lg - 1, lg - 1));
2903 for sl in 0..vl {
2904 e.l(format!(" wire [{}:0] gw_li{sl} = {lg}'d{sl} - gw_rot;", lg - 1));
2905 let mut m = format!(" reg signed [{mvw}:0] gw_slot{sl}; always @* case (gw_li{sl})");
2906 for j in 0..vl {
2907 m.push_str(&format!(" {j}: gw_slot{sl} = gw_lane{j};"));
2908 }
2909 m.push_str(&format!(" default: gw_slot{sl} = gw_lane0; endcase"));
2910 e.l(m);
2911 e.l(format!(" wire gw_sv{sl} = fc_lv[gw_li{sl}];"));
2912 }
2913 for c in 0..sh.banks {
2914 e.l(format!(
2915 " wire gw_e{c} = gv_we && (gw_first[{}:{lg}] == {}) && gw_sv{}; wire signed [{mvw}:0] gw_d{c} = gw_slot{};",
2916 ax + 3,
2917 c >> lg,
2918 c % vl,
2919 c % vl
2920 ));
2921 }
2922 }
2923 } else {
2924 e.l(" wire gu_we = 1'b0; wire [31:0] gu_wcol = 32'd0; wire signed gu_wd = 1'b0;");
2925 }
2926
2927 e.l(" wire acc_we = sw_we || mu_reload || gu_we;");
2929 e.l(" wire [31:0] acc_row = gu_we ? 32'd0 : aw_row;");
2930 e.l(" wire [31:0] acc_col = gu_we ? gu_wcol : aw_col;");
2931 e.l(format!(" wire signed [{}:0] acc_d = gu_we ? gu_wd : mu_reload ? $signed(mu_rr) : sw_d;", wd.acc - 1));
2932 e.l(" always @(posedge clk) begin");
2933 let pe_cols = sh.array.map_or(0, |a| a.1);
2934 let reload_run = sh.array.is_some() && ctx.runs();
2936 let zero = |c: usize| {
2937 if ctx.runs() {
2938 let reload = if reload_run {
2940 let lands: Vec<String> = (1..=log2_banks(ctx.banks) as usize)
2941 .map(|w| format!("(mr_lw == {w} && mr_cbd == {} && {} < mr_nd) ? 1'b1 : ", c >> w, c & ((1 << w) - 1)))
2942 .collect();
2943 let lane: Vec<String> = (1..=log2_banks(ctx.banks) as usize)
2944 .map(|w| format!("(mr_lw == {w}) ? mu_rl{} : ", c & ((1 << w) - 1)))
2945 .collect();
2946 format!(
2947 "if (mr_reload && ({}1'b0)) acc_b{c}[mr_rowd] <= {}mu_rl0; else ",
2948 lands.concat(),
2949 lane.concat()
2950 )
2951 } else {
2952 String::new()
2953 };
2954 format!("{reload}if (az_we && ar_z[{c}]) acc_b{c}[ar_i] <= 0; else ")
2955 } else {
2956 String::new()
2957 }
2958 };
2959 let lane_write = |c: usize| if sh.slots > 0 && sh.lanes > 1 { format!(" else if (gw_e{c}) acc_b{c}[0] <= gw_d{c};") } else { String::new() };
2961 for c in 0..sh.banks {
2962 if c < pe_cols {
2963 e.l(format!(
2964 " if (mw_we && {c} < mw_n) acc_b{c}[mw_row] <= acc_b{c}[mw_row] + (mu_mv ? mv{c} : pe_o{c}); else {}if (acc_we && acc_col == {c}) acc_b{c}[acc_row] <= acc_d;{}",
2965 zero(c),
2966 lane_write(c)
2967 ));
2968 } else {
2969 e.l(format!(" {}if (acc_we && acc_col == {c}) acc_b{c}[acc_row] <= acc_d;{}", zero(c), lane_write(c)));
2970 }
2971 }
2972 e.l(" end");
2973 let _ = (ctx, tr);
2974 e.s
2975}