reg_map/lib.rs
1//! Derive volatile accesses to a register map and memory-mapped IO.
2//!
3//! The main entry point of this crate is the derive macro [`RegMap`], that generates a new pointer
4//! type to a defined register map.
5//!
6//! **Table of contents**
7//! - [Basic usage](#basic-usage)
8//! - [Register types](#register-types)
9//! - [Basic registers](#basic-registers)
10//! - [Nested register maps](#nested-register-maps)
11//! - [Arrays of registers](#arrays-of-registers)
12//! - [Iterators](#iterators)
13//! - [Access permissions](#access-permissions)
14//! - [Type layout and representation](#type-layout-and-representation)
15//! - [Thread safety](#thread-safety)
16//! - [Principle of operation](#principle-of-operation)
17//! - [Sample generated code](#sample-generated-code)
18//! - [Comparison with other crates](#comparison-with-other-crates)
19//! - [volatile](#volatile)
20//! - [volatile-register](#volatile-register)
21//! - [Further reading](#further-reading)
22//!
23//! # Basic usage
24//!
25//! ```rust
26//! # mod yoo {
27//! # use reg_map::RegMap;
28//! // define struct Registers with the register map
29//! // and derive the pointer RegistersPtr using the RegMap macro
30//! #[repr(C)]
31//! #[derive(RegMap, Default)]
32//! # pub
33//! struct Registers {
34//! field1: u64,
35//! field2: u32,
36//! #[reg(RO)]
37//! read_only_field: i8,
38//! #[reg(WO)]
39//! write_only_field: u128,
40//! #[reg(RW)]
41//! read_write_is_default: i16,
42//! }
43//! # } // mod yoo
44//! # use yoo::{Registers, RegistersPtr};
45//!
46//! // initialize the base struct
47//! // and obtain a pointer to the registers
48//! let mut regs = Registers::default();
49//! let ptr = RegistersPtr::from_mut(&mut regs);
50//!
51//! // when dealing with e.g. memory-mapped IO (MMIO),
52//! // you'd probably just get a pointer to the data from a known base address
53//! // let ptr = unsafe { RegistersPtr::from_ptr(0xAA55_000 as *mut _) };
54//!
55//! // all write() operations are volatile
56//! ptr.field1().write(10);
57//! ptr.field2().write(32);
58//! ptr.write_only_field().write(76);
59//! ptr.read_write_is_default().write(98);
60//!
61//! // all read() operations are volatile
62//! assert_eq!(ptr.field1().read(), 10);
63//! assert_eq!(ptr.field2().read(), 32);
64//! assert_eq!(ptr.read_only_field().read(), 0);
65//! assert_eq!(ptr.read_write_is_default().read(), 98);
66//! ```
67//!
68//! Read/write permissions are checked at compile time. The following code does not compile:
69//! ```compile_fail
70//! # mod yoo {
71//! # use reg_map::RegMap;
72//! # #[repr(C)]
73//! # #[derive(RegMap, Default)]
74//! # pub struct Registers {
75//! # field1: u64,
76//! # field2: u32,
77//! # #[reg(RO)]
78//! # read_only_field: i8,
79//! # #[reg(WO)]
80//! # write_only_field: u128,
81//! # #[reg(RW)]
82//! # read_write_is_default: i16,
83//! # }
84//! # } // mod yoo
85//! # use yoo::{Registers, RegistersPtr};
86//! # let mut regs = Registers::default();
87//! # let ptr = RegistersPtr::from_mut(&mut regs);
88//! ptr.read_only_field().write(54); // error[E0277]: cannot write to a read-only register
89//! ptr.write_only_field().read(); // error[E0277]: cannot read from a write-only register
90//! ```
91//!
92//! # Register types
93//!
94//! ## Basic registers
95//!
96//! These primitive integer types are supported as basic register types:
97//! - unsigned [`u8`], [`u16`], [`u32`], [`u64`] and [`u128`],
98//! - signed [`i8`], [`i16`], [`i32`], [`i64`] and [`i128`].
99//!
100//! The pointer-sized integer types [`usize`] and [`isize`] are *not* supported.
101//!
102//! For a register map containing a basic register:
103//! ```
104//! # mod yoo {
105//! # use reg_map::RegMap;
106//! #[derive(RegMap, Default)]
107//! #[repr(C)]
108//! struct Basic {
109//! field: u64,
110//! }
111//! # } // mod yoo
112//! ```
113//! The [`RegMap`] derive macro will generate the following abridged code:
114//! ```ignore
115//! struct BasicPtr<'a> { ... };
116//! impl<'a> BasicPtr<'a> {
117//! fn field(&self) -> Reg<'a, u64, ReadWrite> { ... }
118//! }
119//! ```
120//! where the read/write operations on the register are performed through the [`Reg`] type, and the
121//! access permissions default to both read and write.
122//!
123//! ## Nested register maps
124//! Register-map definitions can be nested arbitrarily:
125//! ```
126//! # mod yoo {
127//! # use reg_map::RegMap;
128//! # #[derive(RegMap, Default)]
129//! # #[repr(C)]
130//! # struct Basic {
131//! # field: u64,
132//! # }
133//! #[derive(RegMap)]
134//! #[repr(C)]
135//! struct Outer {
136//! outer: u64,
137//! inner: Basic,
138//! }
139//! # } // mod yoo
140//! ```
141//! will generate pointer types with the following abridged code:
142//! ```ignore
143//! struct OuterPtr<'a> { ... };
144//! impl<'a> OuterPtr<'a> {
145//! fn outer(&self) -> Reg<'a, u64, ReadWrite> { ... }
146//! fn inner(&self) -> BasicPtr<'a> { ... }
147//! }
148//! ```
149//! where `Basic` and `BasicPtr` are shown in the previous section.
150//!
151//! ## Arrays of registers
152//! Fixed-size arrays of registers are also supported, with both basic and nested registers.
153//! ```
154//! # mod yoo {
155//! # use reg_map::RegMap;
156//! # #[derive(RegMap, Default)]
157//! # #[repr(C)]
158//! # struct Basic {
159//! # field: u64,
160//! # }
161//! #[derive(RegMap, Default)]
162//! #[repr(C)]
163//! struct Many {
164//! basic: [u64; 32],
165//! nested: [Basic; 16],
166//! }
167//! # } // mod yoo
168//! ```
169//! generates the following abridged code:
170//! ```ignore
171//! struct ManyPtr<'a> { ... };
172//! impl<'a> ManyPtr<'a> {
173//! fn basic(&self) -> RegArray<'a, Reg<'a, u64, ReadWrite>, 32> { ... }
174//! fn nested(&self) -> RegArray<'a, BasicPtr<'a>, 16> { ... }
175//! }
176//! ```
177//! where the access to the arrays of registers are provided by the [`RegArray`] type.
178//!
179//! Multidimensional arrays are also supported:
180//! ```
181//! # mod yoo {
182//! # use reg_map::RegMap;
183//! # #[derive(RegMap)]
184//! # #[repr(C)]
185//! # struct Basic {
186//! # field: u64,
187//! # }
188//! #[derive(RegMap)]
189//! #[repr(C)]
190//! struct MultiD {
191//! basic: [[[[u64; 2]; 3]; 5]; 7],
192//! nested: [[[[Basic; 7]; 5]; 3]; 2],
193//! }
194//! # } // mod yoo
195//! ```
196//!
197//! ### Iterators
198//!
199//! It is possible to iterate through arrays using the methods [`RegArray::iter`] and
200//! [`RegArray::iter_slice`]:
201//! ```
202//! # mod yoo {
203//! # use reg_map::RegMap;
204//! # #[derive(RegMap, Default)]
205//! # #[repr(C)]
206//! # pub struct Basic {
207//! # pub field: u64,
208//! # }
209//! # #[derive(RegMap, Default)]
210//! # #[repr(C)]
211//! # pub struct Many {
212//! # pub basic: [u64; 32],
213//! # pub nested: [Basic; 16],
214//! # }
215//! # } // mod yoo
216//! # use yoo::{Many, ManyPtr};
217//! let mut reg = Many::default();
218//! let ptr = ManyPtr::from_mut(&mut reg);
219//!
220//! for (i, basic) in ptr.basic().iter().enumerate() {
221//! basic.write(i as u64);
222//! }
223//! for (i, basic) in ptr.basic().iter().enumerate() {
224//! assert_eq!(basic.read(), i as u64);
225//! }
226//!
227//! for (j, nested) in ptr.nested().iter_slice(2, 7).rev().enumerate() {
228//! nested.field().write(j as u64);
229//! }
230//! for (j, nested) in ptr.nested().iter().enumerate() {
231//! let expected = if (2..7).contains(&j) {
232//! 6 - j
233//! } else {
234//! 0
235//! };
236//! assert_eq!(nested.field().read(), expected as u64);
237//! }
238//! ```
239//!
240//! # Access permissions
241//! Access permissions for each register can be specified with the `#[reg()]` attribute, and
242//! default to read-write if not specified:
243//! ```
244//! # mod yoo {
245//! # use reg_map::RegMap;
246//! #[repr(C)]
247//! #[derive(RegMap)]
248//! struct Permissions {
249//! #[reg(RO)] read_only_register: u64,
250//! #[reg(WO)] write_only_register: u64,
251//! #[reg(RW)] read_write_register: u64,
252//! another_read_only_register: u64,
253//! }
254//! # } // mod yoo
255//! ```
256//! Access permission are implemented through the zero-sized structs:
257//! - [`ReadOnly`](access::ReadOnly) for read-only registers (`#[reg(RO)]` attribute);
258//! - [`WriteOnly`](access::WriteOnly) for write-only registers (`#[reg(WO)]` attribute);
259//! - [`ReadWrite`](access::ReadWrite) for read-write registers (`#[reg(RW)]` attribute, or no attribute).
260//!
261//! Access permission are checked at compile time, as the zero-sized structs above are passed as
262//! type parameters to the generic types [`Reg`] and [`RegArray`] upon definition of the derived
263//! pointer types. Specifically, the [`write`](Reg::write) is just not defined for a read-only
264//! register, and so on.
265//!
266//! # Type layout and representation
267//! The derive macro [`RegMap`] requires the register-map `struct` to have the `C` representation
268//! using the `#[repr(C)]` attribute. Higher alignment requirements can be specified with the
269//! `#[repr(C, align(x))]` attribute. Other representations are not supported and generate a
270//! compile-time error.
271//!
272//! Example:
273//! ```
274//! # mod yoo {
275//! # use reg_map::RegMap;
276//! #[repr(C)]
277//! #[derive(RegMap)]
278//! struct Base {
279//! foo: u32,
280//! baz: u32,
281//! aligned: Data,
282//! }
283//! #[repr(C, align(4096))]
284//! #[derive(RegMap)]
285//! struct Data {
286//! data: [u64; 512],
287//! }
288//! # } // mod yoo
289//! ```
290//!
291//! In summary:
292//! - `#[repr(C)]`: The `C` representation is *required*.
293//! - Default/`Rust` representation is *not* supported.
294//! - `#[repr(transparent)]`: The `transparent` representation is *not* supported.
295//! - `#[repr(align(x))]`: *Raising* the alignment of the register map is supported, in combination
296//! with the `C` representation.
297//! - `#[repr(packed)]`: *Lowering* the alignment of the register map is *not* supported.
298//! This is because unaligned reads and writes are not (currently) supported.
299//!
300//! # Thread safety
301//!
302//! All reads and writes performed through the pointers derived by [`RegMap`] are volatile. However
303//! in Rust, *"just like in C, whether an operation is volatile has no bearing whatsoever on
304//! questions involving concurrent access from multiple threads. Volatile accesses behave exactly
305//! like non-atomic accesses in that regard."* See safety docs for
306//! [`read_volatile`](core::ptr::read_volatile#safety) and
307//! [`write_volatile`](core::ptr::write_volatile#safety).
308//!
309//! There is currently no way in Rust to define memory accesses as both volatile and atomic.
310//! Therefore, the pointers derived by [`RegMap`] are generally not thread safe and thus implement
311//! neither [`Send`] not [`Sync`].
312//!
313//! That said, on some platforms and for some use cases, volatile access and relaxed atomic
314//! accesses are the same. If you know that is the case, you can `unsafe`ly implement `Send` and
315//! `Sync` yourself:
316//!
317//! ```
318//! # mod yoo {
319//! # use reg_map::RegMap;
320//! #[repr(C)]
321//! #[derive(RegMap)]
322//! # pub
323//! struct IPromiseThisIsThreadSafe {
324//! data: u64,
325//! }
326//! # } // mod yoo
327//! # use yoo::IPromiseThisIsThreadSafePtr;
328//! // Safety: I did my homework and this is sound on
329//! // my platform and for my use case. I promise!
330//! unsafe impl Send for IPromiseThisIsThreadSafePtr<'static> {}
331//! unsafe impl Sync for IPromiseThisIsThreadSafePtr<'static> {}
332//! ```
333//!
334//! If something goes wrong, that's on you! See also
335//! [URLO: Volatile + relaxed atomic load/store](https://users.rust-lang.org/t/volatile-relaxed-atomic-load-store/92792).
336//!
337//! # Principle of operation
338//!
339//! The derive macro [`RegMap`] takes as input the definition of a register map (a `struct`), and
340//! generates a custom pointer type that is a wrapper around a raw pointer to the original
341//! `struct`. This custom pointer provides methods to perform read / write volatile operations on
342//! the fields of the register map.
343//!
344//! Importantly, no references to the original register map need to ever be created. Instead, the
345//! derive macro uses the original `struct` definition to calculate the offsets needed for each
346//! memory access. The memory accesses are always performed on raw pointers with volatile
347//! semantics.
348//!
349//! Avoiding creation of references to volatile memory is important to ensure soundness, as
350//! discussed e.g. in
351//! [rust-lang/unsafe-code-guidelines#33](https://github.com/rust-lang/unsafe-code-guidelines/issues/33)
352//! and
353//! [rust-lang/unsafe-code-guidelines#411](https://github.com/rust-lang/unsafe-code-guidelines/issues/411).
354//!
355//! ## Sample generated code
356//!
357//! Some of the content in this section is considered implementation detail and is not subject to
358//! stability guarantees. Nonetheless, it might be useful to have a look at the macro-generated
359//! code to get a better understanding of the functionality of this crate.
360//!
361//! A relatively-simple register-map definition:
362//! ```
363//! # mod yoo {
364//! # use reg_map::RegMap;
365//! #[repr(C)]
366//! #[derive(RegMap)]
367//! struct Test {
368//! scalar_field: u64,
369//! array_field: [u64; 4096],
370//! }
371//! # } // mod yoo
372//! ```
373//!
374//! generates the following code (comments and docs omitted):
375//! ```
376//! # mod yoo {
377//! #[repr(C)]
378//! struct Test {
379//! scalar_field: u64,
380//! array_field: [u64; 4096],
381//! }
382//!
383//! #[allow(non_snake_case)]
384//! mod _mod_Test {
385//! use super::*;
386//!
387//! pub(super) struct TestPtr<'a> {
388//! ptr: ::core::ptr::NonNull<Test>,
389//! _ref: ::core::marker::PhantomData<&'a Test>,
390//! }
391//!
392//! impl<'a> TestPtr<'a> {
393//! #[inline]
394//! const unsafe fn from_nonnull(ptr: ::core::ptr::NonNull<Test>) -> Self {
395//! Self {
396//! ptr,
397//! _ref: ::core::marker::PhantomData,
398//! }
399//! }
400//! #[inline]
401//! pub const unsafe fn from_ptr(ptr: *mut Test) -> Self {
402//! Self::from_nonnull(::core::ptr::NonNull::new_unchecked(ptr))
403//! }
404//! #[inline]
405//! pub fn from_mut(reg: &'a mut Test) -> Self {
406//! unsafe { Self::from_ptr(reg) }
407//! }
408//! #[inline]
409//! pub const fn as_ptr(&self) -> *mut Test {
410//! self.ptr.as_ptr()
411//! }
412//! #[inline]
413//! pub fn scalar_field(&self) -> ::reg_map::Reg<'a, u64, ::reg_map::access::ReadWrite> {
414//! unsafe {
415//! ::reg_map::Reg::__MACRO_ONLY__from_ptr(::core::ptr::addr_of_mut!(
416//! (*self.as_ptr()).scalar_field
417//! ))
418//! }
419//! }
420//! #[inline]
421//! pub fn array_field(
422//! &self,
423//! ) -> ::reg_map::RegArray<'a, ::reg_map::Reg<'a, u64, ::reg_map::access::ReadWrite>, 4096>
424//! {
425//! unsafe {
426//! ::reg_map::RegArray::__MACRO_ONLY__from_ptr(::core::ptr::addr_of_mut!(
427//! (*self.as_ptr()).array_field
428//! ))
429//! }
430//! }
431//! }
432//!
433//! unsafe impl<'a> ::reg_map::RegMapPtr<'a> for TestPtr<'a> {
434//! type RegMap = Test;
435//! #[inline]
436//! unsafe fn from_nonnull(ptr: ::core::ptr::NonNull<Self::RegMap>) -> Self {
437//! Self::from_nonnull(ptr)
438//! }
439//! #[inline]
440//! unsafe fn from_ptr(ptr: *mut Self::RegMap) -> Self {
441//! Self::from_ptr(ptr)
442//! }
443//! #[inline]
444//! fn from_mut(reg: &'a mut Self::RegMap) -> Self {
445//! Self::from_mut(reg)
446//! }
447//! #[inline]
448//! fn as_ptr(&self) -> *mut Self::RegMap {
449//! self.as_ptr()
450//! }
451//! }
452//! }
453//!
454//! use _mod_Test::TestPtr;
455//! # } // mod yoo
456//! ```
457//!
458//! First of all, the derive macro generates a module `_mod_Test` that contains the generated
459//! pointer type `TestPtr`. The reason to define the type inside of a module is to enforce that a
460//! new pointer is only created through the `pub` associated functions `from_ptr` and `from_mut`.
461//! The defined `TestPtr` type is then re-exported out of the module.
462//!
463//! `TestPtr` itself is just a wrapper around a [`NonNull`](core::ptr::NonNull) pointer, plus a
464//! marker field to signal that it is semantically a `&'a Test`.
465//!
466//! A new `TestPtr` can be safely constructed from a `&mut Test` through `TestPtr::from_mut`, or
467//! `unsafe`ly from a `*mut Test` through `TestPtr::from_ptr`. A raw pointer to the underlying data
468//! can be obtained from a live `TestPtr` with the method `TestPtr::as_ptr`.
469//!
470//! The juice of the generated code are the `TestPtr::scalar_field` and `TestPtr::array_field`
471//! methods, which use [`addr_of_mut!`](core::ptr::addr_of_mut) to return a [`Reg`] and a
472//! [`RegArray`], respectively. These provide read / write volatile access without ever creating a
473//! reference to the underlying data.
474//!
475//! Finally, the generated code implements the [`RegMapPtr`] trait on `TestPtr` so that it can be
476//! stored in a [`RegArray`], if needed.
477//!
478//! # Comparison with other crates
479//!
480//! ## `volatile`
481//! The crate [volatile](https://lib.rs/crates/volatile) uses the same principle of operation as
482//! this crate, `reg-map`: a custom pointer type is defined to perform volatile read /write
483//! operations to the underlying memory.
484//!
485//! In fact, `reg-map` is heavily inspired by `volatile`! Differences are mainly ergonomic and in
486//! the exposed API surface.
487//!
488//! ## `volatile-register`
489//! The crate [volatile-register](https://lib.rs/crates/volatile-register), based on
490//! [vcell](https://lib.rs/crates/vcell), exposes a very clean API by providing a wrapper type that
491//! owns the data. In practice, it offers a `VolatileCell` which is *"just like
492//! [`Cell`](core::cell::Cell) but with volatile read / write operations"*.
493//!
494//! Unfortunately, this approach is unsound. See [rust-lang/unsafe-code-guidelines#33: What about: volatile accesses and memory-mapped IO](https://github.com/rust-lang/unsafe-code-guidelines/issues/33)
495//! for details. Long story short, every time there is a `&UnsafeCell<T>`, the compiler is allowed
496//! to insert spurious reads and writes. That is fine for "normal memory", but with volatile memory
497//! and memory-mapped IO reads and writes have side effects so this problematic.
498//!
499//! This crate, `reg-map`, does not use `UnsafeCell` and never creates references to the volatile
500//! memory, avoiding the soundness issue above.
501//!
502//! For the cases where the `volatile-register` approach happens to work, the assembly generated by
503//! the two approaches is identical.
504//!
505//! # Further reading
506//!
507//! Some links to relevant forum threads and GitHub issues:
508//! - [URLO: How to make an access volatile without std library?](https://users.rust-lang.org/t/how-to-make-an-access-volatile-without-std-library/85533)
509//! - [URLO: Volatile + relaxed atomic load/store](https://users.rust-lang.org/t/volatile-relaxed-atomic-load-store/92792)
510//! - [URLO: Why are memory mapped registers implemented with interior mutability?](https://users.rust-lang.org/t/why-are-memory-mapped-registers-implemented-with-interior-mutability/116119)
511//! - [rust-embedded/volatile-register#10: Usage of references is in conflict with use for MMIO](https://github.com/rust-embedded/volatile-register/issues/10)
512//! - [rust-lang/unsafe-code-guidelines#33: What about: volatile accesses and memory-mapped IO](https://github.com/rust-lang/unsafe-code-guidelines/issues/33)
513//! - [rust-lang/unsafe-code-guidelines#411: Can we have VolatileCell](https://github.com/rust-lang/unsafe-code-guidelines/issues/411)
514
515#![no_std]
516
517/// Derive macro to generate a pointer to a register map with volatile reads and writes.
518///
519/// See the [top-level documentation](crate) for usage information and examples.
520pub use reg_map_derive::RegMap;
521
522pub mod access;
523
524mod arr;
525pub use arr::{ArrayElem, RegArray};
526
527mod bounds;
528
529pub mod integers;
530
531mod iter;
532
533mod reg;
534pub use reg::{Reg, RegMapPtr};