spade_mir/
lib.rs

1mod aliasing;
2mod assertion_codegen;
3pub mod codegen;
4pub mod diff;
5pub mod diff_printing;
6mod enum_util;
7pub mod eval;
8pub mod macros;
9pub mod passes;
10pub mod renaming;
11mod type_list;
12pub mod types;
13pub mod unit_name;
14pub mod verilator_wrapper;
15mod verilog;
16mod wal;
17
18use derive_where::derive_where;
19use itertools::Itertools;
20use num::{BigInt, BigUint};
21use renaming::VerilogNameSource;
22use serde::{Deserialize, Serialize};
23use spade_common::id_tracker::ExprID;
24use types::Type;
25
26use spade_common::location_info::Loc;
27use spade_common::name::NameID;
28use spade_common::num_ext::InfallibleToBigInt;
29
30pub use unit_name::UnitName;
31
32#[derive(Clone, PartialEq, Debug, Eq, Hash)]
33pub enum ConstantValue {
34    Int(BigInt),
35    Bool(bool),
36    String(String),
37    HighImp,
38}
39
40impl ConstantValue {
41    pub fn int(val: i32) -> Self {
42        Self::Int(val.to_bigint())
43    }
44}
45
46impl std::fmt::Display for ConstantValue {
47    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48        match self {
49            ConstantValue::Int(val) => write!(f, "{val}"),
50            ConstantValue::Bool(val) => write!(f, "{val}"),
51            ConstantValue::String(val) => write!(f, "{val:?}"),
52            ConstantValue::HighImp => write!(f, "HIGHIMP"),
53        }
54    }
55}
56
57#[derive(Clone, PartialEq, Debug, Hash, Eq, serde::Serialize, serde::Deserialize)]
58pub enum ValueNameSource {
59    Name(NameID),
60    Expr(ExprID),
61}
62
63impl From<NameID> for ValueNameSource {
64    fn from(value: NameID) -> Self {
65        (&value).into()
66    }
67}
68
69impl From<&NameID> for ValueNameSource {
70    fn from(value: &NameID) -> Self {
71        Self::Name(value.clone())
72    }
73}
74
75impl From<&ValueName> for ValueNameSource {
76    fn from(value: &ValueName) -> Self {
77        match value {
78            ValueName::Named(_, _, source) => source.clone(),
79            ValueName::Expr(id) => Self::Expr(*id),
80        }
81    }
82}
83
84/// A name of a value. Can either come from the NameID of the underlying
85/// variable, or the id of the underlying expression
86#[derive(Clone, Debug, Serialize, Deserialize)]
87#[derive_where(Hash, Eq, PartialEq)]
88pub enum ValueName {
89    /// A named value in the code with with an index to make that name locally unique
90    Named(
91        // ID of the name in the generated verilog
92        u64,
93        // Human readable name in the generated verilog. If this is not unique
94        // to the current module, the id will be appended
95        String,
96        // The original name ID from which this name originates
97        // NOTE: Not checked by MIR diff because it is only a metadata field
98        #[derive_where(skip)] ValueNameSource,
99    ),
100    // FIXME: Consider renaming this since it's now used for both patterns and expressions
101    /// An un-named expression. In the resulting verilog, this is called _e_$id
102    Expr(ExprID),
103}
104
105impl ValueName {
106    pub fn verilog_name_source_fwd(&self) -> VerilogNameSource {
107        match self {
108            ValueName::Named(_, _, ValueNameSource::Name(name_id)) => {
109                VerilogNameSource::ForwardName(name_id.clone())
110            }
111            ValueName::Named(_, _, ValueNameSource::Expr(id)) | ValueName::Expr(id) => {
112                VerilogNameSource::ForwardExpr(*id)
113            }
114        }
115    }
116
117    pub fn verilog_name_source_back(&self) -> VerilogNameSource {
118        match self {
119            ValueName::Named(_, _, ValueNameSource::Name(name_id)) => {
120                VerilogNameSource::BackwardName(name_id.clone())
121            }
122            ValueName::Named(_, _, ValueNameSource::Expr(id)) | ValueName::Expr(id) => {
123                VerilogNameSource::BackwardExpr(*id)
124            }
125        }
126    }
127
128    pub fn _test_named(id: u64, name: String) -> Self {
129        use spade_common::name::Path;
130
131        Self::Named(
132            id,
133            name.clone(),
134            NameID(id, Path::from_strs(&[name.as_str()])).into(),
135        )
136    }
137}
138
139impl std::fmt::Display for ValueName {
140    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
141        match self {
142            ValueName::Named(id, s, _) => {
143                // The first identifier of each name has the exact same name as the underlying
144                // spade variable.
145                // However, Vivado has a bug where it treats an escaped `new` as the `new` keyword
146                // from `SystemVerilog`, so we need to special case new here
147                if *id == 0 && s != "new" {
148                    write!(f, "{s}")
149                } else {
150                    write!(f, "{s}_n{id}")
151                }
152            }
153            ValueName::Expr(id) => write!(f, "e{}", id.0),
154        }
155    }
156}
157
158#[derive(Clone, PartialEq, Eq, Hash, Debug)]
159pub struct ParamName {
160    pub name: String,
161    pub no_mangle: Option<Loc<()>>,
162}
163
164#[derive_where(PartialEq, Eq, Hash)]
165#[derive(Clone, Debug)]
166pub enum Operator {
167    // Binary arithmetic operators
168    Add,
169    UnsignedAdd,
170    Sub,
171    UnsignedSub,
172    Mul,
173    UnsignedMul,
174    Div,
175    UnsignedDiv,
176    Mod,
177    UnsignedMod,
178    Eq,
179    NotEq,
180    Gt,
181    UnsignedGt,
182    Lt,
183    UnsignedLt,
184    Ge,
185    UnsignedGe,
186    Le,
187    UnsignedLe,
188    LeftShift,
189    RightShift,
190    ArithmeticRightShift,
191    LogicalAnd,
192    LogicalOr,
193    LogicalXor,
194    LogicalNot,
195    BitwiseAnd,
196    BitwiseOr,
197    BitwiseXor,
198    ReduceAnd,
199    ReduceOr,
200    ReduceXor,
201    USub,
202    Not,
203    ReadPort,
204    ReadWriteInOut,
205    BitwiseNot,
206    // Divide op[0] by 2**op[1] rounding towards 0
207    DivPow2,
208    /// Sign extend the first operand with the provided amount of extra bits
209    SignExtend,
210    ZeroExtend,
211    /// Truncate the first operand to fit the size of the target operand.
212    /// Should not be used on operands which are smaller than the target
213    Truncate,
214    /// Concatenate the bits of all input operands
215    Concat,
216    /// Select [1] if [0] else [2]
217    Select,
218    /// Corresponds to a match statement. If value [0] is true, select [1], if [2] holds, select
219    /// [3] and so on. Values are priorotized in order, i.e. if both [0] and [2] hold, [1] is
220    /// selected
221    // NOTE: We may want to add a MatchUnique for cases where we can guarantee uniqueness,
222    // typically match statements with no wildcards
223    Match,
224    /// Construct an array from the operand expressions
225    ConstructArray,
226    /// Create a mutable array which is modified on the rising edge of the first argument.
227    /// the second argument is an array of (write enable, write address, write data) tuples
228    /// which update the array.
229    DeclClockedMemory {
230        /// Initial values for the memory. Must be const evaluatable
231        initial: Option<Vec<Vec<Statement>>>,
232    },
233    IndexArray,
234    IndexMemory,
235    /// Indexes an array to extract a range of elements
236    RangeIndexArray {
237        start: BigUint,
238        end_exclusive: BigUint,
239    },
240    /// Compiles to verilog [end_exclusive:start]. Supports single bit indexing, (when
241    /// end_exclusive == start + 1, in which case it compiles to [start]
242    RangeIndexBits {
243        start: BigUint,
244        end_exclusive: BigUint,
245    },
246    /// Construct a tuple from all the operand expressions
247    ConstructTuple,
248    /// Construct the nth enum variant with the operand expressions as the payload
249    ConstructEnum {
250        variant: usize,
251    },
252    /// 1 if the input is the specified enum variant
253    IsEnumVariant {
254        variant: usize,
255    },
256    /// Get the `member_index`th member of the `variant`th variant.
257    EnumMember {
258        variant: usize,
259        member_index: usize,
260    },
261    /// Get the `.0`th element of a tuple or a type of the same representation as a tuple, for
262    /// example a Struct.
263    IndexTuple(u64),
264
265    /// Inverts the direction of all bits of a port. I.e. the forward ports
266    /// become backward ports. This is only valid when converting from T to ~T
267    FlipPort,
268
269    /// Given a struct or tuple consisting of mut and non-mut wires, create a new
270    /// struct or tuple with the non-mut copies of the mut wires
271    ///
272    /// As an example `(&mut T1, T2, &mut T3)` becomes `(T1, T3)`
273    // NOTE: For now this variant is a bit of a hack used during wal_trace_lowering
274    // A saner implementation that also solves #252 would be nice
275    // In particular, a dedicated `ReadMutTuple` might be useful
276    // lifeguard spade#252
277    ReadMutWires,
278
279    /// Instantiation of another module with the specified name. The operands are passed
280    /// by name to the entity. The operand name mapping is decided by the `argument_names` field of
281    /// this variant. The first operand gets mapped to the first argument name, and so on.
282    /// The target module can only have a single output which must be the last argument.
283    /// The location of the instantiation is optional but can be passed to improve
284    /// critical path report readability
285    Instance {
286        name: UnitName,
287        params: Vec<(String, ConstantValue)>,
288        /// The names of the arguments in the same order as the operands.
289        /// For instance, if the `i`th argument name is "foo" and the `i`th [`Binding`] is
290        /// `my_port`, the verilog module will be instantiated with `.foo(my_port)`.
291        argument_names: Vec<ParamName>,
292        #[derive_where(skip)]
293        loc: Option<Loc<()>>,
294    },
295    /// Alias another named value
296    Alias,
297    /// Define a variable for the value but don't do anything with it. Useful for creating ports
298    Nop,
299}
300
301impl Operator {
302    pub fn simple_instance(name: UnitName, argument_names: Vec<&str>) -> Self {
303        Self::Instance {
304            name,
305            params: Vec::default(),
306            argument_names: argument_names
307                .iter()
308                .map(|p| ParamName {
309                    name: p.to_string(),
310                    no_mangle: None,
311                })
312                .collect(),
313            loc: None,
314        }
315    }
316}
317
318impl std::fmt::Display for Operator {
319    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
320        match self {
321            Operator::Add => write!(f, "Add"),
322            Operator::UnsignedAdd => write!(f, "UnsignedAdd"),
323            Operator::Sub => write!(f, "Sub"),
324            Operator::UnsignedSub => write!(f, "UnsignedSub"),
325            Operator::Mul => write!(f, "Mul"),
326            Operator::UnsignedMul => write!(f, "UnsignedMul"),
327            Operator::Div => write!(f, "Div"),
328            Operator::UnsignedDiv => write!(f, "UnsignedDiv"),
329            Operator::Mod => write!(f, "Mod"),
330            Operator::UnsignedMod => write!(f, "UnsignedMod"),
331            Operator::Eq => write!(f, "Eq"),
332            Operator::NotEq => write!(f, "NotEq"),
333            Operator::Gt => write!(f, "Gt"),
334            Operator::UnsignedGt => write!(f, "UnsignedGt"),
335            Operator::Lt => write!(f, "Lt"),
336            Operator::UnsignedLt => write!(f, "UnsignedLt"),
337            Operator::Ge => write!(f, "Ge"),
338            Operator::UnsignedGe => write!(f, "UnsignedGe"),
339            Operator::Le => write!(f, "Le"),
340            Operator::UnsignedLe => write!(f, "UnsignedLe"),
341            Operator::RightShift => write!(f, "RightShift"),
342            Operator::ArithmeticRightShift => write!(f, "ArithmeticRightShift"),
343            Operator::LogicalAnd => write!(f, "LogicalAnd"),
344            Operator::LogicalOr => write!(f, "LogicalOr"),
345            Operator::LogicalXor => write!(f, "LogicalXor"),
346            Operator::LogicalNot => write!(f, "LogicalNot"),
347            Operator::BitwiseAnd => write!(f, "BitwiseAnd"),
348            Operator::BitwiseOr => write!(f, "BitwiseOr"),
349            Operator::BitwiseNot => write!(f, "BitwiseNot"),
350            Operator::BitwiseXor => write!(f, "BitwiseXor"),
351            Operator::ReduceAnd => write!(f, "ReduceAnd"),
352            Operator::ReduceOr => write!(f, "ReduceOr"),
353            Operator::ReduceXor => write!(f, "ReduceXor"),
354            Operator::USub => write!(f, "USub"),
355            Operator::Not => write!(f, "Not"),
356            Operator::Select => write!(f, "Select"),
357            Operator::Match => write!(f, "Match"),
358            Operator::LeftShift => write!(f, "LeftShift"),
359            Operator::DivPow2 => write!(f, "DivPow2"),
360            Operator::SignExtend => write!(f, "SignExtend"),
361            Operator::ZeroExtend => write!(f, "ZeroExtend"),
362            Operator::Truncate => write!(f, "Truncate"),
363            Operator::Concat => write!(f, "Concat"),
364            Operator::ConstructEnum { variant } => write!(f, "ConstructEnum({})", variant),
365            Operator::IsEnumVariant { variant } => write!(f, "IsEnumVariant({})", variant),
366            Operator::EnumMember {
367                variant,
368                member_index,
369            } => write!(f, "EnumMember({} {})", variant, member_index),
370            Operator::ConstructTuple => write!(f, "ConstructTuple"),
371            Operator::ConstructArray => write!(f, "ConstructArray"),
372            Operator::DeclClockedMemory { initial } => write!(
373                f,
374                "DeclClockedMemory({})",
375                if let Some(values) = initial {
376                    format!(
377                        "[{}]",
378                        values
379                            .iter()
380                            .map(|v| format!("[{}]", v.iter().map(|v| format!("{v}")).join(", ")))
381                            .join(", ")
382                    )
383                } else {
384                    "None".to_owned()
385                }
386            ),
387            Operator::IndexArray => write!(f, "IndexArray"),
388            Operator::IndexTuple(idx) => write!(f, "IndexTuple({})", idx),
389            Operator::RangeIndexArray {
390                start,
391                end_exclusive: end,
392            } => write!(f, "RangeIndexArray({start}, {end})"),
393            Operator::RangeIndexBits {
394                start,
395                end_exclusive: end,
396            } => write!(f, "RangeIndexBits({start}, {end})"),
397            Operator::IndexMemory => write!(f, "IndexMemory"),
398            Operator::Instance { name, .. } => write!(f, "Instance({})", name.as_verilog()),
399            Operator::Alias => write!(f, "Alias"),
400            Operator::FlipPort => write!(f, "FlipPort"),
401            Operator::ReadMutWires => write!(f, "ReadMutWires"),
402            Operator::Nop => write!(f, "Nop"),
403            Operator::ReadPort => write!(f, "ReadPort"),
404            Operator::ReadWriteInOut => write!(f, "ReadWriteInOut"),
405        }
406    }
407}
408
409#[derive(Clone, PartialEq, Eq, Hash, Debug)]
410pub struct Binding {
411    pub name: ValueName,
412    pub operator: Operator,
413    pub operands: Vec<ValueName>,
414    pub ty: Type,
415    pub loc: Option<Loc<()>>,
416}
417
418impl std::fmt::Display for Binding {
419    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
420        let Binding {
421            name,
422            operator,
423            operands,
424            ty,
425            loc: _,
426        } = self;
427        write!(
428            f,
429            "let {name}: {ty} = {operator}({})",
430            operands.iter().map(|op| format!("{op}")).join(", ")
431        )
432    }
433}
434
435#[derive(Clone, PartialEq, Eq, Hash, Debug)]
436pub struct Register {
437    pub name: ValueName,
438    pub ty: Type,
439    pub clock: ValueName,
440    pub reset: Option<(ValueName, ValueName)>,
441    pub initial: Option<Vec<Statement>>,
442    pub value: ValueName,
443    pub loc: Option<Loc<()>>,
444    /// True if this register corresponds to an fsm with the specified ValueName
445    /// as the actual state
446    pub traced: Option<ValueName>,
447}
448
449impl std::fmt::Display for Register {
450    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
451        let Register {
452            name,
453            ty,
454            clock,
455            reset,
456            initial,
457            value,
458            loc: _,
459            traced: _,
460        } = self;
461
462        let reset = reset
463            .as_ref()
464            .map(|(trig, val)| format!("({trig}, {val})"))
465            .unwrap_or_else(String::new);
466
467        let initial = initial
468            .as_ref()
469            .map(|i| format!("initial({})", i.iter().map(|s| format!("{s}")).join("; ")))
470            .unwrap_or_else(String::new);
471
472        write!(f, "reg({clock}) {name}: {ty}{reset}{initial} = {value}")
473    }
474}
475
476#[derive(Clone, PartialEq, Eq, Hash, Debug)]
477pub enum Statement {
478    Binding(Binding),
479    Register(Register),
480    /// A constant expression with the specified ID and value
481    Constant(ExprID, Type, ConstantValue),
482    Assert(Loc<ValueName>),
483    Set {
484        target: Loc<ValueName>,
485        value: Loc<ValueName>,
486    },
487    /// This is a tracing signal as part of the value `name`. It is used for
488    /// both individual fields if `#[wal_traceable]` and `#[wal_trace]` is used,
489    /// and whole signals if `#[wal_suffix]` is used
490    /// I.e. the result of
491    /// ```
492    /// #[wal_traceable(suffix) struct T {a: A, b: B}
493    ///
494    /// let x: T = ...`
495    /// ```
496    ///
497    /// Will be
498    /// (e(0); IndexStruct(0); x)
499    /// (wal_trace {name: x, val: e(0), suffix: _a_suffix, ty: A}
500    /// (e(1); IndexStruct(1); x)
501    /// (wal_trace {name: x, val: e(0), suffix: _a_suffix, ty: A}
502    WalTrace {
503        name: ValueName,
504        val: ValueName,
505        suffix: String,
506        ty: Type,
507    },
508    Error,
509}
510
511impl std::fmt::Display for Statement {
512    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
513        match self {
514            Statement::Binding(b) => write!(f, "{b}"),
515            Statement::Register(r) => write!(f, "{r}"),
516            Statement::Constant(id, ty, val) => write!(f, "const e{id}: {ty} = {val}", id = id.0),
517            Statement::Assert(val) => write!(f, "assert {val}"),
518            Statement::Set { target, value } => write!(f, "set {target} = {value}"),
519            Statement::WalTrace {
520                name,
521                val,
522                suffix,
523                ty: _,
524            } => write!(f, "wal_trace({name}, {val}, {suffix})"),
525            Statement::Error => write!(f, "Error"),
526        }
527    }
528}
529
530#[derive(Clone, PartialEq, Debug)]
531pub struct MirInput {
532    pub name: String,
533    pub val_name: ValueName,
534    pub ty: Type,
535    pub no_mangle: Option<Loc<()>>,
536}
537#[derive(Clone, PartialEq, Debug)]
538pub struct Entity {
539    /// The name of the module
540    pub name: UnitName,
541    /// A module input which is called `.1` externally and `.2` internally in the module
542    pub inputs: Vec<MirInput>,
543    pub output: ValueName,
544    pub output_type: Type,
545    pub verilog_attr_groups: Vec<Vec<(String, Option<String>)>>,
546    pub statements: Vec<Statement>,
547}
548
549impl std::fmt::Display for Entity {
550    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
551        let Entity {
552            name,
553            inputs,
554            output,
555            output_type,
556            statements,
557            verilog_attr_groups,
558        } = self;
559
560        let inputs = inputs
561            .iter()
562            .map(
563                |MirInput {
564                     name,
565                     val_name,
566                     ty,
567                     no_mangle,
568                 }| {
569                    format!(
570                        "({}{name}, {val_name}, {ty})",
571                        no_mangle.map(|_| "#[no_mangle]").unwrap_or("")
572                    )
573                },
574            )
575            .join(", ");
576
577        let statements = statements.iter().map(|s| format!("\t{s}\n")).join("");
578
579        for attrs in verilog_attr_groups {
580            let contents = attrs
581                .iter()
582                .map(|(key, value)| match value {
583                    Some(v) => format!("{key} = {v:?}"),
584                    None => key.clone(),
585                })
586                .join(",");
587
588            writeln!(f, "#[verilog_attrs({contents})]")?;
589        }
590
591        writeln!(
592            f,
593            "entity {name}({inputs}) -> {output_type} {{",
594            name = name.as_verilog()
595        )?;
596        write!(f, "{statements}")?;
597        write!(f, "}} => {output}")
598    }
599}