Skip to main content

prjunnamed_graphviz/
lib.rs

1use std::collections::{BTreeMap, BTreeSet};
2use std::fmt::Write;
3use std::io;
4use prjunnamed_netlist::{Cell, CellRef, ControlNet, Design, Net, Value};
5
6#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
7struct Edge<'a> {
8    from_cell: CellRef<'a>,
9    to_arg: Option<usize>,
10}
11
12impl<'a> From<CellRef<'a>> for Edge<'a> {
13    fn from(cell: CellRef<'a>) -> Self {
14        Self { from_cell: cell, to_arg: None }
15    }
16}
17
18struct Node<'a> {
19    cell: CellRef<'a>,
20    label: String,
21    args: Vec<String>,
22    inputs: BTreeSet<Edge<'a>>,
23}
24
25impl<'a> Node<'a> {
26    fn new(cell: CellRef<'a>, label: String) -> Self {
27        Self { cell, label, args: Vec::new(), inputs: BTreeSet::new() }
28    }
29
30    fn from_name(cell: CellRef<'a>, name: &str) -> Self {
31        let index = cell.debug_index();
32        let width = cell.output_len();
33        let label = format!("%{index}:{width} = {name}");
34        Self::new(cell, label)
35    }
36
37    fn add_input(&mut self, input: impl Into<Edge<'a>>) {
38        self.inputs.insert(input.into());
39    }
40
41    fn arg(mut self, input: impl ToString) -> Self {
42        self.args.push(input.to_string());
43        self
44    }
45
46    fn net_input(&mut self, net: Net, to_arg: Option<usize>) {
47        if net.is_const() {
48            return;
49        }
50        let (cell, _) = self.cell.design().find_cell(net);
51        self.add_input(Edge { from_cell: cell, to_arg });
52    }
53
54    fn net(mut self, input: &Net) -> Self {
55        let to_arg = Some(self.args.len());
56        self.net_input(*input, to_arg);
57
58        let s = self.cell.design().display_net(input).to_string();
59        self.arg(s)
60    }
61
62    fn value(mut self, input: &Value) -> Self {
63        let to_arg = Some(self.args.len());
64        for net in input.iter() {
65            self.net_input(net, to_arg);
66        }
67
68        let s = self.cell.design().display_value(input).to_string();
69        self.arg(s)
70    }
71
72    fn prefix_value(mut self, prefix: &str, input: &Value) -> Self {
73        let to_arg = Some(self.args.len());
74        for net in input.iter() {
75            self.net_input(net, to_arg);
76        }
77
78        let s = format!("{prefix}{}", self.cell.design().display_value(input));
79        self.arg(s)
80    }
81
82    fn control_net(mut self, input: ControlNet) -> Self {
83        let to_arg = Some(self.args.len());
84        self.net_input(input.net(), to_arg);
85
86        let s = self.cell.design().display_control_net(input);
87        self.arg(s)
88    }
89
90    fn control(mut self, name: &str, input: ControlNet, extra: Option<String>) -> Self {
91        let to_arg = Some(self.args.len());
92        self.net_input(input.net(), to_arg);
93
94        let mut s = format!("{name}={}", self.cell.design().display_control_net(input));
95        if let Some(extra) = extra {
96            write!(&mut s, ",{extra}").unwrap();
97        }
98        self.arg(s)
99    }
100}
101
102struct Context<'a> {
103    /// Name that will be used to refer to high-fanout cells
104    best_name: BTreeMap<CellRef<'a>, String>,
105    fanout: BTreeMap<CellRef<'a>, BTreeSet<CellRef<'a>>>,
106    nodes: Vec<Node<'a>>,
107}
108
109impl<'a> Context<'a> {
110    fn add_node(&mut self, node: Node<'a>) {
111        for input in &node.inputs {
112            self.fanout.entry(input.from_cell).or_default().insert(node.cell);
113        }
114
115        self.nodes.push(node);
116    }
117
118    fn high_fanout(&self, cell: CellRef<'_>) -> Option<usize> {
119        let fanout = self.fanout.get(&cell).map(BTreeSet::len).unwrap_or(0);
120        let threshold = if self.best_name.contains_key(&cell) { 5 } else { 10 };
121
122        if fanout >= threshold { Some(fanout) } else { None }
123    }
124
125    fn print(&self, writer: &mut impl io::Write) -> io::Result<()> {
126        writeln!(writer, "digraph {{")?;
127        writeln!(writer, "  rankdir=LR;")?;
128        writeln!(writer, "  node [fontname=\"monospace\"];")?;
129        for node in &self.nodes {
130            self.print_node(writer, node)?;
131        }
132        writeln!(writer, "}}")
133    }
134
135    fn print_node(&self, writer: &mut impl io::Write, node: &Node<'_>) -> io::Result<()> {
136        let force = node.inputs.len() == 1;
137
138        let mut clarify = vec![BTreeSet::new(); node.args.len()];
139        for input in &node.inputs {
140            if !force && self.high_fanout(input.from_cell).is_some() {
141                let Some(name) = self.best_name.get(&input.from_cell) else { continue };
142                let Some(arg) = input.to_arg else { continue };
143                clarify[arg].insert(name);
144            }
145        }
146
147        let mut label = format!("<out> {}", node.label);
148        for (i, (arg, clarify)) in node.args.iter().zip(clarify).enumerate() {
149            write!(&mut label, " | <arg{i}> {arg}").unwrap();
150            if !clarify.is_empty() {
151                write!(&mut label, " (").unwrap();
152                let mut iter = clarify.into_iter();
153                write!(&mut label, "{:?}", iter.next().unwrap()).unwrap();
154                for input in iter {
155                    write!(&mut label, ", {input:?}").unwrap();
156                }
157                writeln!(&mut label, ")").unwrap();
158            } else if !arg.ends_with('\n') {
159                writeln!(&mut label).unwrap();
160            }
161        }
162
163        let index = node.cell.debug_index();
164        let label = label.escape_default().to_string().replace("\\n", "\\l");
165        writeln!(writer, "  node_{index} [shape=record label=\"{label}\"];")?;
166
167        for input in &node.inputs {
168            if !force && self.high_fanout(input.from_cell).is_some() {
169                continue;
170            }
171
172            let input_index = input.from_cell.debug_index();
173            let port = match input.to_arg {
174                Some(n) => format!("arg{n}"),
175                None => "out".to_string(),
176            };
177
178            writeln!(writer, "  node_{input_index}:out -> node_{index}:{port};")?;
179        }
180
181        if let Some(fanout) = self.high_fanout(node.cell) {
182            let mut label = format!("{fanout} uses");
183            if let Some(name) = self.best_name.get(&node.cell) {
184                write!(&mut label, "\n{name:?}").unwrap();
185            }
186            writeln!(writer, "  stub_{index} [label=\"{}\"];", label.escape_default())?;
187            writeln!(writer, "  node_{index}:out -> stub_{index};")?;
188        }
189
190        Ok(())
191    }
192}
193
194pub fn describe<'a>(writer: &mut impl io::Write, design: &'a Design) -> io::Result<()> {
195    // for each cell, a list of name/debug cells that reference it
196    let mut names: BTreeMap<CellRef<'_>, BTreeSet<CellRef<'_>>> = BTreeMap::new();
197    // for each cell, the shortest name that refers to it
198    let mut best_name: BTreeMap<CellRef<'a>, String> = BTreeMap::new();
199
200    let mut consider_name = |cell: CellRef<'a>, name: &str| {
201        best_name
202            .entry(cell)
203            .and_modify(|prev| {
204                if prev.len() > name.len() {
205                    *prev = name.to_string();
206                }
207            })
208            .or_insert(name.to_string());
209    };
210
211    'outer: for cell in design.iter_cells() {
212        match &*cell.get() {
213            Cell::Name(name, value) | Cell::Debug(name, value) => {
214                let mut prev = None;
215                for net in value.iter() {
216                    let (target, _) = design.find_cell(net);
217                    if let Some(prev) = prev {
218                        if prev != target {
219                            continue 'outer;
220                        }
221                    } else {
222                        prev = Some(target);
223                    }
224                }
225
226                let Some(target) = prev else { continue };
227                if target.output() == *value {
228                    consider_name(target, name);
229                }
230
231                for net in value.iter() {
232                    if !net.is_const() {
233                        let (target, _) = design.find_cell(net);
234                        names.entry(target).or_default().insert(cell);
235                    }
236                }
237            }
238            Cell::Input(name, _) => {
239                consider_name(cell, name);
240            }
241            _ => {}
242        }
243    }
244
245    let mut ctx = Context { best_name, fanout: BTreeMap::new(), nodes: vec![] };
246
247    for cell in design.iter_cells_topo() {
248        let mut node = match &*cell.get() {
249            Cell::Name(_, _) | Cell::Debug(_, _) => continue,
250            Cell::Buf(a) => Node::from_name(cell, "buf").value(a),
251            Cell::Not(a) => Node::from_name(cell, "not").value(a),
252            Cell::And(a, b) => Node::from_name(cell, "and").value(a).value(b),
253            Cell::Or(a, b) => Node::from_name(cell, "or").value(a).value(b),
254            Cell::Xor(a, b) => Node::from_name(cell, "xor").value(a).value(b),
255            Cell::Mux(a, b, c) => Node::from_name(cell, "mux").net(a).value(b).value(c),
256            Cell::Adc(a, b, c) => Node::from_name(cell, "adc").value(a).value(b).net(c),
257            Cell::Aig(arg1, arg2) => Node::from_name(cell, "aig").control_net(*arg1).control_net(*arg2),
258            Cell::Eq(a, b) => Node::from_name(cell, "eq").value(a).value(b),
259            Cell::ULt(a, b) => Node::from_name(cell, "ult").value(a).value(b),
260            Cell::SLt(a, b) => Node::from_name(cell, "slt").value(a).value(b),
261            Cell::Shl(a, b, c) => Node::from_name(cell, "shl").value(a).value(b).arg(c),
262            Cell::UShr(a, b, c) => Node::from_name(cell, "ushr").value(a).value(b).arg(c),
263            Cell::SShr(a, b, c) => Node::from_name(cell, "sshr").value(a).value(b).arg(c),
264            Cell::XShr(a, b, c) => Node::from_name(cell, "xshr").value(a).value(b).arg(c),
265            Cell::Mul(a, b) => Node::from_name(cell, "mul").value(a).value(b),
266            Cell::UDiv(a, b) => Node::from_name(cell, "udiv").value(a).value(b),
267            Cell::UMod(a, b) => Node::from_name(cell, "umod").value(a).value(b),
268            Cell::SDivTrunc(a, b) => Node::from_name(cell, "sdiv_trunc").value(a).value(b),
269            Cell::SDivFloor(a, b) => Node::from_name(cell, "sdiv_floor").value(a).value(b),
270            Cell::SModTrunc(a, b) => Node::from_name(cell, "smod_trunc").value(a).value(b),
271            Cell::SModFloor(a, b) => Node::from_name(cell, "smod_floor").value(a).value(b),
272            Cell::Output(name, value) => Node::from_name(cell, &format!("output {name:?}")).value(value),
273
274            Cell::Dff(flop) => {
275                let mut node = Node::from_name(cell, "dff").value(&flop.data).control("clk", flop.clock, None);
276
277                if flop.has_clear() {
278                    let has_value = flop.clear_value != flop.init_value;
279                    node = node.control("clr", flop.clear, has_value.then(|| flop.clear_value.to_string()));
280                }
281
282                if flop.has_load() {
283                    node = node.control("load", flop.load, None).value(&flop.load_data);
284                }
285
286                if flop.has_reset() {
287                    let has_value = flop.reset_value != flop.init_value;
288                    node = node.control("rst", flop.reset, has_value.then(|| flop.reset_value.to_string()));
289                }
290
291                if flop.has_enable() {
292                    node = node.control("en", flop.enable, None);
293                }
294
295                if flop.has_reset() && flop.has_enable() {
296                    if flop.reset_over_enable {
297                        node = node.arg("rst/en");
298                    } else {
299                        node = node.arg("en/rst");
300                    }
301                }
302
303                if flop.has_init_value() {
304                    node = node.arg(format!("init={}", flop.init_value));
305                }
306
307                node
308            }
309            Cell::Target(target_cell) => {
310                let prototype = design.target_prototype(target_cell);
311                let mut node = Node::from_name(cell, &format!("target {:?}", target_cell.kind));
312                let mut params = String::new();
313                for (param, value) in prototype.params.iter().zip(&target_cell.params) {
314                    writeln!(&mut params, "param {:?} = {value}", param.name).unwrap();
315                }
316
317                if !params.is_empty() {
318                    node = node.arg(params);
319                }
320
321                for input in &prototype.inputs {
322                    let value = target_cell.inputs.slice(input.range.clone());
323                    node = node.prefix_value(&format!("{:?} = ", input.name), &value);
324                }
325
326                node
327            }
328            Cell::Memory(memory) => {
329                let header = format!("memory depth=#{} width=#{}", memory.depth, memory.width);
330                let mut node = Node::from_name(cell, &header);
331                for port in &memory.write_ports {
332                    node = node.prefix_value("write addr=", &port.addr).prefix_value(". data=", &port.data);
333                    if !port.mask.is_ones() {
334                        node = node.prefix_value(". mask=", &port.mask);
335                    }
336                    node = node.control(". clk", port.clock, None);
337                }
338
339                for port in &memory.read_ports {
340                    node = node.prefix_value("read addr=", &port.addr);
341                    if let Some(flop) = &port.flip_flop {
342                        node = node.control(". clk", flop.clock, None);
343                        if flop.has_clear() {
344                            let has_value = flop.clear_value != flop.init_value;
345                            node = node.control(". clr", flop.clear, has_value.then(|| flop.clear_value.to_string()));
346                        }
347
348                        if flop.has_reset() {
349                            let has_value = flop.reset_value != flop.init_value;
350                            node = node.control(". rst", flop.reset, has_value.then(|| flop.reset_value.to_string()));
351                        }
352
353                        if flop.has_enable() {
354                            node = node.control(". en", flop.enable, None);
355                        }
356
357                        if flop.has_reset() && flop.has_enable() {
358                            if flop.reset_over_enable {
359                                node = node.arg(". rst/en");
360                            } else {
361                                node = node.arg(". en/rst");
362                            }
363                        }
364
365                        if flop.has_init_value() {
366                            node = node.arg(format!(". init={}", flop.init_value));
367                        }
368                    }
369                }
370                node
371            }
372            _ => {
373                let label = design.display_cell(cell).to_string();
374                // this sucks but braces are special to Graphviz.
375                // yes, even in strings
376                let label = label.replace("{", "(").replace("}", ")");
377                let mut node = Node::new(cell, label);
378
379                cell.visit(|net| {
380                    if !net.is_const() {
381                        let (cell, _) = design.find_cell(net);
382                        node.add_input(cell);
383                    }
384                });
385
386                node
387            }
388        };
389
390        if let Some(names) = names.get(&cell) {
391            let mut exact_names = String::new();
392            let mut approx_names = String::new();
393            for name in names.iter() {
394                let (Cell::Name(s, v) | Cell::Debug(s, v)) = &*name.get() else { unreachable!() };
395
396                if cell.output() == *v {
397                    writeln!(&mut exact_names, "{s:?}").unwrap();
398                } else {
399                    writeln!(&mut approx_names, "{s:?} = {}", design.display_value(v)).unwrap();
400                }
401            }
402
403            if !exact_names.is_empty() {
404                node = node.arg(exact_names);
405            }
406
407            if !approx_names.is_empty() {
408                node = node.arg(approx_names);
409            }
410        }
411
412        ctx.add_node(node);
413    }
414
415    ctx.print(writer)
416}