GizStack
View .mdOpen in ChatGPTOpen in Claude

gizhw:amdxdna

AMD XDNA (the Ryzen AI NPU, driver amdxdna): its GET_INFO queries (the layouts of drm/amdxdna_accel.h) and the run of a kernel: a hardware context, a device heap that holds the buffers, a firmware image and control code loaded into it, and an ERT START_NPU packet (amdxdna_ctx.h) that starts one run. A request is an ioctl on a Handle of gizhw:device; the NPU's generic view is gizhw:npu.

gizlang
import * as Amdxdna from "gizhw:amdxdna";

Types

Firmware

gizlang
type Firmware = { major: i64; minor: i64; patch: i64; build: i64; }

The XDNA firmware's version and build.

  • major: i64
  • minor: i64
  • patch: i64
  • build: i64

Aie

gizlang
type Aie = { major: i64; minor: i64; }

The AIE (the NPU's array) version.

  • major: i64
  • minor: i64

Bo

gizlang
type Bo = { handle: i64; at: u64; size: i64; offset: u64; host: u64; }

A device buffer: its handle, its device address at, its size, and the offset its host mapping uses (a heap buffer's).

  • handle: i64
  • at: u64
  • size: i64
  • offset: u64
  • host: u64

Context

gizlang
type Context = { h: Device.Handle; hwctx: i64; syncobj: i64; heap: u64; heapAt: u64; }

An open hardware context: its handle, its timeline syncobj, and the device heap mapped at heap (device address heapAt).

  • h: M7.Handle
  • hwctx: i64
  • syncobj: i64
  • heap: u64
  • heapAt: u64

Kernel

gizlang
type Kernel = { pdi: bytes; ctrl: bytes; }

A kernel: its firmware image (PDI) and its control code: an instruction list (an xclbin's insts.bin) or an ELF whose .ctrltext holds it (XRT's full-ELF kernels, such as its gemm).

  • pdi: bytes
  • ctrl: bytes

Command

gizlang
type Command = { bo: Bo; at: u64; }

A command buffer, mapped at at: it holds one run's packet from its submit until the run ends.

  • bo: M1.Bo
  • at: u64

Program

gizlang
type Program = { ctx: Context; ctrl: Bo; size: i64; elf: bool; debug: Bo; cmd: Command; }

A loaded kernel: its control code (size bytes; elf: from an ELF), the debug buffer it writes, and the command buffer of its run.

  • ctx: M1.Context
  • ctrl: M1.Bo
  • size: i64
  • elf: bool
  • debug: M1.Bo
  • cmd: M1.Command

Txn

gizlang
type Txn = { ops: bytes; count: i64; }

AIE transaction (TXN) control code, the instruction list an MLIR-AIE kernel runs (the format of an xclbin's insts.bin): a 4-word header for the whole NPU2 array (6 rows, 8 columns, one memory-tile row), then operations. A tile is (row << 4) | column: row 0 is the shim (interface) tiles, row 1 the memory tiles, rows 2 to 5 the cores.

  • ops: bytes
  • count: i64

Xclbin

gizlang
type Xclbin = { pdis: Vec<bytes>; kernels: Vec<string>; }

An xclbin's parts that an NPU run needs: the firmware images (PDIs) of its AIE partition and the names of its kernels.

  • pdis: Vec<bytes>
  • kernels: Vec<string>

Functions

infoRequest

gizlang
function infoRequest(param: i64, size: i64, buffer: u64): bytes

The request for amdxdna's GET_INFO: a query number, the reply's size, and the reply buffer at buffer.

Effects: fail.

query

gizlang
function query(h: Device.Handle, param: i64, size: i64): bytes throws string

The reply of query param: size bytes the kernel wrote for it.

Effects: fail, io.

parseAie

gizlang
function parseAie(data: bytes): Aie

The AIE version from its 8-byte reply.

Effects: fail.

parseFirmware

gizlang
function parseFirmware(data: bytes): Firmware

The firmware version from its 16-byte reply.

Effects: fail.

aie

gizlang
function aie(h: Device.Handle): Aie throws string

The AIE version of the NPU behind h.

Effects: fail, io.

firmware

gizlang
function firmware(h: Device.Handle): Firmware throws string

The firmware version of the NPU behind h.

Effects: fail, io.

createHwctxRequest

gizlang
function createHwctxRequest(qos: u64): bytes

The request for CREATE_HWCTX: the QoS block at qos, the opcode limit and the tile count.

Effects: fail.

createBoRequest

gizlang
function createBoRequest(size: i64, kind: i64): bytes

The request for CREATE_BO: a buffer of size bytes of kind kind.

Effects: fail.

configRequest

gizlang
function configRequest(hwctx: i64, kind: i64, value: u64, size: i64): bytes

The request for CONFIG_HWCTX of hardware context hwctx: parameter kind, its value or address, and its size.

Effects: fail.

cuConfig

gizlang
function cuConfig(pdi: i64): bytes

The CU configuration: one control unit (CU 0) that runs from the firmware image in buffer pdi.

Effects: fail.

syncRequest

gizlang
function syncRequest(handle: i64, direction: i64, size: i64): bytes

The request for SYNC_BO of buffer handle, in direction (SYNC_TO_DEVICE or SYNC_FROM_DEVICE) over size bytes.

Effects: fail.

execRequest

gizlang
function execRequest(hwctx: i64, cmd: i64, handles: u64 = 0u, count: i64 = 0): bytes

The request for EXEC_CMD on hardware context hwctx: one command, which is buffer cmd itself (the inline form), and the count buffer handles (u32) at address handles that the run uses.

Effects: fail.

startPacket

gizlang
function startPacket(ctrl: u64, size: i64, args: Vec<i64>): bytes

The NPU packet that runs control code at device address ctrl (size bytes) with the arguments args. The opcode is args[0], and each buffer address is 64-bit. The size field counts 32-bit words, and the count is fixed at 16, as XRT writes them; the driver reads a shorter packet at the wrong offsets.

Effects: fail.

npuPacket

gizlang
function npuPacket(ctrl: u64, size: i64, args: Vec<i64>): bytes

The packet that runs an ELF's control code (startPacket's other form, as XRT sends it): the CU mask, the code's device address ctrl, its size in bytes, no properties, then each argument in 64 bits; the count is the words after the header.

Effects: fail.

ctrlText

gizlang
function ctrlText(data: bytes): Option<bytes>

The .ctrltext section of a 32-bit ELF, or none when data is not an ELF.

Effects: fail.

txn

gizlang
function txn(): Txn

An empty transaction.

tile

gizlang
function tile(row: i64, column: i64): i64

The tile at row and column.

Effects: fail.

xclbin

gizlang
function xclbin(data: bytes): Xclbin throws string

Parses xclbin data (XRT's axlf: a header, then 40-byte section headers from byte 456; the section count at 448). An AIE_PARTITION holds an array of aie_pdi records (its size and offset at byte 120), each with its image's size and offset at byte 16; an IP_LAYOUT holds 80-byte ip_data records from byte 8, each named at byte 16.

Effects: fail.

open

gizlang
function open(h: Device.Handle): Context throws string

Opens a hardware context on the NPU behind h, with its device heap mapped into this process. The heap comes first: the driver's hwctx init looks up the client's device heap and fails with ENOENT without it.

Effects: fail, io.

more

gizlang
function more(c: Context): Context throws string

Another hardware context on the NPU and device heap of c, for a second kernel that runs beside the first (a client has one heap).

Effects: fail, io.

load

gizlang
function load(c: Context, kernel: Kernel): Program throws string

Loads kernel into context c: the firmware and control code go to device buffers, the context is configured to run them with a debug buffer, and a command buffer is mapped for the runs.

Effects: fail, io.

command

gizlang
function command(c: Context): Command throws string

A command buffer of context c, for a run submitted while others are in flight.

Effects: fail, io.

code

gizlang
function code(p: Program, ctrl: bytes): Program throws string

Replaces the control code of p with ctrl (an instruction list, such as Txn.code gives); its buffer grows when the code does not fit.

Effects: fail, io.

another

gizlang
function another(p: Program, ctrl: bytes): Program throws string

Another program of the firmware and context of p, with its own control code ctrl (a kernel whose runs differ only in their control code, such as each kind of a model's layers).

Effects: fail, io.

run

gizlang
function run(p: Program, args: Vec<i64>, buffers: Vec<Bo> = []): void throws string

Runs the loaded kernel once with the arguments args (the buffers' device addresses among them) and waits for the run.

Effects: fail, io.

submit

gizlang
function submit(p: Program, cmd: Command, args: Vec<i64>, buffers: Vec<Bo> = []): u64 throws string

Queues a run of p in command buffer cmd and returns its point on the context's timeline; the context runs its commands in order. The run's buffers and its control code go with the command, as XRT sends them, so the driver holds them for the run. Submitting the next run before this one ends saves the wait and the wake-up between them.

Effects: fail, io.

finish

gizlang
function finish(c: Context, point: u64, commands: Vec<Command>): void throws string

Waits until context c reaches point (submit), then checks that each of commands completed.

Effects: fail, io.

debugOf

gizlang
function debugOf(p: Program): bytes throws string

The debug buffer of p as its runs left it.

Effects: fail, io.

close

gizlang
function close(c: Context): void

Closes the context: the kernel releases its hardware context and buffers with the descriptor.

Effects: fail, io.

buffer

gizlang
function buffer(c: Context, size: i64): Bo throws string

A device buffer of size zero bytes; a run reads and writes it by its device address at (its address in this process). Each page is written here: the NPU reaches the buffer through this process's page tables, and its writes to a page the process has not touched yet are lost (a run right after buffer wrote nothing; 20 ms later it did).

Effects: fail, io.

upload

gizlang
function upload(c: Context, bo: Bo, data: bytes, at: i64 = 0): void

Copies data into buffer bo at byte at, before the run that reads it.

Effects: fail, io.

download

gizlang
function download(c: Context, bo: Bo, size: i64, at: i64 = 0): bytes

The size bytes of buffer bo from byte at as the device left them after a run: the CPU caches over them are dropped first, so the read fetches what the device wrote.

Effects: fail, io.

downloadInto

gizlang
function downloadInto(c: Context, bo: Bo, into: mut bytes, at: i64 = 0): void

As download, the into.length bytes from byte at copied into into (a loop that reuses one buffer).

Effects: fail, io.

Methods

Txn.write

gizlang
function Txn.write(self: mut Txn, t: i64, address: i64, value: i64): void

Writes value to the register (or the core's memory word) at address of tile t, as a run-time parameter.

Effects: fail.

Txn.copy

gizlang
function Txn.copy(self: mut Txn, arg: i64, direction: i64, t: i64, bd: i64, channel: i64, offset: i64, length: i64, packet: i64 = -1, token: bool = false, cache: i64 = 2): void

A linear shim DMA of length bytes of kernel argument arg from byte offset, on shim tile t, buffer descriptor bd and channel, in direction (Txn.strided with one dimension).

Effects: fail.

Txn.strided

gizlang
function Txn.strided(self: mut Txn, arg: i64, direction: i64, t: i64, bd: i64, channel: i64, offset: i64, sizes: Vec<i64>, strides: Vec<i64>, unit: i64, packet: i64 = -1, token: bool = false, cache: i64 = 2): void

A shim DMA of kernel argument arg from byte offset, on shim tile t, buffer descriptor bd and channel, in direction: the descriptor, the firmware's patch of the buffer's address into it, the task-complete token (an S2MM always issues one; token asks for one on an MM2S), and the descriptor pushed to the channel's queue. The data is sizes (four, the outermost first; the first repeats the other three) by strides, in elements of unit bytes; the innermost stride is 1, and the DMA moves whole 32-bit words. packet (0 to 31) sends the data as packets with that id.

Effects: fail.

Txn.wait

gizlang
function Txn.wait(self: mut Txn, t: i64, direction: i64, channel: i64): void

Waits for the task-complete token of channel in direction on shim tile t.

Effects: fail.

Txn.code

gizlang
function Txn.code(self: Txn): bytes

The control code: the header, then the operations.

Effects: fail.

Constants

DRIVER

gizlang
const DRIVER = "amdxdna"

The driver name the kernel binds to an XDNA NPU.

S2MM

gizlang
const S2MM = 0

The DMA directions: S2MM moves a stream into memory (the NPU writes the buffer), MM2S the other way.

MM2S

gizlang
const MM2S = 1

The DMA direction that moves memory into a stream (the NPU reads the buffer).

NO_CACHE

gizlang
const NO_CACHE = 0x0

The AXI cache attributes of a shim DMA's reads and writes.

NORMAL_CACHE

gizlang
const NORMAL_CACHE = 0x2

The AXI cache attribute of normal (cacheable) memory.

AGGRESSIVE_CACHE

gizlang
const AGGRESSIVE_CACHE = 0xE

The AXI cache attribute that allows every kind of caching.