Skip to content

IRON Python Configurations

There are several options that exist to configure the IRON Python programming environment.

Default IRON Tensor Class

This is a variable that controls the types of aie.utils.Tensors that are produced by the utility functions tensor, ones, etc. Right now there are two tensor implementations: CPUOnlyTensor and XRTTensor.

By default, if pyxrt is available, the DEFAULT_TENSOR_CLASS is set to XRTTensor. However, you can also manually set this value through the set_tensor_class(), e.g.:

>>> import numpy as np
>>> print(aie.utils.tensor.DEFAULT_TENSOR_CLASS.__name__)
XRTTensor
>>> type(iron.tensor((2, 2), np.int32))
<class 'aie.utils.xrtruntime.tensor.XRTTensor'>
>>> aie.utils.set_tensor_class(aie.utils.tensor.CPUOnlyTensor)
>>> print(aie.utils.tensor.DEFAULT_TENSOR_CLASS.__name__)
CPUOnlyTensor
>>> type(aie.utils.tensor((2, 2), np.int32))
<class 'aie.utils.tensor.CPUOnlyTensor'>

Default IRON Device

If the IRON device is not set, many designs fetch it on demand from the DefaultNPURuntime (a CachedXRTRuntime instance), which queries XRT for the attached NPU. Select an explicit target with iron.set_current_device():

>>> import aie.iron as iron
>>> iron.set_current_device(iron.device.NPU1())
>>> iron.get_current_device()
<abc.NPU1 object at 0x722a65903a10>

Cross-compiling for a different NPU

The target architecture (aie2 / aie2p) is part of the per-design cache key (see compilation_stages.md §Lowering — _compute_artifact_hash mixes target_arch in along with peano + aiecc mtimes), so set_current_device(...) before .compile() is enough to drive a cross-arch build. Each arch lands in its own cache subdirectory, no collision with the binary for whatever NPU is physically attached:

import aie.iron as iron
from aie.iron.device import NPU1Col1, NPU2Col1

# Same generator, two arches → two distinct cache dirs.
for dev_cls in (NPU1Col1, NPU2Col1):
    iron.set_current_device(dev_cls())
    my_design.specialize(N=4096).compile()

Useful for building Strix binaries on Phoenix-only CI hosts (or vice versa), and for shipping pre-built xclbins for multiple NPU generations without needing each one attached at build time.

Arch-aware kernel introspection (mac_dims)

Some aie.iron.kernels factories pick a different MMUL geometry per arch — kernels.mm(int16, int16) is (r, s, t) = (4, 4, 4) on AIE2 (Phoenix) but (4, 4, 8) on AIE2P (Strix). The chosen geometry is exposed on the returned ExternalFunction as .mac_dims, so designs can drive their DMA-layout transforms from the kernel itself instead of hardcoding for one arch:

import aie.iron as iron
from aie.iron.device import NPU2Col1

iron.set_current_device(NPU2Col1())
mm = kernels.mm(dim_m=64, dim_k=64, dim_n=64,
                input_dtype=np.int16, output_dtype=np.int16)
r, s, t = mm.mac_dims

Set the target before constructing an arch-sensitive factory. The factory selects its source and MMUL geometry when it is created.

The full per-arch table lives in python/iron/kernels/linalg.py (_MM_MAC_DIMS). Combined with the cross-compile pattern above, the same generator file produces a correct binary for each arch without ever editing the source.

IRON Cache Location

The IRON jit feature caches compiled objects in a directory defined by NPU_CACHE_HOME. By default this value is the user's home directory.

IRON XRT Runtime Cache Size

The CachedXRTRuntime caches XRT contexts to improve performance. The size of this cache can be configured using the XRT_CONTEXT_CACHE_SIZE environment variable. This is particularly useful in CI environments where multiple tests run in parallel and might exhaust the available NPU contexts.

export XRT_CONTEXT_CACHE_SIZE=1

Diagnostic Output and Log Level

The aie library uses Python's standard logging module for all diagnostic output. Set AIE_LOG_LEVEL to control verbosity. Valid values: DEBUG, INFO, WARNING (default), ERROR, CRITICAL.

AIE_LOG_LEVEL=DEBUG python my_script.py    # show debug messages
AIE_LOG_LEVEL=INFO python my_script.py     # show info and above
AIE_LOG_LEVEL=ERROR python my_script.py    # errors only

For per-module control or routing to a file, use the logging API directly:

import logging

logging.getLogger("aie").setLevel(logging.ERROR)

# Route aie logs to a file instead of the console
handler = logging.FileHandler("aie.log")
handler.setFormatter(logging.Formatter("%(asctime)s %(name)s %(levelname)s %(message)s"))
logging.getLogger("aie").addHandler(handler)
logging.getLogger("aie").propagate = False  # don't also send to root logger

Helper utilities cheat-sheet

The helpers most designs reach for, grouped by where they live. Each has a docstring; help(obj) or print(obj.__doc__) shows it.

Host-side tensor + design construction (aie.iron)

Helper What it does
iron.{tensor, zeros, ones, full, rand, randint, arange, zeros_like} Host-tensor factories (default device="npu", also accept device="cpu"); see also set_tensor_class.
iron.{In, Out, InOut, CompileTime} Type annotation markers for @iron.jit generator parameters (see compilation_stages.md §Appendix A).
iron.{jit, CompilableDesign, CallableDesign} The JIT decorator + the two design wrapper classes (CompilableDesign is the recipe; CallableDesign is the ready-to-run wrapper).
iron.ceildiv(a, b) Pure-integer ceiling division. Same value on every code path; here so designs don't redefine it locally.
iron.{set_current_device, get_current_device} Read/write the active Device (see §Default IRON Device).
iron.kernels.* Pre-packaged kernel factories — mm, conv2dk1, conv2dk3, passthrough, eltwise, etc. Each returns an ExternalFunction ready to bind in a Worker.
iron.{Buffer, Lock, Flow, TileDma, DmaChannel, Bd, Acquire, Release} IRON-Python peers of ObjectFifo for designs that want to hand-wire DMA + sync (canonical example: programming_examples/basic/chaining_channels/).
iron.algorithms.{transform, transform_binary, transform_parallel, for_each} Element-wise dataflow templates — handle Worker / ObjectFifo / Runtime plumbing for one-arg / two-arg / multi-column / fill-and-drain patterns.
iron.{compile_context, get_compile_arg} Dynamic compile-time arg injection. See §compile_context below.

Argparse + runtime glue (aie.iron.device, aie.utils)

Helper What it does
aie.utils.hostruntime.argparse.device_from_args(args) Resolve an explicit parsed args.dev to a Device — collapses from_name(args.dev, n_cols=...) boilerplate. Returns None when target selection is automatic, so run_design_cli() can bind the attached runtime family. n_cols="auto" reads args.n_cols if present, otherwise defaults to 1.
aie.utils.DefaultNPURuntime Module-level CachedXRTRuntime instance; auto-detects NPU1 / NPU2 via XRT. Used by iron.tensor(..., device="npu") and @iron.jit runtime binding.
aie.utils.hostruntime.argparse.{add_compile_args, add_runtime_args} Add the standard --xclbin-path/--insts-path and --xclbin/--instr/-k/--trace_size flags to a parser.
aie.utils.test.create_npu_kernel(opts) Build an NPUKernel (plus optional TraceConfig) from a parsed argparse.Namespace. See programming_examples/basic/vector_scalar_mul/test.py for the canonical use pattern.
aie.utils.benchmark.{run_iters, print_benchmark} Warmup + timed iterations + summary stats. See section-4/section-4a/README.md.
aie.utils.verify.{nearly_equal, count_mismatches} Tolerance-aware compare for LUT / saturating / bf16 outputs that don't match exactly.

Compile-pipeline introspection (aie.utils.compile)

Helper What it does
aie.utils.compile.NPU_CACHE_HOME Cache root Path; defaults to ~/.npu/cache, override with the NPU_CACHE_HOME env var.
aie.utils.compile.jit._dma_size_parser.parse_dma_sizes(kernel_dir) Per-host-arg element counts read from the entry-point aie.runtime_sequence in input_with_addresses.mlir. Backs the tensor-arg validation in stage 5.
logging.getLogger("aie.utils.compile").setLevel(logging.DEBUG) Print every clang++ / aiecc subprocess invocation. See compilation_stages.md §Watching the compile pipeline.

compile_context for nested generator helpers

Most designs supply compile-time values via the explicit CompileTime[T]-annotated generator signature. Some patterns — composite generators, helper functions reused across designs — want to inject values through a helper that doesn't take them as explicit kwargs. compile_context opens a per-thread context that get_compile_arg can read out:

from aie.iron import compile_context, get_compile_arg

def make_fifo_pair(line_ty):
    # Helper has no name_prefix kwarg, but can read one from context.
    prefix = get_compile_arg("prefix", default="")
    return (ObjectFifo(line_ty, name=f"{prefix}in"),
            ObjectFifo(line_ty, name=f"{prefix}out"))

with compile_context(prefix="layer1_"):
    of_in, of_out = make_fifo_pair(line_ty)        # layer1_in, layer1_out

of_in, of_out = make_fifo_pair(line_ty)            # in, out (default)

Contexts nest — inner values shadow outer ones for the duration of the inner with block. Implemented on top of contextvars, so it is thread- and async-safe. CompilableDesign.compile() uses the same mechanism internally to surface the bound CompileTime[T] kwargs to the generator body.

Prefer explicit CompileTime[T] parameters when you can; reserve compile_context for the cases where threading the value through every helper signature would obscure the design.