spade_mir/
macros.rs

1/// Macros for writing mir.
2/// Requires the crate to be in scope, i.e. `use spade_mir;`
3
4#[macro_export]
5macro_rules! value_name {
6    (n($id:expr, $debug_name:expr)) => {
7        spade_mir::ValueName::_test_named($id, $debug_name.to_string())
8    };
9    (e($id:expr)) => {
10        spade_mir::ValueName::Expr(spade_common::id_tracker::ExprID($id))
11    };
12}
13
14#[macro_export]
15macro_rules! if_tracing {
16    () => {None};
17    ($traced_kind:ident $traced_name:tt) => {Some(spade_mir::value_name!($traced_kind $traced_name))}
18}
19
20#[macro_export]
21macro_rules! optional_reset {
22    () => {None};
23    (($rst_trig_kind:ident $rst_trig_name:tt, $rst_val_kind:ident $rst_val_name:tt)) => {
24        Some((
25            spade_mir::value_name!($rst_trig_kind $rst_trig_name),
26            spade_mir::value_name!($rst_val_kind $rst_val_name)
27        ))
28    }
29}
30
31#[macro_export]
32macro_rules! optional_initial {
33    () => {
34        None
35    };
36    (($val:expr)) => {
37        Some($val)
38    };
39}
40
41#[macro_export]
42macro_rules! statement {
43    // Normal constants
44    (
45        const $id:expr; $ty:expr; $value:expr
46    ) => {
47        spade_mir::Statement::Constant(spade_common::id_tracker::ExprID($id), $ty, $value)
48    };
49    // Bindings
50    (
51        $name_kind:ident $name:tt;
52        $type:expr;
53        $operator:ident $(($operator_args:tt))?$({$operator_struct_args:tt})?;
54        $($arg_kind:ident $arg_name:tt),*
55    ) => {
56        spade_mir::Statement::Binding(spade_mir::Binding {
57            name: spade_mir::value_name!($name_kind $name),
58            operator: spade_mir::Operator::$operator$($operator_args)?,
59            operands: vec![
60                $(spade_mir::value_name!($arg_kind $arg_name)),*
61            ],
62            ty: $type,
63            loc: None,
64        })
65    };
66    //register with async reset
67    (
68        $(traced($traced_kind:ident $traced_name:tt))?
69        reg $name_kind:ident $name:tt;
70        $type:expr;
71        clock ($clk_name_kind:ident $clk_name:tt);
72        $(reset $reset:tt)?
73        $(initial $initial:tt)?;
74        $val_kind:ident $val_name:tt
75    ) => {
76        spade_mir::Statement::Register(spade_mir::Register {
77            name: spade_mir::value_name!($name_kind $name),
78            ty: $type,
79            clock: spade_mir::value_name!($clk_name_kind $clk_name),
80            reset: spade_mir::optional_reset!($($reset)?),
81            initial: spade_mir::optional_initial!($($initial)?),
82            value: spade_mir::value_name!($val_kind $val_name),
83            loc: None,
84            traced: spade_mir::if_tracing!($($traced_kind $traced_name)?)
85        })
86    };
87    // Register without reset
88    (
89        $(traced($traced_kind:ident $traced_name:tt))?
90        reg $name_kind:ident $name:tt;
91        $type:expr;
92        clock ($clk_name_kind:ident $clk_name:tt);
93        $val_kind:ident $val_name:tt
94    ) => {
95        spade_mir::Statement::Register(spade_mir::Register {
96            name: spade_mir::value_name!($name_kind $name),
97            ty: $type,
98            clock: spade_mir::value_name!($clk_name_kind $clk_name),
99            reset: None,
100            initial: None,
101            value: spade_mir::value_name!($val_kind $val_name),
102            loc: None,
103            traced: spade_mir::if_tracing!($($traced_kind $traced_name)?)
104        })
105    };
106    // Set statement
107    (set; $lhs_kind:ident $lhs_name:tt; $rhs_kind:ident $rhs_name:tt) => {
108        spade_mir::Statement::Set{
109            target: spade_mir::value_name!($lhs_kind $lhs_name).nowhere(),
110            value: spade_mir::value_name!($rhs_kind $rhs_name).nowhere()
111        }
112    };
113    (
114        assert; $name_kind:ident $name:tt
115    ) => {
116        spade_mir::Statement::Assert(spade_mir::value_name!($name_kind $name).nowhere())
117    };
118    (wal_trace ($name_kind:ident $name_name:tt, $val_kind:ident $val_name:tt, $suffix:expr, $ty:expr) ) => {
119        spade_mir::Statement::WalTrace{
120            name: spade_mir::value_name!($name_kind $name_name),
121            val: spade_mir::value_name!($val_kind $val_name),
122            suffix: $suffix.into(),
123            ty: $ty
124        }
125    }
126}
127
128/// Example
129/// ```
130/// use spade_mir as mir;
131/// use spade_mir::entity;
132/// use spade_mir::types::Type;
133/// entity!("pong"; ("_i_clk", n(0, "clk"), Type::Bool) -> Type::int(6); {
134///     (e(0); Type::int(6); Add; n(1, "value"));
135///     (const 0; Type::int(10); ConstantValue::Int(6));
136///     (reg n(1, "value"); Type::int(6); clock (n(0, "clk")); e(0))
137/// } => n(1, "value"));
138/// ```
139#[macro_export]
140macro_rules! entity {
141    ($name:expr; (
142            $( $arg_name:expr, $arg_intern_kind:ident $arg_intern_name:tt, $arg_type:expr ),* $(,)?
143        ) -> $output_type:expr; {
144            $( $statement:tt );*
145            $(;)?
146        } => $output_name_kind:ident $output_name:tt
147    ) => {
148        spade_mir::Entity {
149            name: spade_mir::unit_name::IntoUnitName::_test_into_unit_name($name),
150            inputs: vec![
151                $(
152                    spade_mir::MirInput {
153                        name: $arg_name.to_string(),
154                        val_name: spade_mir::value_name!($arg_intern_kind $arg_intern_name),
155                        ty: $arg_type,
156                        no_mangle: None
157                    }
158                ),*
159            ],
160            output: spade_mir::value_name!($output_name_kind $output_name),
161            output_type: $output_type,
162            statements: vec![
163                $( spade_mir::statement! $statement ),*
164            ],
165            verilog_attr_groups: vec![],
166        }
167    }
168}
169
170#[macro_export]
171macro_rules! assert_same_mir {
172    ($got:expr, $expected:expr) => {{
173        let mut var_map = spade_mir::diff::VarMap::new();
174
175        if !spade_mir::diff::compare_entity($got, $expected, &mut var_map) {
176            let (got, expected) =
177                spade_mir::diff_printing::translated_strings($got, $expected, &var_map);
178
179            println!("{}:\n{}", "got".red(), got);
180            println!("{}", "==============================================".red());
181            println!("{}:\n{}", "expected".green(), expected);
182            println!(
183                "{}",
184                "==============================================".green()
185            );
186            println!("{}", prettydiff::diff_chars(&got, &expected));
187            println!(
188                "{}",
189                "==============================================".yellow()
190            );
191            panic!("Code mismatch")
192        }
193    }};
194}
195
196#[cfg(test)]
197mod tests {
198    use spade_common::id_tracker::ExprID;
199    use spade_common::name::{NameID, Path};
200    use spade_mir::unit_name::UnitNameKind;
201
202    use crate::{self as spade_mir, MirInput, UnitName};
203    use crate::{types::Type, Binding, ConstantValue, Operator, Register, Statement, ValueName};
204
205    #[test]
206    fn value_name_parsing_works() {
207        assert_eq!(
208            value_name!(n(0, "test")),
209            ValueName::_test_named(0, "test".to_string())
210        );
211        assert_eq!(value_name!(e(0)), ValueName::Expr(ExprID(0)));
212    }
213
214    #[test]
215    fn binding_parsing_works() {
216        let expected = Statement::Binding(Binding {
217            name: ValueName::Expr(ExprID(0)),
218            operator: Operator::Add,
219            operands: vec![
220                ValueName::Expr(ExprID(1)),
221                ValueName::_test_named(1, "test".to_string()),
222            ],
223            ty: Type::Bool,
224            loc: None,
225        });
226
227        assert_eq!(
228            statement!(e(0); Type::Bool; Add; e(1), n(1, "test")),
229            expected
230        );
231    }
232
233    #[test]
234    fn named_parsing_works() {
235        let expected = Statement::Binding(Binding {
236            name: ValueName::_test_named(0, "string".to_string()),
237            operator: Operator::Add,
238            operands: vec![
239                ValueName::Expr(ExprID(1)),
240                ValueName::_test_named(1, "test".to_string()),
241            ],
242            ty: Type::Bool,
243            loc: None,
244        });
245
246        assert_eq!(
247            statement!(n(0, "string"); Type::Bool; Add; e(1), n(1, "test")),
248            expected
249        );
250    }
251
252    #[test]
253    fn register_parsing_works() {
254        let expected = Statement::Register(Register {
255            name: ValueName::_test_named(0, "test".into()),
256            ty: Type::int(5),
257            clock: ValueName::_test_named(1, "clk".into()),
258            reset: None,
259            initial: None,
260            value: ValueName::Expr(ExprID(0)),
261            loc: None,
262            traced: Some(ValueName::Expr(ExprID(2))),
263        });
264
265        assert_eq!(
266            statement!(traced(e(2)) reg n(0, "test"); Type::int(5); clock (n(1, "clk")); e(0)),
267            expected
268        );
269    }
270
271    #[test]
272    fn register_with_reset_parsing_works() {
273        let expected = Statement::Register(Register {
274            name: ValueName::_test_named(0, "test".into()),
275            ty: Type::int(5),
276            clock: ValueName::_test_named(1, "clk".into()),
277            reset: Some((ValueName::Expr(ExprID(1)), ValueName::Expr(ExprID(2)))),
278            initial: None,
279            value: ValueName::Expr(ExprID(0)),
280            loc: None,
281            traced: None,
282        });
283
284        assert_eq!(
285            statement!(reg n(0, "test"); Type::int(5); clock (n(1, "clk")); reset (e(1), e(2)); e(0)),
286            expected
287        );
288    }
289
290    #[test]
291    fn entity_parsing_works() {
292        let expected = crate::Entity {
293            name: UnitName {
294                kind: UnitNameKind::Escaped {
295                    name: "pong".to_string(),
296                    path: vec!["pong".to_string()],
297                },
298                source: NameID(0, Path::from_strs(&["pong"])),
299            },
300            inputs: vec![MirInput {
301                name: "_i_clk".to_string(),
302                val_name: ValueName::_test_named(0, "clk".to_string()),
303                ty: Type::Bool,
304                no_mangle: None,
305            }],
306            output: ValueName::_test_named(1, "value".to_string()),
307            output_type: Type::int(6),
308            statements: vec![
309                statement!(e(0); Type::int(6); Add; n(1, "value")),
310                statement!(reg n(1, "value"); Type::int(6); clock (n(0, "clk")); e(0)),
311            ],
312            verilog_attr_groups: vec![],
313        };
314
315        let result = entity!(&["pong"]; ("_i_clk", n(0, "clk"), Type::Bool) -> Type::int(6); {
316                (e(0); Type::int(6); Add; n(1, "value"));
317                (reg n(1, "value"); Type::int(6); clock (n(0, "clk")); e(0))
318            } => n(1, "value"));
319
320        assert_eq!(result, expected);
321    }
322
323    #[test]
324    fn constant_parsing_works() {
325        let expected = Statement::Constant(ExprID(0), Type::int(10), ConstantValue::int(6));
326
327        let result = statement!(const 0; Type::int(10); ConstantValue::int(6));
328
329        assert_eq!(result, expected);
330    }
331}