Review for a new crate providing volatile access to memory-mapped IO

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!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.