I've just published reg-map
, my first public crate, and I'd be really happy if someone wanted to have a look at the code. I've used this crate in my internal projects for a while, but now I thought I's try to publish it and see if it can be useful to anybody else.
The crate exposes the derive macro RegMap
. The macro takes in a register-map definition, and generates a custom pointer type providing volatile read / write operations on its fields.
The crate works on the stable toolchain and in no_std
environments. Read-only and write-only accesses, nested register maps, (multi-dimensional) arrays of registers and indexing/iteration are supported.
Examples
In addition to the Basic usage section on the documentation, here's some more examples from the test suite:
Simple
#[repr(C)]
#[derive(RegMap, Default)]
pub struct Simple {
field1: u64,
field2: u64,
}
#[repr(C)]
#[derive(RegMap)]
struct Nested {
field1: u64,
field2: NestedInner,
}
#[test]
fn simple() {
let mut regs = Simple {
field1: 23,
field2: 45,
};
let ptr = SimplePtr::from_mut(&mut regs);
assert_eq!(ptr.field1().read(), 23);
assert_eq!(ptr.field2().read(), 45);
ptr.field1().write(32);
ptr.field2().write(54);
assert_eq!(ptr.field1().read(), 32);
assert_eq!(ptr.field2().read(), 54);
assert_eq!(regs.field1, 32);
assert_eq!(regs.field2, 54);
}
#[test]
fn nested() {
let mut regs = Nested {
field1: 1,
field2: NestedInner { inner1: 2 },
};
let ptr = NestedPtr::from_mut(&mut regs);
assert_eq!(ptr.field1().read(), 1);
assert_eq!(ptr.field2().inner1().read(), 2);
ptr.field1().write(10);
ptr.field2().inner1().write(20);
assert_eq!(ptr.field1().read(), 10);
assert_eq!(ptr.field2().inner1().read(), 20);
assert_eq!(regs.field1, 10);
assert_eq!(regs.field2.inner1, 20);
}
Iteration
#[repr(C)]
#[derive(RegMap, Default)]
struct Array {
field1: u64,
field2: [u64; 32],
}
#[test]
fn array_iter() {
let mut regs = Array::default();
let ptr = ArrayPtr::from_mut(&mut regs);
assert_eq!(ptr.field2().len(), 32);
ptr.field1().write(42);
for (i, elem) in ptr.field2().iter().enumerate() {
elem.write(i as u64 * 2 + 1);
}
assert_eq!(ptr.field1().read(), 42);
for (i, elem) in ptr.field2().iter().enumerate() {
assert_eq!(elem.read(), i as u64 * 2 + 1);
}
assert_eq!(regs.field1, 42);
for (i, v) in regs.field2.into_iter().enumerate() {
assert_eq!(v, i as u64 * 2 + 1);
}
}
Multi-dimensional array
#[repr(C)]
#[derive(RegMap, Default)]
struct Array4d {
data: [[[[u64; 2]; 3]; 5]; 7],
}
#[test]
fn array_4d() {
let mut regs = Array4d::default();
let dim0 = regs.data.len();
let dim1 = regs.data[0].len();
let dim2 = regs.data[0][0].len();
let dim3 = regs.data[0][0][0].len();
let ptr = Array4dPtr::from_mut(&mut regs);
assert_eq!(ptr.data().len(), dim0);
for (i, a) in ptr.data().iter().enumerate() {
assert_eq!(a.len(), dim1);
for (j, b) in a.iter().enumerate() {
assert_eq!(b.len(), dim2);
for (k, c) in b.iter().enumerate() {
assert_eq!(c.len(), dim3);
for (m, d) in c.iter().enumerate() {
assert_eq!(d.read(), 0);
d.write((i * dim1 + j * dim2 + k * dim3 + m) as u64);
}
}
}
}
for (i, a) in ptr.data().iter().enumerate() {
for (j, b) in a.iter().enumerate() {
for (k, c) in b.iter().enumerate() {
for (m, d) in c.iter().enumerate() {
assert_eq!(d.read(), (i * dim1 + j * dim2 + k * dim3 + m) as u64);
}
}
}
}
for (i, a) in regs.data.iter().enumerate() {
for (j, b) in a.iter().enumerate() {
for (k, c) in b.iter().enumerate() {
for (m, d) in c.iter().enumerate() {
assert_eq!(*d, (i * dim1 + j * dim2 + k * dim3 + m) as u64);
}
}
}
}
}
Acknowledgements
I've been working on and using this crate since @alice convinced me to ditch volatile-register
due to its soundness issues. Thanks!
This crate also ended up being very similar to the crate volatile
by Philipp Oppermann, which was a big source of inspiration. Its API didn't suit me very well, especially regarding nested and multi-dimensional arrays, so I ended up working on my own crate. I also just wanted to get my hands dirty. I hope the result is still worth sharing and can be useful to someone else.
So yeah, thanks!