rust_hdl_core/
module_defines.rs

1use crate::ast::{Verilog, VerilogLink, VerilogLiteral};
2use crate::atom::AtomKind::{StubInputSignal, StubOutputSignal};
3use crate::atom::{is_atom_signed, Atom, AtomKind};
4use crate::block::Block;
5use crate::check_error::check_all;
6use crate::code_writer::CodeWriter;
7use crate::named_path::NamedPath;
8use crate::probe::Probe;
9use crate::type_descriptor::{TypeDescriptor, TypeKind};
10use crate::verilog_gen::{verilog_combinatorial, verilog_link_extraction};
11use std::collections::BTreeMap;
12
13#[derive(Clone, Debug, Default)]
14struct SubModuleInvocation {
15    kind: String,
16    name: String,
17}
18
19#[derive(Clone, Debug, Default)]
20struct ModuleDetails {
21    atoms: Vec<AtomDetails>,
22    sub_modules: Vec<SubModuleInvocation>,
23    enums: Vec<EnumDefinition>,
24    code: Verilog,
25    links: Vec<VerilogLink>,
26}
27
28#[derive(Clone, Debug, PartialEq)]
29struct EnumDefinition {
30    pub type_name: String,
31    pub discriminant: String,
32    pub value: usize,
33}
34
35#[derive(Clone, Debug)]
36struct AtomDetails {
37    name: String,
38    kind: AtomKind,
39    width: usize,
40    const_val: VerilogLiteral,
41    signed: bool,
42}
43
44fn verilog_atom_name(x: &AtomKind) -> &str {
45    match x {
46        AtomKind::InputParameter => "input wire",
47        AtomKind::OutputParameter => "output reg",
48        AtomKind::StubInputSignal => "reg",
49        AtomKind::StubOutputSignal => "wire",
50        AtomKind::Constant => "localparam",
51        AtomKind::LocalSignal => "reg",
52        AtomKind::InOutParameter => "inout wire",
53        AtomKind::OutputPassthrough => "output wire",
54    }
55}
56
57fn decl(x: &AtomDetails) -> String {
58    let signed = if x.signed { "signed" } else { "" };
59    if x.kind == AtomKind::Constant {
60        format!(
61            "{} {} {} = {};",
62            verilog_atom_name(&x.kind),
63            signed,
64            x.name,
65            x.const_val
66        )
67    } else {
68        if x.width == 1 {
69            format!("{} {} {};", verilog_atom_name(&x.kind), signed, x.name)
70        } else {
71            format!(
72                "{} {} [{}:0] {};",
73                verilog_atom_name(&x.kind),
74                signed,
75                x.width - 1,
76                x.name
77            )
78        }
79    }
80}
81
82#[derive(Default)]
83pub struct ModuleDefines {
84    path: NamedPath,
85    namespace: NamedPath,
86    details: BTreeMap<String, ModuleDetails>,
87}
88
89impl ModuleDefines {
90    fn add_atom(&mut self, module: &str, atom: AtomDetails) {
91        let entry = self.details.entry(module.into()).or_default();
92        entry.atoms.push(atom)
93    }
94    fn add_submodule(&mut self, module: &str, name: &str, kind: &str) {
95        let entry = self.details.entry(module.into()).or_default();
96        entry.sub_modules.push(SubModuleInvocation {
97            kind: kind.to_owned(),
98            name: name.to_owned(),
99        });
100    }
101    fn add_enums(&mut self, module: &str, descriptor: &TypeDescriptor) {
102        let entry = self.details.entry(module.into()).or_default();
103        let enum_name = descriptor.name.clone();
104        match &descriptor.kind {
105            TypeKind::Enum(x) => {
106                for (ndx, label) in x.iter().enumerate() {
107                    let def = EnumDefinition {
108                        type_name: enum_name.clone(),
109                        discriminant: label.into(),
110                        value: ndx,
111                    };
112                    if !entry.enums.contains(&def) {
113                        entry.enums.push(def);
114                    }
115                }
116            }
117            TypeKind::Composite(x) => {
118                for item in x {
119                    self.add_enums(module, &item.kind);
120                }
121            }
122            _ => {}
123        }
124    }
125    fn add_code(&mut self, module: &str, code: Verilog) {
126        let entry = self.details.entry(module.into()).or_default();
127        entry.links = match &code {
128            Verilog::Combinatorial(code) => verilog_link_extraction(code),
129            _ => {
130                vec![]
131            }
132        };
133        entry.code = code;
134    }
135}
136
137impl Probe for ModuleDefines {
138    fn visit_start_scope(&mut self, name: &str, node: &dyn Block) {
139        let top_level = self.path.to_string();
140        self.path.push(name);
141        self.namespace.reset();
142        self.add_submodule(&top_level, name, &self.path.to_string());
143        self.add_code(&self.path.to_string(), node.hdl());
144    }
145
146    fn visit_start_namespace(&mut self, name: &str, _node: &dyn Block) {
147        self.namespace.push(name);
148    }
149
150    fn visit_atom(&mut self, name: &str, signal: &dyn Atom) {
151        let module_path = self.path.to_string();
152        let module_name = self.path.last();
153        let namespace = self.namespace.flat("$");
154        let name = if namespace.is_empty() {
155            name.to_owned()
156        } else {
157            format!("{}${}", namespace, name)
158        };
159        let param = AtomDetails {
160            name: name.clone(),
161            kind: signal.kind(),
162            width: signal.bits(),
163            const_val: signal.verilog(),
164            signed: is_atom_signed(signal),
165        };
166        if param.kind.is_parameter() {
167            let kind = if param.kind == AtomKind::InputParameter {
168                StubInputSignal
169            } else {
170                StubOutputSignal
171            };
172            let parent_param = AtomDetails {
173                name: format!("{}${}", module_name, name.to_owned()),
174                kind,
175                width: signal.bits(),
176                const_val: signal.verilog(),
177                signed: is_atom_signed(signal),
178            };
179            let parent_name = self.path.parent();
180            self.add_atom(&parent_name, parent_param);
181        }
182        self.add_enums(&module_path, &signal.descriptor());
183        self.add_enums(&self.path.parent(), &signal.descriptor());
184        self.add_atom(&module_path, param);
185    }
186
187    fn visit_end_namespace(&mut self, _name: &str, _node: &dyn Block) {
188        self.namespace.pop();
189    }
190
191    fn visit_end_scope(&mut self, _name: &str, _node: &dyn Block) {
192        self.path.pop();
193    }
194}
195
196fn get_link_equivalence(link: &VerilogLink) -> (String, String) {
197    match link {
198        VerilogLink::Forward(link) => (
199            format!("{}${}", link.other_name, link.my_name),
200            format!("{}${}", link.owner_name, link.my_name),
201        ),
202        VerilogLink::Backward(link) => (
203            format!("{}${}", link.owner_name, link.my_name),
204            format!("{}${}", link.other_name, link.my_name),
205        ),
206        VerilogLink::Bidirectional(link) => {
207            if link.my_name.is_empty() {
208                (link.owner_name.clone(), link.other_name.clone())
209            } else {
210                (
211                    format!("{}${}", link.other_name, link.my_name),
212                    format!("{}${}", link.owner_name, link.my_name),
213                )
214            }
215        }
216    }
217}
218
219impl ModuleDefines {
220    fn sub_module_invocation(
221        &self,
222        module_details: &ModuleDetails,
223        child: &SubModuleInvocation,
224        io: &mut CodeWriter,
225    ) {
226        let entry = self.details.get(&child.kind).unwrap();
227        let submodule_kind = match &entry.code {
228            Verilog::Blackbox(b) => &b.name,
229            _ => &child.kind,
230        };
231        let child_args = entry
232            .atoms
233            .iter()
234            .filter(|x| x.kind.is_parameter())
235            .map(|x| {
236                let arg_name = format!("{}${}", child.name, x.name);
237                let arg_name = if self.stub_is_linked_to_module_argument(module_details, &arg_name)
238                {
239                    self.get_linked_argument_name(module_details, &arg_name)
240                } else {
241                    arg_name
242                };
243                format!(".{}({})", x.name, arg_name)
244            })
245            .collect::<Vec<_>>()
246            .join(",\n");
247        io.add(format!("{} {}(\n", submodule_kind, child.name));
248        io.push();
249        io.add(child_args);
250        io.pop();
251        io.add(");\n");
252    }
253    fn module_argument_is_passed_through_to_submodule(
254        &self,
255        module_details: &ModuleDetails,
256        module_arg_name: &str,
257    ) -> bool {
258        for child in &module_details.sub_modules {
259            let entry = self.details.get(&child.kind).unwrap();
260            for child_arg in &entry.atoms {
261                let arg_name = format!("{}${}", child.name, child_arg.name);
262                if self.get_linked_argument_name(module_details, &arg_name) == module_arg_name {
263                    return true;
264                }
265            }
266        }
267        false
268    }
269    fn get_linked_argument_name(&self, module_details: &ModuleDetails, arg_name: &str) -> String {
270        for link in &module_details.links {
271            let equiv = get_link_equivalence(link);
272            if arg_name == equiv.0 {
273                return equiv.1.clone();
274            }
275            if arg_name == equiv.1 {
276                return equiv.0.clone();
277            }
278        }
279        arg_name.to_string()
280    }
281    fn signal_name_is_module_argument(
282        &self,
283        module_details: &ModuleDetails,
284        signal_name: &str,
285    ) -> bool {
286        // We now know the stub is linked to something... but is that a module argument?
287        for atom in &module_details.atoms {
288            if atom.kind.is_parameter() && atom.name == signal_name {
289                return true;
290            }
291        }
292        false
293    }
294    fn stub_is_linked_to_module_argument(
295        &self,
296        module_details: &ModuleDetails,
297        atom_name: &str,
298    ) -> bool {
299        for link in &module_details.links {
300            let equiv = get_link_equivalence(link);
301            if atom_name == equiv.0 || atom_name == equiv.1 {
302                let linked_name = if atom_name == equiv.0 {
303                    equiv.1
304                } else {
305                    equiv.0
306                };
307                if self.signal_name_is_module_argument(module_details, &linked_name) {
308                    return true;
309                }
310            }
311        }
312        false
313    }
314    fn process_module(
315        &self,
316        module_name: &str,
317        module_details: &ModuleDetails,
318        io: &mut CodeWriter,
319    ) {
320        // Remap the output parameters to pass through (net type) in case we have a wrapper
321        let atoms_passthrough = &module_details
322            .atoms
323            .iter()
324            .map(|x| {
325                let mut y = x.clone();
326                if y.kind == AtomKind::OutputParameter {
327                    y.kind = AtomKind::OutputPassthrough;
328                }
329                y
330            })
331            .collect::<Vec<_>>();
332        let wrapper_mode = if let Verilog::Wrapper(_) = &module_details.code {
333            true
334        } else {
335            false
336        };
337        let atoms = if wrapper_mode {
338            io.add("\n// v-- Setting output parameters to net type for wrapped code.\n");
339            &atoms_passthrough
340        } else {
341            &module_details.atoms
342        };
343        let args = atoms
344            .iter()
345            .filter(|x| x.kind.is_parameter())
346            .collect::<Vec<_>>();
347        let stubs = atoms
348            .iter()
349            .filter(|x| x.kind.is_stub())
350            .collect::<Vec<_>>();
351        let consts = atoms
352            .iter()
353            .filter(|x| x.kind == AtomKind::Constant)
354            .collect::<Vec<_>>();
355        let locals = atoms
356            .iter()
357            .filter(|x| x.kind == AtomKind::LocalSignal)
358            .collect::<Vec<_>>();
359        let module_args = args
360            .iter()
361            .map(|x| x.name.to_owned())
362            .collect::<Vec<_>>()
363            .join(",");
364        io.add(format!("\n\nmodule {}({});", module_name, module_args));
365        io.push();
366        if !args.is_empty() {
367            io.add("\n// Module arguments");
368            args.iter().for_each(|x| {
369                if !self.module_argument_is_passed_through_to_submodule(module_details, &x.name)
370                    || x.kind != AtomKind::OutputParameter
371                {
372                    io.add(decl(x))
373                } else {
374                    // For some synthesis engines, you cannot pass a module argument
375                    // to a child module if it is of reg type
376                    let mut x = (*x).clone();
377                    x.kind = AtomKind::OutputPassthrough;
378                    io.add(decl(&x))
379                }
380            });
381        }
382        let submodules = &module_details.sub_modules;
383        if !consts.is_empty() {
384            io.add("\n// Constant declarations");
385            consts.iter().for_each(|x| io.add(decl(x)));
386        }
387        if !module_details.enums.is_empty() & !wrapper_mode {
388            io.add("\n// Enums");
389            module_details.enums.iter().for_each(|x| {
390                io.add(format!(
391                    "localparam {} = {};",
392                    x.discriminant.replace("::", "$"),
393                    x.value
394                ))
395            });
396        }
397        if !stubs.is_empty() & !wrapper_mode {
398            io.add("\n// Stub signals");
399            stubs.iter().for_each(|x| {
400                if !self.stub_is_linked_to_module_argument(module_details, &x.name) {
401                    io.add(decl(x))
402                }
403            });
404        }
405        if !locals.is_empty() & !wrapper_mode {
406            io.add("\n// Local signals");
407            locals.iter().for_each(|x| io.add(decl(x)));
408        }
409        if !submodules.is_empty() & !wrapper_mode {
410            io.add("\n// Sub module instances");
411            for child in submodules {
412                self.sub_module_invocation(module_details, child, io);
413            }
414        }
415        match &module_details.code {
416            Verilog::Combinatorial(code) => {
417                io.add("\n// Update code");
418                io.add(verilog_combinatorial(code));
419            }
420            Verilog::Custom(code) => {
421                io.add("\n// Update code (custom)");
422                io.add(code);
423            }
424            Verilog::Wrapper(c) => {
425                io.add("\n// Update code (wrapper)");
426                io.add(&c.code);
427            }
428            Verilog::Blackbox(_) => {}
429            Verilog::Empty => {}
430        }
431        for x in &module_details.links {
432            let equiv = get_link_equivalence(x);
433            if !self.signal_name_is_module_argument(module_details, &equiv.0)
434                & !self.signal_name_is_module_argument(module_details, &equiv.1)
435            {
436                let txt = match x {
437                    VerilogLink::Forward(x) => {
438                        format!(
439                            "always @(*) {}${} = {}${};",
440                            x.other_name.replace("[", "$").replace("]", ""),
441                            x.my_name,
442                            x.owner_name.replace("[", "$").replace("]", ""),
443                            x.my_name
444                        )
445                    }
446                    VerilogLink::Backward(x) => {
447                        format!(
448                            "always @(*) {}${} = {}${};",
449                            x.owner_name.replace("[", "$").replace("]", ""),
450                            x.my_name,
451                            x.other_name.replace("[", "$").replace("]", ""),
452                            x.my_name
453                        )
454                    }
455                    VerilogLink::Bidirectional(x) => {
456                        if x.my_name.is_empty() {
457                            format!("assign {} = {};", x.owner_name, x.other_name)
458                        } else {
459                            format!(
460                                "assign {}${} = {}${};",
461                                x.owner_name, x.my_name, x.other_name, x.my_name
462                            )
463                        }
464                    }
465                };
466                io.add_line(txt);
467            }
468        }
469        io.pop();
470        io.add(format!("endmodule // {}", module_name));
471    }
472
473    pub fn defines(&self) -> String {
474        let mut io = CodeWriter::default();
475        self.details
476            .iter()
477            .filter(|x| x.0.len() != 0)
478            .filter(|x| !matches!(x.1.code, Verilog::Blackbox(_)))
479            .for_each(|k| {
480                let module_name = k.0;
481                let module_details = k.1;
482                self.process_module(module_name, module_details, &mut io);
483            });
484        self.details.iter().for_each(|x| match &x.1.code {
485            Verilog::Blackbox(b) => io.add(&b.code),
486            Verilog::Wrapper(w) => io.add(&w.cores),
487            _ => {}
488        });
489        io.to_string()
490    }
491}
492
493pub fn generate_verilog<U: Block>(uut: &U) -> String {
494    let mut defines = ModuleDefines::default();
495    check_all(uut).unwrap(); // TODO - make this not panic...
496    uut.accept("top", &mut defines);
497    defines.defines()
498}
499
500pub fn generate_verilog_unchecked<U: Block>(uut: &U) -> String {
501    let mut defines = ModuleDefines::default();
502    uut.accept("top", &mut defines);
503    defines.defines()
504}