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}