rust_hdl_core/
check_write_inputs.rs1use crate::ast::{Verilog, VerilogExpression};
2use crate::atom::{Atom, AtomKind};
3use crate::block::Block;
4use crate::check_error::{CheckError, PathedName, PathedNameList};
5use crate::named_path::NamedPath;
6use crate::probe::Probe;
7use crate::verilog_visitor::VerilogVisitor;
8use std::collections::HashSet;
9
10#[derive(Copy, Clone, Debug, PartialEq)]
11enum Mode {
12 Ignore,
13 Read,
14 Write,
15}
16
17struct VerilogWriteCollector {
18 vars_written: HashSet<String>,
19 vars_read: HashSet<String>,
20 mode: Mode,
21}
22
23impl Default for VerilogWriteCollector {
24 fn default() -> Self {
25 Self {
26 vars_written: Default::default(),
27 vars_read: Default::default(),
28 mode: Mode::Ignore,
29 }
30 }
31}
32
33fn get_write_list(uut: &dyn Block) -> HashSet<String> {
34 match &uut.hdl() {
35 Verilog::Combinatorial(code) => {
36 let mut det = VerilogWriteCollector::default();
37 det.visit_block(code);
38 det.vars_written
39 }
40 _ => Default::default(),
41 }
42}
43
44impl VerilogVisitor for VerilogWriteCollector {
45 fn visit_slice_assignment(
46 &mut self,
47 base: &VerilogExpression,
48 _width: &usize,
49 offset: &VerilogExpression,
50 replacement: &VerilogExpression,
51 ) {
52 let current_mode = self.mode;
53 self.mode = Mode::Read;
54 self.visit_expression(offset);
55 self.visit_expression(replacement);
56 self.mode = Mode::Write;
57 self.visit_expression(base);
58 self.mode = current_mode;
59 }
60
61 fn visit_signal(&mut self, c: &str) {
62 let myname = c.replace("$next", "");
63 match self.mode {
64 Mode::Ignore => {}
65 Mode::Write => {
66 self.vars_written.insert(myname);
67 }
68 Mode::Read => {
69 self.vars_read.insert(myname);
70 }
71 }
72 }
73
74 fn visit_assignment(&mut self, l: &VerilogExpression, r: &VerilogExpression) {
75 let current_mode = self.mode;
76 self.mode = Mode::Read;
77 self.visit_expression(r);
78 self.mode = Mode::Write;
79 self.visit_expression(l);
80 self.mode = current_mode;
81 }
82}
83
84#[derive(Default)]
85struct CheckInputsNotDriven {
86 path: NamedPath,
87 namespace: NamedPath,
88 input_parameters: Vec<Vec<String>>,
89 failures: PathedNameList,
90}
91
92impl Probe for CheckInputsNotDriven {
93 fn visit_start_scope(&mut self, name: &str, _node: &dyn Block) {
94 self.input_parameters.push(vec![]);
95 self.path.push(name);
96 self.namespace.reset();
97 }
98
99 fn visit_start_namespace(&mut self, name: &str, _node: &dyn Block) {
100 self.namespace.push(name);
101 }
102
103 fn visit_atom(&mut self, name: &str, signal: &dyn Atom) {
104 let namespace = self.namespace.flat("$");
105 let name = if namespace.is_empty() {
106 name.to_owned()
107 } else {
108 format!("{}${}", namespace, name)
109 };
110 if let AtomKind::InputParameter = signal.kind() {
111 self.input_parameters.last_mut().unwrap().push(name);
112 }
113 }
114
115 fn visit_end_namespace(&mut self, _name: &str, _node: &dyn Block) {
116 self.namespace.pop();
117 }
118
119 fn visit_end_scope(&mut self, _name: &str, node: &dyn Block) {
120 let written = get_write_list(node);
121 let my_params = self.input_parameters.last().unwrap();
122 for param in my_params {
123 if written.contains(param) {
124 self.failures.push(PathedName {
125 path: self.path.to_string(),
126 name: param.to_owned(),
127 })
128 }
129 }
130 self.path.pop();
131 self.input_parameters.pop();
132 }
133}
134
135pub fn check_inputs_not_written(uut: &dyn Block) -> Result<(), CheckError> {
158 let mut visitor = CheckInputsNotDriven::default();
159 uut.accept("uut", &mut visitor);
160 if visitor.failures.is_empty() {
161 Ok(())
162 } else {
163 Err(CheckError::WritesToInputs(visitor.failures))
164 }
165}