spade_mir/codegen/
mod.rs

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