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
RegionandAccessErrortarget described here. The module, compiler support, andzynx-launchv5 support are not yet implemented.
Host Region Acquisition
An executable composition root declares each required mapping as a private host loan:
// 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:
// 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:
| Payload | Load | Store |
|---|---|---|
i8 | unsafe load_i8(self, offset: usize) throws(AccessError) -> i8 | unsafe store_i8(self, offset: usize, value: i8) throws(AccessError) |
u8 | unsafe load_u8(self, offset: usize) throws(AccessError) -> u8 | unsafe store_u8(self, offset: usize, value: u8) throws(AccessError) |
i16 | unsafe load_i16(self, offset: usize) throws(AccessError) -> i16 | unsafe store_i16(self, offset: usize, value: i16) throws(AccessError) |
u16 | unsafe load_u16(self, offset: usize) throws(AccessError) -> u16 | unsafe store_u16(self, offset: usize, value: u16) throws(AccessError) |
i32 | unsafe load_i32(self, offset: usize) throws(AccessError) -> i32 | unsafe store_i32(self, offset: usize, value: i32) throws(AccessError) |
u32 | unsafe load_u32(self, offset: usize) throws(AccessError) -> u32 | unsafe store_u32(self, offset: usize, value: u32) throws(AccessError) |
i64 | unsafe load_i64(self, offset: usize) throws(AccessError) -> i64 | unsafe store_i64(self, offset: usize, value: i64) throws(AccessError) |
u64 | unsafe load_u64(self, offset: usize) throws(AccessError) -> u64 | unsafe 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 + widthrange is representable and contained in0 .. 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:
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:
{
"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
| Kind | Public surface | Contract |
|---|---|---|
| Type | Region | Opaque host-retained mapping loan with readonly size; non-Copy, non-Clone, neither Send nor Sync. |
| Error | AccessError.Permission | The direction/width is absent from the explicit grant; zero device accesses. |
| Error | AccessError.OutOfRange | The complete byte range is not representable inside the region; zero device accesses. |
| Error | AccessError.Misaligned | The selected mapped address is not naturally aligned; zero device accesses. |
| Methods | load_i8/u8/i16/u16/i32/u32/i64/u64 | Unsafe checked byte-offset load; success performs exactly one same-width RFC 0075 volatile access. |
| Methods | store_i8/u8/i16/u16/i32/u32/i64/u64 | Unsafe checked byte-offset store returning void; success performs exactly one same-width RFC 0075 volatile access. |