Driver Architecture and Design

Relevant source files

This page documents the internal architecture and design principles of the arm_pl031 RTC driver, focusing on the core Rtc struct, register layout, and hardware abstraction patterns. The material covers how the driver translates hardware register operations into safe Rust APIs while maintaining minimal overhead.

For hardware interface details and MMIO operations, see Hardware Interface and MMIO. For register-specific operations and meanings, see Register Operations.

Core Driver Structure

The driver architecture centers around two primary components: the Registers struct that defines the hardware memory layout, and the Rtc struct that provides the safe API interface.

Driver Components and Relationships

flowchart TD
subgraph subGraph3["Hardware Layer"]
    MMIO["Memory-Mapped I/O"]
    PL031["PL031 RTC Hardware"]
end
subgraph subGraph2["Unsafe Operations Layer"]
    VolatileOps["Volatile Operationsread_volatile()write_volatile()"]
    PtrOps["Pointer Operationsaddr_of!()addr_of_mut!()"]
end
subgraph subGraph1["Hardware Abstraction Layer"]
    Registers["Registers struct"]
    RegisterFields["Register Fieldsdr: u32mr: u32lr: u32cr: u8imsc: u8ris: u8mis: u8icr: u8"]
end
subgraph subGraph0["Driver API Layer"]
    Rtc["Rtc struct"]
    PublicMethods["Public Methodsget_unix_timestamp()set_unix_timestamp()set_match_timestamp()matched()interrupt_pending()enable_interrupt()clear_interrupt()"]
end

MMIO --> PL031
PtrOps --> MMIO
PublicMethods --> Registers
RegisterFields --> VolatileOps
Registers --> RegisterFields
Rtc --> PublicMethods
Rtc --> Registers
VolatileOps --> PtrOps

Sources: src/lib.rs(L42 - L44)  src/lib.rs(L15 - L39)  src/lib.rs(L46 - L121) 

Register Memory Layout

The Registers struct provides a direct mapping to the PL031 hardware register layout, ensuring proper alignment and padding for hardware compatibility.

flowchart TD
subgraph subGraph0["Memory Layout (repr C, align 4)"]
    DR["dr: u32Offset: 0x00Data Register"]
    MR["mr: u32Offset: 0x04Match Register"]
    LR["lr: u32Offset: 0x08Load Register"]
    CR["cr: u8Offset: 0x0CControl Register"]
    PAD1["_reserved0: [u8; 3]Offset: 0x0D-0x0F"]
    IMSC["imsc: u8Offset: 0x10Interrupt Mask"]
    PAD2["_reserved1: [u8; 3]Offset: 0x11-0x13"]
    RIS["ris: u8Offset: 0x14Raw Interrupt Status"]
    PAD3["_reserved2: [u8; 3]Offset: 0x15-0x17"]
    MIS["mis: u8Offset: 0x18Masked Interrupt Status"]
    PAD4["_reserved3: [u8; 3]Offset: 0x19-0x1B"]
    ICR["icr: u8Offset: 0x1CInterrupt Clear"]
    PAD5["_reserved4: [u8; 3]Offset: 0x1D-0x1F"]
end

Sources: src/lib.rs(L15 - L39) 

Driver Design Principles

Safety Boundary Architecture

The driver implements a clear safety boundary where all unsafe operations are contained within method implementations, exposing only safe APIs to users.

ComponentSafety LevelResponsibility
Rtc::new()UnsafeInitial pointer validation and construction
Public methodsSafeHigh-level operations with safety guarantees
Volatile operationsUnsafe (internal)Direct hardware register access
Pointer arithmeticUnsafe (internal)Register address calculation

State Management Design

The Rtc struct follows a stateless design pattern, storing only the base pointer to hardware registers without caching any hardware state.

flowchart TD
subgraph subGraph2["Operation Pattern"]
    Read["Read OperationsDirect volatile readsNo local caching"]
    Write["Write OperationsDirect volatile writesImmediate effect"]
end
subgraph subGraph1["Hardware State"]
    HardwareRegs["PL031 Hardware RegistersAll state lives in hardware"]
end
subgraph subGraph0["Rtc State"]
    BasePtr["registers: *mut RegistersBase pointer to MMIO region"]
end

BasePtr --> HardwareRegs
Read --> HardwareRegs
Write --> HardwareRegs

Sources: src/lib.rs(L42 - L44)  src/lib.rs(L63 - L66)  src/lib.rs(L70 - L73) 

Method Organization and Patterns

Functional Categories

The driver methods are organized into distinct functional categories, each following consistent patterns for hardware interaction.

CategoryMethodsPatternRegister Access
Time Managementget_unix_timestamp(),set_unix_timestamp()Read/Write DR/LRDirect timestamp operations
Alarm Managementset_match_timestamp(),matched()Write MR, Read RISMatch register operations
Interrupt Managementenable_interrupt(),interrupt_pending(),clear_interrupt()Read/Write IMSC/MIS/ICRInterrupt control flow

Memory Access Pattern

All hardware access follows a consistent volatile operation pattern using addr_of! and addr_of_mut! macros for safe pointer arithmetic.

flowchart TD

ReadAddr --> ReadVolatile
ReadStart --> ReadAddr
ReadVolatile --> ReadReturn
WriteAddr --> WriteVolatile
WriteStart --> WriteAddr
WriteVolatile --> WriteComplete

Sources: src/lib.rs(L63 - L66)  src/lib.rs(L70 - L73)  src/lib.rs(L108 - L112) 

Concurrency and Thread Safety

Send and Sync Implementation

The driver implements Send and Sync traits with explicit safety documentation, enabling safe usage across thread boundaries.

flowchart TD
subgraph subGraph2["Usage Patterns"]
    SharedRead["Shared Read AccessMultiple &Rtc referencesConcurrent timestamp reading"]
    ExclusiveWrite["Exclusive Write AccessSingle &mut Rtc referenceSerialized configuration changes"]
end
subgraph subGraph1["Safety Guarantees"]
    DeviceMemory["Device Memory PropertiesHardware provides atomicityNo local state to corrupt"]
    ReadSafety["Concurrent Read SafetyMultiple readers allowedHardware state remains consistent"]
end
subgraph subGraph0["Thread Safety Design"]
    SendImpl["unsafe impl Send for RtcDevice memory accessible from any context"]
    SyncImpl["unsafe impl Sync for RtcRead operations safe from multiple threads"]
end

DeviceMemory --> ExclusiveWrite
DeviceMemory --> SharedRead
ReadSafety --> SharedRead
SendImpl --> DeviceMemory
SyncImpl --> ReadSafety

Sources: src/lib.rs(L123 - L128) 

Construction and Initialization

The driver construction follows an unsafe initialization pattern that requires explicit safety contracts from the caller.

Initialization Requirements

The new() method establishes the safety contract for proper driver operation:

  • Base address must point to valid PL031 MMIO registers
  • Memory must be mapped as device memory
  • No other aliases to the memory region
  • 4-byte alignment requirement

Sources: src/lib.rs(L46 - L60)