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 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 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 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(); 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}