RISC-V RV32 Timers: Update a 64-Bit Deadline Without a False Intermediate Compare
On RV32, the architectural machine timer and compare value are still 64 bits wide. Two ordinary 32-bit accesses can expose a combination of old and new halves. A timer port must handle reading the moving counter and writing its deadline as different operations.
This article examines the standard machine-timer contract and OpenSBI's ACLINT MTIMER accessors. Actual register addresses, ordering attributes and platform access support must come from the selected device integration.
Separate a moving counter from a programmed deadline
The machine-level ISA 1.13 timer section specifies a 64-bit mtime and mtimecmp on RV32 as well as RV64. The unsigned comparison posts a machine timer interrupt when time is greater than or equal to the compare value.
The timer advances at a constant platform-defined rate. Do not substitute the processor's current cycle frequency for that rate. Converting a duration into ticks and writing the deadline correctly are separate obligations.
A counter read can cross a low-half rollover between accesses. A compare write can temporarily install an unintended deadline. Disabling the local interrupt handler does not make either pair of bus accesses one atomic 64-bit operation.
Read high, low, then high again
The pinned OpenSBI v1.9 ACLINT driver reads the high half, reads the low half, then checks the high half again. It retries if the two high values differ and combines the retained high/low pair only after they agree.
This prevents returning a low value from one rollover interval with a high value from another. Apply the mechanism through the platform's appropriate MMIO accessors; an arbitrary pointer cast is not a substitute for the bus-access contract.
The driver chooses its accessors according to XLEN and the reported 64-bit MMIO capability. Do not select a 64-bit memory operation merely because a C type is uint64_t or because the underlying register has 64 bits.

Guard the intermediate compare value
For little-endian, strongly ordered I/O, the ISA gives a three-store sequence: set the low half to all ones, write the new high half, then write the new low half. OpenSBI's examined compare-write path follows that pattern.
Consider illustrative values: current time 0x00000001ffffff00, old deadline 0x00000001fffffff0 and new deadline 0x0000000200000020. Both deadlines are in the future. Writing the new low half first produces intermediate value 0x0000000100000020, which is already behind current time and can post an unintended interrupt.
The all-ones low-half guard avoids that smaller intermediate comparison under the stated access-order assumptions. It is a compare-register technique: do not blindly apply the same sequence to changing the clock value itself. The driver explicitly distinguishes those two write cases.
Still tolerate delayed interrupt deassertion
The specification separately allows the comparison's changed result to reach MTIP eventually rather than immediately. A handler can therefore return after programming a future deadline and encounter an occasional still-pending timer interrupt.
That possibility does not mean the three-store update failed. Classify an actual intermediate-deadline error separately from propagation of an already posted interrupt. The specification advises software to tolerate the latter rather than routinely polling MTIP until it falls.
In a controlled register model or target test, exercise low-half rollover reads, deadlines that cross a high-half boundary and compare values already in the past. Record which accesses were performed and whether the final programmed deadline is correct. Use the platform's actual MMIO ordering and endianness; the illustrative arithmetic alone cannot qualify an unrelated bus layout.
No timer register was accessed here. A correct port demonstrates both a coherent timestamp snapshot and a safe deadline update, while preserving the architectural possibility of delayed interrupt-status propagation.