1use std::collections::{BTreeMap, HashMap, HashSet};
46
47use logicaffeine_proof::cycle_ratio::{self, Bound, Edge, GraphError};
48use serde::{Deserialize, Serialize};
49
50use super::lutmap::{self, Lit, Xag};
51use super::netlist::{yosys_run, NetlistError};
52
53#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
55pub struct Delays {
56 pub lut: i64,
57 pub carry: i64,
58 pub dsp: i64,
59 pub memory: i64,
60}
61
62impl Delays {
63 pub const LEVELS: Delays = Delays { lut: 100, carry: 0, dsp: 100, memory: 100 };
65
66 pub const STRATIX10: Delays = Delays { lut: 100, carry: 7, dsp: 457, memory: 330 };
70}
71
72pub const STRATIX10_NS_PER_LEVEL: f64 = 0.549;
74
75#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
77pub struct Cost {
78 pub lut: i64,
79 pub carry: i64,
80 pub dsp: i64,
81 pub memory: i64,
82}
83
84impl Cost {
85 pub const NONE: Cost = Cost { lut: 0, carry: 0, dsp: 0, memory: 0 };
86 pub const LUT: Cost = Cost { lut: 1, ..Cost::NONE };
87 pub const CARRY: Cost = Cost { carry: 1, ..Cost::NONE };
88 pub const DSP: Cost = Cost { dsp: 1, ..Cost::NONE };
89 pub const MEMORY: Cost = Cost { memory: 1, ..Cost::NONE };
90
91 pub fn delay(&self, d: &Delays) -> i64 {
92 self.lut * d.lut + self.carry * d.carry + self.dsp * d.dsp + self.memory * d.memory
93 }
94}
95
96impl std::ops::Add for Cost {
97 type Output = Cost;
98 fn add(self, o: Cost) -> Cost {
99 Cost { lut: self.lut + o.lut, carry: self.carry + o.carry, dsp: self.dsp + o.dsp, memory: self.memory + o.memory }
100 }
101}
102
103#[derive(Clone, Debug, Default)]
105pub struct TimingGraph {
106 pub nodes: Vec<String>,
108 pub edges: Vec<Edge>,
109 pub costs: Vec<Cost>,
112 pub owner: Vec<usize>,
114 pub instances: Vec<String>,
116 pub flops: usize,
118 pub area: Vec<Area>,
120 pub public: Vec<bool>,
122 pub inputs: Vec<usize>,
124 pub outputs: Vec<usize>,
125 pub structure: Option<Structure>,
127 pub boxes: Vec<Boxed>,
129 pub fanout: Option<Fanout>,
131}
132
133#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
135pub enum Driver {
136 Register,
138 Logic,
140 Input,
142 Memory,
144 Box,
146 Point,
148}
149
150#[derive(Clone, Debug, Default, PartialEq, Eq)]
155pub struct Fanout {
156 pub loads: Vec<u64>,
157 pub drivers: Vec<Driver>,
158 pub limits: Vec<Option<u64>>,
159 pub clocks: Vec<bool>,
162}
163
164#[derive(Clone, Debug, PartialEq, Eq)]
166pub struct Wide {
167 pub node: usize,
168 pub name: String,
169 pub loads: u64,
170 pub driver: Driver,
171}
172
173pub fn memory_blocks(width: u64, size: u64, style: &str) -> (u64, u64) {
178 let style = style.to_ascii_lowercase();
179 let (bits, words) = if style.contains("mlab") {
180 (20, 32)
181 } else if style.contains("logic") || style.contains("register") {
182 (1, 4)
183 } else {
184 (40, 512)
185 };
186 (width.div_ceil(bits).max(1), size.div_ceil(words).max(1))
187}
188
189#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
193pub struct Area {
194 pub luts: u64,
195 pub carry: u64,
196 pub half_dsps: u64,
197 pub flops: u64,
198 pub memory_bits: u64,
199}
200
201impl Area {
202 fn add(&mut self, other: &Area) {
203 self.luts += other.luts;
204 self.carry += other.carry;
205 self.half_dsps += other.half_dsps;
206 self.flops += other.flops;
207 self.memory_bits += other.memory_bits;
208 }
209}
210
211pub fn dsp_halves(a: usize, b: usize) -> u64 {
214 let (short, long) = (a.min(b), a.max(b));
215 if short <= 18 && long <= 19 {
216 1
217 } else if long <= 27 {
218 2
219 } else {
220 2 * (a.div_ceil(27) * b.div_ceil(27)) as u64
221 }
222}
223
224#[derive(Clone, Debug, PartialEq, Eq)]
228pub struct Boxed {
229 pub cell: String,
231 pub module: String,
233 pub out: Cost,
236 pub inputs: Vec<usize>,
237 pub outputs: Vec<usize>,
238}
239
240#[derive(Clone, Debug, PartialEq, Eq)]
242pub enum Source {
243 Input { port: String, bit: usize },
245 Lut { pins: Vec<Pin>, table: u64, cone: Option<Cone> },
248 Copy { from: usize, inverted: bool },
250 Cell { cell: usize, port: String, bit: usize },
252 Constant(bool),
253 Register { data: Pin, controls: bool },
255 Inside,
257}
258
259#[derive(Clone, Debug, PartialEq, Eq)]
262pub struct Cone {
263 pub gates: Vec<ConeGate>,
264 pub out: ConeArg,
265}
266
267#[derive(Clone, Copy, Debug, PartialEq, Eq)]
268pub struct ConeGate {
269 pub xor: bool,
270 pub a: ConeArg,
271 pub b: ConeArg,
272}
273
274#[derive(Clone, Copy, Debug, PartialEq, Eq)]
276pub struct ConeArg {
277 pub index: usize,
278 pub inverted: bool,
279}
280
281#[derive(Clone, Copy, Debug, PartialEq, Eq)]
283pub enum Pin {
284 Node(usize),
285 Zero,
286 One,
287}
288
289#[derive(Clone, Debug, PartialEq)]
292pub struct KeptCell {
293 pub name: String,
294 pub kind: String,
295 pub parameters: BTreeMap<String, serde_json::Value>,
296 pub ports: BTreeMap<String, Vec<Pin>>,
297 pub inside: usize,
298}
299
300impl KeptCell {
301 pub fn number(&self, name: &str) -> u64 {
303 number(self.parameters.get(name))
304 }
305}
306
307#[derive(Clone, Debug, Default, PartialEq)]
309pub struct Structure {
310 pub source: Vec<Source>,
311 pub cells: Vec<KeptCell>,
312 pub input_ports: Vec<(String, Vec<Pin>)>,
313 pub output_ports: Vec<(String, Vec<Pin>)>,
314}
315
316#[derive(Clone, Debug, PartialEq, Eq)]
318pub struct Depth {
319 pub depth: i64,
321 pub path: Vec<usize>,
323 pub edges: Vec<usize>,
325 pub instances: Vec<InstanceDepth>,
327}
328
329#[derive(Clone, Debug, PartialEq, Eq)]
331pub struct InstanceDepth {
332 pub path: String,
333 pub total: i64,
335 pub own: i64,
337}
338
339#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
341pub enum End {
342 Register,
343 Output,
344 Box,
345}
346
347#[derive(Clone, Debug, PartialEq, Eq)]
349pub struct Endpoint {
350 pub name: String,
352 pub end: End,
353 pub bits: usize,
354 pub depth: i64,
355 pub path: Vec<usize>,
357}
358
359#[derive(Clone, Debug, PartialEq, Eq)]
361pub enum TimingError {
362 Netlist(NetlistError),
363 Json(String),
365 Cell { name: String, kind: String },
367 Latch(String),
369 CombinationalLoop(Vec<String>),
371}
372
373impl std::fmt::Display for TimingError {
374 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
375 match self {
376 TimingError::Netlist(e) => write!(f, "{e}"),
377 TimingError::Json(why) => write!(f, "the mapped netlist: {why}"),
378 TimingError::Cell { name, kind } => write!(f, "cell {name} is a {kind}, which the timing model has no delay for"),
379 TimingError::Latch(name) => write!(f, "{name} is a level-sensitive latch"),
380 TimingError::CombinationalLoop(names) => write!(f, "a combinational loop: {}", names.join(" -> ")),
381 }
382 }
383}
384
385impl std::error::Error for TimingError {}
386
387impl From<NetlistError> for TimingError {
388 fn from(e: NetlistError) -> TimingError {
389 TimingError::Netlist(e)
390 }
391}
392
393pub const BOX_OUT: &str = "logos_box_out";
396
397pub const BOX_AREA: &str = "logos_box_area";
400
401fn box_area(module: &str, m: &Module) -> Result<Area, TimingError> {
403 let Some(v) = m.attributes.get(BOX_AREA) else { return Ok(Area::default()) };
404 let counts: Option<Vec<u64>> = v.as_str().and_then(|t| t.split(',').map(|c| c.trim().parse().ok()).collect());
405 match counts.as_deref() {
406 Some(&[luts, carry, half_dsps, flops, memory_bits]) => Ok(Area { luts, carry, half_dsps, flops, memory_bits }),
407 _ => Err(TimingError::Json(format!("box {module}: {BOX_AREA} is {v}, not five counts"))),
408 }
409}
410
411fn box_out(module: &str, m: &Module) -> Result<Cost, TimingError> {
413 let Some(v) = m.attributes.get(BOX_OUT) else { return Ok(Cost::NONE) };
414 let counts: Option<Vec<i64>> = v.as_str().and_then(|t| t.split(',').map(|c| c.trim().parse().ok()).collect());
415 match counts.as_deref() {
416 Some(&[lut, carry, dsp, memory]) => Ok(Cost { lut, carry, dsp, memory }),
417 _ => Err(TimingError::Json(format!("box {module}: {BOX_OUT} is {v}, not four counts"))),
418 }
419}
420
421const KEPT: &[&str] = &[
423 "$add", "$sub", "$neg", "$lt", "$le", "$gt", "$ge", "$div", "$mod", "$divfloor", "$modfloor", "$mul", "$mem_v2",
424];
425
426pub fn script(top: &str, params: &[(&str, &str)], boxes: &[(&str, Cost)]) -> String {
428 let mut hierarchy = format!("hierarchy -check -top {top}");
429 for (name, value) in params {
430 hierarchy.push_str(&format!(" -chparam {name} {value}"));
431 }
432 let kept = KEPT.iter().enumerate().map(|(i, t)| if i == 0 { format!("t:{t}") } else { format!("t:{t} %u") }).collect::<Vec<_>>();
433 let mut steps = vec!["read_verilog -sv design.v".to_string()];
434 for (b, out) in boxes {
437 steps.push(format!("select -assert-any ={b}"));
438 steps.push(format!("blackbox ={b}"));
439 if *out != Cost::NONE {
440 steps.push(format!("setattr -mod -set {BOX_OUT} \"{},{},{},{}\" ={b}", out.lut, out.carry, out.dsp, out.memory));
441 }
442 }
443 steps.extend([
444 hierarchy,
445 "proc".into(),
446 "opt_dff -nosdff -keepdc".into(),
447 "opt_mem".into(),
448 "opt_clean".into(),
451 "memory_share -nosat -nowiden".into(),
452 "memory_collect".into(),
453 "select -set ported t:$mem_v2 r:WR_PORTS>2 %i".into(),
454 "memory_map @ported".into(),
455 "opt_expr".into(),
456 "opt_clean".into(),
457 "memory -nomap".into(),
458 "setundef -zero".into(),
459 "opt_expr".into(),
460 "opt_clean".into(),
461 "wreduce".into(),
462 "opt_clean".into(),
463 "select -set unclocked t:$mem_v2 r:RD_CLK_ENABLE<1 %i".into(),
464 "memory_map @unclocked".into(),
465 "opt_clean".into(),
466 format!("techmap {} %n", kept.join(" ")),
467 "opt -fast -nosdff".into(),
468 "flatten".into(),
469 "opt_clean".into(),
470 "write_json timing.json".into(),
471 ]);
472 steps.join("\n") + "\n"
473}
474
475pub fn modules_numbered(verilog: &str, prefixes: &[&str]) -> Vec<String> {
478 verilog
479 .lines()
480 .filter_map(|l| l.strip_prefix("module "))
481 .map(|rest| rest.chars().take_while(|c| c.is_ascii_alphanumeric() || *c == '_').collect::<String>())
482 .filter(|name| prefixes.iter().any(|p| name.strip_prefix(p).is_some_and(|n| !n.is_empty() && n.bytes().all(|b| b.is_ascii_digit()))))
483 .collect()
484}
485
486pub fn netlist(verilog: &str, top: &str, params: &[(&str, &str)], boxes: &[(&str, Cost)]) -> Result<String, TimingError> {
488 let roots: Vec<&str> = std::iter::once(top).chain(boxes.iter().map(|(b, _)| *b)).collect();
489 let run = yosys_run(&super::netlist::reachable_modules(verilog, &roots), &script(top, params, boxes), &["timing.json"])?;
490 if !run.ok {
491 return Err(run.refused().into());
492 }
493 let json = run.files[0].as_ref().ok_or_else(|| TimingError::Netlist(NetlistError::Io("yosys wrote no timing.json".into())))?;
494 Ok(String::from_utf8_lossy(json).into_owned())
495}
496
497pub fn synthesize(verilog: &str, top: &str, params: &[(&str, &str)], boxes: &[(&str, Cost)], delays: &Delays) -> Result<TimingGraph, TimingError> {
499 read(&netlist(verilog, top, params, boxes)?, delays)
500}
501
502#[derive(Deserialize)]
503struct Design {
504 modules: BTreeMap<String, Module>,
505}
506
507#[derive(Deserialize)]
508struct Module {
509 #[serde(default)]
510 attributes: BTreeMap<String, serde_json::Value>,
511 #[serde(default)]
512 ports: BTreeMap<String, Port>,
513 #[serde(default)]
514 cells: BTreeMap<String, Cell>,
515 #[serde(default)]
516 netnames: BTreeMap<String, Net>,
517}
518
519#[derive(Deserialize)]
520struct Port {
521 direction: String,
522 #[serde(default)]
523 bits: Vec<Bit>,
524}
525
526#[derive(Deserialize)]
527struct Cell {
528 #[serde(rename = "type")]
529 kind: String,
530 #[serde(default)]
531 parameters: BTreeMap<String, serde_json::Value>,
532 #[serde(default)]
533 attributes: BTreeMap<String, serde_json::Value>,
534 #[serde(default)]
535 port_directions: BTreeMap<String, String>,
536 #[serde(default)]
537 connections: BTreeMap<String, Vec<Bit>>,
538}
539
540impl Cell {
541 fn reads(&self, port: &str) -> bool {
544 match self.port_directions.get(port) {
545 Some(d) => d == "input",
546 None => !matches!(port, "Y" | "Q" | "RD_DATA"),
547 }
548 }
549}
550
551#[derive(Deserialize)]
552struct Net {
553 bits: Vec<Bit>,
554 #[serde(default)]
555 hide_name: u8,
556 #[serde(default)]
557 attributes: BTreeMap<String, serde_json::Value>,
558}
559
560#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq)]
561#[serde(untagged)]
562enum Bit {
563 Net(u64),
564 Constant(Level),
565}
566
567#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq)]
568enum Level {
569 #[serde(rename = "0")]
570 Zero,
571 #[serde(rename = "1")]
572 One,
573 #[serde(rename = "x")]
574 X,
575 #[serde(rename = "z")]
576 Z,
577}
578
579fn number(v: Option<&serde_json::Value>) -> u64 {
581 match v {
582 Some(serde_json::Value::String(s)) => s.bytes().fold(0u64, |v, b| (v << 1) | (b == b'1') as u64),
583 Some(serde_json::Value::Number(n)) => n.as_u64().unwrap_or(0),
584 _ => 0,
585 }
586}
587
588fn param(cell: &Cell, name: &str) -> u64 {
589 number(cell.parameters.get(name))
590}
591
592fn param_bits(cell: &Cell, name: &str) -> Vec<bool> {
594 match cell.parameters.get(name) {
595 Some(serde_json::Value::String(s)) => s.bytes().rev().map(|b| b == b'1').collect(),
596 Some(serde_json::Value::Number(n)) => {
597 let v = n.as_u64().unwrap_or(0);
598 (0..64).map(|i| v >> i & 1 == 1).collect()
599 }
600 _ => Vec::new(),
601 }
602}
603
604fn instance_of(name: &str, cell: &Cell) -> String {
607 if let Some(rest) = name.strip_prefix("$flatten") {
608 let scope = rest.find(".$").map_or(rest, |end| &rest[..end]);
609 return scope.split(".\\").map(|c| c.trim_start_matches('\\')).collect::<Vec<_>>().join("|");
610 }
611 if let Some(serde_json::Value::String(h)) = cell.attributes.get("hdlname") {
612 let parts: Vec<&str> = h.split(' ').collect();
613 return parts[..parts.len().saturating_sub(1)].join("|");
614 }
615 String::new()
616}
617
618fn is_gate(kind: &str) -> bool {
620 matches!(
621 kind,
622 "$_BUF_" | "$_NOT_" | "$_AND_" | "$_NAND_" | "$_OR_" | "$_NOR_" | "$_XOR_" | "$_XNOR_" | "$_ANDNOT_" | "$_ORNOT_"
623 | "$_MUX_" | "$_NMUX_" | "$_AOI3_" | "$_OAI3_" | "$_AOI4_" | "$_OAI4_"
624 )
625}
626
627fn gate(x: &mut Xag, kind: &str, pin: impl Fn(&str) -> Lit) -> Lit {
629 let (a, b) = (pin("A"), pin("B"));
630 match kind {
631 "$_BUF_" => a,
632 "$_NOT_" => a ^ 1,
633 "$_AND_" => x.and(a, b),
634 "$_NAND_" => x.and(a, b) ^ 1,
635 "$_OR_" => x.or(a, b),
636 "$_NOR_" => x.or(a, b) ^ 1,
637 "$_XOR_" => x.xor(a, b),
638 "$_XNOR_" => x.xor(a, b) ^ 1,
639 "$_ANDNOT_" => x.and(a, b ^ 1),
640 "$_ORNOT_" => x.or(a, b ^ 1),
641 "$_MUX_" => x.mux(pin("S"), b, a),
642 "$_NMUX_" => x.mux(pin("S"), b, a) ^ 1,
643 "$_AOI3_" => {
644 let ab = x.and(a, b);
645 x.or(ab, pin("C")) ^ 1
646 }
647 "$_OAI3_" => {
648 let ab = x.or(a, b);
649 x.and(ab, pin("C")) ^ 1
650 }
651 "$_AOI4_" => {
652 let (ab, cd) = (x.and(a, b), x.and(pin("C"), pin("D")));
653 x.or(ab, cd) ^ 1
654 }
655 "$_OAI4_" => {
656 let (ab, cd) = (x.or(a, b), x.or(pin("C"), pin("D")));
657 x.and(ab, cd) ^ 1
658 }
659 _ => unreachable!("is_gate admits only the gates above"),
660 }
661}
662
663fn idle_levels(kind: &str) -> Option<Vec<(&'static str, bool)>> {
667 let (family, letters) = kind.strip_prefix("$_")?.strip_suffix('_')?.split_once('_')?;
668 let high = |c: char| c == 'P';
669 let l: Vec<char> = letters.chars().collect();
670 Some(match (family, l.as_slice()) {
671 ("DFF", [_]) => vec![],
672 ("DFF" | "SDFF", [_, r, _]) => vec![("R", !high(*r))],
673 ("DFFE", [_, e]) => vec![("E", high(*e))],
674 ("DFFE" | "SDFFE" | "SDFFCE", [_, r, _, e]) => vec![("R", !high(*r)), ("E", high(*e))],
675 ("DFFSR", [_, s, r]) => vec![("S", !high(*s)), ("R", !high(*r))],
676 ("DFFSRE", [_, s, r, e]) => vec![("S", !high(*s)), ("R", !high(*r)), ("E", high(*e))],
677 ("ALDFF", [_, l]) => vec![("L", !high(*l))],
678 ("ALDFFE", [_, l, e]) => vec![("L", !high(*l)), ("E", high(*e))],
679 _ => return None,
680 })
681}
682
683fn controlled(kind: &str, cell: &Cell) -> bool {
686 let Some(idle) = idle_levels(kind) else { return true };
687 idle.iter().any(|&(pin, level)| {
688 let held = if level { Bit::Constant(Level::One) } else { Bit::Constant(Level::Zero) };
689 cell.connections.get(pin).is_none_or(|bits| bits.iter().any(|&b| b != held))
690 })
691}
692
693fn is_flop(kind: &str) -> bool {
694 kind.starts_with("$_") && kind.contains("DFF")
695}
696
697fn is_latch(kind: &str) -> bool {
698 kind.starts_with("$_DLATCH") || kind.starts_with("$_SR_")
699}
700
701struct Builder<'a> {
702 delays: &'a Delays,
703 nodes: Vec<String>,
704 net: HashMap<u64, usize>,
705 names: HashMap<u64, (String, bool)>,
706 public: Vec<bool>,
707 edges: Vec<Edge>,
708 costs: Vec<Cost>,
709 owner: Vec<usize>,
710 instances: Vec<String>,
711 instance: HashMap<String, usize>,
712 flops: usize,
713 area: Vec<Area>,
714 source: Vec<Source>,
715 cells: Vec<KeptCell>,
716 box_modules: HashMap<String, (Cost, Area)>,
718 boxes: Vec<Boxed>,
719 dsp_sums: HashSet<String>,
721 loads: Vec<u64>,
723 drivers: Vec<Option<Driver>>,
724 clocks: HashSet<usize>,
725}
726
727impl Builder<'_> {
728 fn bit(&mut self, b: Bit) -> Option<usize> {
729 let Bit::Net(id) = b else { return None };
730 if let Some(&n) = self.net.get(&id) {
731 return Some(n);
732 }
733 let n = self.nodes.len();
734 let (name, public) = self.names.get(&id).cloned().unwrap_or_else(|| (format!("net{id}"), false));
735 self.nodes.push(name);
736 self.public.push(public);
737 self.source.push(Source::Inside);
738 self.drivers.push(None);
739 self.net.insert(id, n);
740 Some(n)
741 }
742
743 fn point(&mut self, name: String) -> usize {
744 self.nodes.push(name);
745 self.public.push(false);
746 self.source.push(Source::Inside);
747 self.drivers.push(Some(Driver::Point));
748 self.nodes.len() - 1
749 }
750
751 fn pin(&mut self, b: Bit) -> Pin {
752 match b {
753 Bit::Net(_) => Pin::Node(self.bit(b).expect("a net bit is a node")),
754 Bit::Constant(Level::One) => Pin::One,
755 Bit::Constant(_) => Pin::Zero,
756 }
757 }
758
759 fn keep(&mut self, name: &str, cell: &Cell, inside: usize) {
761 let ports: BTreeMap<String, Vec<Pin>> =
762 cell.connections.iter().map(|(p, bits)| (p.clone(), bits.iter().map(|&b| self.pin(b)).collect())).collect();
763 let index = self.cells.len();
764 for (port, pins) in &ports {
765 if cell.reads(port) {
766 continue;
767 }
768 for (bit, pin) in pins.iter().enumerate() {
769 if let Pin::Node(n) = *pin {
770 self.source[n] = Source::Cell { cell: index, port: port.clone(), bit };
771 }
772 }
773 }
774 self.cells.push(KeptCell { name: name.to_string(), kind: cell.kind.clone(), parameters: cell.parameters.clone(), ports, inside });
775 }
776
777 fn edge(&mut self, from: Option<usize>, to: Option<usize>, cost: Cost, registers: i64, owner: usize) {
778 self.edge_loading(from, to, cost, registers, owner, 1);
779 }
780
781 fn edge_loading(&mut self, from: Option<usize>, to: Option<usize>, cost: Cost, registers: i64, owner: usize, loads: u64) {
783 if let (Some(from), Some(to)) = (from, to) {
784 self.edges.push(Edge { from, to, delay: cost.delay(self.delays), registers });
785 self.costs.push(cost);
786 self.owner.push(owner);
787 self.loads.push(loads);
788 }
789 }
790
791 fn memory_port(&mut self, bits: &[Bit], to: Option<usize>, registers: i64, owner: usize, across: u64, down: u64, bits_a_block: Option<u64>) {
795 let mut columns: HashMap<usize, HashSet<u64>> = HashMap::new();
796 let nodes: Vec<Option<usize>> = bits.iter().map(|&b| self.bit(b)).collect();
797 for (i, n) in nodes.iter().enumerate() {
798 if let Some(n) = n {
799 columns.entry(*n).or_default().insert(bits_a_block.map_or(0, |w| i as u64 / w));
800 }
801 }
802 let mut booked: HashSet<usize> = HashSet::new();
803 for n in nodes {
804 let loads = match n {
805 Some(n) if booked.insert(n) => bits_a_block.map_or(across, |_| columns[&n].len() as u64) * down,
806 _ => 0,
807 };
808 self.edge_loading(n, to, Cost::NONE, registers, owner, loads);
809 }
810 }
811
812 fn boxed(&mut self, name: &str, cell: &Cell, owner: usize) {
816 let (out, area) = self.box_modules[&cell.kind];
817 self.area[owner].add(&area);
818 let source = (out != Cost::NONE).then(|| self.point(name.to_string()));
819 let (mut inputs, mut outputs) = (Vec::new(), Vec::new());
820 for (port, bits) in &cell.connections {
821 let reads = cell.reads(port);
822 for (i, &b) in bits.iter().enumerate() {
823 let Some(net) = self.bit(b) else { continue };
824 if reads && port.ends_with("clk") {
825 self.clocks.insert(net);
826 }
827 if reads {
828 let end = self.point(if bits.len() == 1 { format!("{name}.{port}") } else { format!("{name}.{port}[{i}]") });
829 self.edge(Some(net), Some(end), Cost::NONE, 0, owner);
830 inputs.push(end);
831 } else {
832 self.edge(source, Some(net), out, 0, owner);
833 self.drivers[net] = Some(Driver::Box);
834 outputs.push(net);
835 }
836 }
837 }
838 self.boxes.push(Boxed { cell: name.to_string(), module: cell.kind.clone(), out, inputs, outputs });
839 }
840
841 fn owner(&mut self, path: String) -> usize {
842 if let Some(&i) = self.instance.get(&path) {
843 return i;
844 }
845 self.instances.push(path.clone());
846 self.area.push(Area::default());
847 self.instance.insert(path, self.instances.len() - 1);
848 self.instances.len() - 1
849 }
850
851 fn cell(&mut self, name: &str, cell: &Cell) -> Result<(), TimingError> {
852 let owner = self.owner(instance_of(name, cell));
853 let port = |p: &str| cell.connections.get(p).cloned().unwrap_or_default();
854 let kind = cell.kind.as_str();
855 match kind {
856 "$lut" => {
857 self.area[owner].luts += 1;
858 let y = port("Y").first().and_then(|&b| self.bit(b));
859 for a in port("A") {
860 let a = self.bit(a);
861 self.edge(a, y, Cost::LUT, 0, owner);
862 }
863 let inputs = port("A");
864 if let (Some(y), true) = (y, inputs.len() <= 6) {
865 let pins = inputs.iter().map(|&b| self.pin(b)).collect();
866 let table = param_bits(cell, "LUT").iter().enumerate().fold(0u64, |t, (i, &b)| t | (b as u64) << i);
867 self.source[y] = Source::Lut { pins, table, cone: None };
868 }
869 }
870 "$add" | "$sub" if self.dsp_sums.contains(name) => {
871 let inside = self.point(format!("{name}.dsp"));
872 for x in port("A").into_iter().chain(port("B")) {
873 let x = self.bit(x);
874 self.edge(x, Some(inside), Cost::NONE, 0, owner);
875 }
876 for y in port("Y") {
877 let y = self.bit(y);
878 self.edge(Some(inside), y, Cost::NONE, 0, owner);
879 }
880 self.keep(name, cell, inside);
881 }
882 "$add" | "$sub" | "$neg" | "$lt" | "$le" | "$gt" | "$ge" => {
883 let (a, b, y) = (port("A"), port("B"), port("Y"));
884 let compare = matches!(kind, "$lt" | "$le" | "$gt" | "$ge");
885 let width = if compare { a.len().max(b.len()) } else { y.len() };
886 self.area[owner].carry += width as u64;
887 let extend = |bits: &[Bit], signed: bool, k: usize| -> Option<Bit> {
888 if k < bits.len() {
889 Some(bits[k])
890 } else if signed {
891 bits.last().copied()
892 } else {
893 None
894 }
895 };
896 let (sa, sb) = (param(cell, "A_SIGNED") == 1, param(cell, "B_SIGNED") == 1);
897 let mut previous = None;
898 let mut first = None;
899 for k in 0..width {
900 let carry = Some(self.point(format!("{name}.carry[{k}]")));
901 first = first.or(carry);
902 for x in [extend(&a, sa, k), extend(&b, sb, k)].into_iter().flatten() {
903 let x = self.bit(x);
904 self.edge(x, carry, Cost::LUT, 0, owner);
905 }
906 self.edge(previous, carry, Cost::CARRY, 0, owner);
907 previous = carry;
908 if !compare {
909 let out = self.bit(y[k]);
910 self.edge(carry, out, Cost::NONE, 0, owner);
911 }
912 }
913 if compare {
914 let out = y.first().and_then(|&b| self.bit(b));
915 self.edge(previous, out, Cost::NONE, 0, owner);
916 }
917 if let Some(inside) = first {
918 self.keep(name, cell, inside);
919 }
920 }
921 "$div" | "$mod" | "$divfloor" | "$modfloor" => {
922 let (a, b, y) = (port("A"), port("B"), port("Y"));
923 self.area[owner].carry += (a.len() * b.len()) as u64;
924 let stage = Cost { lut: 1, carry: b.len() as i64, ..Cost::NONE };
925 let mut previous = None;
926 let mut stages = Vec::with_capacity(a.len());
927 for s in 0..a.len() {
928 let node = Some(self.point(format!("{name}.stage[{s}]")));
929 let dividend = self.bit(a[a.len() - 1 - s]);
930 self.edge(dividend, node, stage, 0, owner);
931 for &x in &b {
932 let x = self.bit(x);
933 self.edge(x, node, stage, 0, owner);
934 }
935 self.edge(previous, node, stage, 0, owner);
936 previous = node;
937 stages.push(node);
938 }
939 let quotient = matches!(kind, "$div" | "$divfloor");
940 for (i, &out) in y.iter().enumerate() {
941 let from = if quotient { a.len().checked_sub(1 + i).and_then(|s| stages[s]) } else { previous };
942 let out = self.bit(out);
943 self.edge(from, out, Cost::NONE, 0, owner);
944 }
945 if let Some(inside) = stages.first().copied().flatten() {
946 self.keep(name, cell, inside);
947 }
948 }
949 "$mul" => {
950 let (a, b, y) = (port("A"), port("B"), port("Y"));
951 let (sa, sb) = (param(cell, "A_SIGNED") == 1, param(cell, "B_SIGNED") == 1);
952 let shift = power(&a, sa).map(|s| (s, &b, sb)).or_else(|| power(&b, sb).map(|s| (s, &a, sa)));
953 if let Some((s, other, signed)) = shift {
954 for (k, &out) in y.iter().enumerate() {
955 let from = if k < s || other.is_empty() {
956 None
957 } else if k - s < other.len() {
958 Some(other[k - s])
959 } else if signed {
960 other.last().copied()
961 } else {
962 None
963 };
964 let to = self.bit(out);
965 match from.map(|b| self.pin(b)) {
966 Some(Pin::Node(f)) => {
967 self.edge(Some(f), to, Cost::NONE, 0, owner);
968 if let Some(to) = to {
969 self.source[to] = Source::Copy { from: f, inverted: false };
970 }
971 }
972 constant => {
973 if let Some(to) = to {
974 self.source[to] = Source::Constant(constant == Some(Pin::One));
975 }
976 }
977 }
978 }
979 } else {
980 self.area[owner].half_dsps += dsp_halves(a.len(), b.len());
981 let dsp = Some(self.point(format!("{name}.dsp")));
982 for x in a.iter().chain(b.iter()) {
983 let x = self.bit(*x);
984 self.edge(x, dsp, Cost::NONE, 0, owner);
985 }
986 for &out in &y {
987 let out = self.bit(out);
988 self.edge(dsp, out, Cost::DSP, 0, owner);
989 }
990 if let Some(inside) = dsp {
991 self.keep(name, cell, inside);
992 }
993 }
994 }
995 "$mem_v2" => {
996 let (abits, width) = (param(cell, "ABITS") as usize, param(cell, "WIDTH") as usize);
997 let size = param(cell, "SIZE");
998 self.area[owner].memory_bits += width as u64 * size;
999 let style = match cell.attributes.get("ramstyle") {
1000 Some(serde_json::Value::String(s)) => s.clone(),
1001 _ => String::new(),
1002 };
1003 let (across, down) = memory_blocks(width as u64, size, &style);
1004 let bits_a_block = width.div_ceil(across as usize).max(1) as u64;
1005 let reads = param(cell, "RD_PORTS") as usize;
1006 let writes = param(cell, "WR_PORTS") as usize;
1007 let clocked = param_bits(cell, "RD_CLK_ENABLE");
1008 let contents = Some(self.point(format!("{name}.contents")));
1009 for c in port("RD_CLK").into_iter().chain(port("WR_CLK")) {
1010 if let Some(c) = self.bit(c) {
1011 self.clocks.insert(c);
1012 }
1013 }
1014 let (waddr, wdata, wen) = (port("WR_ADDR"), port("WR_DATA"), port("WR_EN"));
1015 for w in 0..writes.max(1) {
1016 let a: Vec<Bit> = waddr.iter().skip(w * abits).take(abits).copied().collect();
1017 self.memory_port(&a, contents, 1, owner, across, down, None);
1018 let d: Vec<Bit> = wdata.iter().skip(w * width).take(width).copied().collect();
1019 self.memory_port(&d, contents, 1, owner, across, down, Some(bits_a_block));
1020 let e: Vec<Bit> = wen.iter().skip(w * width).take(width).copied().collect();
1021 self.memory_port(&e, contents, 1, owner, across, down, Some(bits_a_block));
1022 }
1023 let (addr, data, en, srst, arst) = (port("RD_ADDR"), port("RD_DATA"), port("RD_EN"), port("RD_SRST"), port("RD_ARST"));
1024 for r in 0..reads {
1025 let sync = clocked.get(r).copied().unwrap_or(false);
1026 let held = if sync { 1 } else { 0 };
1027 let read = Some(self.point(format!("{name}.read[{r}]")));
1028 self.edge(contents, read, Cost::NONE, held, owner);
1029 let controls: Vec<Bit> =
1030 addr.iter().skip(r * abits).take(abits).chain(en.get(r)).chain(srst.get(r)).chain(arst.get(r)).copied().collect();
1031 self.memory_port(&controls, read, held, owner, across, down, None);
1032 for &out in data.iter().skip(r * width).take(width) {
1033 let out = self.bit(out);
1034 self.edge(read, out, Cost::MEMORY, 0, owner);
1035 if let Some(out) = out {
1036 self.drivers[out] = Some(Driver::Memory);
1037 }
1038 }
1039 }
1040 }
1041 _ if self.box_modules.contains_key(kind) => self.boxed(name, cell, owner),
1042 _ if is_latch(kind) => return Err(TimingError::Latch(name.to_string())),
1043 _ if is_flop(kind) => {
1044 let q = port("Q").first().and_then(|&b| self.bit(b));
1045 self.flops += 1;
1046 self.area[owner].flops += 1;
1047 if let Some(q) = q {
1048 let data = port("D").first().map_or(Pin::Zero, |&b| self.pin(b));
1049 self.source[q] = Source::Register { data, controls: controlled(kind, cell) };
1050 }
1051 for &c in &port("C") {
1052 if let Some(c) = self.bit(c) {
1053 self.clocks.insert(c);
1054 }
1055 }
1056 for (p, bits) in &cell.connections {
1057 if p == "C" || p == "Q" {
1058 continue;
1059 }
1060 for &x in bits {
1061 let x = self.bit(x);
1062 self.edge(x, q, Cost::NONE, 1, owner);
1063 }
1064 }
1065 }
1066 _ => return Err(TimingError::Cell { name: name.to_string(), kind: kind.to_string() }),
1067 }
1068 Ok(())
1069 }
1070}
1071
1072fn power(bits: &[Bit], signed: bool) -> Option<usize> {
1075 let constant = bits.iter().all(|b| matches!(b, Bit::Constant(_)));
1076 let ones: Vec<usize> = (0..bits.len()).filter(|&i| bits[i] == Bit::Constant(Level::One)).collect();
1077 (constant && ones.len() == 1 && !(signed && ones[0] + 1 == bits.len())).then(|| ones[0])
1078}
1079
1080fn dsp_sums(module: &Module) -> HashSet<String> {
1085 let nets = |bits: &[Bit]| bits.iter().filter_map(|b| if let Bit::Net(id) = b { Some(*id) } else { None }).collect::<Vec<_>>();
1086 let port = |cell: &Cell, p: &str| cell.connections.get(p).cloned().unwrap_or_default();
1087 let mut dsp: HashSet<u64> = HashSet::new();
1088 for cell in module.cells.values().filter(|c| c.kind == "$mul") {
1089 let (sa, sb) = (number(cell.parameters.get("A_SIGNED")) == 1, number(cell.parameters.get("B_SIGNED")) == 1);
1090 if power(&port(cell, "A"), sa).is_none() && power(&port(cell, "B"), sb).is_none() {
1091 dsp.extend(nets(&port(cell, "Y")));
1092 }
1093 }
1094 let mut sums: HashSet<String> = HashSet::new();
1095 loop {
1096 let mut grew = false;
1097 for (name, cell) in &module.cells {
1098 if !matches!(cell.kind.as_str(), "$add" | "$sub") || sums.contains(name) {
1099 continue;
1100 }
1101 let inside = |p: &str| {
1102 let bits = port(cell, p);
1103 let ids = nets(&bits);
1104 !ids.is_empty() && ids.iter().all(|id| dsp.contains(id))
1105 };
1106 if inside("A") && inside("B") {
1107 sums.insert(name.clone());
1108 dsp.extend(nets(&port(cell, "Y")));
1109 grew = true;
1110 }
1111 }
1112 if !grew {
1113 return sums;
1114 }
1115 }
1116}
1117
1118fn unindexed(node: &str) -> &str {
1120 let mut net = node;
1121 while let Some((head, index)) = net.strip_suffix(']').and_then(|n| n.rsplit_once('[')) {
1122 if index.is_empty() || !index.bytes().all(|b| b.is_ascii_digit()) {
1123 break;
1124 }
1125 net = head;
1126 }
1127 net
1128}
1129
1130fn net_names(module: &Module) -> HashMap<u64, (String, bool)> {
1133 let mut best: HashMap<u64, (bool, usize, usize, String)> = HashMap::new();
1134 for (name, net) in &module.netnames {
1135 let hidden = net.hide_name != 0 || name.starts_with('$');
1136 let scopes = name.matches('.').count();
1137 for (i, &b) in net.bits.iter().enumerate() {
1138 let Bit::Net(id) = b else { continue };
1139 let shown = if net.bits.len() == 1 { name.clone() } else { format!("{name}[{i}]") };
1140 let rank = (hidden, scopes, shown.len(), shown);
1141 match best.get(&id) {
1142 Some(have) if *have <= rank => {}
1143 _ => {
1144 best.insert(id, rank);
1145 }
1146 }
1147 }
1148 }
1149 best.into_iter().map(|(id, (hidden, _, _, name))| (id, (name, !hidden))).collect()
1150}
1151
1152pub fn read(json: &str, delays: &Delays) -> Result<TimingGraph, TimingError> {
1154 let design: Design = serde_json::from_str(json).map_err(|e| TimingError::Json(e.to_string()))?;
1155 let flag = |m: &Module, name: &str| match m.attributes.get(name) {
1156 Some(serde_json::Value::String(s)) => s.contains('1'),
1157 Some(serde_json::Value::Number(n)) => n.as_u64() == Some(1),
1158 _ => false,
1159 };
1160 let box_modules: HashMap<String, (Cost, Area)> = design
1161 .modules
1162 .iter()
1163 .filter(|(_, m)| flag(m, "blackbox"))
1164 .map(|(name, m)| Ok((name.clone(), (box_out(name, m)?, box_area(name, m)?))))
1165 .collect::<Result<_, TimingError>>()?;
1166 let module = match design.modules.values().filter(|m| flag(m, "top")).collect::<Vec<_>>().as_slice() {
1167 [m] => *m,
1168 [] if design.modules.len() == 1 => design.modules.values().next().expect("one module"),
1169 _ => return Err(TimingError::Json("no single top module".into())),
1170 };
1171 let mut b = Builder {
1172 delays,
1173 nodes: Vec::new(),
1174 net: HashMap::new(),
1175 names: net_names(module),
1176 public: Vec::new(),
1177 source: Vec::new(),
1178 cells: Vec::new(),
1179 edges: Vec::new(),
1180 costs: Vec::new(),
1181 owner: Vec::new(),
1182 instances: Vec::new(),
1183 instance: HashMap::new(),
1184 flops: 0,
1185 area: Vec::new(),
1186 box_modules,
1187 boxes: Vec::new(),
1188 dsp_sums: dsp_sums(module),
1189 loads: Vec::new(),
1190 drivers: Vec::new(),
1191 clocks: HashSet::new(),
1192 };
1193 b.owner(String::new());
1194 let mut gates: HashMap<u64, (&str, &Cell)> = HashMap::new();
1195 let mut read: Vec<u64> = Vec::new();
1196 let nets = |bits: &[Bit]| bits.iter().filter_map(|b| if let Bit::Net(id) = b { Some(*id) } else { None }).collect::<Vec<_>>();
1197 for (name, cell) in &module.cells {
1198 if is_gate(&cell.kind) {
1199 if let Some(&Bit::Net(y)) = cell.connections.get("Y").and_then(|y| y.first()) {
1200 gates.insert(y, (name, cell));
1201 }
1202 continue;
1203 }
1204 b.cell(name, cell)?;
1205 for (port, bits) in &cell.connections {
1206 if cell.reads(port) {
1207 read.extend(nets(bits));
1208 }
1209 }
1210 }
1211 for port in module.ports.values().filter(|p| p.direction == "output") {
1212 read.extend(nets(&port.bits));
1213 }
1214 b.logic(&gates, read);
1215 let mut ports = |direction: &str| -> Vec<usize> {
1216 module.ports.values().filter(|p| p.direction == direction).flat_map(|p| p.bits.iter().copied()).filter_map(|bit| b.bit(bit)).collect()
1217 };
1218 let (inputs, outputs) = (ports("input"), ports("output"));
1219 let mut named = |direction: &str| -> Vec<(String, Vec<Pin>)> {
1220 module.ports.iter().filter(|(_, p)| p.direction == direction).map(|(name, p)| (name.clone(), p.bits.iter().map(|&bit| b.pin(bit)).collect())).collect()
1221 };
1222 let (input_ports, output_ports) = (named("input"), named("output"));
1223 for (port, pins) in &input_ports {
1224 for (bit, pin) in pins.iter().enumerate() {
1225 if let Pin::Node(n) = *pin {
1226 b.source[n] = Source::Input { port: port.clone(), bit };
1227 }
1228 }
1229 }
1230 let drivers = b
1231 .drivers
1232 .iter()
1233 .zip(&b.source)
1234 .map(|(d, s)| {
1235 d.unwrap_or(match s {
1236 Source::Register { .. } => Driver::Register,
1237 Source::Input { .. } | Source::Inside => Driver::Input,
1238 Source::Lut { .. } | Source::Copy { .. } | Source::Cell { .. } | Source::Constant(_) => Driver::Logic,
1239 })
1240 })
1241 .collect();
1242 let mut asked: HashMap<u64, u64> = HashMap::new();
1243 for net in module.netnames.values() {
1244 let limit = match net.attributes.get("maxfan") {
1245 Some(serde_json::Value::String(v)) => u64::from_str_radix(v, 2).ok(),
1246 Some(serde_json::Value::Number(n)) => n.as_u64(),
1247 _ => None,
1248 };
1249 if let Some(limit) = limit {
1250 for &bit in &net.bits {
1251 if let Bit::Net(id) = bit {
1252 asked.insert(id, limit);
1253 }
1254 }
1255 }
1256 }
1257 let mut limits = vec![None; b.nodes.len()];
1258 for (id, &node) in &b.net {
1259 limits[node] = asked.get(id).copied();
1260 }
1261 let clocks = (0..b.nodes.len()).map(|n| b.clocks.contains(&n)).collect();
1262 let fanout = Fanout { loads: b.loads, drivers, limits, clocks };
1263 let structure = Structure { source: b.source, cells: b.cells, input_ports, output_ports };
1264 Ok(TimingGraph {
1265 nodes: b.nodes,
1266 edges: b.edges,
1267 costs: b.costs,
1268 owner: b.owner,
1269 instances: b.instances,
1270 flops: b.flops,
1271 area: b.area,
1272 public: b.public,
1273 inputs,
1274 outputs,
1275 structure: Some(structure),
1276 boxes: b.boxes,
1277 fanout: Some(fanout),
1278 })
1279}
1280
1281fn cone(xag: &Xag, root: u32, leaves: &[u32], flip: usize, invert: bool) -> Cone {
1284 let mut index: HashMap<u32, usize> = leaves.iter().enumerate().map(|(j, &l)| (l, j)).collect();
1285 let pin_flip = |n: u32, index: &HashMap<u32, usize>| -> bool { index.get(&n).is_some_and(|&j| j < leaves.len() && flip >> j & 1 == 1) };
1286 let mut gates: Vec<ConeGate> = Vec::new();
1287 let mut stack = vec![root];
1288 while let Some(&n) = stack.last() {
1289 if index.contains_key(&n) {
1290 stack.pop();
1291 continue;
1292 }
1293 let (a, b, xor) = match xag.gate(n as usize) {
1294 lutmap::Gate::And(a, b) => (a, b, false),
1295 lutmap::Gate::Xor(a, b) => (a, b, true),
1296 _ => unreachable!("a cut stops every path at a leaf"),
1297 };
1298 let missing: Vec<u32> = [a, b].iter().map(|&l| lutmap::node(l) as u32).filter(|m| !index.contains_key(m)).collect();
1299 if !missing.is_empty() {
1300 stack.extend(missing);
1301 continue;
1302 }
1303 let arg = |l: Lit| {
1304 let m = lutmap::node(l) as u32;
1305 ConeArg { index: index[&m], inverted: lutmap::complemented(l) ^ pin_flip(m, &index) }
1306 };
1307 let gate = ConeGate { xor, a: arg(a), b: arg(b) };
1308 index.insert(n, leaves.len() + gates.len());
1309 gates.push(gate);
1310 stack.pop();
1311 }
1312 let out = ConeArg { index: index[&root], inverted: invert ^ pin_flip(root, &index) };
1313 Cone { gates, out }
1314}
1315
1316struct Logic {
1319 xag: Xag,
1320 lit: HashMap<u64, Lit>,
1321 cut: HashMap<u64, Lit>,
1322 input_net: HashMap<usize, u64>,
1323 group: Vec<u32>,
1324}
1325
1326impl Logic {
1327 fn input(&mut self, net: u64) -> Lit {
1328 let l = self.xag.input();
1329 self.input_net.insert(lutmap::node(l), net);
1330 self.group.push(0);
1331 l
1332 }
1333
1334 fn read(&self, net: u64) -> Option<Lit> {
1336 self.lit.get(&net).or_else(|| self.cut.get(&net)).copied()
1337 }
1338}
1339
1340impl Builder<'_> {
1341 fn literal(&mut self, logic: &mut Logic, gates: &HashMap<u64, (&str, &Cell)>, net: u64) {
1345 let mut open: HashSet<u64> = HashSet::new();
1346 let mut stack: Vec<u64> = vec![net];
1347 while let Some(&top) = stack.last() {
1348 if logic.lit.contains_key(&top) {
1349 stack.pop();
1350 continue;
1351 }
1352 let Some(&(name, cell)) = gates.get(&top) else {
1353 let l = logic.input(top);
1354 logic.lit.insert(top, l);
1355 stack.pop();
1356 continue;
1357 };
1358 open.insert(top);
1359 let mut waiting = false;
1360 for (port, bits) in &cell.connections {
1361 if !cell.reads(port) {
1362 continue;
1363 }
1364 for &bit in bits {
1365 let Bit::Net(id) = bit else { continue };
1366 if logic.read(id).is_some() {
1367 continue;
1368 }
1369 if open.contains(&id) {
1370 let l = logic.input(id);
1371 logic.cut.insert(id, l);
1372 } else {
1373 stack.push(id);
1374 waiting = true;
1375 }
1376 }
1377 }
1378 if waiting {
1379 continue;
1380 }
1381 let owner = self.owner(instance_of(name, cell)) as u32;
1382 let pins: Vec<(&str, Lit)> = cell
1383 .connections
1384 .iter()
1385 .filter(|(p, _)| cell.reads(p))
1386 .map(|(p, bits)| {
1387 let l = match bits.first() {
1388 Some(&Bit::Net(id)) => logic.read(id).expect("a pin is built before its gate"),
1389 Some(Bit::Constant(Level::One)) => lutmap::TRUE,
1390 _ => lutmap::FALSE,
1391 };
1392 (p.as_str(), l)
1393 })
1394 .collect();
1395 let out = gate(&mut logic.xag, &cell.kind, |p| pins.iter().find(|(q, _)| *q == p).map_or(lutmap::FALSE, |&(_, l)| l));
1396 let made = logic.xag.len();
1397 logic.group.resize(made, owner);
1398 logic.lit.insert(top, out);
1399 open.remove(&top);
1400 stack.pop();
1401 }
1402 }
1403
1404 fn logic(&mut self, gates: &HashMap<u64, (&str, &Cell)>, read: Vec<u64>) {
1407 let mut logic = Logic { xag: Xag::new(), lit: HashMap::new(), cut: HashMap::new(), input_net: HashMap::new(), group: vec![0] };
1408 let mut driven: Vec<u64> = read.into_iter().filter(|n| gates.contains_key(n)).collect();
1409 driven.sort_unstable();
1410 driven.dedup();
1411 for &net in &driven {
1412 self.literal(&mut logic, gates, net);
1413 }
1414 let mut cut: Vec<u64> = logic.cut.keys().copied().collect();
1415 cut.sort_unstable();
1416 driven.extend(cut);
1417 driven.sort_unstable();
1418 driven.dedup();
1419 let outputs: Vec<Lit> = driven.iter().map(|n| logic.lit[n]).collect();
1420 let mapped = lutmap::map(&logic.xag, &logic.group, &outputs, 6);
1421 let mut named: Vec<(u64, Lit)> = logic.lit.iter().map(|(&n, &l)| (n, l)).collect();
1422 named.sort_unstable();
1423 let mut net_of: HashMap<usize, u64> = HashMap::new();
1424 for (net, l) in named {
1425 net_of.entry(lutmap::node(l)).or_insert(net);
1426 }
1427 net_of.extend(logic.input_net.iter().map(|(&n, &net)| (n, net)));
1428 let mut timing: HashMap<u32, usize> = HashMap::new();
1429 let mut at = |b: &mut Builder<'_>, m: u32| -> usize {
1430 if let Some(&t) = timing.get(&m) {
1431 return t;
1432 }
1433 let origin = mapped.origin[m as usize] as usize;
1434 let t = match (mapped.exact[m as usize], net_of.get(&origin)) {
1435 (true, Some(&net)) => b.bit(Bit::Net(net)).expect("a net"),
1436 (false, Some(&net)) => {
1437 let base = b.bit(Bit::Net(net)).map(|t| b.nodes[t].clone()).expect("a net");
1438 b.point(format!("{base}~{m}"))
1439 }
1440 (_, None) => b.point(format!("lut{m}")),
1441 };
1442 timing.insert(m, t);
1443 t
1444 };
1445 let owner_of = |m: u32| logic.group[mapped.origin[m as usize] as usize] as usize;
1446 let inverted = |m: u32| -> bool {
1448 let origin = mapped.origin[m as usize] as usize;
1449 if !mapped.exact[m as usize] || matches!(logic.xag.gate(origin), lutmap::Gate::Input) {
1450 return false;
1451 }
1452 match (net_of.get(&origin), mapped.image[origin]) {
1453 (Some(net), Some(img)) => lutmap::complemented(logic.lit[net]) ^ lutmap::complemented(img),
1454 _ => false,
1455 }
1456 };
1457 for lut in &mapped.luts {
1458 let root = at(self, lut.root);
1459 self.area[owner_of(lut.root)].luts += 1;
1460 let mut pins = Vec::with_capacity(lut.leaves.len());
1461 let mut flip = 0usize;
1462 for (j, &leaf) in lut.leaves.iter().enumerate() {
1463 let from = at(self, leaf);
1464 self.edge(Some(from), Some(root), Cost::LUT, 0, owner_of(lut.root));
1465 pins.push(Pin::Node(from));
1466 flip |= (inverted(leaf) as usize) << j;
1467 }
1468 let rows = 1usize << lut.leaves.len();
1469 let table = lutmap::function(&mapped.xag, lut.root, &lut.leaves);
1470 let mut read = (0..rows).fold(0u64, |t, i| t | (table >> (i ^ flip) & 1) << i);
1471 if inverted(lut.root) {
1472 read = !read & if rows == 64 { u64::MAX } else { (1u64 << rows) - 1 };
1473 }
1474 let cone = cone(&mapped.xag, lut.root, &lut.leaves, flip, inverted(lut.root));
1475 self.source[root] = Source::Lut { pins, table: read, cone: Some(cone) };
1476 }
1477 for &net in &driven {
1478 let lit = logic.lit[&net];
1479 let Some(img) = mapped.image[lutmap::node(lit)] else { continue };
1480 let m = lutmap::node(img) as u32;
1481 let complement = lutmap::complemented(lit) ^ lutmap::complemented(img);
1482 let to = self.bit(Bit::Net(net)).expect("a net");
1483 if m == 0 {
1484 self.source[to] = Source::Constant(complement);
1485 continue;
1486 }
1487 let from = at(self, m);
1488 if from != to {
1489 self.edge(Some(from), Some(to), Cost::NONE, 0, owner_of(m));
1490 self.source[to] = Source::Copy { from, inverted: complement ^ inverted(m) };
1491 }
1492 }
1493 }
1494}
1495
1496impl TimingGraph {
1497 pub fn area_under(&self, path: &str) -> Area {
1499 let mut total = Area::default();
1500 for (instance, area) in self.instances.iter().zip(&self.area) {
1501 let under = path.is_empty() || instance == path || instance.strip_prefix(path).is_some_and(|rest| rest.starts_with('|'));
1502 if under {
1503 total.add(area);
1504 }
1505 }
1506 total
1507 }
1508
1509 fn order(&self) -> Result<Vec<usize>, TimingError> {
1512 let n = self.nodes.len();
1513 let mut fanin = vec![0usize; n];
1514 let mut out: Vec<Vec<usize>> = vec![Vec::new(); n];
1515 for (k, e) in self.edges.iter().enumerate() {
1516 if e.registers == 0 {
1517 fanin[e.to] += 1;
1518 out[e.from].push(k);
1519 }
1520 }
1521 let mut ready: Vec<usize> = (0..n).filter(|&v| fanin[v] == 0).collect();
1522 let mut order = Vec::with_capacity(self.edges.len());
1523 let mut done = 0;
1524 while let Some(v) = ready.pop() {
1525 done += 1;
1526 for &k in &out[v] {
1527 order.push(k);
1528 let t = self.edges[k].to;
1529 fanin[t] -= 1;
1530 if fanin[t] == 0 {
1531 ready.push(t);
1532 }
1533 }
1534 }
1535 if done == n {
1536 return Ok(order);
1537 }
1538 let comb: Vec<Edge> = self.edges.iter().filter(|e| e.registers == 0).map(|e| Edge { delay: 0, ..*e }).collect();
1539 let cycle = match cycle_ratio::max_cycle_ratio(n, &comb) {
1540 Ok(Bound::CombinationalLoop(cycle)) => cycle,
1541 other => unreachable!("an unordered node is on a cycle of combinational edges: {other:?}"),
1542 };
1543 Err(TimingError::CombinationalLoop(cycle.iter().map(|&k| self.nodes[comb[k].from].clone()).collect()))
1544 }
1545
1546 fn arrive(&self) -> Result<(Vec<usize>, Vec<i64>, Vec<usize>), TimingError> {
1549 let order = self.order()?;
1550 let mut arrival = vec![0i64; self.nodes.len()];
1551 let mut via = vec![usize::MAX; self.nodes.len()];
1552 for &k in &order {
1553 let e = &self.edges[k];
1554 let reach = arrival[e.from] + e.delay;
1555 if reach > arrival[e.to] || reach == arrival[e.to] && via[e.to] == usize::MAX {
1556 arrival[e.to] = reach;
1557 via[e.to] = k;
1558 }
1559 }
1560 Ok((order, arrival, via))
1561 }
1562
1563 pub fn arrivals(&self) -> Result<Vec<i64>, TimingError> {
1565 Ok(self.arrive()?.1)
1566 }
1567
1568 pub fn fanouts(&self) -> Vec<u64> {
1572 let mut loads = vec![0u64; self.nodes.len()];
1573 for (k, e) in self.edges.iter().enumerate() {
1574 loads[e.from] += self.fanout.as_ref().map_or(1, |f| f.loads[k]);
1575 }
1576 loads
1577 }
1578
1579 pub fn wide(&self, logic: u64, register: u64) -> Vec<Wide> {
1584 let Some(fanout) = &self.fanout else { return Vec::new() };
1585 let mut wide: Vec<Wide> = self
1586 .fanouts()
1587 .into_iter()
1588 .enumerate()
1589 .filter_map(|(node, loads)| {
1590 if fanout.clocks[node] {
1591 return None;
1592 }
1593 let driver = fanout.drivers[node];
1594 let (bound, copy) = match driver {
1595 Driver::Register => (register, fanout.limits[node].map_or(loads, |n| n.min(loads))),
1596 Driver::Logic | Driver::Input | Driver::Memory => (logic, loads),
1597 Driver::Box | Driver::Point => return None,
1598 };
1599 (copy > bound).then(|| Wide { node, name: self.nodes[node].clone(), loads, driver })
1600 })
1601 .collect();
1602 wide.sort_by(|a, b| b.loads.cmp(&a.loads).then_with(|| a.name.cmp(&b.name)));
1603 wide
1604 }
1605
1606 pub fn depth(&self) -> Result<Depth, TimingError> {
1607 let (order, arrival, via) = self.arrive()?;
1608 let n = self.nodes.len();
1609 let mut departure = vec![0i64; n];
1610 for &k in order.iter().rev() {
1611 let e = &self.edges[k];
1612 departure[e.from] = departure[e.from].max(e.delay + departure[e.to]);
1613 }
1614 let mut own = vec![0i64; self.instances.len()];
1615 for &k in &order {
1616 let e = &self.edges[k];
1617 if e.delay > 0 {
1618 own[self.owner[k]] = own[self.owner[k]].max(arrival[e.from] + e.delay + departure[e.to]);
1619 }
1620 }
1621 let mut total: BTreeMap<&str, i64> = BTreeMap::new();
1622 for (i, path) in self.instances.iter().enumerate() {
1623 let mut at = path.as_str();
1624 loop {
1625 let t = total.entry(at).or_insert(0);
1626 *t = (*t).max(own[i]);
1627 match at.rfind('|') {
1628 Some(cut) => at = &at[..cut],
1629 None if !at.is_empty() => at = "",
1630 None => break,
1631 }
1632 }
1633 }
1634 let own_of: HashMap<&str, i64> = self.instances.iter().zip(&own).map(|(p, &d)| (p.as_str(), d)).collect();
1635 let instances = total
1636 .into_iter()
1637 .map(|(path, total)| InstanceDepth { path: path.to_string(), total, own: own_of.get(path).copied().unwrap_or(0) })
1638 .collect();
1639 let end = (0..n).max_by_key(|&v| (arrival[v], std::cmp::Reverse(v))).unwrap_or(0);
1640 let path = if n > 0 { self.trace(&via, end) } else { Vec::new() };
1641 let edges = path.iter().skip(1).map(|&v| via[v]).collect();
1642 Ok(Depth { depth: arrival.get(end).copied().unwrap_or(0), path, edges, instances })
1643 }
1644
1645 pub fn reweigh(&mut self, delays: &Delays) {
1647 assert_eq!(self.costs.len(), self.edges.len(), "a graph read from a netlist has a cost for every edge");
1648 for (e, c) in self.edges.iter_mut().zip(&self.costs) {
1649 e.delay = c.delay(delays);
1650 }
1651 }
1652
1653 pub fn cost(&self, edges: &[usize]) -> Cost {
1655 edges.iter().fold(Cost::NONE, |sum, &k| sum + self.costs[k])
1656 }
1657
1658 fn trace(&self, via: &[usize], end: usize) -> Vec<usize> {
1660 let mut path = vec![end];
1661 let mut at = end;
1662 while via[at] != usize::MAX {
1663 at = self.edges[via[at]].from;
1664 path.push(at);
1665 }
1666 path.reverse();
1667 path
1668 }
1669
1670 pub fn endpoints(&self) -> Result<Vec<Endpoint>, TimingError> {
1673 let (_, arrival, via) = self.arrive()?;
1674 let mut deepest: BTreeMap<(End, &str), (HashSet<usize>, usize)> = BTreeMap::new();
1675 let mut reach = |end: End, bit: usize, from: usize| {
1676 let (bits, best) = deepest.entry((end, unindexed(&self.nodes[bit]))).or_insert_with(|| (HashSet::new(), from));
1677 bits.insert(bit);
1678 if (arrival[from], std::cmp::Reverse(from)) > (arrival[*best], std::cmp::Reverse(*best)) {
1679 *best = from;
1680 }
1681 };
1682 for e in self.edges.iter().filter(|e| e.registers > 0) {
1683 reach(End::Register, e.to, e.from);
1684 }
1685 for &o in &self.outputs {
1686 reach(End::Output, o, o);
1687 }
1688 for &i in self.boxes.iter().flat_map(|b| &b.inputs) {
1689 reach(End::Box, i, i);
1690 }
1691 let mut ends: Vec<Endpoint> = deepest
1692 .into_iter()
1693 .map(|((end, name), (bits, from))| Endpoint { name: name.to_string(), end, bits: bits.len(), depth: arrival[from], path: self.trace(&via, from) })
1694 .collect();
1695 ends.sort_by(|a, b| b.depth.cmp(&a.depth).then(a.end.cmp(&b.end)).then_with(|| a.name.cmp(&b.name)));
1696 Ok(ends)
1697 }
1698
1699 pub fn loop_bound(&self) -> Result<Bound, GraphError> {
1701 cycle_ratio::max_cycle_ratio(self.nodes.len(), &self.edges)
1702 }
1703
1704 pub fn name_loop(&self, bound: &Bound) -> Vec<String> {
1706 let cycle = match bound {
1707 Bound::CombinationalLoop(c) | Bound::Ratio { cycle: c, .. } => c,
1708 Bound::Acyclic => return Vec::new(),
1709 };
1710 (0..cycle.len())
1711 .map(|i| {
1712 let into = &self.edges[cycle[(i + cycle.len() - 1) % cycle.len()]];
1713 let node = &self.nodes[self.edges[cycle[i]].from];
1714 match into.registers {
1715 0 => node.clone(),
1716 r => format!("{node} [{r} register{}]", if r == 1 { "" } else { "s" }),
1717 }
1718 })
1719 .collect()
1720 }
1721}
1722
1723#[derive(Clone, Debug, PartialEq)]
1725pub struct Fitted {
1726 pub delays: Delays,
1727 pub predicted: Vec<f64>,
1729 pub error: f64,
1731}
1732
1733pub fn fit(samples: &mut [(TimingGraph, f64)], candidates: &[Delays]) -> Fitted {
1740 assert!(samples.iter().all(|(_, q)| *q > 0.0), "every measured depth is positive");
1741 let depths = |samples: &mut [(TimingGraph, f64)], d: &Delays| -> Vec<f64> {
1742 samples
1743 .iter_mut()
1744 .map(|(g, _)| {
1745 g.reweigh(d);
1746 g.depth().expect("a sample loops through no logic without a register").depth as f64
1747 })
1748 .collect()
1749 };
1750 let logs = |samples: &[(TimingGraph, f64)], depth: &[f64]| -> Vec<f64> { samples.iter().zip(depth).map(|((_, q), d)| (q * 100.0 / d).ln()).collect() };
1751 let rms = |v: &[f64], centre: f64| (v.iter().map(|l| (l - centre).powi(2)).sum::<f64>() / v.len() as f64).sqrt();
1752 let mut best: Option<(f64, Delays)> = None;
1753 for c in candidates {
1754 let depth = depths(samples, c);
1755 if depth.iter().any(|&d| d <= 0.0) {
1756 continue;
1757 }
1758 let l = logs(samples, &depth);
1759 let centre = l.iter().sum::<f64>() / l.len() as f64;
1760 let error = rms(&l, centre);
1761 if best.map_or(true, |(e, _)| error < e - 1e-12) {
1762 let s = centre.exp();
1763 let scaled = |x: i64| (x as f64 * s).round() as i64;
1764 best = Some((error, Delays { lut: scaled(c.lut), carry: scaled(c.carry), dsp: scaled(c.dsp), memory: scaled(c.memory) }));
1765 }
1766 }
1767 let (_, delays) = best.expect("a candidate under which every sample has depth");
1768 let depth = depths(samples, &delays);
1769 let error = rms(&logs(samples, &depth), 0.0);
1770 Fitted { delays, predicted: depth.iter().map(|d| d / 100.0).collect(), error }
1771}
1772
1773#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
1775pub struct Calibration {
1776 pub rows: Vec<CalibrationRow>,
1777 #[serde(default)]
1779 pub refused: Vec<RefusedBuild>,
1780 #[serde(default, skip_serializing_if = "Option::is_none")]
1782 pub area: Option<AreaFit>,
1783}
1784
1785#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1788pub struct AreaFit {
1789 pub scale: f64,
1790 pub carry: f64,
1791 pub floor: u64,
1792 pub instances: usize,
1793 pub error: f64,
1794}
1795
1796impl AreaFit {
1797 pub fn aluts(&self, area: &Area) -> f64 {
1799 self.scale * (area.luts as f64 + self.carry * area.carry as f64)
1800 }
1801}
1802
1803pub const AREA_CARRIES: [f64; 61] = {
1805 let mut k = [0.0; 61];
1806 let mut i = 0;
1807 while i < 61 {
1808 k[i] = i as f64 * 0.05;
1809 i += 1;
1810 }
1811 k
1812};
1813
1814pub fn fit_area(samples: &[(Area, u64)], floor: u64, carries: &[f64]) -> AreaFit {
1818 let large: Vec<&(Area, u64)> = samples.iter().filter(|(a, aluts)| *aluts >= floor && a.luts + a.carry > 0).collect();
1819 let mut best: Option<AreaFit> = None;
1820 for &carry in carries {
1821 let logs: Vec<f64> = large.iter().map(|(a, aluts)| (*aluts as f64 / (a.luts as f64 + carry * a.carry as f64)).ln()).collect();
1822 let centre = logs.iter().sum::<f64>() / logs.len().max(1) as f64;
1823 let error = (logs.iter().map(|l| (l - centre).powi(2)).sum::<f64>() / logs.len().max(1) as f64).sqrt();
1824 if best.as_ref().is_none_or(|b| error < b.error - 1e-12) {
1825 best = Some(AreaFit { scale: centre.exp(), carry, floor, instances: large.len(), error });
1826 }
1827 }
1828 best.expect("a carry weight to try")
1829}
1830
1831#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1834pub struct RefusedBuild {
1835 pub build: String,
1836 pub verilog_sha256: String,
1837 pub why: String,
1838}
1839
1840#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1843pub struct CalibrationRow {
1844 pub build: String,
1845 pub top: String,
1846 pub verilog_sha256: String,
1847 pub delays: Delays,
1848 pub instances: Vec<CalibratedInstance>,
1849}
1850
1851#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1854pub struct CalibratedInstance {
1855 pub path: String,
1856 pub quartus: f64,
1857 pub predicted: i64,
1858 #[serde(default, skip_serializing_if = "Option::is_none")]
1859 pub area: Option<InstanceArea>,
1860}
1861
1862#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1864pub struct InstanceArea {
1865 pub quartus: QuartusArea,
1866 pub model: Area,
1867}
1868
1869#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
1871pub struct QuartusArea {
1872 pub aluts: u64,
1873 pub registers: u64,
1874 pub dsp: u64,
1875 pub memory_bits: u64,
1876}
1877
1878impl CalibrationRow {
1879 pub fn top(&self) -> Option<&CalibratedInstance> {
1881 self.instances.iter().find(|i| i.path.is_empty())
1882 }
1883}
1884
1885#[cfg(test)]
1886#[path = "../../../logicaffeine_tests/unit/logicaffeine_compile/codegen_sva/timing_graph/tests.rs"]
1887mod tests;