Skip to main content

logicaffeine_compile/codegen_sva/
timing_graph.rs

1//! A circuit's timing graph: how deep its logic is, where the depth sits, and the bound its loops put on the clock,
2//! known before Quartus runs.
3//!
4//! [`script`] elaborates a module through yosys the way an FPGA flow sees it, flattened with every cell keeping the
5//! instance path it came from: adders, subtractors, negations, comparisons and dividers stay whole, as the carry
6//! chains the fabric builds them from; multipliers stay whole, as DSP blocks; a memory read through a clock stays
7//! whole, as a block RAM (its read register found once each flip-flop's enable is its own, so a read held under an
8//! enable is the RAM's; a value cleared to zero stays logic before its register, not a reset pin), and one read
9//! without a clock becomes logic; a memory written through more than the two
10//! ports a block RAM has (an array cleared by a loop) becomes registers before the memory passes, which compare its
11//! ports pair by pair, once the writes to one address (a word's byte lanes) are one port; everything else becomes
12//! yosys's simple gates. An
13//! undefined bit of the source is 0, fixed once the memories are collected and before anything is optimized, so the
14//! graph is one circuit the source allows and what [`super::autopipe::emit`] writes from it is exactly that circuit.
15//!
16//! [`read`] maps the gates onto six-input LUTs ([`super::lutmap`], wide AND, OR and XOR trees balanced for LUTs) and
17//! turns the netlist into a graph over net bits whose edges carry a delay and a register count, the retiming graph of
18//! Leiserson & Saxe:
19//! - a LUT adds [`Delays::lut`] from each input to its output, and so does a `$lut` cell a netlist brings mapped;
20//! - a carry chain enters each bit through a LUT, [`Delays::lut`], and carries [`Delays::carry`] from bit to bit; a
21//!   comparison is the chain's carry out;
22//! - a divider is the array of subtractions that builds it, one a quotient bit from the top: each stage a LUT and a
23//!   carry across the divisor, the quotient bit out of its stage and the remainder out of the last;
24//! - a multiplier is a DSP block, [`Delays::dsp`] from its operands to its product, unless one operand is a
25//!   constant power of two, which is wiring;
26//! - a memory holds its contents one register past its write port; a read port with a clock holds one more and
27//!   adds [`Delays::memory`] to its data, and one without adds [`Delays::memory`] from its address;
28//! - a flip-flop is an edge from each of its inputs to its output holding one register.
29//!
30//! [`TimingGraph::depth`] times the combinational edges: the deepest path, and for every instance the deepest path
31//! through its own cells and through its subtree, the two figures Quartus prints as an entity's Max Depth.
32//! [`TimingGraph::endpoints`] is the deepest path into each register and each output, so a pipeline's stages are
33//! each as deep as the paths into the registers that end them.
34//! [`TimingGraph::loop_bound`] is the maximum cycle ratio ([`logicaffeine_proof::cycle_ratio`]): the clock no
35//! retiming of the design can beat, with the loop that sets it, certified.
36//!
37//! The model is one clock: every flip-flop counts as a register of the same clock.
38//!
39//! A module [`script`] is given as a box, or one the source declares `(* blackbox *)`, is timed apart: emptied before
40//! the hierarchy is elaborated, nothing under it is ever read, and each instance is a [`Boxed`], every bit it reads
41//! the end of the paths into it ([`End::Box`]), as an output is, and every bit it drives the start of the paths out of
42//! it, as an input is. A shell is so timed around the engine it holds, in seconds; a loop through the box is the box's
43//! to time, and [`super::autopipe`] cuts nothing around one.
44
45use 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/// The delay each kind of cell adds, in a common integer unit.
54#[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    /// Logic levels in hundredths: a LUT is one level.
64    pub const LEVELS: Delays = Delays { lut: 100, carry: 0, dsp: 100, memory: 100 };
65
66    /// The D5005's Stratix 10 as fit27's worst setup paths measured it (slow 900 mV, 100 °C), in hundredths of a LUT
67    /// level with the wire into the next ([`STRATIX10_NS_PER_LEVEL`]: a LUT's 0.139 ns and a 0.41 ns hop): a carry bit
68    /// 0.039 ns, a DSP block 2.10 ns and a RAM read 1.40 ns, each with its hop out.
69    pub const STRATIX10: Delays = Delays { lut: 100, carry: 7, dsp: 457, memory: 330 };
70}
71
72/// The nanoseconds a level of [`Delays::STRATIX10`] takes: a LUT and the interconnect hop into the next.
73pub const STRATIX10_NS_PER_LEVEL: f64 = 0.549;
74
75/// What an edge or a path adds, counted by kind: its delay at any [`Delays`] is the sum of each count times its delay.
76#[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/// A timing graph: a node per net bit and per internal point of a carry chain, DSP block or memory.
104#[derive(Clone, Debug, Default)]
105pub struct TimingGraph {
106    /// Each node's name: the net's name and bit, or the cell's and its point.
107    pub nodes: Vec<String>,
108    pub edges: Vec<Edge>,
109    /// What each edge adds, by kind, for a graph read from a netlist ([`TimingGraph::reweigh`]); empty for one built
110    /// by hand.
111    pub costs: Vec<Cost>,
112    /// The instance whose cell made each edge, an index into `instances`.
113    pub owner: Vec<usize>,
114    /// Instance paths in the form Quartus prints them, `|`-separated; the top is `""`.
115    pub instances: Vec<String>,
116    /// Flip-flop bits.
117    pub flops: usize,
118    /// What each instance's own cells take, by instance ([`TimingGraph::area_under`] sums a subtree).
119    pub area: Vec<Area>,
120    /// Whether each node is a bit of a net the design names (a signal of its source, not one synthesis made).
121    pub public: Vec<bool>,
122    /// The module's input and output port bits, ports by name and each one's bits in order.
123    pub inputs: Vec<usize>,
124    pub outputs: Vec<usize>,
125    /// How each node is made, when the graph was read from a netlist: what writing it back out as Verilog needs.
126    pub structure: Option<Structure>,
127    /// The instances of modules timed apart.
128    pub boxes: Vec<Boxed>,
129    /// The loads each net drives and what drives it, for a graph read from a netlist; none for one built by hand.
130    pub fanout: Option<Fanout>,
131}
132
133/// What drives a node of a graph read from a netlist.
134#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
135pub enum Driver {
136    /// A flip-flop's output.
137    Register,
138    /// A LUT, a kept cell (carry chain, DSP block) or a constant.
139    Logic,
140    /// An input port of the module, or a net nothing in it drives.
141    Input,
142    /// A memory's read data.
143    Memory,
144    /// An output of a module timed apart.
145    Box,
146    /// A point inside a cell, no net.
147    Point,
148}
149
150/// The loads of a graph read from a netlist: what each edge loads the node it starts from with — a cell input one, a
151/// memory port the blocks of its RAM it reaches ([`memory_blocks`]) — each node's driver, the most loads its net asks
152/// a copy of its driver to take (`(* maxfan = N *)`, as Quartus duplicates a register to keep each copy to N), and
153/// which nodes are clocks.
154#[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    /// Whether each node is a clock: a flip-flop's clock pin, a memory's clock ports or a box's clock input (a port its
160    /// module names `clk` or `…clk`) takes it.
161    pub clocks: Vec<bool>,
162}
163
164/// A net past its bound: its node and name, the loads it drives and what drives it.
165#[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
173/// The blocks a memory of `width` bits by `size` words takes, across its width and down its depth, in the RAM its
174/// `ramstyle` asks for: an MLAB holds 20 bits by 32 words, an M20K 40 bits by 512, and a memory that asks for registers
175/// (`logic`) is a register a bit, read through four-word multiplexers; one that asks for nothing is an M20K, as Quartus
176/// builds it.
177pub 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/// What cells take as the timing graph maps them: six-input LUTs, carry-chain bits (a divider's every stage a chain as
190/// wide as its divisor), DSP blocks in halves ([`dsp_halves`]), flip-flop bits and memory bits. A black box takes
191/// nothing here.
192#[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
211/// The halves of a Stratix 10 DSP block an `a`×`b` product takes: two products of at most 18×19 share a block, one of
212/// at most 27×27 fills it, and a wider one is tiled in 27×27 blocks.
213pub 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/// An instance of a module timed apart, a black box: each bit it reads a node that ends the paths into it, as an
225/// output does, and each bit it drives a node that starts the paths out of it, as an input does. None of its own logic
226/// is in the graph, nor any loop through it.
227#[derive(Clone, Debug, PartialEq, Eq)]
228pub struct Boxed {
229    /// The instance's path.
230    pub cell: String,
231    /// The module it is an instance of.
232    pub module: String,
233    /// What each of its outputs adds as it leaves it: nothing for outputs that are registers, a block RAM's read for
234    /// a memory.
235    pub out: Cost,
236    pub inputs: Vec<usize>,
237    pub outputs: Vec<usize>,
238}
239
240/// How the value at a node is made.
241#[derive(Clone, Debug, PartialEq, Eq)]
242pub enum Source {
243    /// Bit `bit` of input port `port`.
244    Input { port: String, bit: usize },
245    /// A LUT: bit `i` of `table` is its value when pin `j` holds bit `j` of `i`; `cone`, when the mapping made it,
246    /// the AND and XOR gates it covers.
247    Lut { pins: Vec<Pin>, table: u64, cone: Option<Cone> },
248    /// Another node's value, or its complement.
249    Copy { from: usize, inverted: bool },
250    /// Bit `bit` of output `port` of kept cell `cell`.
251    Cell { cell: usize, port: String, bit: usize },
252    Constant(bool),
253    /// A flip-flop's output: what `data` held a clock before, and whether an enable, a reset or a set steps it besides.
254    Register { data: Pin, controls: bool },
255    /// A point inside a kept cell, a memory's output, or a net nothing here drives.
256    Inside,
257}
258
259/// The gates a mapped LUT covers, over its pins: each gate's operands an earlier gate or a pin, plain or inverted,
260/// and the LUT's output one of them.
261#[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/// A cone operand: pin `index`, or gate `index − pins` past the pins; inverted or not.
275#[derive(Clone, Copy, Debug, PartialEq, Eq)]
276pub struct ConeArg {
277    pub index: usize,
278    pub inverted: bool,
279}
280
281/// A pin's signal: a node, or a constant.
282#[derive(Clone, Copy, Debug, PartialEq, Eq)]
283pub enum Pin {
284    Node(usize),
285    Zero,
286    One,
287}
288
289/// A cell kept whole (a carry chain, a divider, a DSP product): its kind, its parameters, each port's bits, and a
290/// node inside it whose stage is the cell's.
291#[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    /// A parameter yosys writes as a binary string or a number.
302    pub fn number(&self, name: &str) -> u64 {
303        number(self.parameters.get(name))
304    }
305}
306
307/// What a graph was read from, node by node: every node's source, the kept cells, and the ports by name.
308#[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/// How deep a graph's logic is.
317#[derive(Clone, Debug, PartialEq, Eq)]
318pub struct Depth {
319    /// The deepest combinational path.
320    pub depth: i64,
321    /// Its nodes, from where it starts to where it ends.
322    pub path: Vec<usize>,
323    /// Its edges, in order: the one from each node of the path to the next.
324    pub edges: Vec<usize>,
325    /// Every instance with a cell in its subtree, in path order. A cell counts for an instance when it adds delay.
326    pub instances: Vec<InstanceDepth>,
327}
328
329/// The deepest path through an instance's cells.
330#[derive(Clone, Debug, PartialEq, Eq)]
331pub struct InstanceDepth {
332    pub path: String,
333    /// Through any cell of its subtree.
334    pub total: i64,
335    /// Through its own cells; 0 when it has none.
336    pub own: i64,
337}
338
339/// What ends a path: a register, which one clock must reach, an output of the module, or a box's input.
340#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
341pub enum End {
342    Register,
343    Output,
344    Box,
345}
346
347/// The deepest path into a register or an output, over all its bits.
348#[derive(Clone, Debug, PartialEq, Eq)]
349pub struct Endpoint {
350    /// The net's name without its bit, or a memory's point.
351    pub name: String,
352    pub end: End,
353    pub bits: usize,
354    pub depth: i64,
355    /// The path's nodes, from where it starts to the bit it ends at: a register's input, or the output.
356    pub path: Vec<usize>,
357}
358
359/// Why a module has no timing graph.
360#[derive(Clone, Debug, PartialEq, Eq)]
361pub enum TimingError {
362    Netlist(NetlistError),
363    /// The mapped netlist is not one this reads.
364    Json(String),
365    /// A cell of a kind the model has no delay for.
366    Cell { name: String, kind: String },
367    /// A level-sensitive latch: the model has no transparent element.
368    Latch(String),
369    /// A loop of logic through no register, named node by node.
370    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
393/// The module attribute that says what a box's outputs add as they leave it: its [`Cost`]'s four counts, `lut,carry,
394/// dsp,memory`; a box without it adds nothing.
395pub const BOX_OUT: &str = "logos_box_out";
396
397/// The module attribute that says the area each instance of a box takes, as a device block's stand-in declares it:
398/// its [`Area`]'s five counts, `luts,carry,half_dsps,flops,memory_bits`; a box without it takes none.
399pub const BOX_AREA: &str = "logos_box_area";
400
401/// A box's area as [`BOX_AREA`] writes it.
402fn 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
411/// A box's output cost as [`BOX_OUT`] writes it.
412fn 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
421/// The cells kept whole as carry chains, DSP blocks and memories; everything else becomes simple gates.
422const KEPT: &[&str] = &[
423    "$add", "$sub", "$neg", "$lt", "$le", "$gt", "$ge", "$div", "$mod", "$divfloor", "$modfloor", "$mul", "$mem_v2",
424];
425
426/// The yosys script that maps `top` with `params` set and writes `timing.json`.
427pub 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    // A box is emptied before the hierarchy is elaborated, so nothing under it ever is; a box no module is named is
435    // an error, not a design timed whole.
436    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        // A memory no port reads is dropped before its ports are collected: yosys 0.33 collects one into a cell its
449        // own check refuses.
450        "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
475/// The modules `verilog` defines whose names are one of `prefixes` and a number, in order of definition: the family a
476/// generator writes one module of per width, to name as boxes.
477pub 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
486/// The netlist [`script`] writes for `top` of `verilog` with `params` set, as yosys JSON.
487pub 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
497/// Map `top` of `verilog` with `params` set, each module of `boxes` a box, and read its timing graph.
498pub 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    /// Whether `port` is an input: as the netlist says, else by yosys's pin names, where outputs are `Y`, `Q` and
542    /// `RD_DATA`.
543    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
579/// A parameter yosys writes as a binary string, most significant bit first, or as a number.
580fn 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
592/// A parameter's bits, least significant first.
593fn 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
604/// The instance a flattened cell came from, `|`-separated: from a `$flatten\a.\b.$…` name, or from the `hdlname`
605/// attribute of a public one.
606fn 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
618/// Whether `kind` is one of yosys's simple combinational gates, which [`read`] maps onto LUTs.
619fn 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
627/// A simple gate's output, its pins' literals given by `pin`.
628fn 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
663/// The level at which each control pin of a yosys flip-flop leaves it a plain register — an enable at its active level,
664/// a reset, a set or a load at its inactive one — read from the polarity letters its name carries (`$_SDFFE_PP0N_`:
665/// clock rising, reset active high to 0, enable active low). `None` for a kind of flip-flop not named here.
666fn 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
683/// Whether a flip-flop steps under anything but its clock: a control pin not held at the level that leaves it plain,
684/// or a kind [`idle_levels`] does not name.
685fn 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    /// The modules the netlist holds as black boxes with what their outputs add, and the instances of them read so far.
717    box_modules: HashMap<String, (Cost, Area)>,
718    boxes: Vec<Boxed>,
719    /// The adders inside DSP blocks ([`dsp_sums`]).
720    dsp_sums: HashSet<String>,
721    /// What each edge loads its start with, each node's driver where its source does not say it, and the clocks.
722    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    /// Keep `cell` whole, `inside` the node whose stage is its own, and make its output bits its.
760    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    /// An edge that loads its start with `loads`.
782    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    /// Edges from `bits`, a memory port's, into `to`: each net's loads on the port, booked on its first edge, are the
792    /// block columns its bits fall in (`bits_a_block` bits to a column; none, every column of the `across`) times the
793    /// `down` blocks of each.
794    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    /// An instance of a black box: a node for each net bit it reads, the end of the paths into that bit, and the net
813    /// bits it drives the start of the paths out of it, each reached from the box's own node after what its outputs
814    /// add when they add anything; its declared area added to the instance holding it.
815    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
1072/// The bit set in `bits` when they are a constant power of two (and, signed, not the sign bit): the shift a product by
1073/// them is.
1074fn 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
1080/// The adders of `module` that sit inside DSP blocks, as Quartus packs them: each whose two operands are products,
1081/// sums found so already or constants, at least one bit of each a product's or a sum's (a block's adder of its two
1082/// products, and the chain-in adder of the block after it), found to a fixpoint. An adder with an operand from the
1083/// fabric is a carry chain of its own.
1084fn 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
1118/// A node's name without the bit its net is indexed by, nor the word of the array it is a register of.
1119fn 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
1130/// A net's display name for each bit, and whether it is public: a public name before a generated one, then the name
1131/// of the outermost scope (a flattened instance's names carry its path, `.`-separated), then the shorter.
1132fn 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
1152/// The timing graph of a netlist [`script`] mapped.
1153pub 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
1281/// The gates between `root` and its cut `leaves` as a [`Cone`] over the pins, pin `j` holding leaf `j` inverted when
1282/// bit `j` of `flip` is set, and the output inverted when `invert`.
1283fn 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
1316/// The gates as an AND-XOR graph: each gate-driven net's literal, the net each input stands for, the instance each
1317/// node was made in, and the nets read inside a loop of their own, each read there through an input of its own.
1318struct 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    /// The literal a reader of `net` uses.
1335    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    /// Build the literal of `net`: an input when no gate drives it, else its gate over its pins' literals, pins first,
1342    /// without recursion. A net met again while its own fanin is being built is read there through an input of its
1343    /// own, so the graph stays acyclic and the loop stays in the timing graph, closed through that net.
1344    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    /// Map the gates onto LUTs and add their edges: each LUT's, from its leaves to its root, and one of no delay into
1405    /// every gate-driven net read past the gates, or read inside its own loop, from the LUT that computes it.
1406    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        // Whether a mapped node's timing node holds the node's complement: a net named it, and the net is it inverted.
1447        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    /// What instance `path` and every instance under it take ([`Area`]); the top is `""`, the whole graph.
1498    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    /// The combinational edges in an order where each comes after every edge into its source, or the loop that
1510    /// prevents one.
1511    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    /// The combinational order, each node's arrival (the deepest combinational path ending there) and the edge
1547    /// that path arrives by.
1548    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    /// Each node's arrival: the deepest combinational path ending there.
1564    pub fn arrivals(&self) -> Result<Vec<i64>, TimingError> {
1565        Ok(self.arrive()?.1)
1566    }
1567
1568    /// How deep the logic is, overall and through every instance.
1569    /// Each node's loads: the cell inputs it reaches, a memory port counted as the blocks it reaches; every edge one
1570    /// load in a graph built by hand.
1571    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    /// The nets past their bound, widest first (ties by name): those logic or a memory drives past `logic` loads, a
1580    /// register past `register` — a register whose net asks for at most N loads (`maxfan`) being copies of N each, past
1581    /// it only when N is — an input past `logic` (whatever drives it outside); a box's outputs are the box's to bound, a
1582    /// clock rides a clock network, and a point inside a cell is no net. Empty for a graph built by hand.
1583    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    /// Every edge's delay from its cost at `delays`: the graph as reading its netlist at `delays` makes it.
1646    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    /// What `edges` add together.
1654    pub fn cost(&self, edges: &[usize]) -> Cost {
1655        edges.iter().fold(Cost::NONE, |sum, &k| sum + self.costs[k])
1656    }
1657
1658    /// The deepest combinational path ending at `end`, node by node from where it starts.
1659    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    /// The deepest path into each register and each output, deepest first. A register's path ends at its input: a
1671    /// delay on a registered edge counts after the register, in the next stage.
1672    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    /// The bound the design's loops put on its clock, in the graph's delay unit per register.
1700    pub fn loop_bound(&self) -> Result<Bound, GraphError> {
1701        cycle_ratio::max_cycle_ratio(self.nodes.len(), &self.edges)
1702    }
1703
1704    /// A bound's loop, node by node in order, a register's output marked with the registers it comes out of.
1705    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/// Delays fitted to measured depths ([`fit`]).
1724#[derive(Clone, Debug, PartialEq)]
1725pub struct Fitted {
1726    pub delays: Delays,
1727    /// Each sample's depth at `delays`, in the unit a delay of 100 is one of.
1728    pub predicted: Vec<f64>,
1729    /// The root mean square of the log of each prediction over its measure.
1730    pub error: f64,
1731}
1732
1733/// The delays under which each graph's depth comes nearest its measured depth (in the unit a delay of 100 is one of):
1734/// of `candidates`, each scaled by the factor that fits it best. A candidate's ratios choose every critical path, and
1735/// a graph's depth scales with all its delays together, so the best scale is the geometric mean of measure over
1736/// depth, applied to every delay; the error is the root mean square of each log ratio of prediction to measure. Each
1737/// graph is left reweighed to the delays fitted. The graphs are read from netlists and loop through no logic without
1738/// a register; every measure is positive.
1739pub 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/// The model's depth and area beside Quartus's, build by build: what `timing_calibrate` records, never edited by hand.
1774#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
1775pub struct Calibration {
1776    pub rows: Vec<CalibrationRow>,
1777    /// The builds whose Verilog the model could not map, each with why.
1778    #[serde(default)]
1779    pub refused: Vec<RefusedBuild>,
1780    /// The ALUTs the model predicts from an instance's area, fitted to every instance's ([`fit_area`]).
1781    #[serde(default, skip_serializing_if = "Option::is_none")]
1782    pub area: Option<AreaFit>,
1783}
1784
1785/// ALUTs from an instance's [`Area`]: `scale × (luts + carry × carry bits)`, fitted to the `instances` of at least
1786/// `floor` ALUTs, `error` the root mean square of the log of each prediction over Quartus's count.
1787#[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    /// The ALUTs `area` is predicted to take.
1798    pub fn aluts(&self, area: &Area) -> f64 {
1799        self.scale * (area.luts as f64 + self.carry * area.carry as f64)
1800    }
1801}
1802
1803/// The carry weights [`fit_area`] searches against a LUT's: 0 to 3 in twentieths.
1804pub 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
1814/// The area fit of `samples` (an instance's area, Quartus's ALUTs) with at least `floor` ALUTs: of the `carries`, the
1815/// weight under which the log ratios of Quartus's count over `luts + weight × carry` spread least, the scale their
1816/// geometric mean. Small instances are left out: a few LUTs either way is most of them.
1817pub 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/// A synthesis the model could not map: its Verilog by SHA-256 and what refused it (yosys's error line, or the
1832/// timing graph's).
1833#[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/// One synthesis: its Verilog by SHA-256, the delays the model used, and the deepest path through each instance,
1841/// as Quartus reported it (logic levels) and as the model predicts it (the model's unit).
1842#[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/// An instance both report: its path (`""` for the top), Quartus's Max Depth and the model's depth, and what it takes
1852/// as Quartus's synthesis counted it beside what the model maps it to.
1853#[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/// An instance's area two ways: Quartus's synthesis hierarchy, and the timing graph's [`Area`] of its subtree.
1863#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1864pub struct InstanceArea {
1865    pub quartus: QuartusArea,
1866    pub model: Area,
1867}
1868
1869/// What Quartus's synthesis hierarchy counts for an instance and everything under it.
1870#[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    /// The top's figures.
1880    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;