Skip to content

std.mmio

std.mmio exposes one host-provided, bounds-carrying authority for direct memory-mapped device-register access. It does not map physical addresses from program input and does not expose the mapping as a raw pointer.

Migration status: RFC 0076 accepts the Region and AccessError target described here. The module, compiler support, and zynx-launch v5 support are not yet implemented.

Host Region Acquisition

An executable composition root declares each required mapping as a private host loan:

Zynx
// RFC 0076 target; implementation pending.
import std.mmio;

@host let uart: mmio.Region in _;

fn main() throws(mmio.AccessError) {
	unsafe {
		let status = try uart.load_u32(4);
		try uart.store_u32(8, status | (1 as u32));
	}
}

Region is an opaque, non-Copy, non-Clone value which is neither Send nor Sync. Its sole public field is the exact byte extent of the mapping:

Zynx
// RFC 0076 target declaration shape; implementation pending.
export struct Region: !Send, !Sync {
	@readonly export var size: usize,
}

Only a trusted target/host adapter may create the external-mapping provenance inside a Region. User code has no constructor, from_address, from_ptr, addr, ptr, as_ptr, duplicate, close, or unmap operation. It cannot move, replace, exclusively borrow, or retain the host-loaned value beyond its private instance region. The host retains the mapping owner until the entry has fully quiesced, including all work carrying a permitted ordinary loan.

Only the direct body of parameterless main resolves the host binding. Helpers and device packages receive an ordinary explicit mmio.Region in L parameter. There is no registry, enumeration, discovery, default mapping, wildcard, first device, or lookup by a name known to program code.

Exact Access Surface

Region has exactly the following sixteen unsafe methods. They are concrete methods, not a generic load<T>/store<T> family:

PayloadLoadStore
i8unsafe load_i8(self, offset: usize) throws(AccessError) -> i8unsafe store_i8(self, offset: usize, value: i8) throws(AccessError)
u8unsafe load_u8(self, offset: usize) throws(AccessError) -> u8unsafe store_u8(self, offset: usize, value: u8) throws(AccessError)
i16unsafe load_i16(self, offset: usize) throws(AccessError) -> i16unsafe store_i16(self, offset: usize, value: i16) throws(AccessError)
u16unsafe load_u16(self, offset: usize) throws(AccessError) -> u16unsafe store_u16(self, offset: usize, value: u16) throws(AccessError)
i32unsafe load_i32(self, offset: usize) throws(AccessError) -> i32unsafe store_i32(self, offset: usize, value: i32) throws(AccessError)
u32unsafe load_u32(self, offset: usize) throws(AccessError) -> u32unsafe store_u32(self, offset: usize, value: u32) throws(AccessError)
i64unsafe load_i64(self, offset: usize) throws(AccessError) -> i64unsafe store_i64(self, offset: usize, value: i64) throws(AccessError)
u64unsafe load_u64(self, offset: usize) throws(AccessError) -> u64unsafe store_u64(self, offset: usize, value: u64) throws(AccessError)

Every method takes a byte offset from the start of the exact mapping. Before accessing the device it checks, in this contract:

  • that the selected load or store width is present in the grant;
  • that the complete offset .. offset + width range is representable and contained in 0 .. self.size;
  • that the mapped address plus offset is naturally aligned for that width.

Failure returns one of these ordinary checked errors and performs exactly zero volatile accesses:

Zynx
export enum AccessError {
	Permission,
	OutOfRange,
	Misaligned,
}

On success, a load returns the exact selected integer and a store returns ordinary void(). Signed and unsigned methods preserve the same-width raw bit representation. They perform no numeric or endian conversion. A compiler may remove a bounds or alignment check that it proves statically, but it may not weaken the result or turn an invalid access into a device access.

Every dynamically reached successful method produces exactly one corresponding private RFC 0075 volatile operation of the same width. It may not widen, narrow, split, merge, repeat, speculate, emulate, or lower that access through a helper. The compiler preserves source-sequenced order relative to other volatile operations exactly as required by std.mem.volatile.

The methods remain unsafe after their grant, bounds, and alignment checks. Those checks cannot prove that a device permits a particular read or value: registers may be read-to-clear, write-one-to-clear, trigger DMA, reserve bit patterns, or require device-specific sequencing. A target-specific peripheral package should contain that proof and expose the smallest safe semantic API it can justify.

Launch Grant

zynx-launch v5 copies the complete v4 Directory and Network schemas unchanged and adds the canonical mmio grant kind. A grant uses a target/host mapping name, never an address supplied by the program or launch document:

json
{
  "format": "zynx-launch",
  "version": 5,
  "requirements_digest": "blake3:...",
  "grants": {
    "uart": {
      "kind": "mmio",
      "source": {
        "kind": "mapping",
        "name": "uart0"
      },
      "policy": {
        "load": {
          "byte_widths": [1, 2, 4]
        },
        "store": {
          "byte_widths": [4]
        }
      }
    }
  }
}

The grant key must match one exact @host binding whose canonical type is std.mmio.Region. Both policy.load.byte_widths and policy.store.byte_widths are mandatory allow-only arrays. Their only valid members are 1, 2, 4, and 8; omission, an unknown width, or a duplicate is invalid. An explicit empty array is valid deny-all. Signedness does not add another right: for example, width 4 authorizes both load_i32 and load_u32 for that direction.

The trusted target configuration resolves uart0 to one exact bounded mapping and owns its physical base, byte extent, mapping attributes, and lifetime. The launch document has no physical address, caller-chosen extent, cache mode, endian mode, discovery rule, default, or wildcard. Unknown mappings, overlapping grants, unstable lifetimes, unsupported widths, or a target that cannot certify the complete mapping and one-access contract reject the launch transaction before main.

Deliberate Omissions

std.mmio supplies no:

  • public pointer projection or integer-address constructor;
  • subregion, split, dynamic range loan, or independent mapping owner;
  • typed ReadRegister<T>, WriteRegister<T>, Register<T>, or typestate;
  • modify, read-modify-write, field, compound-assignment, or bulk operation;
  • atomicity, fence, happens-before, ordinary-memory, cross-region, cache, DMA, interrupt, bus-completion, posted-write-completion, or endian guarantee;
  • Future, async operation, implicit blocking bridge, or thread-transfer proof.

Dropping or returning from a method does not prove that a posted device write has completed. Required readback, barriers, cache maintenance, register schemas, and concurrency protocols belong to the target-specific device package.

Every generic Wasm/WASI target rejects std.mmio.Region, its host binding, and the v5 mmio grant kind. It does not reinterpret the region as linear memory or enable RFC 0075's raw-pointer volatile overloads.

API Summary

KindPublic surfaceContract
TypeRegionOpaque host-retained mapping loan with readonly size; non-Copy, non-Clone, neither Send nor Sync.
ErrorAccessError.PermissionThe direction/width is absent from the explicit grant; zero device accesses.
ErrorAccessError.OutOfRangeThe complete byte range is not representable inside the region; zero device accesses.
ErrorAccessError.MisalignedThe selected mapped address is not naturally aligned; zero device accesses.
Methodsload_i8/u8/i16/u16/i32/u32/i64/u64Unsafe checked byte-offset load; success performs exactly one same-width RFC 0075 volatile access.
Methodsstore_i8/u8/i16/u16/i32/u32/i64/u64Unsafe checked byte-offset store returning void; success performs exactly one same-width RFC 0075 volatile access.

Released under the MIT License.