imxrt_dcd/
macros.rs

1// Macros for constructing commands using RAL register definitions.
2// User-facing docs => crate-level docs.
3
4// Host macro for all implementation detail rules. Semi-public but expected to be not called by
5// users directly. Excluded from SemVer guarantees.
6#[doc(hidden)]
7#[macro_export]
8macro_rules! internal {
9    // Recursively parses value / mask arguments of the public-facing macros.
10    //
11    // This follows the "TT Muncher" pattern:
12    // https://danielkeep.github.io/tlborm/book/pat-incremental-tt-munchers.html
13    //
14    // Macro Args:
15    // - `access`: e.g. `{W::*, RW::*}` (for importing the correct field value enumerators)
16
17    // `field: value`
18    (@build_value
19     $access:tt $field:ident : $value:expr $(, $($rest:tt)*)?) => {
20        {
21            #[allow(unused_imports)]
22            use reg::$field::$access;
23            (($value << reg::$field::offset) & reg::$field::mask)
24        }
25        $(
26            | $crate::internal!(@build_value $access $($rest)*)
27        )?
28    };
29
30    // `@field`
31    (@build_value
32     $access:tt @ $field:ident $(, $($rest:tt)*)?) => {
33        reg::$field::mask
34        $(
35            | $crate::internal!(@build_value $access $($rest)*)
36        )?
37    };
38
39    // arbitrary expression
40    (@build_value
41     $access:tt $expr:expr $(, $($rest:tt)*)?) => {
42        {
43            #[allow(unused_imports)]
44            use reg::*;
45            $expr
46        }
47        $(
48            | $crate::internal!(@build_value $access $($rest)*)
49        )?
50    };
51
52    // termination for trailing comma
53    (@build_value
54     $access:tt) => {0};
55
56    // Constructs a generic Write command from RAL parts. This is shared between all Write macros.
57    //
58    // - `width` is inferred from the RAL register type (e.g. `RWRegister<u16>` => `Width::B2`)
59    // - `address` is computed from the RAL instance-register pair.
60    // - `value` is provided --- the expression can refer to `periph` and `reg` aliases.
61    (@make_write_command
62     $op:ident, $periph:path, $instance:ident, $reg:ident $([$offset:expr])*, $value:expr ) => {{
63        #[allow(unused_imports)]
64        use $periph as periph;
65        #[allow(unused_imports)]
66        use $periph::{$reg as reg};
67
68        $crate::Command::Write($crate::Write {
69            width: $crate::Width::from_reg(unsafe { &(*(periph::$instance)).$reg $([$offset])* }),
70            op: $crate::WriteOp::$op,
71            address: unsafe {
72                ::core::ptr::addr_of!((*(periph::$instance)).$reg $([$offset])*) as u32
73            },
74            value: $value,
75        })
76    }};
77
78    // Constructs a generic Check command from RAL parts. This is shared between all Check macros.
79    //
80    // - `width` is inferred from the RAL register type (e.g. `RWRegister<u16>` => `Width::B2`)
81    // - `address` is computed from the RAL instance-register pair.
82    // - `mask` is provided --- the expression can refer to `periph` and `reg` aliases.
83    // - `count` is provided.
84    (@make_check_command
85     $cond:ident, $count:expr, $periph:path, $instance:ident, $reg:ident $([$offset:expr])*, $mask:expr) => {{
86        use $periph as periph;
87        #[allow(unused_imports)]
88        use periph::$reg as reg;
89        $crate::Command::Check($crate::Check {
90            width: $crate::Width::from_reg(unsafe { &(*(periph::$instance)).$reg $([$offset])* }),
91            cond: $crate::CheckCond::$cond,
92            address: unsafe {
93                ::core::ptr::addr_of!((*(periph::$instance)).$reg $([$offset])*) as u32
94            },
95            mask: $mask,
96            count: $count,
97        })
98    }};
99}
100
101/// Creates a DCD command that (over-)writes to the specified RAL register,
102/// i.e. `register = arg1 | arg2 | ...` .
103///
104/// Syntax:
105/// ```ignore
106/// write_reg!(ral::path::to::peripheral, INSTANCE, REGISTER, ...args)
107/// ```
108/// Each `arg` can be `FIELD: value`, `@FIELD` (= all bits of the field), or an arbitrary expression.
109/// All args are bitwise-OR'd together to form the final value.
110/// See [crate-level docs](crate) for details on `args`.
111///
112/// Returns a [`crate::Command::Write`] with [`crate::WriteOp::Write`].
113///
114/// # Example
115///
116/// ```
117/// # use imxrt_dcd as dcd;
118/// # use imxrt_ral as ral;
119/// # _ =
120/// dcd::write_reg!(
121///     ral::ccm_analog, CCM_ANALOG, PLL_ARM, @BYPASS, BYPASS_CLK_SRC: CLK1)
122/// # ;
123/// ```
124#[macro_export]
125macro_rules! write_reg {
126    ($periph:path, $instance:ident, $reg:ident $([$offset:expr])*, $($args:tt)+) => {{
127        $crate::internal!(@make_write_command
128            Write, $periph, $instance, $reg $([$offset])*,
129            $crate::internal!(@build_value {W::*, RW::*} $($args)+)
130        )
131    }};
132}
133
134/// Creates a DCD command that sets specified bits / fields to 1 in the specified RAL register,
135/// i.e. `register |= arg1 | arg2 | ...` .
136///
137/// Syntax:
138/// ```ignore
139/// write_reg!(ral::path::to::peripheral, INSTANCE, REGISTER, ...args)
140/// ```
141/// Each `arg` can be `FIELD: value`, `@FIELD` (= all bits of the field), or an arbitrary expression.
142/// All args are bitwise-OR'd together to form the final value.
143/// See [crate-level docs](crate) for details on `args`.
144///
145/// Returns a [`crate::Command::Write`] with [`crate::WriteOp::Set`].
146///
147/// # Example
148///
149/// ```
150/// # use imxrt_dcd as dcd;
151/// # use imxrt_ral as ral;
152/// # _ =
153/// dcd::set_reg!(ral::ccm_analog, CCM_ANALOG, PLL_ARM, @ENABLE)
154/// # ;
155/// ```
156#[macro_export]
157macro_rules! set_reg {
158    ($periph:path, $instance:ident, $reg:ident $([$offset:expr])*, $($args:tt)+) => {{
159        $crate::internal!(@make_write_command
160            Set, $periph, $instance, $reg $([$offset])*,
161            $crate::internal!(@build_value {W::*, RW::*} $($args)+)
162        )
163    }};
164}
165
166/// Creates a DCD command that clears specified bits / fields to 0 in the specified RAL register,
167/// i.e. `register &= !(arg1 | arg2 | ...)` .
168///
169/// Syntax:
170/// ```ignore
171/// write_reg!(ral::path::to::peripheral, INSTANCE, REGISTER, ...args)
172/// ```
173/// Each `arg` can be `FIELD: value`, `@FIELD` (= all bits of the field), or an arbitrary expression.
174/// All args are bitwise-OR'd together to form the final value.
175/// See [crate-level docs](crate) for details on `args`.
176///
177/// Returns a [`crate::Command::Write`] with [`crate::WriteOp::Clear`].
178///
179/// # Example
180///
181/// ```
182/// # use imxrt_dcd as dcd;
183/// # use imxrt_ral as ral;
184/// # _ =
185/// dcd::clear_reg!(ral::ccm, CCM, CBCMR, @PERIPH_CLK2_SEL)
186/// # ;
187/// ```
188#[macro_export]
189macro_rules! clear_reg {
190    ($periph:path, $instance:ident, $reg:ident $([$offset:expr])*, $($args:tt)+) => {{
191        $crate::internal!(@make_write_command
192            Clear, $periph, $instance, $reg $([$offset])*,
193            $crate::internal!(@build_value {W::*, RW::*} $($args)+)
194        )
195    }};
196}
197
198/// Creates a DCD command that polls (indefinitely) to check if all specified bits / fields are 0
199/// in the specified RAL register, i.e. `(register & (arg1 | arg2 | ...)) == 0` .
200///
201/// Syntax:
202/// ```ignore
203/// check_all_clear!(ral::path::to::peripheral, INSTANCE, REGISTER, ...args)
204/// ```
205/// Each `arg` can be `FIELD: value`, `@FIELD` (= all bits of the field), or an arbitrary expression.
206/// All args are bitwise-OR'd together to form the final check mask.
207/// See [crate-level docs](crate) for details on `args`.
208///
209/// Returns a [`crate::Command::Check`] with [`crate::CheckCond::AllClear`].
210///
211/// # Example
212///
213/// ```
214/// # use imxrt_dcd as dcd;
215/// # use imxrt_ral as ral;
216/// # _ =
217/// dcd::check_all_clear!(ral::ccm, CCM, CDHIPR, @PERIPH_CLK_SEL_BUSY, @PERIPH2_CLK_SEL_BUSY)
218/// # ;
219/// ```
220///
221#[macro_export]
222macro_rules! check_all_clear {
223    ($periph:path, $instance:ident, $reg:ident $([$offset:expr])*, $($args:tt)+) => {{
224        $crate::internal!(@make_check_command
225            AllClear, None, $periph, $instance, $reg $([$offset])*,
226            $crate::internal!(@build_value {R::*, RW::*} $($args)+)
227        )
228    }};
229}
230
231/// Creates a DCD command that polls (indefinitely) to check if any specified bits / fields are 0
232/// in the specified RAL register, i.e. `(register & (arg1 | arg2 | ...)) != (arg1 | arg2 | ...)` .
233///
234/// Syntax:
235/// ```ignore
236/// check_any_clear!(ral::path::to::peripheral, INSTANCE, REGISTER, ...args)
237/// ```
238/// Each `arg` can be `FIELD: value`, `@FIELD` (= all bits of the field), or an arbitrary expression.
239/// All args are bitwise-OR'd together to form the final check mask.
240/// See [crate-level docs](crate) for details on `args`.
241///
242/// Returns a [`crate::Command::Check`] with [`crate::CheckCond::AnyClear`].
243///
244/// # Example
245///
246/// ```
247/// # use imxrt_dcd as dcd;
248/// # use imxrt_ral as ral;
249/// # _ =
250/// dcd::check_any_clear!(ral::ccm, CCM, CDHIPR, @PERIPH_CLK_SEL_BUSY, @PERIPH2_CLK_SEL_BUSY)
251/// # ;
252/// ```
253///
254#[macro_export]
255macro_rules! check_any_clear {
256    ($periph:path, $instance:ident, $reg:ident $([$offset:expr])*, $($args:tt)+) => {{
257        $crate::internal!(@make_check_command
258            AnyClear, None, $periph, $instance, $reg $([$offset])*,
259            $crate::internal!(@build_value {R::*, RW::*} $($args)+)
260        )
261    }};
262}
263
264/// Creates a DCD command that polls (indefinitely) to check if all specified bits / fields are 1
265/// in the specified RAL register, i.e. `(register & (arg1 | arg2 | ...)) == (arg1 | arg2 | ...)` .
266///
267/// Syntax:
268/// ```ignore
269/// check_all_set!(ral::path::to::peripheral, INSTANCE, REGISTER, ...args)
270/// ```
271/// Each `arg` can be `FIELD: value`, `@FIELD` (= all bits of the field), or an arbitrary expression.
272/// All args are bitwise-OR'd together to form the final check mask.
273/// See [crate-level docs](crate) for details on `args`.
274///
275/// Returns a [`crate::Command::Check`] with [`crate::CheckCond::AllSet`].
276///
277/// # Example
278///
279/// ```
280/// # use imxrt_dcd as dcd;
281/// # use imxrt_ral as ral;
282/// # _ =
283/// dcd::check_all_set!(ral::ccm_analog, CCM_ANALOG, PLL_ARM, @LOCK)
284/// # ;
285/// ```
286///
287#[macro_export]
288macro_rules! check_all_set {
289    ($periph:path, $instance:ident, $reg:ident $([$offset:expr])*, $($args:tt)+) => {{
290        $crate::internal!(@make_check_command
291            AllSet, None, $periph, $instance, $reg $([$offset])*,
292            $crate::internal!(@build_value {R::*, RW::*} $($args)+)
293        )
294    }};
295}
296
297/// Creates a DCD command that polls (indefinitely) to check if any specified bits / fields are 1
298/// in the specified RAL register, i.e. `(register & (arg1 | arg2 | ...)) != 0` .
299///
300/// Syntax:
301/// ```ignore
302/// check_any_set!(ral::path::to::peripheral, INSTANCE, REGISTER, ...args)
303/// ```
304/// Each `arg` can be `FIELD: value`, `@FIELD` (= all bits of the field), or an arbitrary expression.
305/// All args are bitwise-OR'd together to form the final check mask.
306/// See [crate-level docs](crate) for details on `args`.
307///
308/// Returns a [`crate::Command::Check`] with [`crate::CheckCond::AnySet`].
309///
310/// # Example
311///
312/// ```
313/// # use imxrt_dcd as dcd;
314/// # use imxrt_ral as ral;
315/// # _ =
316/// dcd::check_any_set!(ral::iomuxc, IOMUXC, SW_PAD_CTL_PAD_GPIO_B0_03, @DSE)
317/// # ;
318/// ```
319///
320#[macro_export]
321macro_rules! check_any_set {
322    ($periph:path, $instance:ident, $reg:ident $([$offset:expr])*, $($args:tt)+) => {{
323        $crate::internal!(@make_check_command
324            AnySet, None, $periph, $instance, $reg $([$offset])*,
325            $crate::internal!(@build_value {R::*, RW::*} $($args)+)
326        )
327    }};
328}
329
330#[cfg(test)]
331mod tests {
332    use crate as dcd;
333    use imxrt_ral as ral; // feature = "imxrt1062"
334
335    #[test]
336    fn field_mask_shorthand() {
337        assert_eq!(
338            dcd::write_reg!(
339                ral::ccm_analog, CCM_ANALOG, PLL_ARM, @BYPASS, BYPASS_CLK_SRC: CLK1),
340            dcd::write_reg!(
341                ral::ccm_analog,
342                CCM_ANALOG,
343                PLL_ARM,
344                BYPASS::mask | (0b01 << 14)
345            ),
346        );
347    }
348
349    #[test]
350    fn trailing_comma() {
351        assert_eq!(
352            dcd::write_reg!(
353                ral::ccm_analog, CCM_ANALOG, PLL_ARM, @BYPASS, BYPASS_CLK_SRC: CLK1, ),
354            dcd::write_reg!(
355                ral::ccm_analog,
356                CCM_ANALOG,
357                PLL_ARM,
358                BYPASS::mask | (0b01 << 14)
359            ),
360        );
361    }
362
363    #[test]
364    fn write_example() {
365        // Here we exercise the argument-parsing / value-building logic. Since it's shared between
366        // all macros, we don't need to test it in every variant.
367        assert_eq!(
368            dcd::write_reg!(
369                ral::ccm_analog, CCM_ANALOG, PLL_ARM, @BYPASS, BYPASS_CLK_SRC: CLK1),
370            dcd::Command::Write(dcd::Write {
371                width: dcd::Width::B4,
372                op: dcd::WriteOp::Write,
373                address: 0x400D_8000,
374                value: 0x0001_4000,
375            }),
376        );
377    }
378
379    #[test]
380    fn set_example() {
381        assert_eq!(
382            dcd::set_reg!(ral::ccm_analog, CCM_ANALOG, PLL_ARM, @ENABLE),
383            dcd::Command::Write(dcd::Write {
384                width: dcd::Width::B4,
385                op: dcd::WriteOp::Set,
386                address: 0x400D_8000,
387                value: 1 << 13,
388            }),
389        );
390    }
391
392    #[test]
393    fn clear_example() {
394        assert_eq!(
395            dcd::clear_reg!(ral::ccm, CCM, CBCMR, @PERIPH_CLK2_SEL),
396            dcd::Command::Write(dcd::Write {
397                width: dcd::Width::B4,
398                op: dcd::WriteOp::Clear,
399                address: 0x400F_C018,
400                value: 0b11 << 12,
401            }),
402        );
403    }
404
405    #[test]
406    fn check_all_clear_example() {
407        assert_eq!(
408            dcd::check_all_clear!(ral::ccm, CCM, CDHIPR, @PERIPH_CLK_SEL_BUSY, @PERIPH2_CLK_SEL_BUSY),
409            dcd::Command::Check(dcd::Check {
410                width: dcd::Width::B4,
411                cond: dcd::CheckCond::AllClear,
412                address: 0x400F_C048,
413                mask: (1 << 3) | (1 << 5),
414                count: None,
415            }),
416        )
417    }
418
419    #[test]
420    fn check_any_clear_example() {
421        assert_eq!(
422            dcd::check_any_clear!(ral::ccm, CCM, CDHIPR, @PERIPH_CLK_SEL_BUSY, @PERIPH2_CLK_SEL_BUSY),
423            dcd::Command::Check(dcd::Check {
424                width: dcd::Width::B4,
425                cond: dcd::CheckCond::AnyClear,
426                address: 0x400F_C048,
427                mask: (1 << 3) | (1 << 5),
428                count: None,
429            }),
430        )
431    }
432
433    #[test]
434    fn check_all_set_example() {
435        assert_eq!(
436            dcd::check_all_set!(ral::ccm_analog, CCM_ANALOG, PLL_ARM, @LOCK),
437            dcd::Command::Check(dcd::Check {
438                width: dcd::Width::B4,
439                cond: dcd::CheckCond::AllSet,
440                address: 0x400D_8000,
441                mask: 1 << 31,
442                count: None,
443            }),
444        )
445    }
446
447    #[test]
448    fn check_any_set_example() {
449        assert_eq!(
450            dcd::check_any_set!(ral::iomuxc, IOMUXC, SW_PAD_CTL_PAD_GPIO_B0_03, @DSE),
451            dcd::Command::Check(dcd::Check {
452                width: dcd::Width::B4,
453                cond: dcd::CheckCond::AnySet,
454                address: 0x401F_8338,
455                mask: 0b111 << 3,
456                count: None,
457            }),
458        )
459    }
460
461    #[test]
462    fn auto_detect_width() {
463        {
464            let c = dcd::write_reg!(ral::usb, USB1, CAPLENGTH, 0);
465            let dcd::Command::Write(w) = c else { panic!() };
466            assert_eq!(w.width, dcd::Width::B1);
467        }
468        {
469            let c = dcd::write_reg!(ral::usb, USB1, HCIVERSION, 0);
470            let dcd::Command::Write(w) = c else { panic!() };
471            assert_eq!(w.width, dcd::Width::B2);
472        }
473        {
474            let c = dcd::write_reg!(ral::usb, USB1, HCSPARAMS, 0);
475            let dcd::Command::Write(w) = c else { panic!() };
476            assert_eq!(w.width, dcd::Width::B4);
477        }
478    }
479}