Expand description
Peripheral Management
Most peripherals are implemented as memory mapped I/O (MMIO). Intrinsically, this means that accessing a peripheral requires dereferencing a raw pointer that points to the peripheral’s memory.
Generally, Tock peripherals are modeled by two structures, such as:
/// The MMIO Structure.
#[repr(C)]
#[allow(dead_code)]
pub struct PeripheralRegisters {
control: VolatileCell<u32>,
interrupt_mask: VolatileCell<u32>,
}
/// The Tock object that holds all information for this peripheral.
pub struct PeripheralHardware<'a> {
mmio_address: StaticRef<PeripheralRegisters>,
clock: &'a ChipSpecificPeripheralClock,
}
The first structure mirrors the MMIO specification. The second structure holds a pointer to the actual address in memory. It also holds other information for the peripheral. Kernel traits will be implemented for this peripheral hardware structure. As the peripheral cannot derefence the raw MMIO pointer safely, Tock provides the PeripheralManager interface:
impl hil::uart::Configure for PeripheralHardware {
fn configure(&self, params: hil::uart::Parameters) -> Result<(), ErrorCode> {
let peripheral = &PeripheralManager::new(self);
peripheral.registers.control.set(0x0);
// ^^^^^^^^^-- This is type &PeripheralRegisters
Ok(())
}
}
Each peripheral must tell the kernel where its registers live in memory:
/// Teaching the kernel how to create PeripheralRegisters.
use kernel::platform::chip::NoClockControl;
impl PeripheralManagement<NoClockControl> for PeripheralHardware {
type RegisterType = PeripheralRegisters;
fn get_registers(&self) -> &PeripheralRegisters {
&*self.mmio_address
}
}
Note, this example kept the mmio_address
in the PeripheralHardware
structure, which is useful when there are multiple copies of the same
peripheral (e.g. multiple UARTs). For single-instance peripherals, it’s fine
to simply return the address directly from get_registers
.
§Peripheral Clocks
To facilitate low-power operation, PeripheralManager captures the peripheral’s clock upon instantiation. The intention is to exploit Ownership Based Resource Management to capture peripheral power state.
To enable this, peripherals must inform the kernel which clock they use, and
when the clock should be enabled and disabled. Implementations of the
before/after_mmio_access
methods must take care to not access hardware
without enabling clocks if needed if they use hardware for bookkeeping.
use kernel::utilities::peripheral_management::PeripheralManagement;
use kernel::utilities::StaticRef;
use kernel::platform::chip::ClockInterface;
// A dummy clock for this example.
// Real peripherals that do not have clocks should use NoClockControl from this module.
struct ExampleClock {};
impl ClockInterface for ExampleClock {
fn is_enabled(&self) -> bool { true }
fn enable(&self) { }
fn disable(&self) { }
}
// Dummy hardware for this example.
struct SpiRegisters {};
struct SpiHw<'a> {
mmio_address: StaticRef<SpiRegisters>,
clock: &'a ExampleClock,
busy: bool,
};
/// Teaching the kernel which clock controls SpiHw.
impl<'a> PeripheralManagement<ExampleClock> for SpiHw<'a> {
type RegisterType = SpiRegisters;
fn get_registers(&self) -> &SpiRegisters { &*self.mmio_address }
fn get_clock(&self) -> &ExampleClock { self.clock }
fn before_peripheral_access(&self, clock: &ExampleClock, _registers: &SpiRegisters) {
clock.enable();
}
fn after_peripheral_access(&self, clock: &ExampleClock, _registers: &SpiRegisters) {
if !self.busy {
clock.disable();
}
}
}
Structs§
- Object used to access memory mapped registers with the
PeripheralManagement
interface.
Traits§
- A structure encapsulating a peripheral should implement this trait.