rust_hdl_core/
verilog_gen.rs

1use evalexpr::ContextWithMutableVariables;
2use num_bigint::BigUint;
3use regex::Regex;
4
5use crate::ast::{
6    VerilogBlock, VerilogBlockOrConditional, VerilogCase, VerilogConditional, VerilogExpression,
7    VerilogLink, VerilogLinkDetails, VerilogLiteral, VerilogLoop, VerilogMatch, VerilogOp,
8    VerilogOpUnary,
9};
10use crate::code_writer::CodeWriter;
11use crate::verilog_visitor::{walk_block, VerilogVisitor};
12
13struct LoopVariable {
14    variable: String,
15    value: usize,
16}
17
18#[derive(Default)]
19struct VerilogCodeGenerator {
20    io: CodeWriter,
21    loops: Vec<LoopVariable>,
22    links: Vec<VerilogLink>,
23}
24
25impl VerilogCodeGenerator {
26    fn array_index_simplification(&self, a: &str) -> String {
27        let re = Regex::new(r"\[([^\]]*)\]").unwrap();
28        let mut context = evalexpr::HashMapContext::new();
29        for lvar in &self.loops {
30            let _ = context.set_value(lvar.variable.clone(), (lvar.value as i64).into());
31        }
32        if let Some(x) = re.captures(a) {
33            if x.len() == 2 {
34                if let Some(txt) = x.get(1) {
35                    let arg = evalexpr::eval_with_context(txt.as_str(), &context).unwrap();
36                    return re.replace(a, format!("$${}", arg)).to_string();
37                }
38            }
39        }
40        a.to_string()
41    }
42
43    fn link_fixup(&self, x: &VerilogLinkDetails) -> VerilogLinkDetails {
44        VerilogLinkDetails {
45            my_name: self.ident_fixup(&x.my_name),
46            owner_name: self.ident_fixup(&x.owner_name),
47            other_name: self.ident_fixup(&x.other_name),
48        }
49    }
50
51    fn ident_fixup(&self, a: &str) -> String {
52        let mut x = a.to_owned();
53        for index in &self.loops {
54            if x == index.variable {
55                x = format!("{}", index.value);
56            }
57        }
58        if x.starts_with(".") {
59            x.remove(0);
60        }
61        x = x
62            .replace(".", "$")
63            .replace("::", "$")
64            .trim_end_matches("$next")
65            .to_owned();
66        if x.contains('[') {
67            x = self.array_index_simplification(&x);
68        }
69        x
70    }
71}
72
73impl ToString for VerilogCodeGenerator {
74    fn to_string(&self) -> String {
75        self.io.to_string()
76    }
77}
78
79pub fn verilog_link_extraction(code: &VerilogBlock) -> Vec<VerilogLink> {
80    let mut gen = VerilogCodeGenerator::default();
81    gen.visit_block(code);
82    gen.links
83}
84
85pub fn verilog_combinatorial(code: &VerilogBlock) -> String {
86    let mut gen = VerilogCodeGenerator::default();
87    gen.visit_block(code);
88    format!("always @(*) {}\n", gen.to_string())
89}
90
91impl VerilogVisitor for VerilogCodeGenerator {
92    fn visit_block(&mut self, b: &VerilogBlock) {
93        self.io.writeln("begin");
94        self.io.push();
95        walk_block(self, b);
96        self.io.pop();
97        self.io.add_line("end");
98    }
99
100    fn visit_loop(&mut self, a: &VerilogLoop) {
101        let start = a.from.as_usize();
102        let end = a.to.as_usize();
103        for i in start..end {
104            self.loops.push(LoopVariable {
105                variable: a.index.clone(),
106                value: i,
107            });
108            walk_block(self, &a.block);
109            self.loops.pop();
110        }
111    }
112
113    fn visit_slice_assignment(
114        &mut self,
115        base: &VerilogExpression,
116        width: &usize,
117        offset: &VerilogExpression,
118        replacement: &VerilogExpression,
119    ) {
120        self.visit_expression(base);
121        self.io.write("[(");
122        self.visit_expression(offset);
123        self.io.write(format!(")+:({})] = ", width));
124        self.visit_expression(replacement);
125        self.io.writeln(";");
126    }
127
128    fn visit_conditional(&mut self, c: &VerilogConditional) {
129        self.io.write("if (");
130        self.visit_expression(&c.test);
131        self.io.write(") ");
132        self.visit_block(&c.then);
133        self.visit_block_or_conditional(&c.otherwise);
134    }
135
136    fn visit_block_or_conditional(&mut self, o: &VerilogBlockOrConditional) {
137        match &o {
138            VerilogBlockOrConditional::Block(b) => {
139                self.io.write("else ");
140                self.visit_block(&b);
141            }
142            VerilogBlockOrConditional::Conditional(c) => {
143                self.io.write("else ");
144                self.visit_statement(c);
145            }
146            VerilogBlockOrConditional::None => {}
147        }
148    }
149
150    fn visit_match(&mut self, m: &VerilogMatch) {
151        self.io.write("case (");
152        self.visit_expression(&m.test);
153        self.io.writeln(")");
154        self.io.push();
155        m.cases.iter().for_each(|x| self.visit_case(x));
156        self.io.pop();
157        self.io.writeln("endcase")
158    }
159
160    fn visit_comment(&mut self, x: &str) {
161        self.io.add(format!("// {}", x));
162    }
163
164    fn visit_signal(&mut self, sig: &str) {
165        self.io.write(self.ident_fixup(sig));
166    }
167
168    fn visit_literal(&mut self, v: &VerilogLiteral) {
169        self.io.write(v.to_string());
170    }
171
172    fn visit_link(&mut self, l: &[VerilogLink]) {
173        for link in l {
174            self.links.push(match link {
175                VerilogLink::Forward(x) => VerilogLink::Forward(self.link_fixup(x)),
176                VerilogLink::Backward(x) => VerilogLink::Backward(self.link_fixup(x)),
177                VerilogLink::Bidirectional(x) => VerilogLink::Bidirectional(self.link_fixup(x)),
178            })
179        }
180    }
181
182    fn visit_case(&mut self, c: &VerilogCase) {
183        self.io.write(self.ident_fixup(&c.condition));
184        self.io.writeln(":");
185        self.io.push();
186        self.visit_block(&c.block);
187        self.io.pop();
188    }
189
190    fn visit_binop(&mut self, l: &VerilogExpression, o: &VerilogOp, r: &VerilogExpression) {
191        self.visit_expression(l);
192        self.io.write(" ");
193        self.io.write(match o {
194            VerilogOp::Add => "+",
195            VerilogOp::Sub => "-",
196            VerilogOp::Mul => "*",
197            VerilogOp::LogicalAnd => "&&",
198            VerilogOp::LogicalOr => "||",
199            VerilogOp::BitXor => "^",
200            VerilogOp::BitAnd => "&",
201            VerilogOp::BitOr => "|",
202            VerilogOp::Shl => "<<",
203            VerilogOp::Shr => ">>",
204            VerilogOp::Eq => "==",
205            VerilogOp::Lt => "<",
206            VerilogOp::Le => "<=",
207            VerilogOp::Ne => "!=",
208            VerilogOp::Ge => ">=",
209            VerilogOp::Gt => ">",
210        });
211        self.io.write(" ");
212        self.visit_expression(r);
213    }
214
215    fn visit_unop(&mut self, o: &VerilogOpUnary, r: &VerilogExpression) {
216        self.io.write(match o {
217            VerilogOpUnary::Not => "~",
218            VerilogOpUnary::Neg => "-",
219            VerilogOpUnary::All => "&",
220            VerilogOpUnary::Any => "|",
221            VerilogOpUnary::Xor => "^",
222        });
223        self.visit_expression(r);
224    }
225
226    fn visit_assignment(&mut self, l: &VerilogExpression, r: &VerilogExpression) {
227        self.visit_expression(l);
228        self.io.write(" = ");
229        self.visit_expression(r);
230        self.io.writeln(";");
231    }
232
233    fn visit_paren(&mut self, e: &VerilogExpression) {
234        self.io.write("(");
235        self.visit_expression(e);
236        self.io.write(")");
237    }
238
239    fn visit_cast(&mut self, e: &VerilogExpression, bits: &usize) {
240        self.io.write("((");
241        self.visit_expression(e);
242        let mask = (BigUint::from(1_u32) << bits) - 1_u32;
243        self.io.write(format!(") & {}'h{:x})", bits, mask))
244    }
245
246    fn visit_signed(&mut self, a: &VerilogExpression) {
247        self.io.write("$signed(");
248        self.visit_expression(a);
249        self.io.write(")");
250    }
251
252    fn visit_unsigned(&mut self, a: &VerilogExpression) {
253        self.io.write("$unsigned(");
254        self.visit_expression(a);
255        self.io.write(")");
256    }
257
258    fn visit_index(&mut self, a: &VerilogExpression, b: &VerilogExpression) {
259        self.visit_expression(a);
260        self.io.write("[");
261        self.visit_expression(b);
262        self.io.write("]");
263    }
264
265    fn visit_slice(&mut self, sig: &VerilogExpression, width: &usize, offset: &VerilogExpression) {
266        self.visit_expression(sig);
267        self.io.write("[(");
268        self.visit_expression(offset);
269        self.io.write(format!(")+:({})]", width));
270    }
271
272    fn visit_index_replace(
273        &mut self,
274        sig: &VerilogExpression,
275        ndx: &VerilogExpression,
276        val: &VerilogExpression,
277    ) {
278        self.io.write("(");
279        self.visit_expression(sig);
280        self.io.write(" & ~(1 << (");
281        self.visit_expression(ndx);
282        self.io.write(")) | ((");
283        self.visit_expression(val);
284        self.io.write(") << (");
285        self.visit_expression(ndx);
286        self.io.write(")))");
287    }
288}
289
290#[test]
291fn test_array_replacement() {
292    let re = Regex::new(r"\[([^\]]*)\]").unwrap();
293    let test = "a[((i+1))]";
294    let captures = re.captures(test);
295    let mut context = evalexpr::HashMapContext::new();
296    context.set_value("i".to_string(), 5.into()).unwrap();
297    if let Some(x) = re.captures(test) {
298        println!("Match {:?}", x);
299        if x.len() == 2 {
300            if let Some(txt) = x.get(1) {
301                let arg = evalexpr::eval_with_context(txt.as_str(), &context).unwrap();
302                println!("Replace {} -> {}", txt.as_str(), arg);
303                println!("Update {}", re.replace(test, format!("$${}", arg)))
304            }
305        }
306    }
307    assert!(captures.is_some());
308}
309
310pub fn filter_blackbox_directives(t: &str) -> String {
311    let mut in_black_box = false;
312    let mut ret = vec![];
313    for line in t.split("\n") {
314        in_black_box = in_black_box || line.starts_with("(* blackbox *)");
315        if !in_black_box {
316            ret.push(line);
317        }
318        if line.starts_with("endmodule") {
319            in_black_box = false;
320        }
321    }
322    ret.join("\n")
323}
324
325#[test]
326fn test_filter_bb_directives() {
327    let p = r#"
328blah
329more code
330goes here
331
332(* blackbox *)
333module my_famous_module(
334    super_secret_arg1,
335    super_secret_arg2,
336    super_secret_arg3);
337/* Comment */
338endmodule
339
340stuff
341"#;
342    let q = filter_blackbox_directives(p);
343    println!("{}", q);
344    assert!(!q.contains("blackbox"));
345    assert!(!q.contains("module"));
346    assert!(!q.contains("endmodule"));
347    assert!(q.contains("more code"));
348    assert!(q.contains("stuff"));
349}