rust_hdl_core/
check_timing.rs

1use crate::ast::{
2    Verilog, VerilogConditional, VerilogExpression, VerilogLink, VerilogLinkDetails, VerilogMatch,
3};
4use crate::atom::{Atom, AtomKind};
5use crate::block::Block;
6use crate::named_path::NamedPath;
7use crate::probe::Probe;
8use crate::type_descriptor::TypeKind;
9use crate::verilog_visitor::VerilogVisitor;
10use petgraph::algo::{connected_components, is_cyclic_directed};
11use petgraph::dot::Dot;
12use petgraph::prelude::*;
13use petgraph::unionfind::UnionFind;
14use petgraph::visit::NodeIndexable;
15use std::collections::HashMap;
16
17#[derive(Clone, Debug, Copy, PartialEq)]
18enum SignalNodeKind {
19    Normal,
20    Bidirectional,
21    Source,
22    Sink,
23}
24
25#[derive(Clone, Debug, Copy, PartialEq)]
26enum SignalEdgeKind {
27    Assign,
28    Input,
29    Clock,
30    Reset,
31    Output,
32    Extern,
33    Constant,
34}
35
36#[derive(Clone, Debug, PartialEq)]
37struct SignalNode {
38    pub name: String,
39    pub kind: SignalNodeKind,
40}
41
42#[derive(Clone, Debug, Default)]
43struct SignalGraph {
44    pub graph: Graph<SignalNode, SignalEdgeKind, Directed>,
45}
46
47impl SignalGraph {
48    fn dot(&self) -> String {
49        format!("{:?}", Dot::new(&self.graph))
50    }
51    fn add_signal_node(&mut self, node: &SignalNode) -> NodeIndex {
52        let index = self.graph.node_indices().find(|i| self.graph[*i].eq(node));
53        match index {
54            Some(n) => n,
55            None => self.graph.add_node(node.clone()),
56        }
57    }
58    fn add_signal_edge(&mut self, from: &SignalNode, to: NodeIndex, kind: SignalEdgeKind) {
59        let from_index = self.add_signal_node(from);
60        if !self.graph.contains_edge(from_index, to) {
61            self.graph.add_edge(from_index, to, kind);
62        }
63    }
64}
65
66#[derive(Clone, Debug, Copy)]
67enum ExpressionMode {
68    Write,
69    Read,
70}
71
72type ReadScope = Vec<SignalNode>;
73
74struct TimingChecker {
75    path: NamedPath,
76    namespace: NamedPath,
77    mode: ExpressionMode,
78    write_name: String,
79    read_names: Vec<ReadScope>,
80    pub graph: SignalGraph,
81}
82
83impl Default for TimingChecker {
84    fn default() -> Self {
85        Self {
86            path: Default::default(),
87            namespace: Default::default(),
88            mode: ExpressionMode::Write,
89            write_name: "".to_string(),
90            read_names: vec![],
91            graph: Default::default(),
92        }
93    }
94}
95
96impl TimingChecker {
97    fn set_write_mode(&mut self) {
98        self.mode = ExpressionMode::Write;
99    }
100    fn set_read_mode(&mut self) {
101        self.mode = ExpressionMode::Read;
102    }
103    fn push_read_scope(&mut self) {
104        self.read_names.push(vec![]);
105    }
106    fn pop_read_scope(&mut self) {
107        self.read_names.pop();
108    }
109    fn clear_scope(&mut self) {
110        self.read_names.clear();
111        self.read_names.push(Default::default());
112    }
113    fn add_read(&mut self, name: &str, kind: SignalNodeKind) {
114        if self.read_names.is_empty() {
115            self.read_names.push(ReadScope::default());
116        }
117        self.read_names.last_mut().unwrap().push(SignalNode {
118            name: name.into(),
119            kind: kind,
120        })
121    }
122    fn add_code(&mut self, module: &str, code: Verilog) {
123        if let Verilog::Combinatorial(code) = &code {
124            self.visit_block(code);
125        }
126    }
127    fn add_write(&mut self, write_name: &str, kind: SignalNodeKind, edge: SignalEdgeKind) {
128        assert!(!write_name.is_empty());
129        let write_node = SignalNode {
130            name: write_name.into(),
131            kind,
132        };
133        let write_id = self.graph.add_signal_node(&write_node);
134        for scope in &self.read_names {
135            for read in scope {
136                println!("Adding edge from {:?} -> {:?}", read, write_node);
137                self.graph.add_signal_edge(read, write_id, edge);
138            }
139        }
140    }
141    fn link_fixup(&self, x: &VerilogLinkDetails) -> (String, String) {
142        let v1 = format!(
143            "{}${}${}",
144            self.path.to_string(),
145            x.other_name.replace('[', "$").replace(']', ""),
146            x.my_name
147        );
148        let v2 = format!(
149            "{}${}${}",
150            self.path.to_string(),
151            x.owner_name.replace('[', "$").replace(']', ""),
152            x.my_name
153        );
154        (v1, v2)
155    }
156}
157
158impl VerilogVisitor for TimingChecker {
159    fn visit_conditional(&mut self, c: &VerilogConditional) {
160        self.push_read_scope();
161        self.visit_expression(&c.test);
162        self.visit_block(&c.then);
163        self.visit_block_or_conditional(&c.otherwise);
164        self.pop_read_scope();
165    }
166    fn visit_match(&mut self, m: &VerilogMatch) {
167        self.visit_expression(&m.test);
168        self.push_read_scope();
169        for case in &m.cases {
170            self.visit_case(case);
171        }
172        self.pop_read_scope();
173    }
174    fn visit_assignment(&mut self, l: &VerilogExpression, r: &VerilogExpression) {
175        self.push_read_scope();
176        self.write_name = Default::default();
177        self.mode = ExpressionMode::Write;
178        self.visit_expression(l);
179        self.mode = ExpressionMode::Read;
180        self.visit_expression(r);
181        let write_name = self.write_name.clone();
182        self.add_write(&write_name, SignalNodeKind::Normal, SignalEdgeKind::Assign);
183        self.pop_read_scope();
184    }
185    fn visit_slice_assignment(
186        &mut self,
187        base: &VerilogExpression,
188        width: &usize,
189        offset: &VerilogExpression,
190        replacement: &VerilogExpression,
191    ) {
192        self.push_read_scope();
193        self.write_name = Default::default();
194        self.mode = ExpressionMode::Write;
195        self.visit_expression(base);
196        self.mode = ExpressionMode::Read;
197        self.visit_expression(offset);
198        self.visit_expression(replacement);
199        let write_name = self.write_name.clone();
200        self.add_write(&write_name, SignalNodeKind::Normal, SignalEdgeKind::Assign);
201        self.pop_read_scope();
202    }
203    fn visit_signal(&mut self, c: &str) {
204        let c = format!("{}${}", self.path.to_string(), c).replace("$next", "");
205        match self.mode {
206            ExpressionMode::Write => self.write_name = c,
207            ExpressionMode::Read => self.add_read(&c, SignalNodeKind::Normal),
208        }
209    }
210    fn visit_link(&mut self, c: &[VerilogLink]) {
211        for link in c {
212            match link {
213                VerilogLink::Forward(x) => {
214                    self.push_read_scope();
215                    let (w, r) = self.link_fixup(x);
216                    self.add_read(&r, SignalNodeKind::Normal);
217                    self.add_write(&w, SignalNodeKind::Normal, SignalEdgeKind::Assign);
218                    self.pop_read_scope();
219                }
220                VerilogLink::Backward(x) => {
221                    self.push_read_scope();
222                    let (r, w) = self.link_fixup(x);
223                    self.add_read(&r, SignalNodeKind::Normal);
224                    self.add_write(&w, SignalNodeKind::Normal, SignalEdgeKind::Assign);
225                    self.pop_read_scope();
226                }
227                VerilogLink::Bidirectional(x) => {
228                    let (w, r) = self.link_fixup(x);
229                    self.push_read_scope();
230                    self.add_read(&r, SignalNodeKind::Bidirectional);
231                    self.add_write(&w, SignalNodeKind::Bidirectional, SignalEdgeKind::Assign);
232                    self.pop_read_scope();
233                }
234            }
235        }
236    }
237}
238
239impl Probe for TimingChecker {
240    fn visit_start_scope(&mut self, name: &str, node: &dyn Block) {
241        println!("Start scope {}", name);
242        self.path.push(name);
243        self.namespace.reset();
244        self.add_code(&self.path.to_string(), node.hdl());
245        for info in &node.timing() {
246            // The timing info represents a register.  A register
247            // adds a write dependency based on the clock
248            // The use of the clock decouples the outputs from the inputs.
249            //
250            // We capture this by adding a fake node to the signal graph
251            self.push_read_scope();
252            self.add_read(
253                &format!("{}${}", self.path.to_string(), info.name),
254                SignalNodeKind::Source,
255            );
256            for output in &info.outputs {
257                self.add_write(
258                    &format!("{}${}", self.path.to_string(), output),
259                    SignalNodeKind::Normal,
260                    SignalEdgeKind::Output,
261                );
262            }
263            self.pop_read_scope();
264            let write_name = format!("{}${}", self.path.to_string(), info.name);
265            for input in &info.inputs {
266                self.push_read_scope();
267                self.add_read(
268                    &format!("{}${}", self.path.to_string(), input),
269                    SignalNodeKind::Normal,
270                );
271                self.add_write(&write_name, SignalNodeKind::Sink, SignalEdgeKind::Input);
272                self.pop_read_scope();
273            }
274            self.push_read_scope();
275            self.add_read(
276                &format!("{}${}", self.path.to_string(), info.clock),
277                SignalNodeKind::Normal,
278            );
279            self.add_write(&write_name, SignalNodeKind::Sink, SignalEdgeKind::Clock);
280            self.pop_read_scope();
281        }
282        self.clear_scope();
283    }
284    fn visit_start_namespace(&mut self, name: &str, _node: &dyn Block) {
285        println!("Start Namespace {}", name);
286        self.namespace.push(name);
287    }
288    fn visit_atom(&mut self, name: &str, signal: &dyn Atom) {
289        let module_path = self.path.to_string();
290        let namespace = self.namespace.flat("$");
291        let name = if namespace.is_empty() {
292            name.to_owned()
293        } else {
294            format!("{}${}", namespace, name)
295        };
296        // Add an async source for all input parameters at the top scope
297        let is_top_scope = self.path.to_string().eq("top");
298        let global_signal_name = format!("{}${}", self.path.to_string(), name);
299        match signal.kind() {
300            AtomKind::InputParameter | AtomKind::InOutParameter => {
301                if is_top_scope {
302                    let my_id = self.graph.add_signal_node(&SignalNode {
303                        name: global_signal_name.clone(),
304                        kind: SignalNodeKind::Normal,
305                    });
306                    self.graph.add_signal_edge(
307                        &SignalNode {
308                            name: format!("extern${}", name),
309                            kind: SignalNodeKind::Source,
310                        },
311                        my_id,
312                        SignalEdgeKind::Extern,
313                    );
314                }
315            }
316            AtomKind::Constant => {
317                let my_id = self.graph.add_signal_node(&SignalNode {
318                    name: global_signal_name.clone(),
319                    kind: SignalNodeKind::Normal,
320                });
321                self.graph.add_signal_edge(
322                    &SignalNode {
323                        name: format!("const${}", name),
324                        kind: SignalNodeKind::Source,
325                    },
326                    my_id,
327                    SignalEdgeKind::Constant,
328                );
329            }
330            _ => {}
331        }
332        let descriptor = &signal.descriptor();
333        if let TypeKind::Enum(x) = &descriptor.kind {
334            for label in x {
335                let label = label.replace("::", "$");
336                let my_id = self.graph.add_signal_node(&SignalNode {
337                    name: format!("{}${}", module_path, label),
338                    kind: SignalNodeKind::Normal,
339                });
340                self.graph.add_signal_edge(
341                    &SignalNode {
342                        name: format!("const${}", label),
343                        kind: SignalNodeKind::Source,
344                    },
345                    my_id,
346                    SignalEdgeKind::Constant,
347                );
348                let my_id = self.graph.add_signal_node(&SignalNode {
349                    name: format!("{}${}", self.path.parent(), label),
350                    kind: SignalNodeKind::Normal,
351                });
352                self.graph.add_signal_edge(
353                    &SignalNode {
354                        name: format!("const${}", label),
355                        kind: SignalNodeKind::Source,
356                    },
357                    my_id,
358                    SignalEdgeKind::Constant,
359                );
360            }
361        }
362    }
363    fn visit_end_namespace(&mut self, _name: &str, _node: &dyn Block) {
364        self.namespace.pop();
365    }
366    fn visit_end_scope(&mut self, _name: &str, _node: &dyn Block) {
367        self.path.pop();
368    }
369}
370
371pub fn check_timing<U: Block>(uut: &U) {
372    let mut scan = TimingChecker::default();
373    uut.accept("top", &mut scan);
374    //println!("{}",&scan.graph);
375    let dot = scan.graph.dot();
376    println!("Graph is cyclic: {}", is_cyclic_directed(&scan.graph.graph));
377    println!(
378        "Number of connected components: {}",
379        connected_components(&scan.graph.graph)
380    );
381    let g = &scan.graph.graph;
382    let mut vertex_sets = UnionFind::new(g.node_count());
383    for edge in g.edge_references() {
384        let (a, b) = (edge.source(), edge.target());
385        vertex_sets.union(g.to_index(a), g.to_index(b));
386    }
387    let mut labels = vertex_sets.into_labeling();
388    let mut unique_labels = labels.clone();
389    unique_labels.sort_unstable();
390    unique_labels.dedup();
391    // For each label, we extract the vertices and build a new graph
392    for subgraph in unique_labels {
393        let mut remap: HashMap<_, _> = Default::default();
394        let mut s: Graph<SignalNode, SignalEdgeKind> = Graph::default();
395        for (ndx, label) in labels.iter().enumerate() {
396            if *label == subgraph {
397                let old_index = g.from_index(ndx);
398                let old_weight = g[g.from_index(ndx)].clone();
399                let new_index = s.add_node(old_weight);
400                remap.insert(old_index, new_index);
401            }
402        }
403        for edge in g.edge_references() {
404            let (a, b) = (edge.source(), edge.target());
405            assert!(!(remap.contains_key(&a) ^ remap.contains_key(&b)));
406            if remap.contains_key(&a) {
407                let a_new = remap.get(&a).unwrap();
408                let b_new = remap.get(&b).unwrap();
409                s.add_edge(*a_new, *b_new, *edge.weight());
410            }
411        }
412        /*        println!("Number of elements in subgraph {}", remap.len());
413        std::fs::write(
414            format!("sub_graph_{}.dot", subgraph),
415            format!("{:?}", Dot::new(&s)),
416        )
417        .unwrap();*/
418    }
419    //std::fs::write("dag.dot", dot).unwrap();
420}