1use itertools::Itertools;
2use nesty::{code, Code};
3use spade_codespan_reporting::term::termcolor;
4
5use num::{BigInt, BigUint, One, Signed, ToPrimitive, Zero};
6use spade_common::id_tracker::ExprIdTracker;
7use spade_common::location_info::Loc;
8use spade_common::name::NameID;
9use spade_common::num_ext::InfallibleToBigUint;
10use spade_diagnostics::emitter::CodespanEmitter;
11use spade_diagnostics::{CodeBundle, CompilationError, DiagHandler};
12
13use crate::aliasing::flatten_aliases;
14use crate::assertion_codegen::AssertedExpression;
15use crate::eval::eval_statements;
16use crate::renaming::{make_names_predictable, VerilogNameMap};
17use crate::type_list::TypeList;
18use crate::types::Type;
19use crate::unit_name::{InstanceMap, InstanceNameTracker};
20use crate::verilog::{self, assign, localparam_size_spec, logic, size_spec};
21use crate::wal::insert_wal_signals;
22use crate::{
23 enum_util, Binding, ConstantValue, Entity, MirInput, Operator, ParamName, Statement, ValueName,
24};
25
26pub mod util;
27
28pub use util::{escape_path, mangle_entity, mangle_input, mangle_output, TupleIndex};
29
30struct Context<'a> {
31 types: &'a TypeList,
32 source_code: &'a Option<CodeBundle>,
33 instance_names: &'a mut InstanceNameTracker,
34 instance_map: &'a mut InstanceMap,
35 unit_nameid: &'a NameID,
37}
38
39fn source_attribute(loc: &Option<Loc<()>>, code: &Option<CodeBundle>) -> Option<String> {
41 match (loc, code) {
42 (Some(l), Some(c)) => Some(format!(r#"(* src = "{}" *)"#, c.source_loc(l))),
43 _ => None,
44 }
45}
46
47fn add_to_name_map(name_map: &mut VerilogNameMap, name: &ValueName, ty: &Type) {
48 if ty.size() != BigUint::zero() {
49 name_map.insert(&name.var_name(), name.verilog_name_source_fwd());
50 }
51 if ty.backward_size() != BigUint::zero() {
52 name_map.insert(&name.backward_var_name(), name.verilog_name_source_back());
53 }
54}
55
56fn statement_declaration(
57 statement: &Statement,
58 code: &Option<CodeBundle>,
59 name_map: &mut VerilogNameMap,
60) -> Code {
61 match statement {
62 Statement::Binding(binding) => {
63 add_to_name_map(name_map, &binding.name, &binding.ty);
64 let name = binding.name.var_name();
65
66 let forward_declaration = if binding.ty.size() != BigUint::zero() {
67 let inner = vec![match &binding.ty {
68 crate::types::Type::Memory { inner, length } => {
69 let inner_w = inner.size();
70 if inner_w > 1u32.to_biguint() {
71 format!("logic[{inner_w}-1:0] {name}[{length}-1:0];")
72 } else {
73 format!("logic {name}[{length}-1:0];")
74 }
75 }
76 _ => logic(&name, &binding.ty.size()),
77 }];
78 code![
79 [0] source_attribute(&binding.loc, code);
80 [0] inner
81 ]
82 } else {
83 code![]
84 };
85
86 let backward_declaration = if binding.ty.backward_size() != BigUint::zero() {
87 code![
88 [0] source_attribute(&binding.loc, code);
89 [0] logic(
90 &binding.name.backward_var_name(),
91 &binding.ty.backward_size(),
92 );
93 ]
94 } else {
95 code![]
96 };
97
98 let ops = &binding
99 .operands
100 .iter()
101 .map(ValueName::var_name)
102 .collect::<Vec<_>>();
103
104 let assignment = match &binding.operator {
107 Operator::Alias => match binding.ty {
108 crate::types::Type::Memory { .. } => {
109 vec![format!("`define {} {}", name, ops[0])]
110 }
111 _ => vec![],
112 },
113 _ => vec![],
114 };
115
116 code! {
117 [0] &forward_declaration;
118 [0] &backward_declaration;
119 [0] &assignment;
120 }
121 }
122 Statement::Register(reg) => {
123 if reg.ty.backward_size() != BigUint::zero() {
124 panic!("Attempting to put value with a backward_size != 0 in a register")
125 }
126 if reg.ty.size() != BigUint::zero() {
127 add_to_name_map(name_map, ®.name, ®.ty);
128 let name = reg.name.var_name();
129 let declaration = verilog::reg(&name, ®.ty.size());
130 code! {
131 [0] source_attribute(®.loc, code);
132 [0] &declaration;
133 }
134 } else {
135 code! {}
136 }
137 }
138 Statement::Constant(_, _, _) => {
139 code! {}
141 }
142 Statement::Assert(_) => {
143 code! {}
144 }
145 Statement::Set { .. } => {
146 code! {}
147 }
148 Statement::WalTrace { .. } => {
149 panic!("Encountered a WalTrace mir node during codegen");
150 }
151 Statement::Error => {
152 println!("WARNING: Running codegen on a Statement::Error");
153 code! {
154 [0] "// Codegen ran for an Error statement"
155 }
156 }
157 }
158}
159
160fn compute_tuple_index(idx: u64, sizes: &[BigUint]) -> TupleIndex {
161 let mut start_bit = BigUint::zero();
163 for i in 0..idx {
164 start_bit += &sizes[i as usize];
165 }
166
167 let target_width = &sizes[idx as usize];
168
169 let end_bit = &start_bit + target_width;
170
171 let total_width: BigUint = sizes.iter().sum();
172
173 if target_width == &0u32.to_biguint() {
175 TupleIndex::ZeroWidth
176 } else if sizes.iter().sum::<BigUint>() == 1u32.to_biguint() {
177 TupleIndex::None
178 } else if target_width == &1u32.to_biguint() {
179 TupleIndex::Single(total_width - start_bit - 1u32.to_biguint())
180 } else {
181 TupleIndex::Range {
182 left: &total_width - start_bit - 1u32.to_biguint(),
183 right: &total_width - end_bit,
184 }
185 }
186}
187
188fn forward_expression_code(binding: &Binding, types: &TypeList, ops: &[ValueName]) -> String {
189 let self_type = &binding.ty;
190 let op_names = ops.iter().map(|op| op.var_name()).collect::<Vec<_>>();
191
192 let name = binding.name.var_name();
193
194 macro_rules! binop {
195 ($verilog:expr) => {{
196 assert!(
197 binding.operands.len() == 2,
198 "expected 2 operands to binary operator"
199 );
200 format!("{} {} {}", op_names[0], $verilog, op_names[1])
201 }};
202 }
203
204 macro_rules! signed_binop {
205 ($verilog:expr) => {{
206 assert!(
207 binding.operands.len() == 2,
208 "expected 2 operands to binary operator"
209 );
210 format!(
211 "$signed({}) {} $signed({})",
212 op_names[0], $verilog, op_names[1]
213 )
214 }};
215 }
216
217 macro_rules! unop {
218 ($verilog:expr) => {{
219 assert!(
220 binding.operands.len() == 1,
221 "expected 1 operands to binary operator"
222 );
223 format!("{}{}", $verilog, op_names[0])
224 }};
225 }
226
227 match &binding.operator {
228 Operator::Add => signed_binop!("+"),
229 Operator::UnsignedAdd => binop!("+"),
230 Operator::Sub => signed_binop!("-"),
231 Operator::UnsignedSub => binop!("-"),
232 Operator::Mul => signed_binop!("*"),
233 Operator::UnsignedMul => binop!("*"),
234 Operator::Div => signed_binop!("/"),
235 Operator::UnsignedDiv => binop!("/"),
236 Operator::Mod => signed_binop!("%"),
237 Operator::UnsignedMod => binop!("%"),
238 Operator::Eq => binop!("=="),
239 Operator::NotEq => binop!("!="),
240 Operator::Gt => signed_binop!(">"),
241 Operator::UnsignedGt => binop!(">"),
242 Operator::Lt => signed_binop!("<"),
243 Operator::UnsignedLt => binop!("<"),
244 Operator::Ge => signed_binop!(">="),
245 Operator::UnsignedGe => binop!(">="),
246 Operator::Le => signed_binop!("<="),
247 Operator::UnsignedLe => binop!("<="),
248 Operator::LeftShift => binop!("<<"),
249 Operator::RightShift => binop!(">>"),
250 Operator::ArithmeticRightShift => signed_binop!(">>>"),
251 Operator::LogicalAnd => binop!("&&"),
252 Operator::LogicalOr => binop!("||"),
253 Operator::LogicalXor => binop!("^"),
254 Operator::LogicalNot => {
255 assert!(
256 op_names.len() == 1,
257 "Expected exactly 1 operand to not operator"
258 );
259 format!("!{}", op_names[0])
260 }
261 Operator::BitwiseNot => {
262 assert!(
263 op_names.len() == 1,
264 "Expected exactly 1 operand to bitwise not operator"
265 );
266 format!("~{}", op_names[0])
267 }
268 Operator::BitwiseAnd => binop!("&"),
269 Operator::BitwiseOr => binop!("|"),
270 Operator::BitwiseXor => binop!("^"),
271 Operator::USub => unop!("-"),
272 Operator::Not => unop!("!"),
273 Operator::ReduceAnd => unop!("&"),
274 Operator::ReduceOr => unop!("|"),
275 Operator::ReduceXor => unop!("^"),
276 Operator::DivPow2 => {
277 let dividend = &op_names[0];
285 let divisor = &op_names[1];
286 code! {
287 [0] "always_comb begin";
288 [1] format!("if ({divisor} == 0) begin");
289 [2] format!("{name} = {dividend};");
290 [1] "end";
291 [1] format!("else begin");
292 [2] format!("{name} = $signed($signed({dividend}) + $signed(1 << ({divisor} - 1))) >>> $signed({divisor});");
293 [1] "end";
294 [0] "end";
295 }.to_string()
296 }
297 Operator::Truncate => {
298 format!(
299 "{}[{}:0]",
300 op_names[0],
301 binding.ty.size() - 1u32.to_biguint()
302 )
303 }
304 Operator::Concat => {
305 format!(
306 "{{{}}}",
307 op_names.iter().map(|op| op.to_string()).join(", ")
308 )
309 }
310 Operator::SignExtend => {
311 let operand_size = types[&ops[0]].size();
312 let self_size = self_type.size();
313 if self_size > operand_size {
314 let extra_bits = self_size - &operand_size;
315 match extra_bits.to_u32() {
316 Some(0) => unreachable!(),
317 Some(1) => format!(
318 "{{{}[{}], {}}}",
319 op_names[0],
320 operand_size - 1u32.to_biguint(),
321 op_names[0]
322 ),
323 _ => format!(
324 "#[#[ {extra_bits} #[ {op}[{last_index}] #]#], {op}#]",
325 op = op_names[0],
326 last_index = operand_size - 1u32.to_biguint(),
327 )
328 .replace("#[", "{")
332 .replace("#]", "}"),
333 }
334 } else {
335 op_names[0].to_string()
336 }
337 }
338 Operator::ZeroExtend => {
339 let operand_size = types[&ops[0]].size();
340 let self_size = self_type.size();
341 if self_size > operand_size {
342 let extra_bits = self_size - operand_size;
343 format!("{{{}'b0, {}}}", extra_bits, op_names[0])
344 } else {
345 op_names[0].clone()
346 }
347 }
348 Operator::Match => {
349 assert!(
350 op_names.len() % 2 == 0,
351 "Match statements must have an even number of operands"
352 );
353
354 let num_branches = op_names.len() / 2;
355
356 let mut conditions = vec![];
357 let mut cases = vec![];
358 for i in 0..num_branches {
359 let cond = &op_names[i * 2];
360 let result = &op_names[i * 2 + 1];
361
362 conditions.push(cond.clone());
363
364 let zeros = (0..i).map(|_| '0').collect::<String>();
365 let unknowns = (0..(num_branches - i - 1)).map(|_| '?').collect::<String>();
366 cases.push(format!(
367 "{}'b{}1{}: {} = {};",
368 num_branches, zeros, unknowns, name, result
369 ))
370 }
371
372 let fallback = format!("{}'dx", self_type.size());
373
374 code! (
375 [0] "always_comb begin";
376 [1] format!("priority casez ({{{}}})", conditions.join(", "));
377 [2] cases;
378 [2] format!("{num_branches}'b?: {name} = {fallback};");
379 [1] "endcase";
380 [0] "end";
381 )
382 .to_string()
383 }
384 Operator::Select => {
385 assert!(
386 binding.operands.len() == 3,
387 "expected 3 operands to Select operator"
388 );
389 format!("{} ? {} : {}", op_names[0], op_names[1], op_names[2])
390 }
391 Operator::IndexTuple(idx) => {
392 let inner_types = match &types[&ops[0]] {
393 Type::Tuple(fields) => fields.clone(),
394 Type::Struct(fields) => fields.iter().map(|(_name, ty)| ty.clone()).collect(),
395 Type::Array { inner, length } => {
396 vec![(**inner).clone(); length.to_usize().unwrap()]
397 }
398 _ => panic!("Tuple index with non-tuple input"),
399 };
400 let sizes = inner_types.iter().map(|t| t.size()).collect::<Vec<_>>();
401 let idx = compute_tuple_index(*idx, &sizes);
402 format!("{}{}", op_names[0], idx.verilog_code())
403 }
404 Operator::ConstructArray { .. } => {
405 format!(
408 "{{{}}}",
409 op_names
410 .iter()
411 .cloned()
412 .rev()
413 .collect::<Vec<_>>()
414 .join(", ")
415 )
416 }
417 Operator::IndexArray => {
418 let Type::Array { length, .. } = &types[&ops[0]] else {
419 panic!("Array index with non-array input");
420 };
421 let member_size = self_type.size();
422 if length != &BigUint::one() {
423 if member_size == 1u32.to_biguint() {
424 format!("{}[{}]", op_names[0], op_names[1])
425 } else {
426 let end_index = format!("{} * {}", op_names[1], member_size);
427 let offset = member_size;
428
429 format!("{}[{}+:{}]", op_names[0], end_index, offset)
431 }
432 } else {
433 format!("{}", op_names[0])
434 }
435 }
436 Operator::RangeIndexArray {
437 start,
438 end_exclusive: end,
439 } => {
440 let (member_size, input_length) = match &types[&ops[0]] {
441 Type::Array { inner, length } => (inner.size(), length),
442 _ => panic!("Range index with non-array input"),
443 };
444 let num_elems = end - start;
445 if input_length == &BigUint::one() {
446 op_names[0].clone()
447 } else if member_size == BigUint::one() && num_elems == BigUint::one() {
448 format!("{}[{}]", op_names[0], start)
449 } else {
450 let end_index = (end * &member_size) - BigUint::one();
451 let offset = member_size * num_elems;
452
453 format!("{}[{}-:{}]", op_names[0], end_index, offset)
455 }
456 }
457 Operator::IndexMemory => {
458 format!("{}[{}]", op_names[0], op_names[1])
459 }
460 Operator::RangeIndexBits {
461 start,
462 end_exclusive,
463 } => {
464 if end_exclusive - start == 1u32.to_biguint() {
465 format!("{}[{start}]", op_names[0])
466 } else {
467 format!("{}[{end_exclusive}:{start}]", op_names[0])
468 }
469 }
470 Operator::DeclClockedMemory { initial } => {
471 let (addr_w, inner_w, write_ports) = match &types[&ops[1]] {
472 Type::Array { inner, length } => match &**inner {
473 Type::Tuple(fields) => match &fields[..] {
474 [_, Type::UInt(addr_w), inner] => Some((addr_w, inner.size(), length)),
475 _ => None,
476 },
477 _ => None,
478 },
479 _ => None,
480 }
481 .expect("the write ports have an incorrect type");
482
483 let full_port_width = 1u32.to_biguint() + addr_w + &inner_w;
484
485 let initial_block = if let Some(vals) = initial {
486 let assignments = vals
487 .iter()
488 .enumerate()
489 .map(|(i, v)| {
490 let val = eval_statements(v).as_string();
491
492 format!("{}[{i}] = 'b{val};", name)
493 })
494 .collect::<Vec<_>>();
495 code! {
496 [0] "initial begin";
497 [1] assignments;
498 [0] "end";
499 }
500 } else {
501 code! {}
502 };
503
504 let update_blocks = (0..write_ports.to_usize().expect("Too many write ports"))
505 .map(|port| {
506 let we_index =
507 &full_port_width * (port + 1u32.to_biguint()) - 1u32.to_biguint();
508
509 let addr_start = &full_port_width * port + &inner_w;
510 let addr = if *addr_w == 1u32.to_biguint() {
511 format!("{}[{}]", op_names[1], addr_start)
512 } else {
513 format!(
514 "{}[{}:{}]",
515 op_names[1],
516 &addr_start + addr_w - 1u32.to_biguint(),
517 addr_start
518 )
519 };
520
521 let write_value_start = port * &full_port_width;
522 let (write_index, write_value) = if inner_w == 1u32.to_biguint() {
523 (
524 format!("{}[{addr}]", name),
525 format!("{}[{}]", op_names[1], &write_value_start),
526 )
527 } else {
528 (
529 format!("{}[{addr}]", name),
530 format!(
531 "{}[{end}:{write_value_start}]",
532 op_names[1],
533 end = &write_value_start + &inner_w - 1u32.to_biguint()
534 ),
535 )
536 };
537 let we_signal = format!("{}[{we_index}]", op_names[1]);
538
539 code! {
540 [0] format!("if ({we_signal}) begin");
541 [1] format!("{write_index} <= {write_value};");
542 [0] "end"
543 }
544 .to_string()
545 })
546 .join("\n");
547
548 code! {
549 [0] initial_block;
550 [0] format!("always @(posedge {clk}) begin", clk = op_names[0]);
551 [1] update_blocks;
552 [0] "end";
553 }
554 .to_string()
555 }
556 Operator::ConstructEnum { variant } => {
557 let Type::Enum(options) = &binding.ty else {
558 panic!("Attempted enum construction of non-enum");
559 };
560
561 let tag_size = enum_util::tag_size(options.len());
562
563 let members = &options[*variant];
564
565 let included_members = members
566 .iter()
567 .zip(op_names)
568 .filter_map(|(ty, name)| {
569 if ty.size() != BigUint::ZERO {
570 Some(name)
571 } else {
572 None
573 }
574 })
575 .collect::<Vec<_>>();
576
577 let variant_member_size = members.iter().map(|t| t.size()).sum::<BigUint>();
580
581 let padding_size = binding.ty.size() - tag_size - variant_member_size;
582
583 let padding_text = if padding_size != BigUint::zero() {
584 format!(", {}'bX", padding_size)
585 } else {
586 String::new()
587 };
588
589 let ops_text = if included_members.is_empty() {
590 String::new()
591 } else {
592 format!(
593 "{}{}",
594 if tag_size != 0 { ", " } else { "" },
595 included_members.join(", ")
596 )
597 };
598
599 let tag = if tag_size != 0 {
600 format!("{tag_size}'d{variant}")
601 } else {
602 "".to_string()
603 };
604
605 format!("{{{tag}{ops_text}{padding_text}}}")
606 }
607 Operator::IsEnumVariant { variant } => {
608 let enum_type = &types[&ops[0]];
609
610 if enum_type.size() == BigUint::ZERO {
612 "1".to_string()
613 } else {
614 let tag_size = enum_util::tag_size(enum_type.assume_enum().len());
615 let total_size = enum_type.size();
616
617 let tag_end = &total_size - 1u32.to_biguint();
618 let tag_start = &total_size - tag_size as u64;
619
620 if tag_size == 0 {
621 "1".to_string()
622 } else if total_size == 1u32.to_biguint() {
623 format!("{} == 1'd{}", op_names[0], variant)
624 } else if tag_end == tag_start {
625 format!("{}[{}] == {}'d{}", op_names[0], tag_end, tag_size, variant)
626 } else {
627 format!(
628 "{}[{}:{}] == {}'d{}",
629 op_names[0], tag_end, tag_start, tag_size, variant
630 )
631 }
632 }
633 }
634 Operator::EnumMember {
635 variant,
636 member_index,
637 } => {
638 let enum_type = &types[&ops[0]];
639
640 let variant_list = enum_type.assume_enum();
641 let tag_size = enum_util::tag_size(variant_list.len());
642 let full_size = enum_type.size();
643
644 let member_start = (tag_size as u64)
645 + variant_list[*variant][0..*member_index]
646 .iter()
647 .map(|t| t.size())
648 .sum::<BigUint>();
649
650 let member_end = &member_start + variant_list[*variant][*member_index].size();
651
652 let upper_idx = &full_size - &member_start - 1u32.to_biguint();
653 let lower_idx = full_size - &member_end;
654 if upper_idx == lower_idx && tag_size == 0 {
655 op_names[0].clone()
656 } else {
657 format!("{}[{}:{}]", op_names[0], upper_idx, lower_idx)
658 }
659 }
660 Operator::ReadPort => ops[0].backward_var_name(),
661 Operator::ReadWriteInOut => {
662 String::new()
664 }
665 Operator::FlipPort => {
666 String::new()
668 }
669 Operator::ReadMutWires => {
670 String::new()
672 }
673 Operator::ConstructTuple => {
674 let mut members = ops
675 .iter()
676 .filter(|op| types[op].size() != BigUint::zero())
677 .map(|op| op.var_name());
678 format!("{{{}}}", members.join(", "))
681 }
682 Operator::Instance { .. } => {
683 String::new()
685 }
686 Operator::Alias => {
687 String::new()
689 }
690 Operator::Nop => String::new(),
691 }
692}
693
694fn backward_expression_code(binding: &Binding, types: &TypeList, ops: &[ValueName]) -> String {
695 let self_type = &binding.ty;
696 let op_names = ops
697 .iter()
698 .map(ValueName::backward_var_name)
699 .collect::<Vec<_>>();
700 let fwd_op_names = ops.iter().map(ValueName::var_name).collect::<Vec<_>>();
701 match &binding.operator {
702 Operator::Add
703 | Operator::UnsignedAdd
704 | Operator::Sub
705 | Operator::UnsignedSub
706 | Operator::Mul
707 | Operator::UnsignedMul
708 | Operator::Div
709 | Operator::UnsignedDiv
710 | Operator::Mod
711 | Operator::UnsignedMod
712 | Operator::Eq
713 | Operator::NotEq
714 | Operator::Gt
715 | Operator::UnsignedGt
716 | Operator::Lt
717 | Operator::UnsignedLt
718 | Operator::Ge
719 | Operator::UnsignedGe
720 | Operator::Le
721 | Operator::UnsignedLe
722 | Operator::LeftShift
723 | Operator::RightShift
724 | Operator::ArithmeticRightShift
725 | Operator::LogicalAnd
726 | Operator::LogicalOr
727 | Operator::LogicalXor
728 | Operator::LogicalNot
729 | Operator::BitwiseAnd
730 | Operator::BitwiseOr
731 | Operator::BitwiseXor
732 | Operator::USub
733 | Operator::Not
734 | Operator::BitwiseNot
735 | Operator::DivPow2
736 | Operator::ReduceAnd
737 | Operator::ReduceOr
738 | Operator::ReduceXor
739 | Operator::SignExtend
740 | Operator::ZeroExtend
741 | Operator::Concat
742 | Operator::DeclClockedMemory { .. }
743 | Operator::ConstructEnum { .. }
744 | Operator::IsEnumVariant { .. }
745 | Operator::EnumMember { .. }
746 | Operator::RangeIndexBits { .. }
747 | Operator::IndexMemory
748 | Operator::Select
749 | Operator::Match
750 | Operator::ReadPort
751 | Operator::Truncate => panic!(
752 "{} cannot be used on types with backward size",
753 binding.operator
754 ),
755 Operator::ConstructArray => {
756 format!(
759 "{{{}}}",
760 op_names
761 .iter()
762 .cloned()
763 .rev()
764 .collect::<Vec<_>>()
765 .join(", ")
766 )
767 }
768 Operator::IndexArray => {
769 let Type::Array { length, .. } = &types[&ops[0]] else {
770 panic!("Attempt to use array indexing on non-array type");
771 };
772 let member_size = self_type.backward_size();
773 if length != &BigUint::one() {
774 if member_size == 1u32.to_biguint() {
775 format!("{}[{}]", op_names[0], fwd_op_names[1])
776 } else {
777 let end_index = format!("{} * {}", fwd_op_names[1], member_size);
778 let offset = member_size;
779
780 format!("{}[{}+:{}]", op_names[0], end_index, offset)
782 }
783 } else {
784 op_names[0].clone()
785 }
786 }
787 Operator::RangeIndexArray {
788 start,
789 end_exclusive: end,
790 } => {
791 let (member_size, input_length) = match &types[&ops[0]] {
792 Type::Array { inner, length } => (inner.size(), length),
793 _ => panic!("Range index with non-array input"),
794 };
795 let elems = end - start;
796 if input_length == &BigUint::one() {
797 op_names[0].clone()
798 } else if member_size == BigUint::one() && elems == BigUint::one() {
799 format!("{}[{}]", op_names[0], start)
800 } else {
801 let end_index = format!("{} * {}", start, member_size);
802 let offset = member_size * elems;
803
804 format!("{}[{}+:{}]", op_names[0], end_index, offset)
806 }
807 }
808 Operator::ConstructTuple => {
809 let mut members = ops
810 .iter()
811 .filter(|op| types[op].backward_size() != BigUint::zero())
812 .map(|op| op.backward_var_name());
813 format!("{{{}}}", members.join(", "))
814 }
815 Operator::IndexTuple(index) => {
816 let inner_types = match &types[&ops[0]] {
817 Type::Tuple(fields) => fields.clone(),
818 Type::Struct(fields) => fields.iter().map(|(_name, ty)| ty.clone()).collect(),
819 Type::Array { inner, length } => {
820 vec![(**inner).clone(); length.to_usize().unwrap()]
821 }
822 _ => panic!("Tuple index with non-tuple input"),
823 };
824
825 let sizes = inner_types
829 .iter()
830 .map(|t| t.backward_size())
831 .collect::<Vec<_>>();
832 let index = compute_tuple_index(*index, &sizes);
833 format!("{}{}", op_names[0], index.verilog_code())
834 }
835 Operator::FlipPort => {
836 String::new()
838 }
839 Operator::ReadMutWires => {
840 String::new()
842 }
843 Operator::ReadWriteInOut => {
844 String::new()
846 }
847 Operator::Instance { .. } => String::new(),
848 Operator::Alias => {
849 String::new()
851 }
852 Operator::Nop => String::new(),
853 }
854}
855
856fn statement_code(statement: &Statement, ctx: &mut Context) -> Code {
857 match statement {
858 Statement::Binding(binding) => {
859 let name = binding.name.var_name();
860 let back_name = binding.name.backward_var_name();
861
862 let ops = &binding
863 .operands
864 .iter()
865 .map(ValueName::var_name)
866 .collect::<Vec<_>>();
867
868 let back_ops = &binding
869 .operands
870 .iter()
871 .map(ValueName::backward_var_name)
872 .collect::<Vec<_>>();
873
874 let forward_expression = if binding.ty.size() != BigUint::zero() {
875 Some(forward_expression_code(
876 binding,
877 ctx.types,
878 &binding.operands,
879 ))
880 } else {
881 None
882 };
883 let backward_expression = if binding.ty.backward_size() != BigUint::zero() {
884 Some(backward_expression_code(
885 binding,
886 ctx.types,
887 &binding.operands,
888 ))
889 } else {
890 None
891 };
892
893 let assignment = match &binding.operator {
895 Operator::Instance{name: module_name, params, argument_names, loc} => {
896 let param_string = if params.is_empty() {
897 "".into()
898 } else {
899 let param_strings = params.iter().map(|(name, value)| format!(".{}({})", name, value)).collect::<Vec<_>>();
900 format!("#({})", param_strings.join(", "))
901 };
902 let mut args = binding
904 .operands
905 .iter()
906 .zip(argument_names)
907 .flat_map(|(port, ParamName{name, no_mangle})| {
908 let ty = &ctx.types[port];
909
910 let mut result = vec![];
913 if ty.size() != BigUint::zero() {
914 result.push(format!(
915 ".{}({})",
916 mangle_input(no_mangle, name),
917 port.var_name()
918 ))
919 }
920 if ty.backward_size() != BigUint::zero() {
921 result.push(format!(
922 ".{}({})",
923 mangle_output(no_mangle, name),
924 port.backward_var_name()
925 ))
926 }
927 result
928 }).collect::<Vec<_>>();
929
930 if binding.ty.size() != BigUint::zero() {
931 args.push(format!(".output__({name})"));
932 }
933 if binding.ty.backward_size() != BigUint::zero() {
934 args.push(format!(".input__({back_name})"));
935 }
936
937 let instance_name = module_name.instance_name(
938 ctx.unit_nameid.clone(),
939 ctx.instance_map,
940 ctx.instance_names
941 );
942
943 code!{
944 [0] source_attribute(loc, ctx.source_code);
945 [0] format!(
946 "{}{} \\{} ({});",
947 &module_name.as_verilog(),
948 if param_string.is_empty() { "".into() } else { format!("{}", param_string)},
949 instance_name,
950 args.join(", ")
951 )
952 }.to_string()
953 }
954 Operator::Alias => match binding.ty {
955 crate::types::Type::Memory { .. } => {
956 "".to_string()
958 }
959 _ => code! {
960 [0] forward_expression.map(|_| format!("assign {} = {};", name, ops[0]));
961 [0] backward_expression.map(|_| format!("assign {} = {};", back_ops[0], back_name));
962 }.to_string()
963 },
964 Operator::Match => forward_expression.unwrap(),
965 Operator::DivPow2 => forward_expression.unwrap(),
966 Operator::Nop => String::new(),
967 Operator::FlipPort => {
968 let has_fwd = binding.ty.size() != BigUint::zero();
969 let has_back = binding.ty.backward_size() != BigUint::zero();
970 code! {
973 [0] has_fwd.then(|| format!("assign {} = {};", name, back_ops[0]));
974 [0] has_back.then(|| format!("assign {} = {};", ops[0], back_name));
975 }
976 .to_string()
977 }
978 Operator::ReadMutWires => {
979 code! {
982 [0] format!("assign {} = {};", name, back_ops[0]);
983 }
984 .to_string()
985 }
986 Operator::DeclClockedMemory { .. } => forward_expression.unwrap(),
987 Operator::ReadWriteInOut => {
988 let total_size_minus_one = binding.ty.size() - BigUint::one();
989 let payload_size = binding.ty.size() - BigUint::one();
990 let payload_size_minus_one = &payload_size - BigUint::one();
991 code! {
992 [0] format!("assign {} = {}[{}] ? {}[{}:0] : {}'bZ;",
993 ops[0],
994 back_name, total_size_minus_one,
995 back_name, payload_size_minus_one,
996 payload_size);
997 [0] format!("assign {} = {}[{}] ? {{ 1'b0, {}'bX }} : {{ 1'b1, {} }};",
998 name,
999 back_name, total_size_minus_one,
1000 payload_size,
1001 ops[0])
1002 }
1003 .to_string()
1004 }
1005 _ => code! {
1006 [0] forward_expression.map(|f| format!("assign {} = {};", name, f));
1007 [0] backward_expression.map(|b| format!("assign {} = {};", b, back_name));
1008 }
1009 .to_string(),
1010 };
1011
1012 code! {
1013 [0] &assignment
1014 }
1015 }
1016 Statement::Register(reg) => {
1017 let name = reg.name.var_name();
1018 let main_body = if let Some((rst_trig, rst_val)) = ®.reset {
1019 code! {
1020 [0] &format!("always @(posedge {}) begin", reg.clock.var_name());
1021 [1] &format!("if ({}) begin", rst_trig.var_name());
1022 [2] &format!("{} <= {};", name, rst_val.var_name());
1023 [1] &"end";
1024 [1] &"else begin";
1025 [2] &format!("{} <= {};", name, reg.value.var_name());
1026 [1] &"end";
1027 [0] &"end"
1028 }
1029 } else {
1030 code! {
1031 [0] &format!("always @(posedge {}) begin", reg.clock.var_name());
1032 [1] &format!("{} <= {};", name, reg.value.var_name());
1033 [0] &"end"
1034 }
1035 };
1036
1037 let initial_block = if let Some(initial) = reg.initial.as_ref() {
1038 code! {
1039 [0] "initial begin";
1040 [1] format!("{} = 'b{};", name, eval_statements(initial).as_string());
1041 [0] "end";
1042 }
1043 } else {
1044 code![]
1045 };
1046
1047 code! {
1048 [0] initial_block;
1049 [0] main_body
1050 }
1051 }
1052 Statement::Constant(id, t, value) => {
1053 let name = ValueName::Expr(*id).var_name();
1054
1055 let expression = match value {
1056 ConstantValue::Int(val) => {
1057 let size = match t {
1058 crate::types::Type::Int(size) | crate::types::Type::UInt(size) => size,
1059 _ => panic!("Const integer that is not const"),
1060 };
1061
1062 let val_abs = val.abs();
1063 let sign = if val < &BigInt::zero() { "-" } else { "" };
1064
1065 let size_spec = if *size >= 32u32.to_biguint() {
1067 format!("{size}'d")
1068 } else {
1069 String::new()
1070 };
1071 format!("{sign}{size_spec}{val_abs}")
1072 }
1073 ConstantValue::Bool(val) => format!("{}", if *val { 1 } else { 0 }),
1074 ConstantValue::String(val) => format!("{:?}", val),
1075 ConstantValue::HighImp => "'bz".to_string(),
1076 };
1077
1078 if t.size() != BigUint::ZERO {
1079 let size = localparam_size_spec(&t.size());
1080
1081 let assignment = format!("localparam{size} {name} = {expression};");
1082
1083 code! {
1084 [0] &assignment
1085 }
1086 } else {
1087 code! {}
1088 }
1089 }
1090 Statement::Assert(val) => {
1091 let mut msg_buf = termcolor::Buffer::ansi();
1095 let mut diag_handler = DiagHandler::new(Box::new(CodespanEmitter));
1096
1097 AssertedExpression(val.clone()).report(
1098 &mut msg_buf,
1099 ctx.source_code.as_ref().unwrap(),
1100 &mut diag_handler,
1101 );
1102
1103 let msg = String::from_utf8(msg_buf.as_slice().into())
1104 .map_err(|e| {
1105 println!("Internal error {e}: Failed to generate assert message, invalid utf-8 returned by codespan");
1106 })
1107 .unwrap_or_else(|_| String::new())
1108 .lines()
1109 .map(|line| {
1110 format!(r#"$display("{line}");"#)
1111 })
1112 .join("\n");
1113
1114 let val_var = val.var_name();
1115 code! {
1116 [0] format!("`ifndef SYNTHESIS");
1117 [0] format!("always @({val_var}) begin");
1118 [1] "#0";
1125 [1] format!("assert ({val_var})");
1126 [1] "else begin";
1127 [2] msg;
1128 [2] r#"$error("Assertion failed");"#;
1129 [2] r#"$fatal(1);"#;
1130 [1] "end";
1131 [0] "end";
1132 [0] format!("`endif")
1133 }
1134 }
1135 Statement::Set { target, value } => {
1136 let mut assignments = Vec::new();
1137 if ctx.types[&value.inner].size() != BigUint::ZERO {
1138 assignments.push(format!(
1139 "assign {} = {};",
1140 target.backward_var_name(),
1141 value.var_name(),
1142 ));
1143 }
1144 if ctx.types[&value.inner].backward_size() != BigUint::ZERO {
1145 assignments.push(format!(
1146 "assign {} = {};",
1147 value.backward_var_name(),
1148 target.var_name(),
1149 ))
1150 };
1151
1152 code! {
1153 [0] assignments;
1154 }
1155 }
1156 Statement::WalTrace { .. } => {
1157 panic!("Encountered a WalTrace mir node during codegen");
1158 }
1159 Statement::Error => {
1160 code! {
1161 [0] "// Codegen ran for an error node"
1162 }
1163 }
1164 }
1165}
1166
1167#[cfg(test)]
1168fn statement_code_and_declaration(
1169 statement: &Statement,
1170 types: &TypeList,
1171 source_code: &CodeBundle,
1172) -> Code {
1173 use spade_common::name::Path;
1174
1175 let mut ctx = Context {
1176 types,
1177 source_code: &Some(source_code.clone()),
1178 instance_names: &mut InstanceNameTracker::new(),
1179 instance_map: &mut InstanceMap::new(),
1180 unit_nameid: &NameID(0, Path::from_strs(&["dummy"])),
1181 };
1182 code! {
1183 [0] statement_declaration(statement, &Some(source_code.clone()), &mut VerilogNameMap::new());
1184 [0] statement_code(statement, &mut ctx);
1185 }
1186}
1187
1188#[derive(Clone)]
1190pub struct Codegenable(pub Entity);
1191
1192pub fn prepare_codegen(mut entity: Entity, expr_idtracker: &ExprIdTracker) -> Codegenable {
1193 flatten_aliases(&mut entity);
1194 make_names_predictable(&mut entity);
1195 insert_wal_signals(&mut entity, expr_idtracker, &mut None);
1196
1197 Codegenable(entity)
1198}
1199
1200fn codegen_verilog_attr_groups(groups: &[Vec<(String, Option<String>)>]) -> Code {
1201 let lines = groups
1202 .iter()
1203 .map(|attrs| {
1204 let contents = attrs
1205 .iter()
1206 .map(|(key, value)| match value {
1207 Some(v) => format!(r#"{key} = "{v}""#),
1208 None => key.clone(),
1209 })
1210 .join(", ");
1211
1212 format!("(* {contents} *)")
1213 })
1214 .join("\n");
1215
1216 code! { [0] lines; }
1217}
1218
1219pub fn entity_code(
1227 entity: &Codegenable,
1228 instance_map: &mut InstanceMap,
1229 source_code: &Option<CodeBundle>,
1230) -> (Code, VerilogNameMap) {
1231 let mut name_map = VerilogNameMap::new();
1232
1233 let Codegenable(entity) = entity;
1234
1235 let verilog_attr_groups = codegen_verilog_attr_groups(&entity.verilog_attr_groups);
1236
1237 let types = &TypeList::from_entity(entity);
1238
1239 let entity_name = entity.name.as_verilog();
1240
1241 let inputs = &entity.inputs;
1242
1243 let inputs = inputs.iter().map(
1244 |MirInput {
1245 name,
1246 val_name,
1247 ty,
1248 no_mangle,
1249 }| {
1250 if ty.size() != BigUint::zero() {
1251 name_map.insert(name, val_name.verilog_name_source_fwd());
1252 }
1253 if ty.backward_size() != BigUint::zero() {
1254 name_map.insert(name, val_name.verilog_name_source_back());
1255 }
1256
1257 let size = ty.size();
1258 let (input_head, input_code) = if size != BigUint::zero() {
1259 let name = mangle_input(no_mangle, name);
1260
1261 name_map.insert(&name, val_name.verilog_name_source_fwd());
1262
1263 let input_or_inout = match ty {
1264 Type::InOut(_) => "inout",
1265 _ => "input",
1266 };
1267
1268 let alias_assignment = if no_mangle.is_none() {
1273 code! {
1274 [0] &logic(&val_name.var_name(), &size);
1275 [0] &assign(&val_name.var_name(), &name)
1276 }
1277 } else {
1278 code! {}
1279 };
1280 (
1281 format!("{input_or_inout}{} {}", size_spec(&size), name),
1282 alias_assignment,
1283 )
1284 } else {
1285 (String::new(), code! {})
1286 };
1287
1288 let backward_size = ty.backward_size();
1289 let (output_head, output_code) = if backward_size != BigUint::zero() {
1290 let name = mangle_output(no_mangle, name);
1291 name_map.insert(&name, val_name.verilog_name_source_back());
1292 (
1293 format!("output{} {}", size_spec(&backward_size), name),
1294 code! {
1295 [0] &logic(&val_name.backward_var_name(), &backward_size);
1296 [0] &assign(&name, &val_name.backward_var_name())
1297 },
1298 )
1299 } else {
1300 (String::new(), code! {})
1301 };
1302
1303 let spacing = if !input_head.is_empty() && !output_head.is_empty() {
1304 ", "
1305 } else {
1306 ""
1307 };
1308 (
1309 format!("{input_head}{spacing}{output_head}"),
1310 code! {
1311 [0] input_code;
1312 [0] output_code;
1313 },
1314 )
1315 },
1316 );
1317
1318 let (inputs, input_assignments): (Vec<_>, Vec<_>) = inputs.unzip();
1319
1320 let back_port_size = entity.output_type.backward_size();
1321 let (back_port_definition, back_port_assignment) = if back_port_size != BigUint::zero() {
1322 let def = code! {
1323 [0] format!(
1324 "input{} input__",
1325 size_spec(&entity.output_type.backward_size())
1326 );
1327 };
1328 let assignment = code! {
1329 [0] assign(&entity.output.backward_var_name(), "input__")
1330 };
1331 (Some(def), Some(assignment))
1332 } else {
1333 (None, None)
1334 };
1335
1336 let output_size = entity.output_type.size();
1337 let (output_definition, output_assignment) = if output_size != BigUint::zero() {
1338 let def = code! {
1339 [0] format!("output{} output__", size_spec(&output_size))
1340 };
1341 let assignment = code! {[0] assign("output__", &entity.output.var_name())};
1342
1343 name_map.insert("output__", entity.output.verilog_name_source_fwd());
1344
1345 (Some(def), Some(assignment))
1346 } else {
1347 (None, None)
1348 };
1349
1350 let mut ctx = Context {
1351 types,
1352 source_code,
1353 instance_names: &mut InstanceNameTracker::new(),
1354 instance_map,
1355 unit_nameid: &entity.name.source,
1356 };
1357
1358 let mut body = Code::new();
1359
1360 for stmt in &entity.statements {
1361 body.join(&statement_declaration(stmt, source_code, &mut name_map))
1362 }
1363 for stmt in &entity.statements {
1364 body.join(&statement_code(stmt, &mut ctx))
1365 }
1366
1367 let port_definitions = inputs
1369 .into_iter()
1370 .map(|s| code! { [0] s})
1371 .chain(output_definition)
1372 .chain(back_port_definition)
1373 .map(|code| code.to_string())
1374 .filter(|s| !s.is_empty())
1375 .join(",\n");
1376
1377 let code = code! {
1378 [0] verilog_attr_groups;
1379 [0] &format!("module {} (", entity_name);
1380 [2] &port_definitions;
1381 [1] &");";
1382 [1] "`ifdef COCOTB_SIM";
1383 [1] "string __top_module;";
1384 [1] "string __vcd_file;";
1385 [1] "initial begin";
1386 [2] format!(r#"if ($value$plusargs("TOP_MODULE=%s", __top_module) && __top_module == "{top_name}" && $value$plusargs("VCD_FILENAME=%s", __vcd_file)) begin"#, top_name = entity.name.without_escapes());
1387 [3] format!("$dumpfile (__vcd_file);");
1388 [3] format!("$dumpvars (0, {entity_name});");
1389 [2] "end";
1390 [1] "end";
1391 [1] "`endif";
1392 [1] &input_assignments;
1393 [1] &body;
1394 [1] &output_assignment;
1395 [1] &back_port_assignment;
1396 [0] &"endmodule"
1397 };
1398 (code, name_map)
1399}
1400
1401#[macro_export]
1402macro_rules! assert_same_code {
1403 ($got:expr, $expected:expr) => {{
1404 let got = $got;
1405 let expected = $expected;
1406 if got != expected {
1407 println!("{}:\n{}", "got".red(), got);
1408 println!("{}", "==============================================".red());
1409 println!("{}:\n{}", "expected".green(), expected);
1410 println!(
1411 "{}",
1412 "==============================================".green()
1413 );
1414 println!("{}", prettydiff::diff_chars(got, expected));
1415 println!(
1416 "{}",
1417 "==============================================".yellow()
1418 );
1419 panic!("Code mismatch")
1420 }
1421 }};
1422}
1423
1424#[cfg(test)]
1425mod tests {
1426 use super::*;
1427 use colored::Colorize;
1428 use spade_common::id_tracker::ExprID;
1429 use spade_common::location_info::WithLocation;
1430 use spade_common::name::Path;
1431
1432 use crate as spade_mir;
1433 use crate::{entity, statement, types::Type};
1434
1435 use indoc::indoc;
1436
1437 #[test]
1438 fn size_1_wires_have_no_size_spec() {
1439 let binding = statement!(e(0); Type::Bool; Add; e(1), e(2));
1440
1441 let expected = indoc!(
1442 r#"
1443 logic _e_0;
1444 assign _e_0 = $signed(_e_1) + $signed(_e_2);"#
1445 );
1446
1447 assert_same_code!(
1448 &statement_code_and_declaration(
1449 &binding,
1450 &TypeList::empty(),
1451 &CodeBundle::new("".to_string())
1452 )
1453 .to_string(),
1454 expected
1455 );
1456 }
1457
1458 #[test]
1459 fn binding_code_works() {
1460 let binding = statement!(e(0); Type::int(5); Add; e(1), e(2));
1461
1462 let expected = indoc!(
1463 r#"
1464 logic[4:0] _e_0;
1465 assign _e_0 = $signed(_e_1) + $signed(_e_2);"#
1466 );
1467
1468 assert_same_code!(
1469 &statement_code_and_declaration(
1470 &binding,
1471 &TypeList::empty(),
1472 &CodeBundle::new("".to_string())
1473 )
1474 .to_string(),
1475 expected
1476 );
1477 }
1478
1479 #[test]
1480 fn registers_without_reset_work() {
1481 let reg = statement!(reg n(0, "r"); Type::int(7); clock (e(0)); e(1));
1482
1483 let expected = indoc!(
1484 r#"
1485 reg[6:0] \r ;
1486 always @(posedge _e_0) begin
1487 \r <= _e_1;
1488 end"#
1489 );
1490
1491 assert_same_code!(
1492 &statement_code_and_declaration(
1493 ®,
1494 &TypeList::empty(),
1495 &CodeBundle::new("".to_string())
1496 )
1497 .to_string(),
1498 expected
1499 );
1500 }
1501
1502 #[test]
1503 fn registers_with_reset_work() {
1504 let reg = statement!(reg n(0, "r"); Type::int(7); clock (e(0)); reset (e(2), e(3)); e(1));
1505
1506 let expected = indoc!(
1507 r#"
1508 reg[6:0] \r ;
1509 always @(posedge _e_0) begin
1510 if (_e_2) begin
1511 \r <= _e_3;
1512 end
1513 else begin
1514 \r <= _e_1;
1515 end
1516 end"#
1517 );
1518
1519 assert_same_code!(
1520 &statement_code_and_declaration(
1521 ®,
1522 &TypeList::empty(),
1523 &CodeBundle::new("".to_string())
1524 )
1525 .to_string(),
1526 expected
1527 );
1528 }
1529
1530 #[test]
1531 fn registers_with_initial_values_work() {
1532 let initial_value = vec![
1533 statement!(const 4; Type::int(7); ConstantValue::int(0b10_1100)),
1534 statement!(e(5); Type::int(7); Alias; e(4)),
1535 ];
1536 let reg =
1537 statement!(reg n(0, "r"); Type::int(7); clock (e(0)); initial (initial_value); e(1));
1538
1539 let expected = indoc!(
1540 r#"
1541 reg[6:0] \r ;
1542 initial begin
1543 \r = 'b0101100;
1544 end
1545 always @(posedge _e_0) begin
1546 \r <= _e_1;
1547 end"#
1548 );
1549
1550 assert_same_code!(
1551 &statement_code_and_declaration(
1552 ®,
1553 &TypeList::empty(),
1554 &CodeBundle::new("".to_string())
1555 )
1556 .to_string(),
1557 expected
1558 );
1559 }
1560
1561 #[test]
1562 fn entity_codegen_works() {
1563 let input = entity!(&["pong"]; ("op", n(0, "op"), Type::int(6)) -> Type::int(6); {
1564 (e(0); Type::int(6); Add; n(0, "op"), e(1))
1565 } => e(0));
1566
1567 let expected = indoc!(
1568 r#"
1569
1570 module \pong (
1571 input[5:0] op_i,
1572 output[5:0] output__
1573 );
1574 `ifdef COCOTB_SIM
1575 string __top_module;
1576 string __vcd_file;
1577 initial begin
1578 if ($value$plusargs("TOP_MODULE=%s", __top_module) && __top_module == "pong" && $value$plusargs("VCD_FILENAME=%s", __vcd_file)) begin
1579 $dumpfile (__vcd_file);
1580 $dumpvars (0, \pong );
1581 end
1582 end
1583 `endif
1584 logic[5:0] \op ;
1585 assign \op = op_i;
1586 logic[5:0] _e_0;
1587 assign _e_0 = $signed(\op ) + $signed(_e_1);
1588 assign output__ = _e_0;
1589 endmodule"#
1590 );
1591
1592 assert_same_code!(
1593 &entity_code(
1594 &prepare_codegen(input.clone(), &mut ExprIdTracker::new()),
1595 &mut InstanceMap::new(),
1596 &None
1597 )
1598 .0
1599 .to_string(),
1600 expected
1601 );
1602 }
1603
1604 #[test]
1605 fn verilog_attr_groups_work_on_entity_declarations() {
1606 let mut input = entity!(&["pong"]; ("op", n(0, "op"), Type::int(6)) -> Type::int(6); {
1607 (e(0); Type::int(6); Add; n(0, "op"), e(1))
1608 } => e(0));
1609
1610 input.verilog_attr_groups = vec![
1611 vec![("alone".into(), None)],
1612 vec![
1613 ("standalone".into(), None),
1614 ("key".into(), Some("value".into())),
1615 ],
1616 ];
1617
1618 let expected = indoc!(
1619 r#"
1620 (* alone *)
1621 (* standalone, key = "value" *)
1622 module \pong (
1623 input[5:0] op_i,
1624 output[5:0] output__
1625 );
1626 `ifdef COCOTB_SIM
1627 string __top_module;
1628 string __vcd_file;
1629 initial begin
1630 if ($value$plusargs("TOP_MODULE=%s", __top_module) && __top_module == "pong" && $value$plusargs("VCD_FILENAME=%s", __vcd_file)) begin
1631 $dumpfile (__vcd_file);
1632 $dumpvars (0, \pong );
1633 end
1634 end
1635 `endif
1636 logic[5:0] \op ;
1637 assign \op = op_i;
1638 logic[5:0] _e_0;
1639 assign _e_0 = $signed(\op ) + $signed(_e_1);
1640 assign output__ = _e_0;
1641 endmodule"#
1642 );
1643
1644 assert_same_code!(
1645 &entity_code(
1646 &prepare_codegen(input.clone(), &mut ExprIdTracker::new()),
1647 &mut InstanceMap::new(),
1648 &None
1649 )
1650 .0
1651 .to_string(),
1652 expected
1653 );
1654 }
1655
1656 #[test]
1657 fn no_mangle_input_does_not_clash() {
1658 let input = spade_mir::Entity {
1659 name: spade_mir::unit_name::IntoUnitName::_test_into_unit_name("test"),
1660 inputs: vec![spade_mir::MirInput {
1661 name: "a".to_string(),
1662 val_name: ValueName::_test_named(0, "a".to_string()),
1663 ty: Type::Bool,
1664 no_mangle: Some(().nowhere()),
1665 }],
1666 output: ValueName::Expr(ExprID(0)),
1667 output_type: Type::Bool,
1668 statements: vec![],
1669 verilog_attr_groups: vec![],
1670 };
1671
1672 let expected = indoc!(
1673 r#"
1674
1675 module test (
1676 input a,
1677 output output__
1678 );
1679 `ifdef COCOTB_SIM
1680 string __top_module;
1681 string __vcd_file;
1682 initial begin
1683 if ($value$plusargs("TOP_MODULE=%s", __top_module) && __top_module == "test" && $value$plusargs("VCD_FILENAME=%s", __vcd_file)) begin
1684 $dumpfile (__vcd_file);
1685 $dumpvars (0, test);
1686 end
1687 end
1688 `endif
1689 assign output__ = _e_0;
1690 endmodule"#
1691 );
1692
1693 assert_same_code!(
1694 &entity_code(
1695 &prepare_codegen(input.clone(), &mut ExprIdTracker::new()),
1696 &mut InstanceMap::new(),
1697 &None
1698 )
1699 .0
1700 .to_string(),
1701 expected
1702 );
1703 }
1704
1705 #[test]
1706 fn no_mangle_output_does_not_clash() {
1707 let input = spade_mir::Entity {
1708 name: spade_mir::unit_name::IntoUnitName::_test_into_unit_name("test"),
1709 inputs: vec![spade_mir::MirInput {
1710 name: "a".to_string(),
1711 val_name: ValueName::_test_named(0, "a".to_string()),
1712 ty: Type::Backward(Box::new(Type::Bool)),
1713 no_mangle: Some(().nowhere()),
1714 }],
1715 output: ValueName::Expr(ExprID(0)),
1716 output_type: Type::Bool,
1717 statements: vec![],
1718 verilog_attr_groups: vec![],
1719 };
1720
1721 let expected = indoc!(
1722 r#"
1723
1724 module test (
1725 output a,
1726 output output__
1727 );
1728 `ifdef COCOTB_SIM
1729 string __top_module;
1730 string __vcd_file;
1731 initial begin
1732 if ($value$plusargs("TOP_MODULE=%s", __top_module) && __top_module == "test" && $value$plusargs("VCD_FILENAME=%s", __vcd_file)) begin
1733 $dumpfile (__vcd_file);
1734 $dumpvars (0, test);
1735 end
1736 end
1737 `endif
1738 logic \a_mut ;
1739 assign a = \a_mut ;
1740 assign output__ = _e_0;
1741 endmodule"#
1742 );
1743
1744 assert_same_code!(
1745 &entity_code(
1746 &prepare_codegen(input.clone(), &mut ExprIdTracker::new()),
1747 &mut InstanceMap::new(),
1748 &None
1749 )
1750 .0
1751 .to_string(),
1752 expected
1753 );
1754 }
1755
1756 #[test]
1757 fn pure_backward_input_produces_output_port() {
1758 let ty = Type::Backward(Box::new(Type::int(3)));
1759 let input = entity!(&["test"]; ("a", n(0, "a"), ty) -> Type::int(6); {
1760 (const 0; Type::int(6); crate::ConstantValue::int(3))
1761 } => e(0));
1762
1763 let expected = indoc!(
1764 r#"
1765
1766 module \test (
1767 output[2:0] a_o,
1768 output[5:0] output__
1769 );
1770 `ifdef COCOTB_SIM
1771 string __top_module;
1772 string __vcd_file;
1773 initial begin
1774 if ($value$plusargs("TOP_MODULE=%s", __top_module) && __top_module == "test" && $value$plusargs("VCD_FILENAME=%s", __vcd_file)) begin
1775 $dumpfile (__vcd_file);
1776 $dumpvars (0, \test );
1777 end
1778 end
1779 `endif
1780 logic[2:0] \a_mut ;
1781 assign a_o = \a_mut ;
1782 localparam[5:0] _e_0 = 3;
1783 assign output__ = _e_0;
1784 endmodule"#
1785 );
1786
1787 assert_same_code!(
1788 &entity_code(
1789 &prepare_codegen(input.clone(), &mut ExprIdTracker::new()),
1790 &mut InstanceMap::new(),
1791 &None
1792 )
1793 .0
1794 .to_string(),
1795 expected
1796 );
1797 }
1798
1799 #[test]
1800 fn mixed_backward_input_works() {
1801 let ty = Type::Tuple(vec![Type::int(4), Type::Backward(Box::new(Type::int(3)))]);
1802 let input = entity!(&["test"]; ("a", n(0, "a"), ty) -> Type::int(6); {
1803 (const 0; Type::int(6); crate::ConstantValue::int(3))
1804 } => e(0));
1805
1806 let expected = indoc!(
1807 r#"
1808
1809 module \test (
1810 input[3:0] a_i, output[2:0] a_o,
1811 output[5:0] output__
1812 );
1813 `ifdef COCOTB_SIM
1814 string __top_module;
1815 string __vcd_file;
1816 initial begin
1817 if ($value$plusargs("TOP_MODULE=%s", __top_module) && __top_module == "test" && $value$plusargs("VCD_FILENAME=%s", __vcd_file)) begin
1818 $dumpfile (__vcd_file);
1819 $dumpvars (0, \test );
1820 end
1821 end
1822 `endif
1823 logic[3:0] \a ;
1824 assign \a = a_i;
1825 logic[2:0] \a_mut ;
1826 assign a_o = \a_mut ;
1827 localparam[5:0] _e_0 = 3;
1828 assign output__ = _e_0;
1829 endmodule"#
1830 );
1831
1832 assert_same_code!(
1833 &entity_code(
1834 &prepare_codegen(input.clone(), &mut ExprIdTracker::new()),
1835 &mut InstanceMap::new(),
1836 &None
1837 )
1838 .0
1839 .to_string(),
1840 expected
1841 );
1842 }
1843
1844 #[test]
1845 fn mixed_backward_output_works() {
1846 let ty = Type::Tuple(vec![Type::int(4), Type::Backward(Box::new(Type::int(3)))]);
1847 let input = entity!("test"; () -> ty; {
1848 } => e(0));
1849
1850 let expected = indoc!(
1851 r#"
1852
1853 module test (
1854 output[3:0] output__,
1855 input[2:0] input__
1856 );
1857 `ifdef COCOTB_SIM
1858 string __top_module;
1859 string __vcd_file;
1860 initial begin
1861 if ($value$plusargs("TOP_MODULE=%s", __top_module) && __top_module == "test" && $value$plusargs("VCD_FILENAME=%s", __vcd_file)) begin
1862 $dumpfile (__vcd_file);
1863 $dumpvars (0, test);
1864 end
1865 end
1866 `endif
1867 assign output__ = _e_0;
1868 assign _e_0_mut = input__;
1869 endmodule"#
1870 );
1871
1872 assert_same_code!(
1873 &entity_code(
1874 &prepare_codegen(input.clone(), &mut ExprIdTracker::new()),
1875 &mut InstanceMap::new(),
1876 &None
1877 )
1878 .0
1879 .to_string(),
1880 expected
1881 );
1882 }
1883
1884 #[test]
1885 fn constant_codegen_works() {
1886 let input = statement!(const 0; Type::int(10); crate::ConstantValue::int(6));
1887
1888 let expected = indoc!(
1889 r#"
1890 localparam[9:0] _e_0 = 6;"#
1891 );
1892
1893 assert_same_code!(
1894 &statement_code_and_declaration(
1895 &input,
1896 &TypeList::empty(),
1897 &CodeBundle::new("".to_string())
1898 )
1899 .to_string(),
1900 expected
1901 )
1902 }
1903
1904 #[test]
1905 fn pipeline_with_stage_references_codegens_correctly() {
1906 let inst_name = spade_mir::UnitName::_test_from_strs(&["A"]);
1907 let input = entity!(&["pl"]; (
1908 "clk", n(3, "clk"), Type::Bool,
1909 ) -> Type::int(16); {
1910 (reg n(10, "x__s1"); Type::int(16); clock(n(3, "clk")); n(0, "x_"));
1911 (e(0); Type::int(16); simple_instance((inst_name, vec![])););
1913 (n(0, "x_"); Type::int(16); Alias; e(0));
1914 (n(1, "x"); Type::int(16); Alias; n(0, "x_"));
1916 } => n(1, "x")
1917 );
1918
1919 let expected = indoc!(
1921 r#"
1922
1923 module \pl (
1924 input clk_i,
1925 output[15:0] output__
1926 );
1927 `ifdef COCOTB_SIM
1928 string __top_module;
1929 string __vcd_file;
1930 initial begin
1931 if ($value$plusargs("TOP_MODULE=%s", __top_module) && __top_module == "pl" && $value$plusargs("VCD_FILENAME=%s", __vcd_file)) begin
1932 $dumpfile (__vcd_file);
1933 $dumpvars (0, \pl );
1934 end
1935 end
1936 `endif
1937 logic \clk ;
1938 assign \clk = clk_i;
1939 reg[15:0] \x__s1 ;
1940 logic[15:0] \x_ ;
1941 logic[15:0] \x ;
1942 always @(posedge \clk ) begin
1943 \x__s1 <= \x_ ;
1944 end
1945 \A \A_0 (.output__(\x_ ));
1946 assign \x = \x_ ;
1947 assign output__ = \x ;
1948 endmodule"#
1949 );
1950
1951 assert_same_code!(
1952 &entity_code(
1953 &prepare_codegen(input.clone(), &mut ExprIdTracker::new()),
1954 &mut InstanceMap::new(),
1955 &None
1956 )
1957 .0
1958 .to_string(),
1959 expected
1960 );
1961 }
1962
1963 #[test]
1964 fn duplicate_names_adds_nx() {
1965 let input = entity!(&["pl"]; (
1966 ) -> Type::int(16); {
1967 (n(1, "x"); Type::int(16); Not; e(0));
1968 (n(2, "x"); Type::int(16); Not; e(1));
1969 } => n(1, "x")
1970 );
1971
1972 let expected = indoc!(
1973 r#"
1974
1975 module \pl (
1976 output[15:0] output__
1977 );
1978 `ifdef COCOTB_SIM
1979 string __top_module;
1980 string __vcd_file;
1981 initial begin
1982 if ($value$plusargs("TOP_MODULE=%s", __top_module) && __top_module == "pl" && $value$plusargs("VCD_FILENAME=%s", __vcd_file)) begin
1983 $dumpfile (__vcd_file);
1984 $dumpvars (0, \pl );
1985 end
1986 end
1987 `endif
1988 logic[15:0] \x ;
1989 logic[15:0] x_n1;
1990 assign \x = !_e_0;
1991 assign x_n1 = !_e_1;
1992 assign output__ = \x ;
1993 endmodule"#
1994 );
1995
1996 assert_same_code! {
1997 &entity_code(&prepare_codegen(input.clone(), &mut ExprIdTracker::new()), &mut InstanceMap::new(), &None).0.to_string(),
1998 expected
1999 }
2000 }
2001
2002 #[test]
2003 fn instance_map_is_populated() {
2004 let inst1_name = NameID(10, Path::from_strs(&["test1"]));
2005 let inst1_unit_name = spade_mir::UnitName {
2006 kind: spade_mir::unit_name::UnitNameKind::Unescaped("test1".into()),
2007 source: inst1_name.clone(),
2008 };
2009 let inst2_name = NameID(11, Path::from_strs(&["test1"]));
2010 let inst2_unit_name = spade_mir::UnitName {
2011 kind: spade_mir::unit_name::UnitNameKind::Unescaped("test1".into()),
2012 source: inst2_name.clone(),
2013 };
2014
2015 let top_name = NameID(1, Path::from_strs(&["top"]));
2016 let top_unit_name = spade_mir::UnitName {
2017 kind: spade_mir::unit_name::UnitNameKind::Unescaped("test1".into()),
2018 source: top_name.clone(),
2019 };
2020 let input = entity!(&top_unit_name; (
2021 "clk", n(3, "clk"), Type::Bool,
2022 ) -> Type::int(16); {
2023 (reg n(10, "x__s1"); Type::int(16); clock(n(3, "clk")); n(0, "x_"));
2024 (e(0); Type::int(16); Instance({
2026 name: inst1_unit_name,
2027 params: vec![],
2028 argument_names: vec![
2029 ParamName{name: "a".to_string(), no_mangle: None},
2030 ParamName{name: "b".to_string(), no_mangle: None},
2031 ],
2032 loc: None
2033 }););
2034 (e(0); Type::int(16); Instance({
2035 name: inst2_unit_name,
2036 params: vec![],
2037 argument_names: vec![
2038 ParamName{name: "a".to_string(), no_mangle: None},
2039 ParamName{name: "b".to_string(), no_mangle: None},
2040 ],
2041 loc: None
2042 }););
2043 (n(0, "x_"); Type::int(16); Alias; e(0));
2044 (n(1, "x"); Type::int(16); Alias; n(0, "x_"));
2046 } => n(1, "x")
2047 );
2048
2049 let mut instance_map = InstanceMap::new();
2050 entity_code(
2051 &prepare_codegen(input, &mut ExprIdTracker::new()),
2052 &mut instance_map,
2053 &None,
2054 );
2055
2056 let top = instance_map
2057 .inner
2058 .get(&(top_name.clone()))
2059 .expect("Failed to get top");
2060
2061 assert_eq!(
2062 top.get(&"test1_0".to_string())
2063 .expect("failed to get test1_0"),
2064 &inst1_name
2065 );
2066 assert_eq!(
2067 top.get(&"test1_1".to_string())
2068 .expect("failed to get test1_0"),
2069 &inst2_name
2070 );
2071 }
2072}
2073
2074#[cfg(test)]
2075mod backward_expression_tests {
2076 use super::*;
2077 use colored::Colorize;
2078 use spade_common::id_tracker::ExprID;
2079
2080 use crate as spade_mir;
2081 use crate::{statement, types::Type};
2082
2083 use indoc::indoc;
2084
2085 #[test]
2086 fn backward_alias_works() {
2087 let ty = Type::Backward(Box::new(Type::int(8)));
2088 let stmt = statement!(e(0); ty; Alias; e(1));
2089
2090 let expected = indoc! {
2091 r#"
2092 logic[7:0] _e_0_mut;
2093 assign _e_1_mut = _e_0_mut;"#
2094 };
2095
2096 assert_same_code!(
2097 &statement_code_and_declaration(
2098 &stmt,
2099 &TypeList::empty(),
2100 &CodeBundle::new("".to_string())
2101 )
2102 .to_string(),
2103 expected
2104 );
2105 }
2106
2107 #[test]
2108 fn backward_index_tuple_works() {
2109 let tuple_members = vec![Type::backward(Type::int(8)), Type::backward(Type::int(4))];
2110 let ty = Type::backward(Type::int(4));
2111 let stmt = statement!(e(0); ty; IndexTuple((1)); e(1));
2112
2113 let expected = indoc! {
2114 r#"
2115 logic[3:0] _e_0_mut;
2116 assign _e_1_mut[3:0] = _e_0_mut;"#
2117 };
2118
2119 assert_same_code!(
2120 &statement_code_and_declaration(
2121 &stmt,
2122 &TypeList::empty().with(ValueName::Expr(ExprID(1)), Type::Tuple(tuple_members)),
2123 &CodeBundle::new("".to_string())
2124 )
2125 .to_string(),
2126 expected
2127 );
2128 }
2129
2130 #[test]
2131 fn flip_port_works() {
2132 let out_type = Type::Tuple(vec![Type::backward(Type::int(2)), Type::int(4)]);
2133 let stmt = statement!(e(0); out_type; FlipPort; e(1));
2134
2135 let expected = indoc! {
2136 r#"
2137 logic[3:0] _e_0;
2138 logic[1:0] _e_0_mut;
2139 assign _e_0 = _e_1_mut;
2140 assign _e_1 = _e_0_mut;"#
2141 };
2142
2143 assert_same_code!(
2144 &statement_code_and_declaration(
2145 &stmt,
2146 &TypeList::empty(),
2147 &CodeBundle::new("".to_string())
2148 )
2149 .to_string(),
2150 expected
2151 );
2152 }
2153
2154 #[test]
2155 fn construct_tuple_works() {
2156 let tuple_members = vec![Type::backward(Type::int(8)), Type::backward(Type::int(4))];
2157 let ty = Type::Tuple(tuple_members);
2158 let stmt = statement!(e(0); ty; ConstructTuple; e(1), e(2));
2159
2160 let type_list = TypeList::empty()
2161 .with(ValueName::Expr(ExprID(1)), Type::backward(Type::int(8)))
2162 .with(ValueName::Expr(ExprID(2)), Type::backward(Type::int(4)));
2163
2164 let expected = indoc! {
2165 r#"
2166 logic[11:0] _e_0_mut;
2167 assign {_e_1_mut, _e_2_mut} = _e_0_mut;"#
2168 };
2169
2170 assert_same_code!(
2171 &statement_code_and_declaration(&stmt, &type_list, &CodeBundle::new("".to_string()))
2172 .to_string(),
2173 expected
2174 );
2175 }
2176
2177 #[test]
2178 fn construct_tuple_works_on_mixed_direction_types() {
2179 let tuple_members = vec![
2180 Type::backward(Type::int(8)),
2181 Type::Tuple(vec![Type::backward(Type::int(4)), Type::int(4)]),
2182 Type::int(3),
2183 ];
2184 let ty = Type::Tuple(tuple_members);
2185 let stmt = statement!(e(0); ty; ConstructTuple; e(1), e(2), e(3));
2186
2187 let expected = indoc! {
2188 r#"
2189 logic[6:0] _e_0;
2190 logic[11:0] _e_0_mut;
2191 assign _e_0 = {_e_2, _e_3};
2192 assign {_e_1_mut, _e_2_mut} = _e_0_mut;"#
2193 };
2194
2195 let type_list = TypeList::empty()
2196 .with(ValueName::Expr(ExprID(1)), Type::backward(Type::int(8)))
2197 .with(
2198 ValueName::Expr(ExprID(2)),
2199 Type::Tuple(vec![Type::backward(Type::int(4)), Type::int(4)]),
2200 )
2201 .with(ValueName::Expr(ExprID(3)), Type::int(3));
2202
2203 assert_same_code!(
2204 &statement_code_and_declaration(&stmt, &type_list, &CodeBundle::new("".to_string()))
2205 .to_string(),
2206 expected
2207 );
2208 }
2209
2210 #[test]
2211 fn construct_array_works() {
2212 let ty = Type::Array {
2213 inner: Box::new(Type::backward(Type::int(5))),
2214 length: 3u32.to_biguint(),
2215 };
2216 let stmt = statement!(e(0); ty; ConstructArray; e(1), e(2), e(3));
2217
2218 let expected = indoc! {
2219 r#"
2220 logic[14:0] _e_0_mut;
2221 assign {_e_3_mut, _e_2_mut, _e_1_mut} = _e_0_mut;"#
2222 };
2223
2224 assert_same_code!(
2225 &statement_code_and_declaration(
2226 &stmt,
2227 &TypeList::empty(),
2228 &CodeBundle::new("".to_string())
2229 )
2230 .to_string(),
2231 expected
2232 );
2233 }
2234}
2235
2236#[cfg(test)]
2237mod expression_tests {
2238 use super::*;
2239 use colored::Colorize;
2240 use spade_codespan::Span;
2241 use spade_common::id_tracker::ExprID;
2242 use spade_common::location_info::WithLocation;
2243 use spade_common::num_ext::InfallibleToBigInt;
2244
2245 use crate::{self as spade_mir, value_name, UnitName};
2246 use crate::{statement, types::Type};
2247
2248 use indoc::{formatdoc, indoc};
2249
2250 macro_rules! binop_test {
2251 ($name:ident, $ty:expr, $verilog_ty:expr, $op:ident, $verilog_op:expr) => {
2252 #[test]
2253 fn $name() {
2254 let stmt = statement!(e(0); $ty; $op; e(1), e(2));
2255
2256 let expected = formatdoc!(
2257 r#"
2258 logic{} _e_0;
2259 assign _e_0 = _e_1 {} _e_2;"#, $verilog_ty, $verilog_op
2260 );
2261
2262 assert_same_code!(&statement_code_and_declaration(&stmt, &TypeList::empty(), &CodeBundle::new("".to_string())).to_string(), &expected)
2263 }
2264 }
2265 }
2266
2267 macro_rules! signed_binop_test {
2268 ($name:ident, $ty:expr, $verilog_ty:expr, $op:ident, $verilog_op:expr) => {
2269 #[test]
2270 fn $name() {
2271 let stmt = statement!(e(0); $ty; $op; e(1), e(2));
2272
2273 let expected = formatdoc!(
2274 r#"
2275 logic{} _e_0;
2276 assign _e_0 = $signed(_e_1) {} $signed(_e_2);"#, $verilog_ty, $verilog_op
2277 );
2278
2279 assert_same_code!(&statement_code_and_declaration(&stmt, &TypeList::empty(), &CodeBundle::new("".to_string())).to_string(), &expected)
2280 }
2281 }
2282 }
2283
2284 macro_rules! unop_test {
2285 ($name:ident, $ty:expr, $verilog_ty:expr, $op:ident, $verilog_op:expr) => {
2286 #[test]
2287 fn $name() {
2288 let stmt = statement!(e(0); $ty; $op; e(1));
2289
2290 let expected = formatdoc!(
2291 r#"
2292 logic{} _e_0;
2293 assign _e_0 = {}_e_1;"#, $verilog_ty, $verilog_op
2294 );
2295
2296 assert_same_code!(&statement_code_and_declaration(&stmt, &TypeList::empty(), &CodeBundle::new("".to_string())).to_string(), &expected)
2297 }
2298 }
2299 }
2300
2301 signed_binop_test!(binop_add_works, Type::int(2), "[1:0]", Add, "+");
2302 signed_binop_test!(binop_sub_works, Type::int(2), "[1:0]", Sub, "-");
2303 signed_binop_test!(binop_mul_works, Type::int(2), "[1:0]", Mul, "*");
2304 binop_test!(
2305 binop_left_shift_works,
2306 Type::int(2),
2307 "[1:0]",
2308 LeftShift,
2309 "<<"
2310 );
2311 binop_test!(
2312 binop_right_shift_works,
2313 Type::int(2),
2314 "[1:0]",
2315 RightShift,
2316 ">>"
2317 );
2318 signed_binop_test!(
2319 binop_arithmetic_right_shift_works,
2320 Type::int(2),
2321 "[1:0]",
2322 ArithmeticRightShift,
2323 ">>>"
2324 );
2325 binop_test!(binop_eq_works, Type::Bool, "", Eq, "==");
2326 signed_binop_test!(binop_gt_works, Type::Bool, "", Gt, ">");
2327 signed_binop_test!(binop_lt_works, Type::Bool, "", Lt, "<");
2328 signed_binop_test!(binop_ge_works, Type::Bool, "", Ge, ">=");
2329 signed_binop_test!(binop_le_works, Type::Bool, "", Le, "<=");
2330 binop_test!(binop_logical_and_works, Type::Bool, "", LogicalAnd, "&&");
2331 binop_test!(binop_logical_or_works, Type::Bool, "", LogicalOr, "||");
2332 binop_test!(bitwise_xor_works, Type::int(32), "[31:0]", BitwiseXor, "^");
2333 binop_test!(logical_xor_works, Type::Bool, "", LogicalXor, "^");
2335 unop_test!(not_works, Type::Bool, "", Not, "!");
2336 unop_test!(usub_works, Type::int(2), "[1:0]", USub, "-");
2337
2338 #[test]
2339 fn select_operator_works() {
2340 let stmt = statement!(e(0); Type::int(2); Select; e(1), e(2), e(3));
2341
2342 let expected = indoc!(
2343 r#"
2344 logic[1:0] _e_0;
2345 assign _e_0 = _e_1 ? _e_2 : _e_3;"#
2346 );
2347
2348 assert_same_code!(
2349 &statement_code_and_declaration(
2350 &stmt,
2351 &TypeList::empty(),
2352 &CodeBundle::new("".to_string())
2353 )
2354 .to_string(),
2355 expected
2356 );
2357 }
2358
2359 #[test]
2360 fn not_operator_works() {
2361 let stmt = statement!(e(0); Type::Bool; LogicalNot; e(1));
2362
2363 let expected = indoc!(
2364 r#"
2365 logic _e_0;
2366 assign _e_0 = !_e_1;"#
2367 );
2368
2369 assert_same_code!(
2370 &statement_code_and_declaration(
2371 &stmt,
2372 &TypeList::empty(),
2373 &CodeBundle::new("".to_string())
2374 )
2375 .to_string(),
2376 expected
2377 );
2378 }
2379
2380 #[test]
2381 fn match_operator_works() {
2382 let stmt = statement!(e(0); Type::int(2); Match; e(1), e(2), e(3), e(4));
2383
2384 let expected = indoc!(
2385 r#"
2386 logic[1:0] _e_0;
2387 always_comb begin
2388 priority casez ({_e_1, _e_3})
2389 2'b1?: _e_0 = _e_2;
2390 2'b01: _e_0 = _e_4;
2391 2'b?: _e_0 = 2'dx;
2392 endcase
2393 end"#
2394 );
2395
2396 assert_same_code!(
2397 &statement_code_and_declaration(
2398 &stmt,
2399 &TypeList::empty(),
2400 &CodeBundle::new("".to_string())
2401 )
2402 .to_string(),
2403 expected
2404 );
2405 }
2406
2407 #[test]
2408 fn boolean_constants_are_1_and_0() {
2409 let stmt = statement!(const 0; Type::Bool; ConstantValue::Bool(true));
2410
2411 let expected = indoc!(
2412 r#"
2413 localparam[0:0] _e_0 = 1;"#
2414 );
2415
2416 assert_same_code!(
2417 &statement_code_and_declaration(
2418 &stmt,
2419 &TypeList::empty(),
2420 &CodeBundle::new("".to_string())
2421 )
2422 .to_string(),
2423 expected
2424 );
2425 }
2426
2427 #[test]
2428 fn tuple_assembly_operator_works() {
2429 let ty = Type::Tuple(vec![Type::int(6), Type::int(3)]);
2430 let stmt = statement!(e(0); ty; ConstructTuple; e(1), e(2));
2431
2432 let expected = indoc!(
2433 r#"
2434 logic[8:0] _e_0;
2435 assign _e_0 = {_e_1, _e_2};"#
2436 );
2437
2438 assert_same_code!(
2439 &statement_code_and_declaration(
2440 &stmt,
2441 &TypeList::empty()
2442 .with(ValueName::Expr(ExprID(1)), Type::int(6))
2443 .with(ValueName::Expr(ExprID(2)), Type::int(3)),
2444 &CodeBundle::new("".to_string())
2445 )
2446 .to_string(),
2447 expected
2448 );
2449 }
2450
2451 #[test]
2452 fn enum_construction_operator_works() {
2453 let ty = Type::Enum(vec![vec![], vec![], vec![Type::int(10), Type::int(5)]]);
2454 let stmt = statement!(e(0); ty; ConstructEnum({variant: 2}); e(1), e(2));
2455
2456 let expected = indoc!(
2457 r#"
2458 logic[16:0] _e_0;
2459 assign _e_0 = {2'd2, _e_1, _e_2};"#
2460 );
2461
2462 assert_same_code!(
2463 &statement_code_and_declaration(
2464 &stmt,
2465 &TypeList::empty(),
2466 &CodeBundle::new("".to_string())
2467 )
2468 .to_string(),
2469 expected
2470 );
2471 }
2472
2473 #[test]
2474 fn is_enum_variant_operator_works() {
2475 let ty = Type::Enum(vec![vec![], vec![], vec![Type::int(10), Type::int(5)]]);
2476 let stmt = statement!(e(0); Type::Bool; IsEnumVariant({variant: 2}); e(1));
2477
2478 let expected = indoc!(
2479 r#"
2480 logic _e_0;
2481 assign _e_0 = _e_1[16:15] == 2'd2;"#
2482 );
2483
2484 assert_same_code!(
2485 &statement_code_and_declaration(
2486 &stmt,
2487 &TypeList::empty().with(ValueName::Expr(ExprID(1)), ty),
2488 &CodeBundle::new("".to_string())
2489 )
2490 .to_string(),
2491 expected
2492 );
2493 }
2494
2495 #[test]
2496 fn is_enum_variant_operator_works_for_1_wide_tags() {
2497 let ty = Type::Enum(vec![vec![], vec![Type::int(10), Type::int(5)]]);
2498 let stmt = statement!(e(0); Type::Bool; IsEnumVariant({variant: 1}); e(1));
2499
2500 let expected = indoc!(
2501 r#"
2502 logic _e_0;
2503 assign _e_0 = _e_1[15] == 1'd1;"#
2504 );
2505
2506 assert_same_code!(
2507 &statement_code_and_declaration(
2508 &stmt,
2509 &TypeList::empty().with(ValueName::Expr(ExprID(1)), ty),
2510 &CodeBundle::new("".to_string())
2511 )
2512 .to_string(),
2513 expected
2514 );
2515 }
2516
2517 #[test]
2518 fn enum_member_access_operator_works() {
2519 let ty = Type::Enum(vec![vec![], vec![], vec![Type::int(10), Type::int(5)]]);
2520 let stmt = statement!(e(0); Type::int(5); EnumMember({variant: 2, member_index: 1}); e(1));
2521
2522 let expected = indoc!(
2523 r#"
2524 logic[4:0] _e_0;
2525 assign _e_0 = _e_1[4:0];"#
2526 );
2527
2528 assert_same_code!(
2529 &statement_code_and_declaration(
2530 &stmt,
2531 &TypeList::empty().with(ValueName::Expr(ExprID(1)), ty),
2532 &CodeBundle::new("".to_string())
2533 )
2534 .to_string(),
2535 expected
2536 );
2537 }
2538
2539 #[test]
2540 fn enum_construction_inserts_padding_undef_where_needed() {
2541 let ty = Type::Enum(vec![
2542 vec![],
2543 vec![Type::int(5)],
2544 vec![Type::int(10), Type::int(5)],
2545 ]);
2546 let stmt = statement!(e(0); ty; ConstructEnum({variant: 1}); e(1));
2547
2548 let expected = indoc!(
2549 r#"
2550 logic[16:0] _e_0;
2551 assign _e_0 = {2'd1, _e_1, 10'bX};"#
2552 );
2553
2554 assert_same_code!(
2555 &statement_code_and_declaration(
2556 &stmt,
2557 &TypeList::empty(),
2558 &CodeBundle::new("".to_string())
2559 )
2560 .to_string(),
2561 expected
2562 );
2563 }
2564
2565 #[test]
2566 fn tuple_indexing_works_for_first_value() {
2567 let ty = Type::Tuple(vec![Type::int(6), Type::int(3)]);
2568 let stmt = statement!(e(0); Type::int(6); IndexTuple((0)); e(1));
2569
2570 let expected = indoc!(
2571 r#"
2572 logic[5:0] _e_0;
2573 assign _e_0 = _e_1[8:3];"#
2574 );
2575
2576 assert_same_code!(
2577 &statement_code_and_declaration(
2578 &stmt,
2579 &TypeList::empty().with(ValueName::Expr(ExprID(1)), ty),
2580 &CodeBundle::new("".to_string())
2581 )
2582 .to_string(),
2583 expected
2584 );
2585 }
2586 #[test]
2587 fn tuple_indexing_works() {
2588 let ty = Type::Tuple(vec![Type::int(6), Type::int(3)]);
2589 let stmt = statement!(e(0); Type::int(6); IndexTuple((1)); e(1));
2590
2591 let expected = indoc!(
2592 r#"
2593 logic[5:0] _e_0;
2594 assign _e_0 = _e_1[2:0];"#
2595 );
2596
2597 assert_same_code!(
2598 &statement_code_and_declaration(
2599 &stmt,
2600 &TypeList::empty().with(ValueName::Expr(ExprID(1)), ty),
2601 &CodeBundle::new("".to_string())
2602 )
2603 .to_string(),
2604 expected
2605 );
2606 }
2607
2608 #[test]
2609 fn tuple_indexing_works_for_bools() {
2610 let ty = Type::Tuple(vec![Type::Bool, Type::int(3)]);
2611 let stmt = statement!(e(0); Type::Bool; IndexTuple((0)); e(1));
2612
2613 let expected = indoc!(
2614 r#"
2615 logic _e_0;
2616 assign _e_0 = _e_1[3];"#
2617 );
2618
2619 assert_same_code!(
2620 &statement_code_and_declaration(
2621 &stmt,
2622 &TypeList::empty().with(ValueName::Expr(ExprID(1)), ty),
2623 &CodeBundle::new("".to_string())
2624 )
2625 .to_string(),
2626 expected
2627 );
2628 }
2629
2630 #[test]
2631 fn large_negative_literals_codegen_correctly() {
2632 let statement = statement!(const 0; Type::int(64); ConstantValue::int(-1));
2633
2634 let expected = indoc!(
2635 r#"
2636 localparam[63:0] _e_0 = -64'd1;"#
2637 );
2638
2639 assert_same_code!(
2640 &statement_code_and_declaration(
2641 &statement,
2642 &TypeList::empty(),
2643 &CodeBundle::new("".to_string())
2644 )
2645 .to_string(),
2646 expected
2647 );
2648 }
2649
2650 #[test]
2651 fn array_literals_work() {
2652 let ty = Type::Array {
2653 inner: Box::new(Type::int(3)),
2654 length: 3u32.to_biguint(),
2655 };
2656 let statement = statement!(e(0); ty; ConstructArray; e(1), e(2), e(3));
2657
2658 let expected = indoc!(
2659 r#"
2660 logic[8:0] _e_0;
2661 assign _e_0 = {_e_3, _e_2, _e_1};"#
2662 );
2663
2664 assert_same_code!(
2665 &statement_code_and_declaration(
2666 &statement,
2667 &TypeList::empty(),
2668 &CodeBundle::new("".to_string())
2669 )
2670 .to_string(),
2671 expected
2672 );
2673 }
2674
2675 #[test]
2676 fn array_indexing_works() {
2677 let statement = statement!(e(0); Type::int(3); IndexArray; e(1), e(2));
2678
2679 let expected = indoc!(
2680 r#"
2681 logic[2:0] _e_0;
2682 assign _e_0 = _e_1[_e_2 * 3+:3];"#
2683 );
2684
2685 assert_same_code!(
2686 &statement_code_and_declaration(
2687 &statement,
2688 &TypeList::empty().with(
2689 ValueName::Expr(ExprID(1)),
2690 Type::Array {
2691 inner: Box::new(Type::int(3)),
2692 length: 3_u32.to_biguint(),
2693 }
2694 ),
2695 &CodeBundle::new("".to_string())
2696 )
2697 .to_string(),
2698 expected
2699 );
2700 }
2701
2702 #[test]
2703 fn array_indexing_works_for_1_bit_values() {
2704 let statement = statement!(e(0); Type::Bool; IndexArray; e(1), e(2));
2705
2706 let expected = indoc!(
2707 r#"
2708 logic _e_0;
2709 assign _e_0 = _e_1[_e_2];"#
2710 );
2711
2712 assert_same_code!(
2713 &statement_code_and_declaration(
2714 &statement,
2715 &TypeList::empty().with(
2716 ValueName::Expr(ExprID(1)),
2717 Type::Array {
2718 inner: Box::new(Type::Bool),
2719 length: 4_u32.to_biguint(),
2720 }
2721 ),
2722 &CodeBundle::new("".to_string())
2723 )
2724 .to_string(),
2725 expected
2726 );
2727 }
2728
2729 #[test]
2730 fn array_indexing_works_for_1_element_bool_arrays() {
2731 let statement = statement!(e(0); Type::Bool; IndexArray; e(1), e(2));
2732
2733 let expected = indoc!(
2734 r#"
2735 logic _e_0;
2736 assign _e_0 = _e_1;"#
2737 );
2738
2739 assert_same_code!(
2740 &statement_code_and_declaration(
2741 &statement,
2742 &TypeList::empty().with(
2743 ValueName::Expr(ExprID(1)),
2744 Type::Array {
2745 inner: Box::new(Type::Bool),
2746 length: 1_u32.to_biguint(),
2747 }
2748 ),
2749 &CodeBundle::new("".to_string())
2750 )
2751 .to_string(),
2752 expected
2753 );
2754 }
2755
2756 #[test]
2757 fn array_indexing_works_for_1_element_int_arrays() {
2758 let statement = statement!(e(0); Type::int(10); IndexArray; e(1), e(2));
2759
2760 let expected = indoc!(
2761 r#"
2762 logic[9:0] _e_0;
2763 assign _e_0 = _e_1;"#
2764 );
2765
2766 assert_same_code!(
2767 &statement_code_and_declaration(
2768 &statement,
2769 &TypeList::empty().with(
2770 ValueName::Expr(ExprID(1)),
2771 Type::Array {
2772 inner: Box::new(Type::int(10)),
2773 length: 1_u32.to_biguint(),
2774 }
2775 ),
2776 &CodeBundle::new("".to_string())
2777 )
2778 .to_string(),
2779 expected
2780 );
2781 }
2782
2783 #[test]
2784 fn range_array_indexing_works() {
2785 let ty = Type::Array {
2786 inner: Box::new(Type::int(10)),
2787 length: 2u32.to_biguint(),
2788 };
2789 let statement = statement!(e(0); ty; RangeIndexArray({
2790 start: 1u32.to_biguint(),
2791 end_exclusive: 2u32.to_biguint(),
2792 }); e(1), e(2));
2793
2794 let expected = indoc!(
2795 r#"
2796 logic[19:0] _e_0;
2797 assign _e_0 = _e_1[19-:10];"#
2798 );
2799
2800 assert_same_code!(
2801 &statement_code_and_declaration(
2802 &statement,
2803 &TypeList::empty().with(
2804 ValueName::Expr(ExprID(1)),
2805 Type::Array {
2806 inner: Box::new(Type::int(10)),
2807 length: 4u32.to_biguint(),
2808 }
2809 ),
2810 &CodeBundle::new("".to_string())
2811 )
2812 .to_string(),
2813 expected
2814 );
2815 }
2816
2817 #[test]
2818 fn range_array_indexing_on_bool_array_works() {
2819 let ty = Type::Array {
2820 inner: Box::new(Type::Bool),
2821 length: 2u32.to_biguint(),
2822 };
2823 let statement = statement!(e(0); ty; RangeIndexArray({
2824 start: 1u32.to_biguint(),
2825 end_exclusive: 2u32.to_biguint(),
2826 }); e(1), e(2));
2827
2828 let expected = indoc!(
2829 r#"
2830 logic[1:0] _e_0;
2831 assign _e_0 = _e_1[1];"#
2832 );
2833
2834 assert_same_code!(
2835 &statement_code_and_declaration(
2836 &statement,
2837 &TypeList::empty().with(
2838 ValueName::Expr(ExprID(1)),
2839 Type::Array {
2840 inner: Box::new(Type::Bool),
2841 length: 4u32.to_biguint(),
2842 }
2843 ),
2844 &CodeBundle::new("".to_string())
2845 )
2846 .to_string(),
2847 expected
2848 );
2849 }
2850
2851 #[test]
2852 fn range_array_indexing_on_single_element_array_works() {
2853 let ty = Type::Array {
2854 inner: Box::new(Type::int(10)),
2855 length: 1u32.to_biguint(),
2856 };
2857 let statement = statement!(e(0); ty; RangeIndexArray({
2858 start: 1u32.to_biguint(),
2859 end_exclusive: 2u32.to_biguint(),
2860 }); e(1), e(2));
2861
2862 let expected = indoc!(
2863 r#"
2864 logic[9:0] _e_0;
2865 assign _e_0 = _e_1;"#
2866 );
2867
2868 assert_same_code!(
2869 &statement_code_and_declaration(
2870 &statement,
2871 &TypeList::empty().with(
2872 ValueName::Expr(ExprID(1)),
2873 Type::Array {
2874 inner: Box::new(Type::int(10)),
2875 length: 1u32.to_biguint(),
2876 }
2877 ),
2878 &CodeBundle::new("".to_string())
2879 )
2880 .to_string(),
2881 expected
2882 );
2883 }
2884
2885 #[test]
2886 fn entity_instantiation_works() {
2887 let inst_name = UnitName::_test_from_strs(&["e_test"]);
2888 let stmt = statement!(
2889 e(0); Type::Bool; Instance({
2890 name: inst_name,
2891 params: vec![],
2892 argument_names: vec![
2893 ParamName{name: "a".to_string(), no_mangle: None},
2894 ParamName{name: "b".to_string(), no_mangle: None},
2895 ],
2896 loc: None
2897 });
2898 e(1),
2899 e(2)
2900 );
2901
2902 let expected = indoc!(
2903 r#"
2904 logic _e_0;
2905 \e_test \e_test_0 (.a_i(_e_1), .b_i(_e_2), .output__(_e_0));"#
2906 );
2907
2908 assert_same_code!(
2909 &statement_code_and_declaration(
2910 &stmt,
2911 &TypeList::empty()
2912 .with(ValueName::Expr(ExprID(1)), Type::Bool)
2913 .with(ValueName::Expr(ExprID(2)), Type::Bool),
2914 &CodeBundle::new("".to_string())
2915 )
2916 .to_string(),
2917 expected
2918 );
2919 }
2920
2921 #[test]
2922 fn entity_instantiation_with_back_and_forward_ports_works() {
2923 let inst_name = UnitName::_test_from_strs(&["e_test"]);
2924 let ty = Type::Tuple(vec![Type::backward(Type::Bool), Type::Bool]);
2925 let stmt = statement!(e(0); ty; Instance({
2926 name: inst_name,
2927 params: vec![],
2928 argument_names: vec![
2929 ParamName{name: "a".to_string(), no_mangle: None},
2930 ParamName{name: "b".to_string(), no_mangle: None},
2931 ],
2932 loc: None
2933 }); e(1), e(2));
2934
2935 let expected = indoc!(
2936 r#"
2937 logic _e_0;
2938 logic _e_0_mut;
2939 \e_test \e_test_0 (.a_i(_e_1), .b_i(_e_2), .output__(_e_0), .input__(_e_0_mut));"#
2940 );
2941
2942 assert_same_code!(
2943 &statement_code_and_declaration(
2944 &stmt,
2945 &TypeList::empty()
2946 .with(ValueName::Expr(ExprID(1)), Type::Bool)
2947 .with(ValueName::Expr(ExprID(2)), Type::Bool),
2948 &CodeBundle::new("".to_string())
2949 )
2950 .to_string(),
2951 expected
2952 );
2953 }
2954
2955 #[test]
2956 fn entity_instantiation_with_back_ports_works() {
2957 let ty = Type::backward(Type::Bool);
2958 let stmt = statement!(e(0); ty; Instance({
2959 name:UnitName::_test_from_strs(&["e_test"]),
2960 params: vec![],
2961 argument_names: vec![
2962 ParamName{name: "a".to_string(), no_mangle: None},
2963 ParamName{name: "b".to_string(), no_mangle: None},
2964 ],
2965 loc: None
2966 }); e(1), e(2));
2967
2968 let expected = indoc!(
2969 r#"
2970 logic _e_0_mut;
2971 \e_test \e_test_0 (.a_i(_e_1), .b_i(_e_2), .input__(_e_0_mut));"#
2972 );
2973
2974 assert_same_code!(
2975 &statement_code_and_declaration(
2976 &stmt,
2977 &TypeList::empty()
2978 .with(ValueName::Expr(ExprID(1)), Type::Bool)
2979 .with(ValueName::Expr(ExprID(2)), Type::Bool),
2980 &CodeBundle::new("".to_string())
2981 )
2982 .to_string(),
2983 expected
2984 );
2985 }
2986
2987 #[test]
2988 fn entity_instantiation_with_back_inputs_works() {
2989 let ty = Type::Bool;
2990 let stmt = statement!(e(0); ty; Instance({
2991 name:UnitName::_test_from_strs(&["test"]),
2992 params: vec![],
2993 argument_names: vec![
2994 ParamName{name: "a".to_string(), no_mangle: None},
2995 ParamName{name: "b".to_string(), no_mangle: None},
2996 ],
2997 loc: None
2998 }); e(1), e(2));
2999
3000 let expected = indoc!(
3001 r#"
3002 logic _e_0;
3003 \test \test_0 (.a_i(_e_1), .a_o(_e_1_mut), .b_o(_e_2_mut), .output__(_e_0));"#
3004 );
3005
3006 let type_list = TypeList::empty()
3007 .with(
3008 ValueName::Expr(ExprID(1)),
3009 Type::Tuple(vec![Type::Bool, Type::backward(Type::Bool)]),
3010 )
3011 .with(ValueName::Expr(ExprID(2)), Type::backward(Type::Bool));
3012
3013 assert_same_code!(
3014 &statement_code_and_declaration(&stmt, &type_list, &CodeBundle::new("".to_string()))
3015 .to_string(),
3016 expected
3017 );
3018 }
3019
3020 #[test]
3021 fn decl_clocked_array_works() {
3022 let t = Type::Memory {
3023 inner: Box::new(Type::int(6)),
3024 length: 16u32.to_biguint(),
3025 };
3026 let stmt = statement!(e(0); t; DeclClockedMemory({initial: None}); e(1), e(2));
3027
3028 let expected = indoc!(
3031 r#"
3032 logic[6-1:0] _e_0[16-1:0];
3033 always @(posedge _e_1) begin
3034 if (_e_2[10]) begin
3035 _e_0[_e_2[9:6]] <= _e_2[5:0];
3036 end
3037 if (_e_2[21]) begin
3038 _e_0[_e_2[20:17]] <= _e_2[16:11];
3039 end
3040 end"#
3041 );
3042
3043 assert_same_code!(
3044 &statement_code_and_declaration(
3045 &stmt,
3046 &TypeList::empty().with(
3047 ValueName::Expr(ExprID(2)),
3048 Type::Array {
3049 inner: Box::new(Type::Tuple(vec![Type::Bool, Type::uint(4), Type::int(6)])),
3050 length: 2u32.to_biguint(),
3051 }
3052 ),
3053 &CodeBundle::new("".to_string())
3054 )
3055 .to_string(),
3056 expected
3057 );
3058 }
3059
3060 #[test]
3061 fn decl_clocked_array_with_1_bit_address_works() {
3062 let t = Type::Memory {
3063 inner: Box::new(Type::int(6)),
3064 length: 2u32.to_biguint(),
3065 };
3066 let stmt = statement!(e(0); t; DeclClockedMemory({initial: None}); e(1), e(2));
3067
3068 let expected = indoc!(
3069 r#"
3070 logic[6-1:0] _e_0[2-1:0];
3071 always @(posedge _e_1) begin
3072 if (_e_2[7]) begin
3073 _e_0[_e_2[6]] <= _e_2[5:0];
3074 end
3075 end"#
3076 );
3077
3078 assert_same_code!(
3079 &statement_code_and_declaration(
3080 &stmt,
3081 &TypeList::empty().with(
3082 ValueName::Expr(ExprID(2)),
3083 Type::Array {
3084 inner: Box::new(Type::Tuple(vec![Type::Bool, Type::uint(1), Type::int(6)])),
3085 length: 1u32.to_biguint(),
3086 }
3087 ),
3088 &CodeBundle::new("".to_string())
3089 )
3090 .to_string(),
3091 expected
3092 );
3093 }
3094
3095 #[test]
3096 fn decl_clocked_array_with_1_bit_data_works() {
3097 let t = Type::Memory {
3098 inner: Box::new(Type::Bool),
3099 length: 16u32.to_biguint(),
3100 };
3101 let stmt = statement!(e(0); t; DeclClockedMemory({initial: None}); e(1), e(2));
3102
3103 let expected = indoc!(
3106 r#"
3107 logic _e_0[16-1:0];
3108 always @(posedge _e_1) begin
3109 if (_e_2[5]) begin
3110 _e_0[_e_2[4:1]] <= _e_2[0];
3111 end
3112 end"#
3113 );
3114
3115 assert_same_code!(
3116 &statement_code_and_declaration(
3117 &stmt,
3118 &TypeList::empty().with(
3119 ValueName::Expr(ExprID(2)),
3120 Type::Array {
3121 inner: Box::new(Type::Tuple(vec![Type::Bool, Type::uint(4), Type::Bool])),
3122 length: 1u32.to_biguint(),
3123 }
3124 ),
3125 &CodeBundle::new("".to_string())
3126 )
3127 .to_string(),
3128 expected
3129 );
3130 }
3131
3132 #[test]
3133 fn decl_clocked_memory_with_initial_works() {
3134 let t = Type::Memory {
3135 inner: Box::new(Type::Int(6u32.to_biguint())),
3136 length: 16u32.to_biguint(),
3137 };
3138 let stmt = statement!(e(0); t; DeclClockedMemory({
3139 initial: Some(vec![
3140 vec![statement!(const 10; Type::Int(6u32.to_biguint()); ConstantValue::Int(10.to_bigint()))],
3141 vec![statement!(const 10; Type::Int(6u32.to_biguint()); ConstantValue::Int(5.to_bigint()))],
3142 ])
3143 }); e(1), e(2));
3144
3145 let expected = indoc!(
3148 r#"
3149 logic[6-1:0] _e_0[16-1:0];
3150 initial begin
3151 _e_0[0] = 'b001010;
3152 _e_0[1] = 'b000101;
3153 end
3154 always @(posedge _e_1) begin
3155 if (_e_2[10]) begin
3156 _e_0[_e_2[9:6]] <= _e_2[5:0];
3157 end
3158 if (_e_2[21]) begin
3159 _e_0[_e_2[20:17]] <= _e_2[16:11];
3160 end
3161 end"#
3162 );
3163
3164 assert_same_code!(
3165 &statement_code_and_declaration(
3166 &stmt,
3167 &TypeList::empty().with(
3168 ValueName::Expr(ExprID(2)),
3169 Type::Array {
3170 inner: Box::new(Type::Tuple(vec![Type::Bool, Type::uint(4), Type::int(6)])),
3171 length: 2u32.to_biguint(),
3172 }
3173 ),
3174 &CodeBundle::new("".to_string())
3175 )
3176 .to_string(),
3177 expected
3178 );
3179 }
3180
3181 #[test]
3182 fn truncate_works() {
3183 let stmt = statement!(e(0); Type::int(5); Truncate; e(1));
3184
3185 let expected = indoc! {
3186 r#"
3187 logic[4:0] _e_0;
3188 assign _e_0 = _e_1[4:0];"#
3189 };
3190
3191 assert_same_code!(
3192 &statement_code_and_declaration(
3193 &stmt,
3194 &TypeList::empty(),
3195 &CodeBundle::new("".to_string())
3196 )
3197 .to_string(),
3198 expected
3199 );
3200 }
3201
3202 #[test]
3203 fn sext_works_for_many_bits() {
3204 let stmt = statement!(e(0); Type::int(5); SignExtend; e(1));
3205
3206 let expected = indoc! {
3207 r#"
3208 logic[4:0] _e_0;
3209 assign _e_0 = {{ 2 { _e_1[2] }}, _e_1};"#
3210 };
3211
3212 assert_same_code!(
3213 &statement_code_and_declaration(
3214 &stmt,
3215 &TypeList::empty()
3216 .with(ValueName::Expr(ExprID(0)), Type::Int(5_u32.to_biguint()))
3217 .with(ValueName::Expr(ExprID(1)), Type::Int(3_u32.to_biguint())),
3218 &CodeBundle::new("".to_string())
3219 )
3220 .to_string(),
3221 expected
3222 );
3223 }
3224 #[test]
3225 fn sext_works_for_one_bits() {
3226 let stmt = statement!(e(0); Type::int(4); SignExtend; e(1));
3227
3228 let expected = indoc! {
3229 r#"
3230 logic[3:0] _e_0;
3231 assign _e_0 = {_e_1[2], _e_1};"#
3232 };
3233
3234 assert_same_code!(
3235 &statement_code_and_declaration(
3236 &stmt,
3237 &TypeList::empty()
3238 .with(ValueName::Expr(ExprID(0)), Type::Int(4_u32.to_biguint()))
3239 .with(ValueName::Expr(ExprID(1)), Type::Int(3_u32.to_biguint())),
3240 &CodeBundle::new("".to_string())
3241 )
3242 .to_string(),
3243 expected
3244 );
3245 }
3246 #[test]
3247 fn sext_works_for_zero_bits() {
3248 let stmt = statement!(e(0); Type::int(3); SignExtend; e(1));
3249
3250 let expected = indoc! {
3251 r#"
3252 logic[2:0] _e_0;
3253 assign _e_0 = _e_1;"#
3254 };
3255
3256 assert_same_code!(
3257 &statement_code_and_declaration(
3258 &stmt,
3259 &TypeList::empty()
3260 .with(ValueName::Expr(ExprID(0)), Type::Int(3_u32.to_biguint()))
3261 .with(ValueName::Expr(ExprID(1)), Type::Int(3_u32.to_biguint())),
3262 &CodeBundle::new("".to_string())
3263 )
3264 .to_string(),
3265 expected
3266 );
3267 }
3268
3269 #[test]
3270 fn zext_works_for_many_bits() {
3271 let stmt = statement!(e(0); Type::int(5); ZeroExtend; e(1));
3272
3273 let expected = indoc! {
3274 r#"
3275 logic[4:0] _e_0;
3276 assign _e_0 = {2'b0, _e_1};"#
3277 };
3278
3279 assert_same_code!(
3280 &statement_code_and_declaration(
3281 &stmt,
3282 &TypeList::empty()
3283 .with(ValueName::Expr(ExprID(0)), Type::UInt(5_u32.to_biguint()))
3284 .with(ValueName::Expr(ExprID(1)), Type::UInt(3_u32.to_biguint())),
3285 &CodeBundle::new("".to_string())
3286 )
3287 .to_string(),
3288 expected
3289 );
3290 }
3291 #[test]
3292 fn zext_works_for_one_bits() {
3293 let stmt = statement!(e(0); Type::int(4); ZeroExtend; e(1));
3294
3295 let expected = indoc! {
3296 r#"
3297 logic[3:0] _e_0;
3298 assign _e_0 = {1'b0, _e_1};"#
3299 };
3300
3301 assert_same_code!(
3302 &statement_code_and_declaration(
3303 &stmt,
3304 &TypeList::empty()
3305 .with(ValueName::Expr(ExprID(0)), Type::UInt(4_u32.to_biguint()))
3306 .with(ValueName::Expr(ExprID(1)), Type::UInt(3_u32.to_biguint())),
3307 &CodeBundle::new("".to_string())
3308 )
3309 .to_string(),
3310 expected
3311 );
3312 }
3313
3314 #[test]
3315 fn zext_works_for_zero_bits() {
3316 let stmt = statement!(e(0); Type::int(3); ZeroExtend; e(1));
3317
3318 let expected = indoc! {
3319 r#"
3320 logic[2:0] _e_0;
3321 assign _e_0 = _e_1;"#
3322 };
3323
3324 assert_same_code!(
3325 &statement_code_and_declaration(
3326 &stmt,
3327 &TypeList::empty()
3328 .with(ValueName::Expr(ExprID(0)), Type::UInt(3_u32.to_biguint()))
3329 .with(ValueName::Expr(ExprID(1)), Type::UInt(3_u32.to_biguint())),
3330 &CodeBundle::new("".to_string())
3331 )
3332 .to_string(),
3333 expected
3334 );
3335 }
3336
3337 #[test]
3338 fn div_pow2_works() {
3339 let stmt = statement!(e(0); Type::int(3); DivPow2; e(1), e(2));
3340
3341 let expected = indoc! {
3342 r#"
3343 logic[2:0] _e_0;
3344 always_comb begin
3345 if (_e_2 == 0) begin
3346 _e_0 = _e_1;
3347 end
3348 else begin
3349 _e_0 = $signed($signed(_e_1) + $signed(1 << (_e_2 - 1))) >>> $signed(_e_2);
3350 end
3351 end"#
3352 };
3353
3354 assert_same_code!(
3355 &statement_code_and_declaration(
3356 &stmt,
3357 &TypeList::empty(),
3358 &CodeBundle::new("".to_string())
3359 )
3360 .to_string(),
3361 expected
3362 )
3363 }
3364
3365 #[test]
3366 fn concat_works() {
3367 let stmt = statement!(e(0); Type::int(8); Concat; e(1), e(2));
3368
3369 let expected = indoc! {
3370 r#"
3371 logic[7:0] _e_0;
3372 assign _e_0 = {_e_1, _e_2};"#
3373 };
3374
3375 assert_same_code!(
3376 &statement_code_and_declaration(
3377 &stmt,
3378 &TypeList::empty(),
3379 &CodeBundle::new("".to_string())
3380 )
3381 .to_string(),
3382 expected
3383 );
3384 }
3385
3386 #[test]
3387 fn assertion_codegen_works() {
3388 let stmt = Statement::Assert(value_name!(e(0)).at(0, &Span::new(1, 2)));
3389
3390 let expected = indoc! {
3395 "
3396 `ifndef SYNTHESIS
3397 always @(_e_0) begin
3398 #0
3399 assert (_e_0)
3400 else begin
3401 $display(\"\x1b[0m\x1b[1m\x1b[38;5;9merror\x1b[0m\x1b[1m: Assertion failed\x1b[0m\");
3402 $display(\" \x1b[0m\x1b[34m┌─\x1b[0m <str>:1:2\");
3403 $display(\" \x1b[0m\x1b[34m│\x1b[0m\");
3404 $display(\"\x1b[0m\x1b[34m1\x1b[0m \x1b[0m\x1b[34m│\x1b[0m a\x1b[0m\x1b[38;5;9mb\x1b[0mcd\");
3405 $display(\" \x1b[0m\x1b[34m│\x1b[0m \x1b[0m\x1b[38;5;9m^\x1b[0m \x1b[0m\x1b[38;5;9mThis expression is false\x1b[0m\");
3406 $display(\"\");
3407 $error(\"Assertion failed\");
3408 $fatal(1);
3409 end
3410 end
3411 `endif"
3412 };
3413
3414 let source_code = CodeBundle::new("abcd".to_string());
3415
3416 assert_same_code!(
3417 &statement_code_and_declaration(&stmt, &TypeList::empty(), &source_code).to_string(),
3418 expected
3419 );
3420 }
3421
3422 #[test]
3423 fn set_codegen_works() {
3424 let stmt = Statement::Set {
3425 target: value_name!(e(0)).nowhere(),
3426 value: value_name!(e(1)).nowhere(),
3427 };
3428
3429 let expected = indoc! {
3430 r#"
3431 assign _e_0_mut = _e_1;"#
3432 };
3433
3434 assert_same_code!(
3435 &statement_code_and_declaration(
3436 &stmt,
3437 &TypeList::empty()
3438 .with(ValueName::Expr(ExprID(0)), Type::backward(Type::Bool))
3439 .with(ValueName::Expr(ExprID(1)), Type::Bool),
3440 &CodeBundle::new("".to_string())
3441 )
3442 .to_string(),
3443 expected
3444 );
3445 }
3446
3447 #[test]
3448 fn read_mut_wire_codegen_works() {
3449 let stmt = statement!(e(0); Type::int(8); ReadPort; e(1));
3450
3451 let expected = indoc! {
3452 r#"
3453 logic[7:0] _e_0;
3454 assign _e_0 = _e_1_mut;"#
3455 };
3456
3457 assert_same_code!(
3458 &statement_code_and_declaration(
3459 &stmt,
3460 &TypeList::empty(),
3461 &CodeBundle::new("".to_string())
3462 )
3463 .to_string(),
3464 expected
3465 );
3466 }
3467
3468 #[test]
3469 fn const_codegen_large_bit_width_works() {
3470 let stmt = statement!(const 0; Type::int(32); crate::ConstantValue::int(3));
3471
3472 let expected = indoc! {
3473 r#"
3474 localparam[31:0] _e_0 = 32'd3;"#
3475 };
3476
3477 assert_same_code!(
3478 &statement_code_and_declaration(
3479 &stmt,
3480 &TypeList::empty(),
3481 &CodeBundle::new("".to_string())
3482 )
3483 .to_string(),
3484 expected
3485 );
3486 }
3487
3488 #[test]
3489 #[should_panic]
3490 fn compute_index_regression() {
3491 let result = compute_tuple_index(
3492 2,
3493 &[
3494 24u32.to_biguint(),
3495 17u32.to_biguint(),
3496 0u32.to_biguint(),
3497 2u32.to_biguint(),
3498 1u32.to_biguint(),
3499 1u32.to_biguint(),
3500 ],
3501 );
3502
3503 result.verilog_code();
3504 }
3505
3506 #[test]
3507 fn inout_codegens_as_inout() {
3508 let input = spade_mir::Entity {
3509 name: spade_mir::unit_name::IntoUnitName::_test_into_unit_name("test"),
3510 inputs: vec![spade_mir::MirInput {
3511 name: "a".to_string(),
3512 val_name: ValueName::_test_named(0, "a".to_string()),
3513 ty: Type::InOut(Box::new(Type::Bool)),
3514 no_mangle: Some(().nowhere()),
3515 }],
3516 output: ValueName::Expr(ExprID(0)),
3517 output_type: Type::unit(),
3518 statements: vec![],
3519 verilog_attr_groups: vec![],
3520 };
3521
3522 let expected = indoc!(
3523 r#"
3524
3525 module test (
3526 inout a
3527 );
3528 `ifdef COCOTB_SIM
3529 string __top_module;
3530 string __vcd_file;
3531 initial begin
3532 if ($value$plusargs("TOP_MODULE=%s", __top_module) && __top_module == "test" && $value$plusargs("VCD_FILENAME=%s", __vcd_file)) begin
3533 $dumpfile (__vcd_file);
3534 $dumpvars (0, test);
3535 end
3536 end
3537 `endif
3538 endmodule"#
3539 );
3540
3541 assert_same_code!(
3542 &entity_code(
3543 &prepare_codegen(input.clone(), &mut ExprIdTracker::new()),
3544 &mut InstanceMap::new(),
3545 &None
3546 )
3547 .0
3548 .to_string(),
3549 expected
3550 );
3551 }
3552}