Volatile memory operations.
This library contains wrappers around raw pointers to perform volatile memory operations. This
is mainly useful for memory mapped I/O. Writing to the peripheral memory addresses will
normally be optimized out by the compiler. The Volatile
type wraps a memory address to
perform volatile operations in order to force the compiler to keep these memory accesses and
stores.
The creation of a Volatile
pointer is generally unsafe, but the actual operations that you
can perform on it are considered safe by Rust's standards. This is because the actual memory
operations are performed through the Deref
and DerefMut
traits, which are defined as safe
methods. It is important to remember that a Volatile
pointer is nearly identical to a
primitive pointer, and so all dereferencing operations on one should be considered unsafe (even
if not enforced by the compiler).
Examples
use Volatile;
const IO_ADDR: *const u32 = 0x4000_4400 as *const _;
unsafe
On some embedded devices you may want to do something like wait for a certain amount of time to pass measured by some amount of ticks.
// Some tick counter that may be updated by a hardware interrupt
static mut TICKS: usize = 0;
while unsafe
Normally, the Rust compilier would optimize this kind of operation into just an infinite
loop
, since the value of TICKS
can't change in a single threaded environment, but TICKS
could be updated by some hardware interrupt, so we want to keep reloading the value in order to
check it. So to get around this we can use a Volatile
pointer to force the compiler to reload
the value every time through the loop.
use Volatile;
static mut TICKS: usize = 0;
unsafe
Now the value of TICKS
will be reloaded every time through the loop.
Oftentimes when working with memory mapped peripherals, you will have a block of memory that
you want to be working on that contains control, status, and data registers for some hardware
peripheral. These are often best represented as structs with each of their registers as fields,
but without volatile operations, loads and stores to these memory addresses often get optimized
out by the compiler. To get around this you can use a Volatile
pointer to point at the mapped
address and have it be represented as a struct of the correct type.
use Volatile;
const USART_ADDR: *const Usart = 0x4000_4400 as *const _;
// For transmitting and receiving data over serial
let recieved = unsafe ;
Every field access to a pointed at struct will be considered volatile and so will not be optimized out by the compiler.
Just as with primitive pointers, Volatile
pointers can be created from valid references
safely, though their use should still be considered unsafe.
#
use Volatile;
let x: u32 = 0;
let ptr = from;