Skip to main content

volatile_table/
lib.rs

1//Copyright 2026 #UlinProject Denis Kotlyarov (Денис Котляров)
2
3//Licensed under the Apache License, Version 2.0 (the "License");
4//you may not use this file except in compliance with the License.
5//You may obtain a copy of the License at
6
7//	   http://www.apache.org/licenses/LICENSE-2.0
8
9//Unless required by applicable law or agreed to in writing, software
10//distributed under the License is distributed on an "AS IS" BASIS,
11//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//See the License for the specific language governing permissions and
13// limitations under the License.
14
15// #UlinProject 2026
16
17#![cfg_attr(docsrs, feature(doc_cfg))]
18#![no_std]
19
20pub mod access;
21pub mod ptr;
22
23/// A helper macro to resolve volatile pointer types and access rights.
24///
25/// This macro maps shorthand syntax (like `rw`, `ro`, `wo`) and data types
26/// to the underlying [`VolatilePtr`] implementation. It is the core engine
27/// behind the type generation in `volatile_table!`.
28///
29/// # Syntax Patterns:
30///
31/// - `[ <$t:ty> ]`       => Read-Write pointer to type `$t`.
32/// - `[ rw <$t:ty> ]`    => Read-Write pointer to type `$t`.
33/// - `[ ro <$t:ty> ]`    => Read-Only pointer to type `$t`.
34/// - `[ wo <$t:ty> ]`    => Write-Only pointer to type `$t`.
35/// - `[ rw ]`, `[ ro ]`, `[ wo ]` => Access-specific pointer to `usize` (default).
36/// - `[]`                => Defaults to `rw <usize>`.
37///
38/// # Examples
39///
40/// ```rust
41/// use volatile_table::volatile_type;
42///
43/// // Resolves to VolatilePtr<RW, u32>
44/// type ControlReg = volatile_type![rw <u32>];
45///
46/// // Resolves to VolatilePtr<RO, usize>
47/// type StatusReg = volatile_type![ro];
48/// ```
49#[macro_export]
50macro_rules! volatile_type {
51    [ <$t: ty> ] => {
52        $crate::ptr::VolatilePtr<$crate::access::RW, $t>
53    };
54    [ rw <$t: ty> ] => {
55        $crate::ptr::VolatilePtr<$crate::access::RW, $t>
56    };
57    [ ro <$t: ty> ] => {
58        $crate::ptr::VolatilePtr<$crate::access::RO, $t>
59    };
60
61    [ wo <$t: ty> ] => {
62        $crate::ptr::VolatilePtr<$crate::access::WO, $t>
63    };
64
65    [ rw ] => {
66        $crate::ptr::VolatilePtr<$crate::access::RW, usize>
67    };
68    [ ro ] => {
69        $crate::ptr::VolatilePtr<$crate::access::RO, usize>
70    };
71
72    [ wo ] => {
73        $crate::ptr::VolatilePtr<$crate::access::WO, usize>
74    };
75
76    [] => {
77        $crate::volatile_type!(rw)
78    };
79}
80
81/// A universal constructor macro for creating volatile pointers.
82///
83/// This macro provides a flexible DSL for instantiating [`VolatilePtr`] from raw pointers
84/// or memory addresses (as `usize`). It handles access rights and type casting automatically.
85///
86/// # Syntax Variations:
87///
88/// 1. **Address-based (from_usize)**: Starts with `@from_usize`.
89/// 2. **Pointer-based (default)**: Uses `from_ptr` internally.
90/// 3. **Typed**: Use `<T>` to specify the data type.
91/// 4. **Access-controlled**: Use `rw`, `ro`, or `wo` to set permissions.
92///
93/// # Patterns & Examples:
94///
95/// ```rust
96/// use volatile_table::volatile;
97///
98/// static mut SOME_VAR: usize = 0;
99///
100/// // 1. Default (Read-Write, usize, from raw pointer)
101/// let p = volatile!(&raw mut SOME_VAR);
102///
103/// // 2. Custom Type and Access (Read-Only, u32)
104/// let p = volatile!(ro <usize> : &raw mut SOME_VAR);
105///
106/// // 3. From a literal address (common in embedded)
107/// // This uses the @from_usize flag
108/// let p = volatile!(@from_usize rw <u32> : 0x4000_1000);
109///
110/// // 4. Shorthand for typed RW
111/// let p = volatile!(<usize> : &raw mut SOME_VAR);
112/// ```
113///
114/// # Internal flags:
115/// - `@from_usize`: Forces the use of usize as a pointer
116/// - `@ignore_from_usize`: Flag that excludes the previous `@from_usize` flag; (used internally only.)
117#[macro_export]
118macro_rules! volatile {
119    [ $(@from_usize)? @ignore_from_usize $access:ident <$ty:ty> : $($a:tt)+ ] => {
120        <$crate::volatile_type!($access <$ty>)>::from_ptr($($a)*)
121    };
122    [ $(@from_usize)? @ignore_from_usize $tt:ident : $($a:tt)+ ] => {
123        <$crate::volatile_type!($tt)>::from_ptr($($a)*)
124    };
125    [ $(@from_usize)? @ignore_from_usize <$ty:ty> : $($a:tt)+ ] => {
126        <$crate::volatile_type!(<$ty>)>::from_ptr($($a)*)
127    };
128    [ $(@from_usize)? @ignore_from_usize $($a:tt)+ ] => {
129        <$crate::volatile_type!(rw)>::from_ptr($($a)*)
130    };
131
132
133    [ @from_usize $access:ident <$ty:ty> : $($a:tt)+ ] => {
134        <$crate::volatile_type!($access <$ty>)>::from_usize($($a)*)
135    };
136    [ @from_usize $tt:ident : $($a:tt)+ ] => {
137        <$crate::volatile_type!($tt)>::from_usize($($a)*)
138    };
139    [ @from_usize <$ty:ty> : $($a:tt)+ ] => {
140        <$crate::volatile_type!(<$ty>)>::from_usize($($a)*)
141    };
142    [ @from_usize $($a:tt)+ ] => {
143        <$crate::volatile_type!(rw)>::from_usize($($a)*)
144    };
145
146
147    [ $access:ident <$ty:ty> : $($a:tt)+ ] => {
148        <$crate::volatile_type!($access <$ty>)>::from_ptr($($a)*)
149    };
150    [ $tt:ident : $($a:tt)+ ] => {
151        <$crate::volatile_type!($tt)>::from_ptr($($a)*)
152    };
153    [ <$ty:ty> : $($a:tt)+ ] => {
154        <$crate::volatile_type!(<$ty>)>::from_ptr($($a)*)
155    };
156    [ $($a:tt)+ ] => {
157        <$crate::volatile_type!(rw)>::from_ptr($($a)*)
158    };
159}
160
161/// The main macro for defining volatile register tables and memory maps.
162///
163/// `volatile_table!` allows you to declare memory-mapped registers as typed constants
164/// and create logical groups (maps) with relative offsets.
165///
166/// # Syntax Patterns
167///
168/// 1. **Direct Declaration**: `access <type> NAME = address;`
169///    - Declares a `const` volatile pointer.
170///    - If `<type>` is omitted, it defaults to `usize`.
171///    - Addresses are automatically handled as `usize` via `@from_usize` logic.
172///
173/// 2. **Memory Mapping**: `map [BASE_REG]: { OFFSET_REGS }`
174///    - Defines a set of registers relative to a base address.
175///    - Automatically calculates addresses using the `+=` syntax inside the block.
176///
177/// 3. **Metadata & Visibility**: Supports doc comments `///` and visibility modifiers (e.g., `pub`).
178///
179/// # Examples
180///
181/// ### Simple Register Definition
182/// ```rust
183/// use volatile_table::volatile_table;
184/// volatile_table! {
185///     /// System clock control
186///     pub rw <u32> CLK_CTRL = 0x4000_0000;
187///     /// Status register (read-only)
188///     ro <u32> STATUS = 0x4000_0004;
189/// }
190/// ```
191///
192/// ### Advanced Memory Map (e.g., UART Driver)
193/// ```rust
194/// use volatile_table::volatile_table;
195/// volatile_table! {
196///     map [pub rw <u32> UART_BASE = 0xC810_04C0]: {
197///         wo <u32> TX_FIFO += 0x00;
198///         ro <u32> RX_FIFO += 0x04;
199///         rw <u32> CONTROL += 0x08;
200///     }
201/// }
202/// ```
203///
204/// # How it works
205/// - For entries with `<type>`, the macro assumes the initialization value is a memory address (`usize`).
206/// - For entries without `<type>`, it treats the value as a pointer expression unless flagged.
207/// - The `map` block expands into a series of constant definitions where each child register
208///   is calculated as `BASE_ADDR + OFFSET`.
209#[macro_export]
210macro_rules! volatile_table {
211    [
212        $(#[$meta:meta])*
213        $vis:vis $access_type:ident $n: ident $(= $(@$addition_exprflag:tt)? $e: expr)? $(
214            ;
215            $($all:tt)*
216        )?
217    ] => { // FROM_TYPE
218        $(#[$meta])*
219        $vis const $n:   $crate::volatile_type!($access_type) $(= $crate::volatile!($(@$addition_exprflag)? $access_type: $e))?;
220
221        $(
222            $crate::volatile_table!{
223                $($all)*
224            }
225        )?
226    };
227    [
228        $(#[$meta:meta])*
229        $vis:vis $access_type:ident <$ty: ty> $n: ident $(= $(@$addition_exprflag:tt)? $e: expr)? $(
230            ;
231            $($all:tt)*
232        )?
233    ] => { // FROM_USIZE
234        $(#[$meta])*
235        $vis const $n:   $crate::volatile_type!($access_type <$ty>) $(= $crate::volatile!(@from_usize $(@$addition_exprflag)? $access_type <$ty>: $e))?;
236
237        $(
238            $crate::volatile_table!{
239                $($all)*
240            }
241        )?
242    };
243    [
244        map [
245            $(#[$meta:meta])*
246            $vis:vis $access_type:ident $n: ident $(= $(@$addition_exprflag:tt)? $e: expr)?
247        ]: {
248            $($map_code:tt)*
249        } $(
250            ;
251            $($all:tt)*
252        )?
253    ] => { // FROM_TYPE
254        $crate::_volatile_map! {
255            [$n]: {
256                $($map_code)*
257            }
258        }
259
260        $crate::volatile_table! {
261            $(#[$meta])*
262            $vis $access_type $n $(= $(@$addition_exprflag)? $e)?
263
264            $(; $($all)*)?
265        }
266    };
267    [
268        map [
269            $(#[$meta:meta])*
270            $vis:vis $access_type:ident <$ty: ty> $n: ident $(= $(@$addition_exprflag:tt)? $e: expr)?
271        ]: {
272            $($map_code:tt)*
273        } $(
274            ;
275            $($all:tt)*
276        )?
277    ] => { // FROM_USIZE
278        $crate::_volatile_map! {
279            [$n]: {
280                $($map_code)*
281            }
282        }
283        $crate::volatile_table! {
284            $(#[$meta])*
285            $vis $access_type <$ty> $n $(= $(@$addition_exprflag)? $e)?
286
287            $(; $($all)*)?
288        }
289    };
290    [
291        map [
292            $n: path
293        ]: {
294            $($map_code:tt)*
295        } $(
296            ;
297            $($all:tt)*
298        )?
299    ] => { // FROM_PATH
300        $crate::_volatile_map! {
301            [$n]: {
302                $($map_code)*
303            }
304        }
305        $(
306            $crate::volatile_table! {
307                $($all)*
308            }
309        )?
310    };
311    [ ] => [];
312}
313
314/// **Internal use only.**
315///
316/// This macro handles the expansion of the `map` block within [`volatile_table!`].
317/// It recursively processes register definitions and calculates their absolute
318/// addresses based on a base pointer using byte offsets.
319#[doc(hidden)]
320#[macro_export]
321macro_rules! _volatile_map {
322    [
323        [$map_name: expr]: {
324            $(#[$meta:meta])*
325            $vis:vis $access_type:ident $n: ident $(+= $(@$addition_exprflag:tt)? $e: expr)? $(
326                ;
327                $($all:tt)*
328            )?
329        } $(;)?
330    ] => { // FROM_TYPE
331        $crate::volatile_table! {
332            $(#[$meta])*
333            $vis $access_type $n $(= @ignore_from_usize $(@$addition_exprflag)? unsafe {
334                $map_name.raw_byte_add($e).as_raw_ptr()
335            })?;
336        }
337
338        $(
339            $crate::_volatile_map! {
340                [$map_name]: {
341                    $($all)*
342                }
343            }
344        )?
345    };
346    [
347        [$map_name: expr]: {
348            $(#[$meta:meta])*
349            $vis:vis $access_type:ident <$ty: ty> $n: ident $(+= $(@$addition_exprflag:tt)? $e: expr)? $(
350                ;
351                $($all:tt)*
352            )?
353        } $(;)?
354    ] => { // FROM_USIZE
355        $crate::volatile_table! {
356            $(#[$meta])*
357            $vis $access_type <$ty> $n $(= @ignore_from_usize $(@$addition_exprflag)? unsafe {
358                $map_name.raw_byte_add($e).as_raw_ptr()
359            })?;
360        }
361
362        $(
363            $crate::_volatile_map! {
364                [$map_name]: {
365                    $($all)*
366                }
367            }
368        )?
369    };
370
371    [
372        [$map_name: expr]: {} $(;)?
373    ] => {};
374    [ ] => [];
375}