[−][src]Crate voladdress
voladdress
is a crate that makes it easy to work with volatile memory
addresses (eg: memory mapped hardware).
When working with volatile memory, it's assumed that you'll generally be working with one or more of:
- A single address (
VolAddress
) - A block of contiguous memory addresses (
VolBlock
) - A series of evenly strided memory addresses (
VolSeries
)
All the types have unsafe
creation and then safe use, so that the
actual usage is as ergonomic as possible. Obviously you tend to use an
address far more often than you name an address, so that should be the best
part of the experience. Iterators are also provided for the VolBlock
and
VolSeries
types.
For example, on the GBA there's a palette of 256 color values (u16
) for
the background palette starting at 0x500_0000
, so you might write
something like
use typenum::consts::U256; use voladdress::{VolBlock, VolAddress}; pub type Color = u16; pub const PALRAM_BG: VolBlock<Color,U256> = unsafe { VolBlock::new(0x500_0000) };
And then in your actual program you might do something like this
fn main() { let i = 5; // the palette is all 0 (black) at startup. assert_eq!(PALRAM_BG.index(i).read(), 0); // we can make that index blue instead. const BLUE: u16 = 0b11111; PALRAM_BG.index(i).write(BLUE); assert_eq!(PALRAM_BG.index(i).read(), BLUE); }
You could use an address of any *mut T
that you have (which is how the
tests and doctests work), but the intent is that you use this crate with
memory mapped hardware. Exactly what hardware is memory mapped where depends
on your target device. Please read your target device's documentation.
Why Use This?
It may seem rather silly to have special types for what is basically a *mut T
. However, when reading and writing with a normal pointer (eg: *ptr
or
*ptr = x;
) Rust will desugar that to the
read and
write functions. The
compiler is allowed to elide these accesses if it "knows" what the value is
already going to be, or if it "knows" that the read will never be seen.
However, when working with memory mapped hardware the read and write
operations have various side effects that the compiler isn't aware of, so
the access must not be elided. You have to use
read_volatile
and
write_volatile,
which are immune to being elided by the compiler. The rust standard library
doesn't have a way to "tag" a pointer as being volatile to force that
volatile access always be used, and so we have this crate.
There are other crates that address the general issue of volatile memory,
but none that I've seen are as easy to use as this one. They generally
expect you to cast the target address (a usize
that you get out of your
hardware manual) into a raw pointer to their crate's volatile type (eg: let p = 1234 as *mut VolatileCell<u16>
), but then you have to dereference that
raw pointer each time you call read or write, and it always requires
parenthesis too, because of prescience rules (eg: let a = (*p).read();
).
You end up with unsafe
blocks and parens and asterisks all over the code
for no benefit.
This crate is much better than any of that. Once you've decided that the
initial unsafety is alright, and you've created a VolAddress
value for
your target type at the target address, the read
and write
methods are
entirely safe to use and don't require the manual de-reference.
Can't you impl Deref
/DerefMut
and Index
/IndexMut
on these things?
No. Absolutely not. They all return &T
or &mut T
, which use normal reads
and writes, so the accesses can be elided by the compiler. In fact
references end up being more aggressive about access elision than happens
raw pointers. For standard code this is exactly what we want (it makes the
code faster to skip reads and writes we don't need), but with memory mapped
hardware this is the opposite of a good time.
Modules
read_only | This is like the top level module, but types here are read only. |
write_only | This is like the top level module, but types here are write only. |
Structs
VolAddress | Abstracts the use of a volatile memory address. |
VolBlock | A block of addresses all in a row. |
VolIter | An iterator that produces consecutive |
VolSeries | A series of evenly strided addresses. |
VolStridingIter | An iterator that produces strided |