Multiple registers
We can use a struct to represent the memory layout of the UART’s registers,
using types from the safe-mmio
crate to wrap ones which can be read or written
safely.
use safe_mmio::fields::{ReadPure, ReadPureWrite, ReadWrite, WriteOnly}; #[repr(C, align(4))] pub struct Registers { dr: ReadWrite<u16>, _reserved0: [u8; 2], rsr: ReadPure<ReceiveStatus>, _reserved1: [u8; 19], fr: ReadPure<Flags>, _reserved2: [u8; 6], ilpr: ReadPureWrite<u8>, _reserved3: [u8; 3], ibrd: ReadPureWrite<u16>, _reserved4: [u8; 2], fbrd: ReadPureWrite<u8>, _reserved5: [u8; 3], lcr_h: ReadPureWrite<u8>, _reserved6: [u8; 3], cr: ReadPureWrite<u16>, _reserved7: [u8; 3], ifls: ReadPureWrite<u8>, _reserved8: [u8; 3], imsc: ReadPureWrite<u16>, _reserved9: [u8; 2], ris: ReadPure<u16>, _reserved10: [u8; 2], mis: ReadPure<u16>, _reserved11: [u8; 2], icr: WriteOnly<u16>, _reserved12: [u8; 2], dmacr: ReadPureWrite<u8>, _reserved13: [u8; 3], }
#[repr(C)]
tells the compiler to lay the struct fields out in order, following the same rules as C. This is necessary for our struct to have a predictable layout, as default Rust representation allows the compiler to (among other things) reorder fields however it sees fit.- There are a number of different crates providing safe abstractions around MMIO
operations; we recommend the
safe-mmio
crate. - The difference between
ReadPure
orReadOnly
(and likewise betweenReadPureWrite
andReadWrite
) is whether reading a register can have side-effects which change the state of the device. E.g. reading the data register pops a byte from the receive FIFO.ReadPure
means that reads have no side-effects, they are purely reading data.