Hardware Interface

Relevant source files

This document covers the low-level hardware interface implementation of the riscv_goldfish RTC driver, focusing on memory-mapped I/O operations, register layout, and the abstraction layer between software and the Goldfish RTC hardware. For higher-level API usage patterns, see API Reference. For time conversion logic, see Time Conversion.

Register Layout and Memory Mapping

The Goldfish RTC hardware exposes a simple memory-mapped interface consisting of two 32-bit registers that together form a 64-bit nanosecond timestamp counter. The driver defines the register offsets as compile-time constants and uses a base address provided during initialization.

Register Map

OffsetRegister NameWidthPurpose
0x00RTC_TIME_LOW32-bitLower 32 bits of nanosecond timestamp
0x04RTC_TIME_HIGH32-bitUpper 32 bits of nanosecond timestamp

Memory Address Calculation

flowchart TD
BaseAddr["base_address"]
LowAddr["Low Register Address"]
HighAddr["High Register Address"]
LowReg["RTC_TIME_LOW Register"]
HighReg["RTC_TIME_HIGH Register"]
NS64["64-bit Nanosecond Value"]

BaseAddr --> HighAddr
BaseAddr --> LowAddr
HighAddr --> HighReg
HighReg --> NS64
LowAddr --> LowReg
LowReg --> NS64

The driver calculates absolute register addresses by adding the register offset constants to the base address provided during Rtc::new() construction.

Sources: src/lib.rs(L6 - L7)  src/lib.rs(L31 - L33) 

Memory-Mapped I/O Operations

The hardware interface implements volatile memory operations to ensure proper communication with the RTC hardware. The driver provides two core memory access functions that handle the low-level register reads and writes.

Volatile Memory Access Functions

flowchart TD
ReadOp["read(reg: usize)"]
AddrCalc["Address Calculation"]
WriteOp["write(reg: usize, value: u32)"]
ReadPtr["Read Pointer Cast"]
WritePtr["Write Pointer Cast"]
HardwareRead["Hardware Register Read"]
HardwareWrite["Hardware Register Write"]
ReadResult["Return Value"]
WriteResult["Write Complete"]

AddrCalc --> ReadPtr
AddrCalc --> WritePtr
HardwareRead --> ReadResult
HardwareWrite --> WriteResult
ReadOp --> AddrCalc
ReadPtr --> HardwareRead
WriteOp --> AddrCalc
WritePtr --> HardwareWrite

The read() function performs volatile reads to prevent compiler optimizations that might cache register values, while write() ensures immediate hardware updates. Both operations are marked unsafe due to raw pointer manipulation.

Sources: src/lib.rs(L17 - L23) 

64-bit Value Handling Across 32-bit Registers

The Goldfish RTC hardware stores nanosecond timestamps as a 64-bit value, but exposes this through two separate 32-bit registers. The driver implements bidirectional conversion between the unified 64-bit representation and the split register format.

Read Operation Data Flow

flowchart TD
subgraph subGraph2["Value Reconstruction"]
    Combine["(high << 32) | low"]
    UnixTime["Unix Timestamp"]
end
subgraph subGraph1["Driver Read Operations"]
    ReadLow["read(RTC_TIME_LOW)"]
    LowU64["low: u64"]
    ReadHigh["read(RTC_TIME_HIGH)"]
    HighU64["high: u64"]
end
subgraph subGraph0["Hardware Registers"]
    LowHW["RTC_TIME_LOW32-bit hardware"]
    HighHW["RTC_TIME_HIGH32-bit hardware"]
end

Combine --> UnixTime
HighHW --> ReadHigh
HighU64 --> Combine
LowHW --> ReadLow
LowU64 --> Combine
ReadHigh --> HighU64
ReadLow --> LowU64

Write Operation Data Flow

flowchart TD
subgraph subGraph3["Hardware Registers"]
    HighHWOut["RTC_TIME_HIGHupdated"]
    LowHWOut["RTC_TIME_LOWupdated"]
end
subgraph subGraph2["Hardware Write"]
    WriteHigh["write(RTC_TIME_HIGH, high)"]
    WriteLow["write(RTC_TIME_LOW, low)"]
end
subgraph subGraph1["Value Splitting"]
    HighPart["High 32 bits"]
    LowPart["Low 32 bits"]
end
subgraph subGraph0["Input Processing"]
    UnixIn["unix_time: u64"]
    Nanos["time_nanos: u64"]
end

HighPart --> WriteHigh
LowPart --> WriteLow
Nanos --> HighPart
Nanos --> LowPart
UnixIn --> Nanos
WriteHigh --> HighHWOut
WriteLow --> LowHWOut

The write operation updates the high register first, then the low register, to maintain temporal consistency during the split-register update sequence.

Sources: src/lib.rs(L36 - L40)  src/lib.rs(L43 - L49) 

Hardware Abstraction Layer

The Rtc struct provides a clean abstraction over the raw hardware interface, encapsulating the base address and providing safe access patterns for register operations.

Driver Structure and Hardware Mapping

flowchart TD
subgraph subGraph2["Physical Hardware"]
    GoldfishRTC["Goldfish RTC Device64-bit nanosecond counter"]
end
subgraph subGraph1["Hardware Abstraction"]
    BaseAddr["base_address field"]
    MemoryMap["Memory Mapping"]
    RegOffsets["RTC_TIME_LOWRTC_TIME_HIGH"]
end
subgraph subGraph0["Software Layer"]
    RtcStruct["Rtc { base_address: usize }"]
    PublicAPI["get_unix_timestamp()set_unix_timestamp()"]
    PrivateOps["read(reg)write(reg, value)"]
end

BaseAddr --> MemoryMap
MemoryMap --> GoldfishRTC
PrivateOps --> MemoryMap
PublicAPI --> PrivateOps
RegOffsets --> MemoryMap
RtcStruct --> PublicAPI

The abstraction separates hardware-specific details (register offsets, volatile operations) from the public API, allowing safe high-level operations while maintaining direct hardware access efficiency.

Sources: src/lib.rs(L11 - L14)  src/lib.rs(L26 - L50) 

Safety Considerations

The hardware interface implements several safety measures to ensure reliable operation:

  • Volatile Operations: All register access uses read_volatile() and write_volatile() to prevent compiler optimizations that could interfere with hardware communication
  • Unsafe Boundaries: Raw pointer operations are confined to private methods, exposing only safe public APIs
  • Type Safety: Register values are properly cast between u32 hardware format and u64 application format
  • Address Validation: Base address is stored during construction and used consistently for all register calculations

The driver assumes the provided base address corresponds to valid, mapped Goldfish RTC hardware, typically obtained from device tree parsing during system initialization.

Sources: src/lib.rs(L17 - L23)  src/lib.rs(L31 - L33)