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