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