spade_mir/
wal.rs

1use spade_common::id_tracker::{ExprIdTracker, NameIdTracker};
2
3use crate::{types::Type, Binding, ConstantValue, Entity, Operator, Statement, ValueName};
4
5pub fn wal_alias(
6    source: &ValueName,
7    prefix: &str,
8    suffix: &str,
9    ty: &Type,
10    idtracker: &mut Option<&mut NameIdTracker>,
11) -> Statement {
12    // Because we know that traced_name is now unique, we also
13    // know that any signals we generate with that name will be unique.
14    // Therefore, we are free to generate `n(0, <name>_...)`
15    let new_name = ValueName::Named(
16        idtracker.as_mut().map(|t| t.next()).unwrap_or_default(),
17        format!("{prefix}{suffix}"),
18        source.into(),
19    );
20
21    Statement::Binding(Binding {
22        name: new_name,
23        operator: Operator::Alias,
24        operands: vec![source.clone()],
25        ty: ty.clone(),
26        loc: None,
27    })
28}
29
30// NOTE: This pass must run after both flatten_aliases and make_names_predictable
31// after the pass is run.
32// NOTE: idtracker must be set to None for *codegen* to be correct, but it can be set
33// to an ID tracker for testing purposes, for mir diffing to work
34pub fn insert_wal_signals(
35    entity: &mut Entity,
36    expr_idtracker: &mut ExprIdTracker,
37    name_idtracker: &mut Option<&mut NameIdTracker>,
38) {
39    let new_statements = entity
40        .statements
41        .iter()
42        .flat_map(|s| {
43            match s {
44                Statement::Register(reg) => {
45                    if let Some(traced_name) = &reg.traced {
46                        let prefix = traced_name.unescaped_var_name();
47                        let mut result = vec![
48                            // Emit the register itself
49                            s.clone(),
50                            wal_alias(
51                                traced_name,
52                                &prefix,
53                                "__wal_fsm_state",
54                                &reg.ty,
55                                name_idtracker,
56                            ),
57                            wal_alias(
58                                &reg.clock,
59                                &prefix,
60                                "__wal_fsm_clk",
61                                &Type::Bool,
62                                name_idtracker,
63                            ),
64                        ];
65                        let rst_signal = if let Some(rst) = &reg.reset {
66                            rst.0.clone()
67                        } else {
68                            let id = expr_idtracker.next();
69                            result.push(Statement::Constant(
70                                id,
71                                Type::Bool,
72                                ConstantValue::Bool(false),
73                            ));
74                            ValueName::Expr(id)
75                        };
76                        result.push(wal_alias(
77                            &rst_signal,
78                            &prefix,
79                            "__wal_fsm_rst",
80                            &Type::Bool,
81                            name_idtracker,
82                        ));
83                        result
84                    } else {
85                        vec![s.clone()]
86                    }
87                }
88                Statement::WalTrace {
89                    name,
90                    val,
91                    suffix,
92                    ty,
93                } => {
94                    vec![wal_alias(
95                        val,
96                        &name.unescaped_var_name(),
97                        suffix,
98                        ty,
99                        name_idtracker,
100                    )]
101                }
102                other => vec![other.clone()],
103            }
104        })
105        .collect();
106    entity.statements = new_statements;
107}
108
109#[cfg(test)]
110mod test {
111    use spade_common::id_tracker::{ExprIdTracker, NameIdTracker};
112
113    use crate::{self as spade_mir, assert_same_mir, ConstantValue};
114    use crate::{entity, types::Type};
115
116    use super::insert_wal_signals;
117    use colored::Colorize;
118
119    #[test]
120    fn traced_register_has_wal_tracing_applied() {
121        let mut input = entity!(&["name"]; (
122            "clk", n(1, "clk"), Type::Bool,
123            "x", n(2, "x"), Type::Bool,
124            "rst", n(3, "rst"), Type::Bool,
125        ) -> Type::Bool; {
126            (const 0; Type::Bool; ConstantValue::Bool(true));
127            (traced(n(0, "state"))
128                reg n(0, "state"); Type::int(5); clock(n(1, "clk")); reset (n(3, "rst"), e(0)); n(2, "x"));
129        } => n(2, "x"));
130
131        let expected = entity!(&["name"]; (
132            "clk", n(0, "clk"), Type::Bool,
133            "x", n(2, "x"), Type::Bool,
134            "rst", n(3, "rst"), Type::Bool,
135        ) -> Type::Bool; {
136            (const 0; Type::Bool; ConstantValue::Bool(true));
137            (traced(n(1, "state"))
138                reg n(1, "state"); Type::int(5); clock(n(0, "clk")); reset (n(3, "rst"), e(0)); n(2, "x"));
139            (n(10, "state__wal_fsm_state"); Type::int(5); Alias; n(1, "state"));
140            (n(11, "state__wal_fsm_clk"); Type::Bool; Alias; n(0, "clk"));
141            (n(12, "state__wal_fsm_rst"); Type::Bool; Alias; n(3, "rst"));
142        } => n(2, "x"));
143
144        insert_wal_signals(
145            &mut input,
146            &mut ExprIdTracker::new(),
147            &mut Some(&mut NameIdTracker::new_at(100)),
148        );
149
150        assert_same_mir!(&input, &expected);
151    }
152
153    #[test]
154    fn traced_register_without_reset_has_wal_tracing_applied() {
155        let mut input = entity!(&["name"]; (
156            "clk", n(0, "clk"), Type::Bool,
157            "x", n(2, "x"), Type::Bool,
158        ) -> Type::Bool; {
159            (const 0; Type::Bool; ConstantValue::Bool(true));
160            (traced(n(1, "state"))
161                reg n(1, "state"); Type::int(5); clock(n(0, "clk")); n(2, "x"));
162        } => n(2, "x"));
163
164        let expected = entity!(&["name"]; (
165            "clk", n(0, "clk"), Type::Bool,
166            "x", n(2, "x"), Type::Bool,
167        ) -> Type::Bool; {
168            (const 0; Type::Bool; ConstantValue::Bool(true));
169            (traced(n(1, "state"))
170                reg n(1, "state"); Type::int(5); clock(n(0, "clk")); n(2, "x"));
171            (n(10, "state_n1__wal_fsm_state"); Type::int(5); Alias; n(1, "state"));
172            (n(11, "state_n1__wal_fsm_clk"); Type::Bool; Alias; n(0, "clk"));
173            (const 10; Type::Bool; ConstantValue::Bool(false));
174            (n(12, "state_n1__wal_fsm_rst"); Type::Bool; Alias; e(10));
175        } => n(2, "x"));
176
177        insert_wal_signals(
178            &mut input,
179            &mut ExprIdTracker::new_at(10),
180            &mut Some(&mut NameIdTracker::new_at(100)),
181        );
182
183        assert_same_mir!(&input, &expected);
184    }
185
186    #[test]
187    fn traced_suffix_has_tracing_applied() {
188        let inner = vec![
189            ("a".to_string(), Type::int(4)),
190            ("b".to_string(), Type::int(8)),
191        ];
192        let inner_types = inner.iter().map(|f| f.1.clone()).collect::<Vec<_>>();
193        let ty = Type::Struct(inner);
194
195        let mut input = entity!(&["name"]; (
196            "x", n(2, "x"), ty.clone(),
197        ) -> Type::Bool; {
198            (n(0, "y"); ty.clone(); Alias; n(2, "x"));
199            (e(0); Type::int(4); IndexTuple((0, inner_types.clone())); n(0, "y"));
200            (wal_trace(n(0, "y"), e(0), "__a__wal_suffix__", Type::int(4)));
201            (e(1); Type::int(8); IndexTuple((1, inner_types.clone())); n(0, "y"));
202            (wal_trace(n(0, "y"), e(1), "__b__wal_suffix__", Type::int(8)))
203        } => n(2, "x"));
204
205        let expected = entity!(&["name"]; (
206            "x", n(2, "x"), ty.clone(),
207        ) -> Type::Bool; {
208            (n(0, "y"); ty.clone(); Alias; n(2, "x"));
209            (e(0); Type::int(4); IndexTuple((0, inner_types.clone())); n(0, "y"));
210            (n(4, "y__a__wal_suffix__"); Type::int(4); Alias; e(0));
211            (e(1); Type::int(8); IndexTuple((1, inner_types.clone())); n(0, "y"));
212            (n(5, "y__b__wal_suffix__"); Type::int(8); Alias; e(1));
213        } => n(2, "x"));
214
215        insert_wal_signals(
216            &mut input,
217            &mut ExprIdTracker::new_at(10),
218            &mut Some(&mut NameIdTracker::new_at(100)),
219        );
220
221        assert_same_mir!(&input, &expected);
222    }
223}