Skip to main content

virtio_accel_xdna/
lib.rs

1//! AMD XDNA (Ryzen AI NPU) host backend for `virtio-accel`.
2//!
3//! This crate executes device-neutral TOSA 1.0 programs on an AMD XDNA2 NPU through the HRX
4//! runtime (`libhrx`), compiling admitted graphs with the pinned aiecc toolchain as a bounded
5//! subprocess. The design is recorded across the AMD XDNA wayfinder map (issue #78) and its
6//! decision tickets (#82 numerical tier, #83 crate layout, #84 compiler helper, #85 execution
7//! model), whose resolution records live on their respective ticket branches.
8//!
9//! The native modules (`ffi`, `native`) compile only when the build script finds a complete
10//! amdxdna-native HRX prefix (`VIRTIO_ACCEL_HRX_DIR`/`HRX_DIR`, or the `VIRTIO_ACCEL_HRX_LIB_DIR`
11//! escape hatch) and sets the `va_xdna` cfg; `VIRTIO_ACCEL_XDNA` forces the probe on (`1`, failing
12//! loudly) or off (`0`). Hosts without HRX build the portable admission surface (`lower`), the
13//! artifact codec, the offline compiler driver (`compile_artifact`, unix), and a placeholder —
14//! and compile no `unsafe` at all.
15//!
16//! **Scope today:** the full `Accelerator` lifecycle — the HRX device/stream owner, `hrx_buffer`
17//! primitives (persistent mapping, range flush/invalidate, release), and the serialized dispatch
18//! worker bridging `hrx_stream_dispatch`/`synchronize` to a latched nonblocking `poll_event`
19//! (execution-model spec, issue #85). `load_program` accepts the crate-local precompiled format
20//! ([`artifact`]) directly, and a TOSA artifact by admitting it and compiling it with the bounded
21//! aiecc helper subprocess (issue #84). The compilable TOSA subsets today are BF16 IDENTITY,
22//! BF16 → FP32 MATMUL, BF16 MAX_POOL2D, explicit FP8 → BF16 storage conversion, the fused
23//! FP8 → FP32 MATMUL that keeps that promotion on the compute core, exact INT8 IDENTITY,
24//! zero-point-aware INT8 → INT32 MATMUL, and exact INT32 → INT8 RESCALE.
25//! Admission (`lower`) unit-tests on every host.
26
27#![cfg_attr(not(va_xdna), forbid(unsafe_code))]
28
29pub mod artifact;
30#[path = "../experiments/bfp_experiment.rs"]
31pub mod bfp_experiment;
32mod lower;
33
34pub use artifact::{PrecompiledArtifact, XDNA_PRECOMPILED_FORMAT};
35pub use lower::{
36    AdmitError, CompilerSpec, Fp8Format, XDNA_TOSA_CAPABILITY, XDNA_TOSA_FP8_CAPABILITY,
37    XDNA_TOSA_FP8_TARGET, XDNA_TOSA_INTEGER_CAPABILITY, XDNA_TOSA_INTEGER_TARGET, XDNA_TOSA_TARGET,
38    admit,
39};
40
41use virtio_accel_tosa::{CapabilityDescriptor, TosaCapabilityProvider};
42
43#[cfg(va_xdna)]
44const TOSA_CAPABILITIES: &[CapabilityDescriptor] = &[
45    XDNA_TOSA_CAPABILITY,
46    XDNA_TOSA_FP8_CAPABILITY,
47    XDNA_TOSA_INTEGER_CAPABILITY,
48];
49#[cfg(not(va_xdna))]
50const TOSA_CAPABILITIES: &[CapabilityDescriptor] = &[];
51
52/// The HRX runtime publishes no finite upper bound for a loaded program's device residency.
53///
54/// Requiring the maximal charge keeps the provider promise truthful: a process cannot retain
55/// `u64::MAX` bytes for one program. Device integrations set their aggregate program-residency
56/// policy accordingly when admitting an XDNA program. Mirrors the OpenVINO backend, whose runtime
57/// has the same property.
58pub const REQUIRED_RESIDENT_BYTES: u64 = u64::MAX;
59
60/// `BackendError::External` domain tag for this backend's failures ("XDNA" in ASCII), covering
61/// both HRX runtime errors and compiler-helper failures.
62pub const XDNA_ERROR_DOMAIN: u32 = 0x5844_4e41;
63
64/// Failure to initialize an XDNA backend instance.
65#[derive(Clone, Copy, Debug, PartialEq, Eq)]
66pub enum InitError {
67    /// The crate was built without a detected HRX runtime (`libhrx`).
68    RuntimeUnavailable,
69    /// HRX initialized but enumerated no NPU device on this host.
70    DeviceUnavailable,
71    /// The HRX device or stream could not be initialized.
72    Initialization,
73}
74
75impl core::fmt::Display for InitError {
76    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
77        write!(formatter, "{self:?}")
78    }
79}
80
81impl std::error::Error for InitError {}
82
83// The compiler-helper driver is pure safe subprocess code: it needs the pinned toolchain at run
84// time, never HRX, so it compiles on every unix host — the offline catalog-population host is
85// exactly a build machine *without* libhrx. (Unix-only: it drives the helper in its own process
86// group, a unix notion.)
87#[cfg(unix)]
88mod compiler;
89#[cfg(va_xdna)]
90mod ffi;
91#[cfg(va_xdna)]
92mod native;
93#[cfg(va_xdna)]
94pub use native::{
95    XdnaAccelerator, XdnaBuffer, XdnaContext, XdnaEvent, XdnaProgram, XdnaQueue, XdnaResourceCounts,
96};
97#[cfg(all(va_xdna, feature = "test-control"))]
98#[doc(hidden)]
99pub use native::{XdnaTestConfig, XdnaTestFault};
100
101/// Admit a TOSA artifact and compile it to a precompiled-artifact container ([`artifact`]) with
102/// the pinned toolchain, without touching the device.
103///
104/// This is the offline / catalog-population path (compiler-helper contract, issue #84): a build
105/// host produces artifacts that a device-less serving host later loads through the precompiled
106/// format. Requires the toolchain (`VIRTIO_ACCEL_AMDXDNA_TOOLCHAIN`) at run time and no HRX at
107/// all — it is available on any unix build, with or without the `va_xdna` native modules.
108#[cfg(unix)]
109pub fn compile_artifact(
110    tosa: &[u8],
111    target: virtio_accel_tosa::Target,
112) -> Result<Vec<u8>, virtio_accel_core::BackendError> {
113    let spec = lower::admit(tosa, target)?;
114    compiler::Compiler::from_env()?.compile(spec)
115}
116
117/// Placeholder that keeps workspace consumers portable when no HRX runtime was detected.
118#[cfg(not(va_xdna))]
119#[derive(Clone, Copy, Debug, Default)]
120pub struct XdnaAccelerator;
121
122#[cfg(not(va_xdna))]
123impl XdnaAccelerator {
124    /// Report that no HRX runtime was detected when this crate was built.
125    pub fn new() -> Result<Self, InitError> {
126        Err(InitError::RuntimeUnavailable)
127    }
128}
129
130impl TosaCapabilityProvider for XdnaAccelerator {
131    fn tosa_capabilities(&self) -> &'static [CapabilityDescriptor] {
132        TOSA_CAPABILITIES
133    }
134}