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}