Device and discovery reference¶
dau_driver.discovery, dau_driver.device, dau_driver.transfer,
dau_driver.status, dau_driver.capability_decode, dau_driver.storage, and
dau_driver.errors. Register offsets, capability models, and stream types come
from dau-core; this package owns transport and execution.
Discovery¶
discover_devices¶
discover_devices(root: str | Path = Path("/dev")) -> list[DauDevice]
Every DAU-compatible device under root. Scans for xdma*_user nodes paired
with xdma*_h2c_0 and xdma*_c2h_0. When the Linux scan finds nothing, root
is the default, and the platform is macOS, falls back to the DriverKit transport.
Endpoints raising UnhealthyBarError or WrongBitstreamError are skipped.
identify_device¶
identify_device(user_path, *, h2c_path=None, c2h_path=None) -> DauDevice
Identify one device by its user node. Channel paths default to the conventional
siblings. Raises UnhealthyBarError when the magic word reads all ones,
WrongBitstreamError when it reads anything other than DAU_MAGIC_WORD, and
RegisterAccessError on a failed register read.
The register window is located by probing the shell base and then the stream-job contract window, retried a few times so a transient after a PCIe rescan does not masquerade as a wrong bitstream.
DauDevice¶
A frozen dataclass bundling the register window and DMA channel paths behind a transport.
Field |
Type |
Default |
Description |
|---|---|---|---|
|
|
— |
Device name, e.g. |
|
|
— |
Transport backend, e.g. |
|
|
— |
Register window node. |
|
|
— |
Host-to-card channel. |
|
|
— |
Card-to-host channel. |
|
|
— |
The decoded advertisement. |
|
|
|
Added to every register offset. |
|
|
|
Overrides the path-based transport. |
|
|
|
The addressable size of device memory, when known. Enables the wrap guard in |
resolved_transport (property) returns transport, or a PathDeviceTransport
over the three paths.
Registers¶
Method |
Behaviour |
|---|---|
|
Read one 32-bit register at |
|
Write one 32-bit register. Raises |
|
Program a 64-bit address across a register pair, applying the |
|
Raise |
|
A |
|
A |
|
A |
|
Close the transport, if it has a |
Register offsets are window-relative. read_register and write_register
add register_window_offset themselves, so a RegisterOffset is passed as
declared and never pre-biased. The module-level read_u32(path, offset) and
read_u64(path, offset) are the exception: they address the node absolutely,
and a caller using them composes the window itself.
The window is discovered rather than configured.
capability_decode.REGISTER_WINDOW_OFFSETS is the candidate list,
(0x0, 0x1000), the shell base first and the stream-job contract window
second, and discovery returns the first offset whose MAGIC reads back as
the DAU magic word, retrying a few times to let a freshly programmed fabric
settle. A composition top decodes only the low twelve address bits and returns
all ones for anything unmapped, so a health probe must stay on contract
registers inside the 4 KiB window.
64-bit registers are read as two separate 32-bit accesses on purpose: the user BAR can short-read an eight-byte access.
DMA¶
Method |
Behaviour |
|---|---|
|
Validate and return a |
|
The identity/capability words the resident shell advertises, including |
|
Validate, then read. |
|
Validate, then write. |
DMA device addresses are device memory addresses, not AXI-lite register offsets.
DeviceTransport¶
A Protocol. Implement it to swap the backend, as the fakes in the test suite
do.
class DeviceTransport(Protocol):
def read_u32(self, offset: int) -> int: ...
def write_u32(self, offset: int, value: int) -> None: ...
def read_dma(self, device_address: int, byte_count: int) -> bytes: ...
def write_dma(self, device_address: int, data: bytes | bytearray | memoryview) -> int: ...
PathDeviceTransport(user_path, h2c_path, c2h_path) is the file-backed
implementation, using os.pread / os.pwrite through dau_driver.xdma_io.
write_address¶
write_address(device, low_offset, high_offset, address: int, *, memory_bytes=<from device>) -> None
Program a 64-bit address across its LOW/HIGH register pair on any object with a
write_register method. Both halves are always written, including a zero high
half. Raises RegisterAccessError when the address does not fit 64 bits, or when
a memory limit is known and the address is at or past it.
memory_bytes defaults to a sentinel, not to None, and the sentinel means
“read device.memory_bytes”. Passing None explicitly disables the bound
check; passing an integer overrides it. A plain None default would have turned
the guard off for every caller who did not know to opt in.
Offsets are passed explicitly; register pairs are not always adjacent. The job
pair is, but a NoC lane’s high half is at 0x14, because 0x04 is its result
length.
Module-level helpers read_u32(path, offset) and read_u64(path, offset) read
registers directly from a node path.
Transfers¶
DmaDirection¶
A StrEnum: H2C ("h2c"), C2H ("c2h").
DmaTransfer¶
Field |
Type |
Default |
|---|---|---|
|
|
— |
|
|
— |
|
|
— |
|
|
— |
validate_transfer¶
validate_transfer(direction, device_address, byte_count, constraints: DmaConstraints, *, memory_bytes=None) -> None
Raises TransferValidationError when byte_count is not positive,
device_address is negative, the span device_address + byte_count runs past
64 bits or past memory_bytes when one is given, byte_count exceeds
max_transfer_bytes, either value is not aligned to alignment_bytes, or the
direction’s channel count is zero. DauDevice passes its own memory_bytes.
Status¶
DeviceStatus¶
Field |
Type |
|---|---|
|
|
|
|
raw_status is the STATUS word as read. No shell assigns its bits yet, so it
is not decoded into named flags.
DeviceCounters¶
h2c_bytes, c2h_bytes, rows, batches, stalls, all int.
Capability decode¶
Shared register-block decode semantics for every transport backend.
Function |
Returns |
|---|---|
|
The ASCII platform id, or |
|
The |
|
The discovery-time identity snapshot. |
|
|
|
The |
|
|
Register map 0.2 and above advertises the composed truth: OPERATOR_BITMAP
carries only HDL-executed opcodes, typed INT32 at register level, and
HOST_OPCODE_BITMAP carries protocol-level opcodes the host derives from
hardware results. Host opcodes must never be submitted to the device. The
composed-capability words (LANE_COUNT, HOST_OPCODE_BITMAP, SORT_CAPACITY)
are only read on 0.2 and above; all-ones is normalized to not-advertised. The
job-capacity words (INPUT_ROW_CAPACITY, OUTPUT_RECORD_CAPACITY) need 0.3 and
above. Both gates, CAPABILITY_WORDS_REGISTER_MAP_VERSION and
JOB_CAPACITY_REGISTER_MAP_VERSION, are defined in dau_core.registers.
Storage¶
StorageAllocator¶
StorageAllocator(*, tier: str, capacity_bytes: int, base: int = 0,
reserved: tuple[tuple[int, int], ...] = (), align: int = 64)
First-fit allocator over one tier’s [base, base + capacity_bytes), minus the
reserved regions. Raises ValueError for a non-positive capacity, an align
that is not a positive power of two, or a reserved region with non-positive size
or lying outside the tier.
Method |
Behaviour |
|---|---|
|
First-fit, aligned. Raises |
|
Bump the handle’s generation and return the live span to the free list, coalesced. Raises |
|
The handle’s device address, generation-checked. Raises |
|
Resolve, check |
StorageHandle¶
Field |
Type |
Description |
|---|---|---|
|
|
Handle id; recycled after a free. |
|
|
Incremented each time the id is reissued. |
|
|
Device address at allocation time. |
|
|
Granted size. |
|
|
The issuing tier’s name. |
|
|
Binds the handle to the allocator instance that issued it. |
Every operation trusts the allocator’s live record, never the handle’s own copy
of base or capacity.
Handle table¶
HandleTable(device, allocator: StorageAllocator, *, capacity: int)
One allocator’s slots mirrored into one device’s handle table, so a job can name
(id, generation) where it would otherwise name an address. capacity is the
built shell’s handle_table_capacity: a table is hardware, planned into the
design and priced with it, and no register advertises it, so it is passed in
rather than discovered. A non-positive capacity raises ValueError.
Method |
Behaviour |
|---|---|
|
Program the handle’s grant into slot |
|
Drop the slot out of liveness. Unconditional on the allocator: freeing can only make the table reject more jobs, so it is safe whether or not the host has released the span yet. |
|
Name the handle the next job resolves, in place of an input address. |
|
|
install and arm trust different things on purpose. install trusts the
host: the live record is what a slot is programmed from, and a handle the host
no longer considers valid must never become a grant the device accepts. arm
checks nothing: a stale handle has to reach the device, or the table never gets
to reject it.
HandleFaultError¶
The shell would not start the job on the handle it was armed with. It is a
PipelineJobError, so a caller catching every job failure still catches it, and
it has its own type because the point of the table is that this case can be told
apart: the job did not run, nothing was read, nothing was written, and
no result register moved. Results for repeats that did run are dropped with it.
code is a dau_core.registers.HandleFaultCode:
Code |
Meaning |
|---|---|
|
The table has no such slot; capacity is planned at build time, not grown at run time. |
|
The slot is freed, or live at another generation; that region belongs to someone else now. |
|
The job asks for more bytes than the grant. |
A code outside that set came from the datapath, and the datapath ran; it is
raised as a plain PipelineJobError.
Errors¶
StaleHandleError(RuntimeError), StorageExhaustedError(RuntimeError),
HandleTableError(DauDriverError), HandleFaultError(PipelineJobError).
Native extension¶
native_info() -> NativeInfo
NativeInfo fields: backend ("nanobind" or "python"), abi_version,
package_version, extension_available. The pure-Python fallback is used when
dau_driver.extension is not importable; source-tree imports work before the
extension is built.
Exceptions¶
All derive from DauDriverError(Exception).
Exception |
Raised when |
|---|---|
|
No device matched. |
|
A register read or write failed, short-read, or was out of range. |
|
A register read returned all ones. |
|
A transfer’s shape violates the device’s DMA constraints. |
|
A transfer’s I/O failed. |
|
The register window did not report the DAU magic word. |
StreamExecutionError derives from ValueError, and PipelineJobError from
RegisterAccessError.
Test doubles¶
dau_driver.testing is shared across the DAU test suites so the fake device’s
semantics cannot drift between packages. Not imported by production code.
Name |
Purpose |
|---|---|
|
In-memory |
|
The standard fake INT32 aggregation capability set. |
|
A |
|
A |