Overview
Relevant source files
Purpose and Scope
The arm_pl031
crate provides a safe Rust driver for the ARM PL031 Real-Time Clock (RTC) hardware on aarch64 systems. This driver enables embedded and systems software to read and set time values, configure interrupts, and manage RTC functionality through a memory-mapped I/O interface.
The crate is designed for no_std
environments and supports both basic Unix timestamp operations and optional high-level DateTime handling through the chrono
feature. It targets embedded systems, hypervisors, and operating system kernels that need direct hardware access to PL031 RTC devices.
For detailed implementation specifics, see Core Driver Implementation. For optional features and extensions, see Features and Extensions. For practical usage guidance, see Getting Started.
Sources: Cargo.toml(L1 - L20) src/lib.rs(L1 - L8) README.md(L1 - L6)
System Architecture
The arm_pl031
driver implements a layered architecture that provides multiple abstraction levels over the PL031 hardware, from raw register access to high-level DateTime operations.
Core Driver Architecture
flowchart TD subgraph subGraph3["Safe API Layer"] GetTime["get_unix_timestamp()"] SetTime["set_unix_timestamp()"] SetMatch["set_match_timestamp()"] IntOps["Interrupt Operations"] end subgraph subGraph2["Register Interface"] DR["dr: Data Register"] MR["mr: Match Register"] LR["lr: Load Register"] CR["cr: Control Register"] IMSC["imsc: Interrupt Mask"] RIS["ris: Raw Interrupt Status"] MIS["mis: Masked Interrupt Status"] ICR["icr: Interrupt Clear"] end subgraph subGraph1["Driver Core (src/lib.rs)"] Rtc["Rtc struct"] Registers["Registers struct"] SafetyBoundary["unsafe fn new()"] end subgraph subGraph0["Hardware Layer"] PL031["PL031 RTC Hardware"] MMIO["Memory-Mapped Registers"] DT["Device Tree Configuration"] end DT --> PL031 MMIO --> SafetyBoundary PL031 --> MMIO Registers --> CR Registers --> DR Registers --> ICR Registers --> IMSC Registers --> LR Registers --> MIS Registers --> MR Registers --> RIS Rtc --> GetTime Rtc --> IntOps Rtc --> Registers Rtc --> SetMatch Rtc --> SetTime SafetyBoundary --> Rtc
The driver centers around the Rtc
struct which encapsulates a raw pointer to the hardware registers. The Registers
struct defines the memory layout matching the PL031 hardware specification, ensuring correct MMIO access patterns.
Sources: src/lib.rs(L15 - L44) src/lib.rs(L46 - L61)
Feature-Based System Components
The system uses Cargo features to provide modular functionality. The core driver in lib.rs
provides essential RTC operations, while the optional chrono
module adds convenient DateTime handling for applications that need it.
Sources: Cargo.toml(L14 - L19) src/lib.rs(L10 - L11) src/lib.rs(L123 - L128)
Hardware Interface and Safety Model
Memory-Mapped I/O Operations
flowchart TD subgraph subGraph4["Hardware Registers"] HardwareRegs["PL031 MMIO Registers"] PhysicalRTC["Physical RTC Hardware"] end subgraph subGraph3["Volatile MMIO"] ReadVol["addr_of!(*registers.dr).read_volatile()"] WriteVol["addr_of_mut!(*registers.lr).write_volatile()"] RegAccess["Register field access"] end subgraph subGraph2["Safe Operations"] GetUnix["get_unix_timestamp() -> u32"] SetUnix["set_unix_timestamp(u32)"] SetMatch["set_match_timestamp(u32)"] EnableInt["enable_interrupt(bool)"] CheckInt["interrupt_pending() -> bool"] ClearInt["clear_interrupt()"] end subgraph subGraph1["Safety Boundary"] UnsafeNew["unsafe fn new(base_address)"] SafetyDoc["Safety Documentation"] end subgraph subGraph0["Application Code"] UserApp["User Application"] BaseAddr["base_address: *mut u32"] end BaseAddr --> UnsafeNew CheckInt --> ReadVol ClearInt --> WriteVol EnableInt --> WriteVol GetUnix --> ReadVol HardwareRegs --> PhysicalRTC ReadVol --> RegAccess RegAccess --> HardwareRegs SetMatch --> WriteVol SetUnix --> WriteVol UnsafeNew --> CheckInt UnsafeNew --> ClearInt UnsafeNew --> EnableInt UnsafeNew --> GetUnix UnsafeNew --> SafetyDoc UnsafeNew --> SetMatch UnsafeNew --> SetUnix UserApp --> BaseAddr WriteVol --> RegAccess
The driver implements a clear safety boundary where unsafe operations are isolated to the constructor new()
function, while all subsequent operations use safe Rust APIs. All hardware access uses volatile operations to ensure proper MMIO semantics.
Sources: src/lib.rs(L46 - L60) src/lib.rs(L62 - L74) src/lib.rs(L76 - L120)
Key Code Entities and Relationships
The following table maps the primary system components to their code representations:
System Component | Code Entity | File Location | Purpose |
---|---|---|---|
RTC Driver | Rtcstruct | src/lib.rs42-44 | Main driver interface |
Hardware Layout | Registersstruct | src/lib.rs15-39 | Memory-mapped register layout |
Initialization | unsafe fn new() | src/lib.rs47-60 | Driver constructor with safety contract |
Time Reading | get_unix_timestamp() | src/lib.rs62-67 | Read current time from hardware |
Time Setting | set_unix_timestamp() | src/lib.rs69-74 | Set hardware time |
Interrupt Control | enable_interrupt() | src/lib.rs108-113 | Manage interrupt masking |
Interrupt Status | interrupt_pending() | src/lib.rs97-102 | Check interrupt state |
Optional DateTime | chrono module | src/lib.rs10-11 | High-level time operations |
Register Field Mapping
The Registers
struct directly corresponds to the PL031 hardware specification:
Register Field | Hardware Purpose | Access Pattern |
---|---|---|
dr | Data Register - current time | Read-only viaread_volatile() |
mr | Match Register - interrupt trigger | Write viawrite_volatile() |
lr | Load Register - set time | Write viawrite_volatile() |
cr | Control Register - device control | Reserved for future use |
imsc | Interrupt Mask - enable/disable | Write viawrite_volatile() |
ris | Raw Interrupt Status - match state | Read viaread_volatile() |
mis | Masked Interrupt Status - pending | Read viaread_volatile() |
icr | Interrupt Clear - acknowledge | Write viawrite_volatile() |
Sources: src/lib.rs(L17 - L39) src/lib.rs(L63 - L66) src/lib.rs(L70 - L73) src/lib.rs(L78 - L81)