RISC-V MMIO Ordering: Match FENCE Sets to Device and Memory Accesses

A RISC-V driver writes a normal-memory command record and then rings a memory-mapped device doorbell. A fence appears between the two operations, yet the review cannot establish their required ordering. The missing question is which access classes that fence actually names: normal-memory writes and device outputs are distinct sets.

This discussion uses RV32I base ISA 2.1 in specification library v20260120, plus the Linux v6.12 RISC-V barrier definitions and MMIO accessors. The architectural access classes and the Linux implementation assumptions are deliberately identified separately. No device or ordering experiment was run.

Label both sides of the intended edge

The FENCE predecessor and successor sets can contain device input I, device output O, normal-memory read R and normal-memory write W. The execution environment defines which addresses and operations belong to the I/O classes.

An MMIO access can use an ordinary load or store instruction while still being classified as device input or output. Its assembly mnemonic alone does not make it a normal-memory R or W operation. The architectural discussion specifically describes typical uncached MMIO loads and stores being ordered with the I/O bits.

For the abstract command example, declare the record write as W and the doorbell write as O. The desired edge is W -> O. This classification is an assumption to verify against the actual platform’s mapping and device contract, not a conclusion inferred from a C pointer’s type.

Check the set intersection before trusting the fence

A fence rw,rw names normal-memory reads and writes on both sides. It does not include device output in its successor set. Therefore, that instruction alone does not establish the example’s W -> O edge.

A fence w,o directly names that particular predecessor and successor combination. A broader fence iorw,iorw includes it too, along with other classes. The point is not to choose the broadest mnemonic everywhere; it is to show exactly which required relationship the selected platform abstraction supplies.

The I in these sets means device input. It is not shorthand for instruction-fetch synchronization, and this analysis does not replace the separate contract for making modified executable code visible to instruction fetch.

Fence-set worksheet compares memory-only rw,rw, targeted w,o, broad iorw,iorw and ordered-read i,ir against the stated memory-write-to-device-output edge.
Original ordering-set worksheet derived from the architecture and Linux v6.12 accessors; no hardware litmus test was executed.

Read the actual Linux accessor path

In the pinned RISC-V mmio.h, ordered writes perform their pre-write hook through __io_bw(), which uses RISCV_FENCE(w, o). An ordered write such as writel() then performs the I/O write and invokes its post-write bookkeeping hook.

Ordered reads use __io_ar(v) after the read, with RISCV_FENCE(i, ir). That names prior device input before subsequent device input or normal-memory read. The source describes the ordered read path as ordering relative to following normal-memory reads and delay loops, and ordered writes relative to prior normal-memory writes.

These are the relevant source-derived relationships, not complete standalone replacements for the accessors:

ordered write path: prior normal-memory write -> fence w,o -> I/O write
ordered read path:  I/O read -> fence i,ir -> later input or memory read

A Linux driver should use the supported accessors and mapping APIs rather than replace them with a raw pointer dereference and guessed inline assembly. The accessors carry more than the one ordering edge highlighted in this worksheet.

Understand what relaxed omits

The same header says relaxed I/O primitives do not guarantee ordering relative to normal-memory accesses. It still describes ordering with other I/O accesses to the same peripheral under its platform assumption that I/O regions are strongly ordered on channel zero.

Therefore, “relaxed means no ordering of any kind” overstates the source. But replacing writel() with writel_relaxed() also cannot preserve the normal-memory-to-device guarantee merely because both ultimately perform a store. Any missing edge needs a separately justified supported ordering mechanism.

Do not transplant the Linux header’s I/O-channel assumption into arbitrary bare-metal firmware. It belongs to the implementation’s documented platform model. A board with another execution environment needs its own classification and ordering analysis.

Keep SMP barriers and device completion distinct

The v6.12 barrier header explicitly separates device-capable barriers from memory-only SMP barriers. For example, __mb() uses iorw,iorw, while __smp_mb() uses rw,rw. Similar-looking names are insufficient grounds for substituting one at a driver boundary.

Neither the access-set worksheet nor a successful fence proves DMA cache visibility, exclusive ownership or completion of the device command. Those requirements still come from the DMA API, mapping attributes and device-specific protocol. The architecture also excludes out-of-band signaling observations, such as when an interrupt-line change reaches an interrupt controller, from the fence guarantee.

A proposed review should record each operation’s class, the required edge, the selected Linux accessor and the platform assumptions it relies on. Reject an unexplained normal-memory-only fence for an I/O successor, then qualify the remaining DMA and completion obligations separately. This yields a precise ordering argument without presenting a synthetic worksheet as a hardware litmus result.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *