1use std::collections::{BTreeMap, BTreeSet, VecDeque};
27
28use logicaffeine_proof::cycle_ratio::Edge;
29use logicaffeine_proof::min_cost_flow::{min_cost_flow, Arc, FlowError, Solution};
30
31use super::timing_graph::{ConeArg, Pin, Source, TimingGraph};
32
33#[derive(Clone, Copy, Debug, PartialEq, Eq)]
35pub enum Cuts {
36 Named,
38 Nets,
40}
41
42#[derive(Clone, Debug, PartialEq, Eq)]
44pub struct Plan {
45 pub period: i64,
46 pub latency: i64,
47 pub policy: Cuts,
48 pub stage: Vec<i64>,
50 pub registers: BTreeMap<usize, i64>,
52 pub bits: i64,
54}
55
56impl Plan {
57 pub fn with_outputs_registered(&self, graph: &TimingGraph) -> Plan {
61 let mut plan = self.clone();
62 plan.latency += 1;
63 let outputs: BTreeSet<usize> = graph.outputs.iter().copied().collect();
64 for o in outputs {
65 *plan.registers.entry(o).or_insert(0) += 1;
66 plan.bits += 1;
67 }
68 plan
69 }
70
71 pub fn cuts(&self, graph: &TimingGraph) -> Vec<(String, i64, Vec<usize>)> {
73 let mut by: BTreeMap<(String, i64), Vec<usize>> = BTreeMap::new();
74 for (&node, &count) in &self.registers {
75 let name = &graph.nodes[node];
76 let (base, bit) = match name.rfind('[') {
77 Some(at) if name.ends_with(']') => (name[..at].to_string(), name[at + 1..name.len() - 1].parse().unwrap_or(0)),
78 _ => (name.clone(), 0),
79 };
80 by.entry((base, count)).or_default().push(bit);
81 }
82 by.into_iter()
83 .map(|((name, count), mut bits)| {
84 bits.sort_unstable();
85 (name, count, bits)
86 })
87 .collect()
88 }
89}
90
91#[derive(Clone, Debug, PartialEq, Eq)]
93pub enum PipeError {
94 Sequential,
96 EdgeTooDeep { edge: usize, delay: i64 },
98 LatencyTooShort { minimum: i64 },
100 Uncuttable { through: Vec<String> },
103 NegativeLatency,
105 CombinationalLoop,
107 Unbalanced { node: String },
110 Held { register: String },
112 Boxed { cell: String },
114 Flow(FlowError),
115}
116
117impl std::fmt::Display for PipeError {
118 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
119 match self {
120 PipeError::Sequential => write!(f, "the logic holds registers already"),
121 PipeError::EdgeTooDeep { edge, delay } => write!(f, "edge {edge} is {delay} deep, past the period by itself"),
122 PipeError::LatencyTooShort { minimum } => write!(f, "the period needs a latency of at least {minimum}"),
123 PipeError::Uncuttable { through } => write!(f, "logic deeper than the period with nowhere to cut it: {}", through.join(", ")),
124 PipeError::NegativeLatency => write!(f, "a latency is not negative"),
125 PipeError::CombinationalLoop => write!(f, "the logic loops through no register"),
126 PipeError::Unbalanced { node } => write!(f, "the paths into {node} hold different numbers of registers"),
127 PipeError::Held { register } => write!(f, "{register} is a register with more than its data in"),
128 PipeError::Boxed { cell } => write!(f, "{cell} is a box, whose logic is not here to cut"),
129 PipeError::Flow(e) => write!(f, "the flow problem: {e:?}"),
130 }
131 }
132}
133
134impl std::error::Error for PipeError {}
135
136fn cuts(graph: &TimingGraph, policy: Cuts) -> Vec<bool> {
138 let n = graph.nodes.len();
139 let mut cut = match (policy, &graph.structure) {
140 (Cuts::Named, _) => graph.public.clone(),
141 (Cuts::Nets, Some(s)) => s.source.iter().map(|x| !matches!(x, Source::Inside | Source::Register { .. })).collect(),
142 (Cuts::Nets, None) => vec![true; n],
143 };
144 cut.resize(n, false);
145 for &i in graph.inputs.iter().chain(&graph.outputs) {
146 cut[i] = true;
147 }
148 cut
149}
150
151fn order(graph: &TimingGraph) -> (Vec<usize>, Vec<Vec<usize>>) {
154 let n = graph.nodes.len();
155 let mut out: Vec<Vec<usize>> = vec![Vec::new(); n];
156 let mut fanin = vec![0usize; n];
157 for (k, e) in graph.edges.iter().enumerate() {
158 out[e.from].push(k);
159 fanin[e.to] += 1;
160 }
161 let mut ready: VecDeque<usize> = (0..n).filter(|&v| fanin[v] == 0).collect();
162 let mut position = vec![usize::MAX; n];
163 let mut at = 0;
164 while let Some(v) = ready.pop_front() {
165 position[v] = at;
166 at += 1;
167 for &k in &out[v] {
168 let t = graph.edges[k].to;
169 fanin[t] -= 1;
170 if fanin[t] == 0 {
171 ready.push_back(t);
172 }
173 }
174 }
175 (position, out)
176}
177
178fn unboxed(graph: &TimingGraph) -> Result<(), PipeError> {
180 match graph.boxes.first() {
181 Some(b) => Err(PipeError::Boxed { cell: b.cell.clone() }),
182 None => Ok(()),
183 }
184}
185
186pub fn unpipelined(graph: &TimingGraph) -> Result<(TimingGraph, i64), PipeError> {
193 unboxed(graph)?;
194 let n = graph.nodes.len();
195 if let Some(s) = &graph.structure {
196 if let Some(q) = (0..n).find(|&v| matches!(s.source[v], Source::Register { controls: true, .. })) {
197 return Err(PipeError::Held { register: graph.nodes[q].clone() });
198 }
199 }
200 let (mut held, mut wired) = (vec![0usize; n], vec![false; n]);
201 for e in &graph.edges {
202 if e.registers > 0 {
203 held[e.to] += 1;
204 } else {
205 wired[e.to] = true;
206 }
207 }
208 if let Some(q) = (0..n).find(|&v| held[v] > 1 || held[v] == 1 && wired[v]) {
209 return Err(PipeError::Held { register: graph.nodes[q].clone() });
210 }
211 let mut logic = graph.clone();
212 logic.flops = 0;
213 if let Some(s) = &mut logic.structure {
214 for source in &mut s.source {
215 if let Source::Register { data, .. } = *source {
216 *source = match data {
217 Pin::Node(d) => Source::Copy { from: d, inverted: false },
218 Pin::Zero => Source::Constant(false),
219 Pin::One => Source::Constant(true),
220 };
221 }
222 }
223 }
224 for (k, e) in graph.edges.iter().enumerate() {
225 if e.registers > 0 {
226 logic.edges[k].registers = 0;
227 if let Some(s) = &mut logic.structure {
228 if matches!(s.source[e.to], Source::Inside) {
229 s.source[e.to] = Source::Copy { from: e.from, inverted: false };
230 }
231 }
232 }
233 }
234 let (position, out) = order(&logic);
235 if position.iter().any(|&p| p == usize::MAX) {
236 return Err(PipeError::CombinationalLoop);
237 }
238 let mut topo = vec![0usize; n];
239 for (v, &p) in position.iter().enumerate() {
240 topo[p] = v;
241 }
242 let mut late: Vec<Option<i64>> = vec![None; n];
243 for &i in &graph.inputs {
244 late[i] = Some(0);
245 }
246 for &v in &topo {
247 let Some(at) = late[v] else { continue };
248 for &k in &out[v] {
249 let e = &graph.edges[k];
250 let reach = at + e.registers;
251 match late[e.to] {
252 Some(have) if have != reach => return Err(PipeError::Unbalanced { node: graph.nodes[e.to].clone() }),
253 _ => late[e.to] = Some(reach),
254 }
255 }
256 }
257 let mut latency = None;
258 for &o in &graph.outputs {
259 let Some(l) = late[o] else { continue };
260 match latency {
261 Some(have) if have != l => return Err(PipeError::Unbalanced { node: graph.nodes[o].clone() }),
262 _ => latency = Some(l),
263 }
264 }
265 Ok((logic, latency.unwrap_or(0)))
266}
267
268struct Problem {
273 vars: usize,
274 host: usize,
275 sink: Vec<usize>,
276 mirror: Vec<Option<usize>>,
277 constraints: Vec<(usize, usize, i64)>,
278 weight: Vec<i64>,
279 start: Vec<i64>,
281 topo: Vec<usize>,
283 out: Vec<Vec<usize>>,
284}
285
286fn problem(graph: &TimingGraph, period: i64, policy: Cuts) -> Result<Problem, PipeError> {
287 unboxed(graph)?;
288 if graph.flops > 0 || graph.edges.iter().any(|e| e.registers != 0) {
289 return Err(PipeError::Sequential);
290 }
291 if let Some((k, e)) = graph.edges.iter().enumerate().find(|(_, e)| e.delay > period) {
292 return Err(PipeError::EdgeTooDeep { edge: k, delay: e.delay });
293 }
294 let n = graph.nodes.len();
295 let cut = cuts(graph, policy);
296 let (position, out) = order(graph);
297 if position.iter().any(|&p| p == usize::MAX) {
298 return Err(PipeError::CombinationalLoop);
299 }
300 let mut topo = vec![0usize; n];
301 for (v, &p) in position.iter().enumerate() {
302 topo[p] = v;
303 }
304 let mut constraints: Vec<(usize, usize, i64)> = Vec::new();
305 let mut vars = n;
306 let mut mirror: Vec<Option<usize>> = vec![None; n];
307 for v in 0..n {
308 if cut[v] && !out[v].is_empty() {
309 mirror[v] = Some(vars);
310 vars += 1;
311 }
312 }
313 let sink: Vec<usize> = graph.outputs.iter().map(|_| { vars += 1; vars - 1 }).collect();
314 for (i, &o) in graph.outputs.iter().enumerate() {
315 if mirror[o].is_none() {
316 mirror[o] = Some(vars);
317 vars += 1;
318 }
319 constraints.push((o, sink[i], 0));
320 constraints.push((sink[i], mirror[o].expect("an output has a mirror"), 0));
321 }
322 let host = vars;
323 vars += 1;
324 let mut weight = vec![0i64; vars];
325 for v in 0..n {
326 if let Some(m) = mirror[v] {
327 weight[m] += 1;
328 weight[v] -= 1;
329 }
330 }
331 let mut start: Vec<i64> = vec![-1; n];
332 for e in &graph.edges {
333 constraints.push((e.from, e.to, 0));
334 if cut[e.from] {
335 if let Some(m) = mirror[e.from] {
336 constraints.push((e.to, m, 0));
337 }
338 start[e.to] = start[e.to].max(e.delay);
339 } else {
340 constraints.push((e.to, e.from, 0));
341 }
342 }
343 for &i in &graph.inputs {
344 constraints.push((host, i, 0));
345 constraints.push((i, host, 0));
346 }
347 Ok(Problem { vars, host, sink, mirror, constraints, weight, start, topo, out })
348}
349
350impl Problem {
351 fn broken(&self, graph: &TimingGraph, stage: &[i64], period: i64) -> Vec<(usize, usize, i64)> {
357 let n = graph.nodes.len();
358 let mut depth = vec![i64::MIN; n];
359 let mut began = vec![usize::MAX; n];
360 let mut broken = Vec::new();
361 for &v in &self.topo {
362 if self.start[v] >= 0 && self.start[v] > depth[v] {
363 depth[v] = self.start[v];
364 began[v] = v;
365 }
366 if depth[v] == i64::MIN {
367 continue;
368 }
369 if depth[v] > period {
370 broken.push((began[v], v, 1));
371 if self.start[v] < 0 {
372 continue;
373 }
374 depth[v] = self.start[v];
375 began[v] = v;
376 }
377 for &k in &self.out[v] {
378 let e = &graph.edges[k];
379 let reach = depth[v] + e.delay;
380 if stage[e.to] == stage[v] && reach > depth[e.to] {
381 depth[e.to] = reach;
382 began[e.to] = began[v];
383 }
384 }
385 }
386 broken
387 }
388
389 fn earliest(&self, graph: &TimingGraph) -> Result<Vec<i64>, PipeError> {
394 let v = self.vars;
395 let mut start = vec![0usize; v + 1];
396 for &(a, _, _) in &self.constraints {
397 start[a + 1] += 1;
398 }
399 for i in 0..v {
400 start[i + 1] += start[i];
401 }
402 let mut fill = start.clone();
403 let mut arcs = vec![(0usize, 0i64); self.constraints.len()];
404 for &(a, b, l) in &self.constraints {
405 arcs[fill[a]] = (b, l);
406 fill[a] += 1;
407 }
408 let out = |u: usize| &arcs[start[u]..start[u + 1]];
409 let (mut index, mut low, mut on) = (vec![usize::MAX; v], vec![0usize; v], vec![false; v]);
411 let mut comp = vec![usize::MAX; v];
412 let (mut stack, mut order, mut next): (Vec<usize>, Vec<usize>, usize) = (Vec::new(), Vec::new(), 0);
413 for root in 0..v {
414 if index[root] != usize::MAX {
415 continue;
416 }
417 let mut call: Vec<(usize, usize)> = vec![(root, 0)];
418 index[root] = next;
419 low[root] = next;
420 next += 1;
421 stack.push(root);
422 on[root] = true;
423 while let Some(top) = call.last_mut() {
424 let (u, i) = *top;
425 if let Some(&(w, _)) = out(u).get(i) {
426 top.1 += 1;
427 if index[w] == usize::MAX {
428 index[w] = next;
429 low[w] = next;
430 next += 1;
431 stack.push(w);
432 on[w] = true;
433 call.push((w, 0));
434 } else if on[w] {
435 low[u] = low[u].min(index[w]);
436 }
437 continue;
438 }
439 call.pop();
440 if let Some(&(parent, _)) = call.last() {
441 low[parent] = low[parent].min(low[u]);
442 }
443 if low[u] == index[u] {
444 let c = order.len();
445 loop {
446 let w = stack.pop().expect("u is on the stack");
447 on[w] = false;
448 comp[w] = c;
449 if w == u {
450 break;
451 }
452 }
453 order.push(u);
454 }
455 }
456 }
457 if let Some(&(a, b, _)) = self.constraints.iter().find(|&&(a, b, l)| l > 0 && comp[a] == comp[b]) {
458 let mut back = vec![usize::MAX; v];
460 let mut queue: VecDeque<usize> = VecDeque::from([b]);
461 back[b] = b;
462 while let Some(u) = queue.pop_front() {
463 if u == a {
464 break;
465 }
466 for &(w, _) in out(u) {
467 if comp[w] == comp[a] && back[w] == usize::MAX {
468 back[w] = u;
469 queue.push_back(w);
470 }
471 }
472 }
473 let mut through = vec![a];
474 let mut at = a;
475 while at != b {
476 at = back[at];
477 through.push(at);
478 }
479 through.reverse();
480 let n = graph.nodes.len();
481 return Err(PipeError::Uncuttable { through: through.into_iter().filter(|&x| x < n).map(|x| graph.nodes[x].clone()).collect() });
482 }
483 let mut members: Vec<Vec<usize>> = vec![Vec::new(); order.len()];
485 for u in 0..v {
486 members[comp[u]].push(u);
487 }
488 let mut dist = vec![0i64; order.len()];
489 for c in (0..order.len()).rev() {
490 for &u in &members[c] {
491 for &(w, l) in out(u) {
492 if comp[w] != c {
493 dist[comp[w]] = dist[comp[w]].max(dist[c] + l);
494 }
495 }
496 }
497 }
498 let base = dist[comp[self.host]];
499 Ok((0..v).map(|u| dist[comp[u]] - base).collect())
500 }
501
502 fn latest(&self, graph: &TimingGraph, period: i64, latency: i64) -> Vec<i64> {
508 let n = graph.nodes.len();
509 let mut into = vec![0i64; n];
510 for e in &graph.edges {
511 into[e.to] = into[e.to].max(e.delay);
512 }
513 let (mut stage, mut depart) = (vec![latency; n], vec![0i64; n]);
514 for &v in self.topo.iter().rev() {
515 let Some(s) = self.out[v].iter().map(|&k| stage[graph.edges[k].to]).min() else { continue };
516 let d = self.out[v].iter().map(|&k| &graph.edges[k]).filter(|e| stage[e.to] == s).map(|e| e.delay + depart[e.to]).max().unwrap_or(0);
517 (stage[v], depart[v]) = if into[v] + d > period { (s - 1, 0) } else { (s, d) };
518 }
519 stage
520 }
521
522 fn settle(&mut self, graph: &TimingGraph, period: i64) -> Result<Vec<i64>, PipeError> {
524 loop {
525 let stage = self.earliest(graph)?;
526 let broken = self.broken(graph, &stage, period);
527 if broken.is_empty() {
528 return Ok(stage);
529 }
530 self.constraints.extend(broken);
531 }
532 }
533}
534
535pub fn min_latency(graph: &TimingGraph, period: i64, policy: Cuts) -> Result<i64, PipeError> {
538 let mut p = problem(graph, period, policy)?;
539 let stage = p.settle(graph, period)?;
540 Ok(p.sink.iter().map(|&s| stage[s]).max().unwrap_or(0).max(0))
541}
542
543pub fn pipeline(graph: &TimingGraph, period: i64, latency: i64, policy: Cuts) -> Result<Plan, PipeError> {
546 plan(graph, period, latency, policy).map(|(plan, _)| plan)
547}
548
549fn plan(graph: &TimingGraph, period: i64, latency: i64, policy: Cuts) -> Result<(Plan, Problem), PipeError> {
554 if latency < 0 {
555 return Err(PipeError::NegativeLatency);
556 }
557 let mut p = problem(graph, period, policy)?;
558 let earliest = p.settle(graph, period)?;
559 let minimum = p.sink.iter().map(|&s| earliest[s]).max().unwrap_or(0).max(0);
560 if latency < minimum {
561 return Err(PipeError::LatencyTooShort { minimum });
562 }
563 let latest = p.latest(graph, period, latency);
564 let supply: Vec<i64> = p.weight.iter().map(|&w| -w).collect();
565 let n = graph.nodes.len();
566 loop {
567 let mut arcs: Vec<Arc> = p.constraints.iter().map(|&(a, b, l)| Arc { from: a, to: b, capacity: None, cost: -l }).collect();
568 for &s in &p.sink {
569 arcs.push(Arc { from: p.host, to: s, capacity: None, cost: -latency });
570 arcs.push(Arc { from: s, to: p.host, capacity: None, cost: latency });
571 }
572 for v in 0..n {
573 arcs.push(Arc { from: p.host, to: v, capacity: None, cost: -earliest[v] });
574 arcs.push(Arc { from: v, to: p.host, capacity: None, cost: latest[v] });
575 }
576 let potential = match min_cost_flow(p.vars, &supply, &arcs).map_err(PipeError::Flow)? {
577 Solution::Optimal { potential, .. } => potential,
578 Solution::Unbounded { .. } => unreachable!("the earliest schedule meets every constraint and bound at the latency or less"),
579 Solution::Infeasible { .. } => unreachable!("every register count is a mirror's stage less its net's, never below 0"),
580 };
581 let host = -potential[p.host];
582 let stage_of = |v: usize| -> i64 { (-potential[v] - host) as i64 };
583 let stage: Vec<i64> = (0..n).map(stage_of).collect();
584 let broken = p.broken(graph, &stage, period);
585 if !broken.is_empty() {
586 p.constraints.extend(broken);
587 continue;
588 }
589 let mut registers = BTreeMap::new();
590 for v in 0..n {
591 let Some(m) = p.mirror[v] else { continue };
592 let count = stage_of(m) - stage[v];
593 if count > 0 {
594 registers.insert(v, count);
595 }
596 }
597 let bits = registers.values().sum();
598 return Ok((Plan { period, latency, policy, stage, registers, bits }, p));
599 }
600}
601
602pub fn check(graph: &TimingGraph, plan: &Plan) -> bool {
607 let n = graph.nodes.len();
608 if plan.stage.len() != n || graph.flops > 0 || graph.edges.iter().any(|e| e.registers != 0) {
609 return false;
610 }
611 let cut = cuts(graph, plan.policy);
612 if graph.inputs.iter().any(|&i| plan.stage[i] != 0) {
613 return false;
614 }
615 let mut needs: Vec<i64> = vec![0; n];
616 for e in &graph.edges {
617 let r = plan.stage[e.to] - plan.stage[e.from];
618 if r < 0 || (!cut[e.from] && r != 0) {
619 return false;
620 }
621 needs[e.from] = needs[e.from].max(r);
622 }
623 for &o in &graph.outputs {
624 let r = plan.latency - plan.stage[o];
625 if r < 0 {
626 return false;
627 }
628 needs[o] = needs[o].max(r);
629 }
630 for v in 0..n {
631 if plan.registers.get(&v).copied().unwrap_or(0) != needs[v] {
632 return false;
633 }
634 }
635 if plan.registers.values().sum::<i64>() != plan.bits {
636 return false;
637 }
638 let mut piped = TimingGraph { edges: Vec::with_capacity(graph.edges.len()), costs: Vec::new(), owner: Vec::new(), fanout: None, ..graph.clone() };
640 for (k, e) in graph.edges.iter().enumerate() {
641 let r = plan.stage[e.to] - plan.stage[e.from];
642 if r == 0 {
643 piped.edges.push(*e);
644 } else {
645 let q = piped.nodes.len();
646 piped.nodes.push(format!("{}~piped{k}", graph.nodes[e.from]));
647 piped.public.push(false);
648 piped.edges.push(Edge { from: e.from, to: q, delay: 0, registers: r });
649 piped.edges.push(Edge { from: q, to: e.to, delay: e.delay, registers: 0 });
650 }
651 }
652 piped.owner = vec![0; piped.edges.len()];
653 piped.depth().is_ok_and(|d| d.depth <= plan.period)
654}
655
656#[derive(Clone, Debug, PartialEq, Eq)]
658pub enum EmitError {
659 NoStructure,
661 Unsupported { cell: String, kind: String },
663 Undriven { node: String },
665 Control { port: String },
667}
668
669impl std::fmt::Display for EmitError {
670 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
671 match self {
672 EmitError::NoStructure => write!(f, "the graph has no netlist structure to write"),
673 EmitError::Unsupported { cell, kind } => write!(f, "cell {cell} is a {kind}, which is not written out"),
674 EmitError::Undriven { node } => write!(f, "{node} is read but nothing drives it"),
675 EmitError::Control { port } => write!(f, "{port} is read by the logic, so it cannot also be the pipeline's own"),
676 }
677 }
678}
679
680impl std::error::Error for EmitError {}
681
682fn ident(name: &str) -> String {
684 let plain = name.chars().next().is_some_and(|c| c.is_ascii_alphabetic() || c == '_') && name.chars().all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '$');
685 if plain { name.to_string() } else { format!("\\{name} ") }
686}
687
688struct Writer<'a> {
689 plan: &'a Plan,
690 deepest: BTreeMap<usize, i64>,
691 body: Vec<String>,
692 read: BTreeSet<usize>,
694}
695
696impl Writer<'_> {
697 fn tap(&mut self, node: usize, depth: i64) -> String {
699 self.read.insert(node);
700 if depth > 0 {
701 let d = self.deepest.entry(node).or_insert(0);
702 *d = (*d).max(depth);
703 format!("w{node}_d{depth}")
704 } else {
705 format!("w{node}")
706 }
707 }
708
709 fn pin(&mut self, pin: Pin, stage: i64) -> String {
711 match pin {
712 Pin::Node(n) => {
713 let d = stage - self.plan.stage[n];
714 self.tap(n, d)
715 }
716 Pin::Zero => "1'b0".into(),
717 Pin::One => "1'b1".into(),
718 }
719 }
720
721 fn operand(&mut self, pins: &[Pin], stage: i64, signed: bool) -> String {
724 let bits: Vec<String> = pins.iter().rev().map(|&p| self.pin(p, stage)).collect();
725 let v = format!("{{{}}}", bits.join(", "));
726 if signed { format!("$signed({v})") } else { v }
727 }
728}
729
730pub fn emit(graph: &TimingGraph, plan: &Plan, module: &str, enable: Option<&str>) -> Result<String, EmitError> {
735 let s = graph.structure.as_ref().ok_or(EmitError::NoStructure)?;
736 let controls: Vec<&str> = std::iter::once("clk").chain(enable).collect();
738 let mut read = vec![false; graph.nodes.len()];
739 for e in &graph.edges {
740 read[e.from] = true;
741 }
742 for &o in &graph.outputs {
743 read[o] = true;
744 }
745 let mut control_nodes = BTreeSet::new();
746 for (port, pins) in s.input_ports.iter().filter(|(port, _)| controls.contains(&port.as_str())) {
747 for &p in pins {
748 if let Pin::Node(n) = p {
749 if read[n] {
750 return Err(EmitError::Control { port: port.clone() });
751 }
752 control_nodes.insert(n);
753 }
754 }
755 }
756 let mut w = Writer { plan, deepest: BTreeMap::new(), body: Vec::new(), read: BTreeSet::new() };
757 for (i, cell) in s.cells.iter().enumerate() {
758 let stage = plan.stage[cell.inside];
759 let p = |k: &str| cell.ports.get(k).cloned().unwrap_or_default();
760 let num = |k: &str| cell.number(k);
761 let (a, b, y) = (p("A"), p("B"), p("Y"));
762 let (sa, sb) = (num("A_SIGNED") == 1, num("B_SIGNED") == 1);
763 let yw = y.len();
764 let (x, z) = (w.operand(&a, stage, sa && sb), w.operand(&b, stage, sa && sb));
765 let expr = match cell.kind.as_str() {
766 "$add" => format!("{x} + {z}"),
767 "$sub" => format!("{x} - {z}"),
768 "$mul" => format!("{x} * {z}"),
769 "$neg" => format!("-{}", w.operand(&a, stage, sa)),
770 "$lt" => format!("{x} < {z}"),
771 "$le" => format!("{x} <= {z}"),
772 "$gt" => format!("{x} > {z}"),
773 "$ge" => format!("{x} >= {z}"),
774 "$div" => format!("{x} / {z}"),
775 "$mod" => format!("{x} % {z}"),
776 kind => return Err(EmitError::Unsupported { cell: cell.name.clone(), kind: kind.to_string() }),
777 };
778 w.body.push(format!(" wire [{}:0] c{i}_Y = {expr};", yw.max(1) - 1));
779 }
780 for n in 0..graph.nodes.len() {
781 let stage = plan.stage[n];
782 let line = match &s.source[n] {
783 Source::Input { .. } if control_nodes.contains(&n) => continue,
784 Source::Input { port, bit } => format!(" assign w{n} = {}[{bit}];", ident(port)),
785 Source::Lut { pins, cone: Some(cone), .. } => {
786 let read: Vec<String> = pins.iter().map(|&p| w.pin(p, stage)).collect();
787 let arg = |a: ConeArg| {
788 let name = if a.index < read.len() { read[a.index].clone() } else { format!("w{n}_g{}", a.index - read.len()) };
789 if a.inverted { format!("~{name}") } else { name }
790 };
791 let mut lines: Vec<String> = cone
792 .gates
793 .iter()
794 .enumerate()
795 .map(|(i, g)| format!(" wire w{n}_g{i} = {} {} {};", arg(g.a), if g.xor { "^" } else { "&" }, arg(g.b)))
796 .collect();
797 lines.push(format!(" assign w{n} = {};", arg(cone.out)));
798 lines.join("\n")
799 }
800 Source::Lut { pins, table, .. } if pins.is_empty() => format!(" assign w{n} = 1'b{};", table & 1),
801 Source::Lut { pins, table, .. } => {
802 let read: Vec<String> = pins.iter().rev().map(|&p| w.pin(p, stage)).collect();
803 let width = 1usize << pins.len();
804 format!(" assign w{n} = {width}'h{table:x} >> {{{}}};", read.join(", "))
805 }
806 Source::Copy { from, inverted } => {
807 let src = w.pin(Pin::Node(*from), stage);
808 format!(" assign w{n} = {}{src};", if *inverted { "~" } else { "" })
809 }
810 Source::Cell { cell, bit, .. } => format!(" assign w{n} = c{cell}_Y[{bit}];"),
811 Source::Constant(v) => format!(" assign w{n} = 1'b{};", *v as u8),
812 Source::Register { .. } | Source::Inside => continue,
813 };
814 w.body.push(line);
815 }
816 let mut outs = Vec::new();
817 for (port, pins) in &s.output_ports {
818 for (bit, &p) in pins.iter().enumerate() {
819 let src = match p {
820 Pin::Node(n) => w.tap(n, plan.latency - plan.stage[n]),
821 Pin::Zero => "1'b0".into(),
822 Pin::One => "1'b1".into(),
823 };
824 outs.push(format!(" assign {}[{bit}] = {src};", ident(port)));
825 }
826 }
827 if let Some(&n) = w.read.iter().find(|&&n| matches!(s.source[n], Source::Register { .. } | Source::Inside)) {
829 return Err(EmitError::Undriven { node: graph.nodes[n].clone() });
830 }
831 let mut v = String::new();
832 let mut header = vec!["input clk".to_string()];
833 if let Some(en) = enable {
834 header.push(format!("input {}", ident(en)));
835 }
836 for (port, pins) in s.input_ports.iter().filter(|(port, _)| !controls.contains(&port.as_str())) {
837 header.push(format!("input [{}:0] {}", pins.len().max(1) - 1, ident(port)));
838 }
839 for (port, pins) in &s.output_ports {
840 header.push(format!("output [{}:0] {}", pins.len().max(1) - 1, ident(port)));
841 }
842 v.push_str(&format!("module {}(\n {}\n);\n", ident(module), header.join(",\n ")));
843 for n in 0..graph.nodes.len() {
844 if !matches!(s.source[n], Source::Register { .. } | Source::Inside) && !control_nodes.contains(&n) {
845 v.push_str(&format!(" wire w{n};\n"));
846 }
847 }
848 for (&n, &d) in &w.deepest {
849 for k in 1..=d {
850 v.push_str(&format!(" reg w{n}_d{k};\n"));
851 }
852 }
853 for line in &w.body {
854 v.push_str(line);
855 v.push('\n');
856 }
857 for line in &outs {
858 v.push_str(line);
859 v.push('\n');
860 }
861 if !w.deepest.is_empty() {
862 let step = match enable { Some(en) => format!("if ({}) begin", ident(en)), None => "begin".into() };
863 v.push_str(&format!(" always @(posedge clk) {step}\n"));
864 for (&n, &d) in &w.deepest {
865 for k in 1..=d {
866 let prev = if k == 1 { format!("w{n}") } else { format!("w{n}_d{}", k - 1) };
867 v.push_str(&format!(" w{n}_d{k} <= {prev};\n"));
868 }
869 }
870 v.push_str(" end\n");
871 }
872 v.push_str("endmodule\n");
873 Ok(v)
874}
875
876#[cfg(test)]
877#[path = "../../../logicaffeine_tests/unit/logicaffeine_compile/codegen_sva/autopipe/tests.rs"]
878mod tests;