.atargetStatic device class, revision, arrays, memory, commands, formats, and ABI capabilities.Implementation reference | source review 2026-07-11
Analog CIM SDK and Compiler: From Model to Running Code
This page turns the Analog CIM research into an implementable software architecture. It defines the public SDK, compiler IR and passes, target and calibration artifacts, simulator and hardware backends, runtime and firmware contracts, framework integration, tests, and the smallest vertical slice that should work before the project grows. It is a reference design, not a disclosure of iHW, Mythic, IBM, or Tenstorrent internals.
Documentation review
What the previous pages explained, and what was still missing for implementation
The existing pages remain useful for physics, evidence, co-design, and reuse decisions. The changes below remove ambiguous ownership and add the contracts needed to write and test code.
| Priority | Review finding | Why it blocks real code | Resolution in this guide |
|---|---|---|---|
| P1 | Model calibration, silicon characterization, and per-device calibration were discussed under one name. | They have different producers, update schedules, security requirements, and consumers. | Three separate schemas and lifecycle owners are defined. |
| P1 | The executable and serial-number-specific device state were treated as one artifact. | That prevents portable AOT compilation and creates a rebuild for every replacement device. | A device-class executable is bound to a separate device-state bundle at load time. |
| P1 | No stable SDK API, handle ownership, queue, event, buffer, or error contract was specified. | Compiler, runtime, simulator, applications, and tests cannot evolve independently. | A versioned C ABI, C++ HAL, and Python workflow are provided. |
| P1 | Accuracy simulation and performance simulation were not separated strongly enough. | AIHWKit and CrossSim can model numerical non-idealities, but they are not a product cycle/energy model. | Functional, statistical, cycle/resource, and hardware backends have distinct acceptance criteria. |
| P2 | PyTorch, ONNX, StableHLO, ORT, and PJRT appeared as equivalent starting points. | Supporting several frontends before one executable path works multiplies semantic and build risk. | The MVP starts with ONNX; ORT and PJRT are staged integrations. |
| P2 | The compiler pipeline had no repository ownership or executable acceptance test. | A list of passes does not show which library owns state or when a feature is complete. | Modules, CLI commands, CI lanes, and vertical-slice exit tests are defined. |
| P2 | Firmware validation and recovery were described but not represented as a command protocol. | Raw or weakly checked descriptors can damage state, hang queues, or use stale calibration. | A bounded command set, state machine, validation rules, and recovery policy are specified. |
| P3 | Patent evidence and implementation design occupied the same long page. | Readers could confuse a proposed software design with a vendor-disclosed implementation. | The patent page now labels its software mapping as proposed, links here, and makes this page the authoritative build specification. |
Architecture recommendation: build a simulator-first inference stack around one stable executable contract. Keep framework adapters above the compiler, physical device state below the artifact, and vendor-specific hardware behind a HAL. Do not begin by forking TTMetal, implementing PJRT, or supporting a complete model before one statically shaped MatMul compiles and runs end to end.
Product boundaries
Toolchain, SDK, runtime, HAL, driver, and firmware are different products
| Layer | Public responsibility | Must not own | Primary consumers |
|---|---|---|---|
| Model tools | Validate ONNX, collect representative ranges, compare accuracy, and produce model-calibration inputs. | Physical addresses, queue descriptors, kernel-driver calls, or device secrets | ML engineers and compiler CI |
| Compiler toolchain | Import, legalize, quantize, partition, map, schedule, lower, package, inspect, and verify. | Live device ownership, asynchronous request state, or field recovery decisions | Build systems, framework plugins, release engineering |
| Public SDK | Expose device discovery, executable loading, buffers, sessions, queues, events, telemetry, and errors. | Compiler implementation classes, direct register access, or unstable C++ internals | Applications, frameworks, tests, samples |
| Runtime | Validate compatibility, bind device state, manage memory, submit work, synchronize, and apply recovery policy. | Framework graph semantics or unrestricted firmware commands | SDK bindings and execution providers |
| HAL | Provide one internal device interface implemented by simulation and silicon backends. | User-facing ABI promises or model-level graph transformations | Runtime, bring-up tests, emulators |
| Driver | Isolation, DMA/IOMMU, interrupts, mapping, reset, power, and firmware transport. | Model partitioning, quantization policy, or source-level debug meaning | HAL hardware backend and system software |
| Firmware | Validate bounded descriptors, schedule engines, own programming/calibration sequences, report faults, and recover. | Untrusted host pointers, arbitrary array voltages, or policy hidden from telemetry | Driver, runtime, manufacturing tools |
Corrected calibration model
Three kinds of calibration create three different artifacts
| Artifact | Producer and input | Contents | Update trigger | Consumer |
|---|---|---|---|---|
Model calibration .qcal | ML/compiler team; representative model inputs | Activation histograms, clipping, group scales, outlier policy, sensitivity, dataset hash | Model, preprocessing, dataset, or quantization policy changes | Quantization and partition passes |
Target characterization .achar | Silicon/algorithm team; measured chip populations and PVT sweeps | Array distributions, transfer models, ADC/DAC behavior, IR drop, drift, faults, timing, energy coefficients, confidence | Device revision, process, firmware, characterization method, or qualification changes | Compiler operation model and statistical/cycle simulators |
Device state .acal | Manufacturing or firmware; one device serial number | Gain/offset trims, usable-array bitmap, remaps, weak cells, sensor references, calibration age, signature | Factory test, replacement, boot check, thermal event, aging threshold, recalibration, or repair | Runtime, firmware, and hardware backend |
Proposed file extensions are illustrative. The important contract is ownership. A model build
may depend on a target class and characterization version, but it should not embed mutable calibration from a
particular serial number. The runtime binds an accepted .acal bundle and rejects stale or incompatible state.
Keep the portable executable separate from device binding
.acxPortable within declared target requirements; graph, constants, plans, commands, kernels, and debug maps..acalSerial-specific calibration, remap, health, age, and signature loaded independently..atraceCorrelation IDs, timings, bytes, phases, saturation, faults, fallback, and calibration identity.compile_cache_key = hash(
normalized_model,
model_calibration,
target_descriptor,
characterization_version,
compiler_version,
compiler_options,
artifact_schema_major
)
load_binding_key = hash(
executable_target_requirements,
device_revision,
firmware_abi,
device_state_schema,
device_state_calibration_id
)
End-to-end flow
Characterize the target, build the executable, then bind and run
- A1Measure siliconArrays, ADC/DAC, wires, programming, PVT, drift, timing, energy
- A2Fit modelsDistributions, correlations, guard bands, confidence, device classes
- A3Validate predictorsHold-out chips and workloads; numerical and cycle correlation
- A4Sign target dataVersioned
.atargetand.acharartifacts - A5Publish compatibilityCompiler, firmware, simulator, and qualification matrix
- B1Check and importONNX checker, shape inference, source locations, reference tensors
- B2Calibrate modelRepresentative inputs, ranges, clipping, scales, sensitivity
- B3Plan graphSupport, quantization, analog/digital partition, fusion, fallback
- B4Map physical workDigits, arrays, faults, ADC ranges, phases, buffers, schedule
- B5Lower and packageCommands, digital kernels, program recipe, ABI, debug map
- B6Verify artifactFunctional, statistical, cycle, version, schema, and reproducibility gates
- C1Discover deviceRevision, firmware ABI, memory, health, supported modes
- C2Bind device stateValidate calibration signature, age, temperature, and remaps
- C3Load executableCheck target requirements, allocate resources, program/verify weights
- C4Submit requestsBind buffers, enqueue, return event, overlap transfer and compute
- C5Execute and observePulse, MVM, convert, reconstruct, digital work, telemetry
- C6Recover or completeRepeat, remap, recalibrate, refresh, fallback, reset, or return
SDK sequence diagram
One executable moves through five ownership boundaries
The compile path may run once per model release. Device binding and load run per deployment or boot. Submit and completion repeat per request.
- Offline compile
- Model + deployment policy + representative-data identity
- Normalized graph + target/characterization versions
- Import → analyze → partition → map → schedule → lower → verify
.acx+ support, accuracy, mapping, and cost reports- Device binding and load
- Open device + load executable
- Query revision, firmware ABI, health, and
.acal - Validated calibration/remap identity and resource availability
- Program recipe + bounded write/verify commands
- Ready session + compatibility and load report
- Repeated asynchronous request
- Bind buffers + submit queue + request policy
- Return event immediately
- DMA → pulse/MVM → ADC → reconstruct → digital work
- Completion + counters + saturation/fault status
- Output + event status + trace correlation ID
Software architecture
Use a thin stable public API over replaceable compiler and device internals
| Module | Suggested library | Owns | Dependency rule |
|---|---|---|---|
| ONNX frontend | libacim_frontend_onnx | Checker, shape inference, semantic import, source locations, constants | May depend on ONNX and MLIR; must not depend on runtime or driver |
| Compiler core | libacim_compiler | Dialects, analyses, passes, target operation model, reports | Depends on MLIR and schema libraries; hardware access is forbidden |
| Artifact support | libacim_artifact | Schema, serialization, hashes, signatures, inspect, compatibility rules | Shared by compiler and runtime; may not depend on either implementation |
| Public runtime ABI | libacim | C handles, status codes, discovery, sessions, buffers, queues, events, traces | Only stable exported surface; hides C++ classes and backend choice |
| Runtime core | libacim_runtime | Compatibility, memory plan, load, binding, queueing, recovery policy | Depends on artifact and HAL interfaces, never on a framework |
| HAL interfaces | libacim_hal | Backend, device, buffer, executable, command queue, event, telemetry interfaces | No vendor headers in public interfaces; backends are loadable or link-time modules |
| Simulation backends | acim_hal_functional, _analog, _cycle | Exact numerics, non-ideal distributions, timing/resources | Implement the same HAL and artifact semantics as hardware |
| Hardware backend | acim_hal_device | Driver calls, DMA, command submission, firmware events, reset, telemetry | Only module allowed to include driver or firmware transport headers |
| Framework plugins | acim_ort_ep, later acim_pjrt | Framework capabilities, graph handoff, buffer adaptation, fallback integration | Call compiler and public runtime APIs; do not duplicate backend logic |
| Python package | acim_sdk | Ergonomic compiler/runtime wrappers, reports, examples, notebooks | Thin bindings over stable C ABI plus optional compiler Python API |
acim-stack/
include/acim/ stable C API and versioned public structs
api/cpp/ header-only RAII wrapper over the C ABI
python/acim_sdk/ Python compiler and runtime bindings
compiler/
frontend/onnx/ ONNX validation and import
dialect/plan/ logical ACiM planning operations and attributes
dialect/physical/ arrays, slices, converters, calibration requirements
dialect/command/ firmware-visible command graph
analysis/ shape, range, sensitivity, legality, cost, liveness
transforms/ quantize, partition, map, schedule, lower, verify
target/ target and characterization schema readers
artifact/ FlatBuffer schema, serializer, verifier, inspector
runtime/ sessions, buffers, queues, events, load, recovery
hal/ backend interfaces and backend registry
backends/{functional,analog,cycle,device}/
driver/ OS integration; separate release and privilege boundary
firmware/ descriptor parser, scheduler, health, reset, calibration
integrations/{onnxruntime,pjrt}/
tools/ acim-compile, inspect, run, profile, characterize
tests/{unit,ir,golden,differential,hardware,models}/
examples/{matmul,onnx_ep,async_pipeline}/
Detailed repository guide: the accelerator repository blueprint expands this compact ownership sketch into schemas, generated code, C++ and Python packages, GoogleTest and pytest lanes, smoke through qualification tests, CI routing, contribution rules, performance methodology, and a full debug evidence bundle. Its minimal ACiM native example provides the first working CMake/Ninja, compiler, CTest, installation, and functional MVM path.
Compiler implementation
Start with upstream IR, then introduce hardware meaning in three small dialects
MLIR's declarative ODS and dialect-conversion infrastructure provide generated operation classes, verifiers, parsing, printing, and legality-driven lowering. Use them for infrastructure; do not import Tenstorrent target semantics into an ACiM backend.
| IR stage | Recommended representation | Allowed knowledge | Verifier examples |
|---|---|---|---|
| Imported graph | Upstream func, tensor, arith, linalg, quant, or a small normalized frontend layer | Tensor semantics, shapes, constants, source names, QDQ scales | Ranks known where required, contraction dimensions agree, quantized zero is representable |
acim_plan | Logical MVM/Conv regions plus candidate precision, scale domain, fallback, and error budget | Target capabilities and measured operation-model estimates, not physical addresses | Every op has a legal implementation; scale and accumulator types are complete |
acim_physical | Arrays, rows/columns, digit slices, sign paths, ADC grades, phases, buffers, and calibration class | Target geometry, characterization, fault-reserve policy, and static schedule resources | No overlap, current and range limits pass, all slices reconstruct, resources fit |
acim_command | Bounded commands, dependencies, events, DMA, program/verify, execute, telemetry, and recovery points | Firmware ABI and driver-visible memory handles | No raw pointers, all buffer ranges valid, dependency graph acyclic, firmware supports opcode/version |
def ACIMPlan_MvmOp : ACIMPlan_Op<"mvm", [Pure]> {
let summary = "Logical matrix-vector operation eligible for ACiM planning";
let arguments = (ins
AnyRankedTensor:$activation,
AnyRankedTensor:$weight,
ACIM_PrecisionAttr:$precision,
ACIM_ErrorBudgetAttr:$error_budget,
ACIM_FallbackAttr:$fallback);
let results = (outs AnyRankedTensor:$result);
let hasVerifier = 1;
}
def ACIMCmd_ProgramArrayOp : ACIMCmd_Op<"program_array"> {
let summary = "Request a bounded firmware-owned program and verify sequence";
let arguments = (ins
ACIM_ArrayRefType:$array,
ACIM_ProgramImageType:$image,
ACIM_CalibrationClassAttr:$calibration_class,
ACIM_EventType:$dependency);
let results = (outs ACIM_EventType:$completion);
let hasVerifier = 1;
}
Implement passes in dependency order and save every boundary
| Order | Pass/library | Implementation responsibility | Minimum test |
|---|---|---|---|
| 1 | onnx-check-import | Run ONNX checking and shape inference; import only MatMul/Gemm first; preserve source location and constants. | Valid model imports; invalid rank/K dimension returns a stable diagnostic. |
| 2 | canonicalize-contracts | Normalize transpose, bias, broadcast, and contraction forms into one MVM semantic contract. | Equivalent ONNX forms produce identical normalized IR. |
| 3 | attach-quant-contract | Consume QDQ or .qcal; materialize scale, zero point, clipping, rounding, and accumulator type. | Integer software reference exactly matches expected output and saturation. |
| 4 | analyze-capability | Generate reason-coded analog, digital, and host candidates from target capabilities. | Unsupported shapes are rejected or receive an explicit fallback candidate. |
| 5 | partition-and-fuse | Choose regions using boundary conversion and transfer cost, not operation support alone. | A forced analog/digital override produces a report and valid graph. |
| 6 | decompose-weights | Generate radix/sign slices and reconstruction coefficients; preserve logical-to-physical debug mapping. | Reconstructed logical weights exactly equal the quantized reference. |
| 7 | place-arrays | Tile matrices, reserve spares, constrain current, pack slices, and emit calibration-class requirements. | No overlap; capacity and current constraints are checked by an independent verifier. |
| 8 | plan-adc-phases | Select ranges, converter sharing, phase count, repeat policy, and saturation/fallback guard. | Every candidate reports predicted error, phases, saturation margin, and confidence. |
| 9 | plan-memory-schedule | Allocate static arenas and overlap DMA, input encoding, MVM, ADC, reconstruction, and digital work. | Lifetime intervals do not overlap illegally; cycle model accepts the schedule. |
| 10 | lower-command-abi | Emit versioned bounded commands and events; insert firmware validation and recovery checkpoints. | Command schema round-trips and malformed descriptors fail before execution. |
| 11 | serialize-acx | Package target requirements, programs, constants, plans, commands, reports, and debug maps. | Artifact is deterministic, inspectable, schema-conformant, and rejects wrong target major versions. |
| 12 | differential-verify | Run exact reference and selected backends; compare checkpoints and release thresholds. | First divergent op, expected tolerance, seed, profile versions, and tensors are retained. |
Candidate selection needs measured coefficients and uncertainty
candidate = {
implementation: analog | digital | host,
layout: {tiles, groups, buffers, sharding},
numerics: {weight_digits, activation_bits, adc_grade, accumulator},
physical: {arrays, phases, spares, calibration_class},
estimates: {latency_ns, energy_pj, error_distribution, confidence},
boundaries: {copies, conversions, synchronization},
fallback: {trigger, implementation, recovery_cost}
}
minimize latency_weight * latency_p99
+ energy_weight * energy
+ risk_weight * accuracy_risk
+ boundary_weight * transfer_and_conversion
+ wear_weight * programming_and_refresh
subject to error_budget, SRAM, current, ADC range, capacity,
firmware ABI, calibration class, and service policy.
Do not use an accuracy simulator as a latency oracle. CrossSim explicitly states that it does not model accelerator energy, area, or speed. Use AIHWKit or CrossSim adapters for numerical studies, then maintain a separately correlated cycle/resource model for queues, SRAM, NoC, converters, firmware, and digital work.
Public SDK
Stabilize a C ABI, then build C++ and Python ergonomics on top
Opaque handles prevent ABI exposure of STL, compiler, and driver classes. Every public struct starts with its size and API version so newer libraries can accept older callers safely.
#ifndef ACIM_SDK_H_
#define ACIM_SDK_H_
#include <stddef.h>
#include <stdint.h>
#if defined(_WIN32)
# if defined(ACIM_SDK_EXPORTS)
# define ACIM_API __declspec(dllexport)
# else
# define ACIM_API __declspec(dllimport)
# endif
#elif defined(__GNUC__)
# define ACIM_API __attribute__((visibility("default")))
#else
# define ACIM_API
#endif
#define ACIM_API_VERSION 1u
#define ACIM_MAX_RANK 8u
#ifdef __cplusplus
extern "C" {
#endif
typedef struct acim_context acim_context_t;
typedef struct acim_device acim_device_t;
typedef struct acim_executable acim_executable_t;
typedef struct acim_session acim_session_t;
typedef struct acim_queue acim_queue_t;
typedef struct acim_buffer acim_buffer_t;
typedef struct acim_event acim_event_t;
typedef enum acim_status {
ACIM_STATUS_OK = 0,
ACIM_STATUS_INVALID_ARGUMENT,
ACIM_STATUS_UNSUPPORTED,
ACIM_STATUS_INCOMPATIBLE_ARTIFACT,
ACIM_STATUS_STALE_CALIBRATION,
ACIM_STATUS_RESOURCE_EXHAUSTED,
ACIM_STATUS_BUSY,
ACIM_STATUS_TIMEOUT,
ACIM_STATUS_CANCELED,
ACIM_STATUS_DEVICE_FAULT,
ACIM_STATUS_INTERNAL
} acim_status_t;
typedef enum acim_io_kind {
ACIM_IO_INPUT = 0,
ACIM_IO_OUTPUT = 1
} acim_io_kind_t;
typedef enum acim_element_type {
ACIM_ELEMENT_I8 = 0,
ACIM_ELEMENT_I32,
ACIM_ELEMENT_F16,
ACIM_ELEMENT_F32
} acim_element_type_t;
typedef enum acim_memory_flags {
ACIM_MEMORY_HOST_VISIBLE = 1u << 0,
ACIM_MEMORY_HOST_PINNED = 1u << 1,
ACIM_MEMORY_DEVICE_LOCAL = 1u << 2
} acim_memory_flags_t;
typedef struct acim_context_options {
uint32_t struct_size;
uint32_t api_version;
const char* backend_name; /* functional, analog, cycle, device */
const char* backend_options; /* validated JSON or key/value text */
} acim_context_options_t;
typedef struct acim_tensor_desc {
uint32_t struct_size;
uint32_t api_version;
const char* name; /* executable-owned; valid until release */
acim_element_type_t element_type;
uint32_t rank;
int64_t dimensions[ACIM_MAX_RANK];
size_t byte_size;
} acim_tensor_desc_t;
typedef struct acim_buffer_binding {
uint32_t struct_size;
uint32_t api_version;
uint32_t tensor_index;
acim_buffer_t* buffer;
size_t offset;
size_t size;
} acim_buffer_binding_t;
typedef struct acim_error_info {
uint32_t struct_size;
uint32_t api_version;
acim_status_t status;
uint32_t subsystem;
int64_t device_code;
uint64_t trace_id;
char message[256];
} acim_error_info_t;
ACIM_API uint32_t acim_api_version(void);
ACIM_API acim_status_t acim_context_create(
const acim_context_options_t*, acim_context_t**);
ACIM_API acim_status_t acim_context_get_last_error(
acim_context_t*, acim_error_info_t*);
ACIM_API acim_status_t acim_device_count(acim_context_t*, uint32_t*);
ACIM_API acim_status_t acim_device_open(acim_context_t*, uint32_t, acim_device_t**);
ACIM_API acim_status_t acim_device_get_capabilities(
acim_device_t*, char* json, size_t* inout_size);
ACIM_API acim_status_t acim_device_bind_state(
acim_device_t*, const void* bytes, size_t size);
ACIM_API acim_status_t acim_executable_load(
acim_device_t*, const void* bytes, size_t size, acim_executable_t**);
ACIM_API acim_status_t acim_executable_get_tensor_desc(
acim_executable_t*, acim_io_kind_t, uint32_t index, acim_tensor_desc_t*);
ACIM_API acim_status_t acim_session_create(
acim_device_t*, acim_executable_t*, acim_session_t**);
ACIM_API acim_status_t acim_queue_create(
acim_session_t*, uint32_t queue_flags, acim_queue_t**);
ACIM_API acim_status_t acim_buffer_allocate(
acim_device_t*, size_t bytes, uint32_t memory_flags, acim_buffer_t**);
ACIM_API acim_status_t acim_buffer_write(
acim_buffer_t*, size_t offset, const void* source, size_t size);
ACIM_API acim_status_t acim_buffer_read(
acim_buffer_t*, size_t offset, void* destination, size_t size);
ACIM_API acim_status_t acim_queue_submit(
acim_queue_t*, const acim_buffer_binding_t* inputs, size_t input_count,
const acim_buffer_binding_t* outputs, size_t output_count, acim_event_t**);
ACIM_API acim_status_t acim_event_wait(acim_event_t*, uint64_t timeout_ns);
ACIM_API acim_status_t acim_event_cancel(acim_event_t*);
ACIM_API acim_status_t acim_event_get_result(acim_event_t*, acim_error_info_t*);
ACIM_API void acim_event_release(acim_event_t*);
ACIM_API void acim_buffer_release(acim_buffer_t*);
ACIM_API void acim_queue_release(acim_queue_t*);
ACIM_API void acim_session_release(acim_session_t*);
ACIM_API void acim_executable_release(acim_executable_t*);
ACIM_API void acim_device_release(acim_device_t*);
ACIM_API void acim_context_release(acim_context_t*);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* ACIM_SDK_H_ */
| API rule | Required behavior | Test |
|---|---|---|
| Ownership | Every successful create/open/load/allocate returns one owned reference. Parents outlive children; queues are drained or canceled before release; release is called exactly once. | Sanitizer and fault-injection tests cover success, partial construction, outstanding events, and every failure path. |
| Thread safety | Context/device queries are concurrent; a queue is ordered; independent queues may overlap; session concurrency is reported by capabilities, never assumed. | Multi-threaded submit and teardown with deterministic per-queue order and allowed cross-queue overlap. |
| Asynchrony | Submit returns an event without waiting; buffer lifetime extends through event completion. | Two queues overlap transfers and execution in the cycle and hardware backends. |
| Errors | Status codes are stable categories. Synchronous details are thread-local to the context; event results carry structured subsystem, device code, trace ID, and bounded message text. | Firmware fault, timeout, stale calibration, cancellation, and incompatibility map consistently across backends. |
| Versioning | The caller initializes struct_size and api_version; the library copies input strings and ignores compatible unknown trailing fields. | Old header/new library and new header/old library compatibility matrix plus malformed-size tests. |
| Tensor binding | Executable metadata fixes input/output index, name, type, shape, and byte size. Every binding validates index, offset, size, and memory compatibility before enqueue. | Wrong type/shape/order, undersized buffer, overlap, and stale-handle tests fail before device work begins. |
| Device state | The trusted .acal bundle is signature-, family-, schema-, age-, and temperature-checked before executable load and programming. | Wrong serial/family, expired calibration, revoked signature, and remap-capacity failures are reason-coded. |
| Memory | v0 exposes allocated host-visible, pinned, and device-local buffers with blocking read/write convenience calls. Zero-copy import is added only with explicit ownership, coherence, and synchronization rules. | Alignment, bounds, DMA direction, cache maintenance, and IOMMU isolation tests. |
| Cancellation | Cancellation is best-effort and reports whether execution stopped, completed, or required reset. | Cancel before dispatch, during DMA, during MVM, and after completion. |
Python should expose the same lifecycle without hiding events or reports
from acim_sdk import Compiler, CompileOptions, Context, TargetProfile
target = TargetProfile.load("targets/rev_b.atarget", "targets/rev_b.achar")
compiler = Compiler(target)
artifact, report = compiler.compile_onnx(
"models/linear.onnx",
model_calibration="build/linear.qcal",
options=CompileOptions(
accuracy_budget=0.005,
latency_target_us=100,
allow_digital_fallback=True,
save_intermediate_ir="build/ir",
),
)
artifact.save("build/linear.acx")
report.save("build/linear.compile.json")
with Context(backend="functional") as context:
device = context.open_device(0)
session = device.load("build/linear.acx")
output = session.run({"input": input_tensor})
with Context(backend="device") as context:
device = context.open_device(0)
session = device.load("build/linear.acx")
event = session.submit({"input": input_tensor})
output = event.result(timeout=1.0)
event.trace().save("build/linear.atrace")
HAL and backends
Make simulation and silicon execute the same artifact through the same internal interface
class Device {
public:
virtual ~Device() = default;
virtual StatusOr<Capabilities> QueryCapabilities() = 0;
virtual Status BindDeviceState(const DeviceState&) = 0;
virtual StatusOr<ExecutableHandle> Load(const Artifact&) = 0;
virtual StatusOr<BufferHandle> Allocate(const BufferDesc&) = 0;
virtual StatusOr<EventHandle> Submit(
QueueId, ExecutableHandle, Span<BufferBinding>, const RunPolicy&) = 0;
virtual Status Wait(EventHandle, Duration timeout) = 0;
virtual StatusOr<Trace> ReadTrace(EventHandle) = 0;
virtual Status Recover(const RecoveryRequest&) = 0;
};
class Backend {
public:
virtual ~Backend() = default;
virtual StatusOr<std::vector<DeviceInfo>> Enumerate() = 0;
virtual StatusOr<std::unique_ptr<Device>> Open(DeviceId) = 0;
};
| Backend | Purpose | Must model | Must not claim | Exit criterion |
|---|---|---|---|---|
| Functional | Bring up artifact, commands, memory, and exact quantized semantics quickly. | Shapes, types, saturation, rounding, slices, reconstruction, dependencies, fallback | Analog accuracy, timing, energy, area, or silicon behavior | Bit-exact against quantized reference for every supported operation |
| Statistical analog | Study accuracy sensitivity and hardware-aware training. | Programming error, noise, ADC/DAC bounds, drift, IR drop, sign mapping, variation distributions | Cycle latency or full-chip energy unless explicitly modeled and correlated | Error distributions match held-out device traces within declared confidence |
| Cycle/resource | Predict schedule, utilization, queueing, bandwidth, latency, and energy components. | Firmware phases, SRAM/NoC, converters, DMA, overlap, contention, fallback, counters | Task accuracy beyond inputs supplied by the numerical model | Kernel and model timelines correlate with RTL/C++ model and silicon measurements |
| Hardware | Execute on the real driver/firmware/device stack. | All ABI validation, memory, queues, calibration binding, faults, telemetry, recovery | Support not present in the queried firmware/device capabilities | Same artifact passes differential, stress, PVT, reset, and long-run tests |
Executable format
Use a schema with explicit compatibility rules, not an opaque binary blob
namespace acim.artifact;
table Version {
major:ushort (id: 0);
minor:ushort (id: 1);
patch:uint (id: 2);
}
table TargetRequirements {
device_family:string (id: 0);
revision_min:uint (id: 1);
revision_max:uint (id: 2);
firmware_abi:Version (id: 3);
calibration_schema:Version (id: 4);
characterization_hash:[ubyte] (id: 5);
required_features:[string] (id: 6);
}
table Executable {
schema:Version (id: 0);
build_id:string (id: 1);
model_hash:[ubyte] (id: 2);
target:TargetRequirements (id: 3);
graph:[ubyte] (id: 4);
constants:[ubyte] (id: 5);
weight_program:[ubyte] (id: 6);
command_streams:[ubyte] (id: 7);
digital_kernels:[ubyte] (id: 8);
memory_plan:[ubyte] (id: 9);
numerical_contract:[ubyte] (id: 10);
recovery_policy:[ubyte] (id: 11);
debug_map:[ubyte] (id: 12);
compile_report:[ubyte] (id: 13);
signature:[ubyte] (id: 14);
}
root_type Executable;
file_identifier "ACIX";
| Compatibility rule | Required behavior |
|---|---|
| Schema major | Mismatch is rejected unless an explicit offline converter exists. |
| Schema minor | Readers accept older compatible fields; optional new fields use defaults and feature checks. |
| Firmware ABI | Major must match; minor compatibility follows queried feature/opcode support. |
| Target revision | Must fall in the compiled range and expose every required physical capability. |
| Characterization | Hash must be recognized by the runtime policy; unknown or revoked profiles require recompile. |
| Device state | Calibration schema, device family, serial signature, age, and temperature policy are checked at load. |
| Schema CI | Run flatc --conform against the previous released schema and round-trip old artifacts. |
| Security | Verify hashes/signatures before programming; firmware independently validates every descriptor and bound. |
Runtime and firmware
Make state transitions and recovery observable
| Command class | Examples | Firmware validation | Completion evidence |
|---|---|---|---|
| Memory | DMA copy, fill, bind, release | IOMMU handle, bounds, alignment, direction, overlap, lifetime | Bytes, cycles, fault address, queue/event IDs |
| Weight lifecycle | Erase, program, verify, commit, rollback | Array ownership, recipe ID, voltage/current limits, endurance policy, transaction state | Program distribution, retries, failed cells, remap, image hash |
| Compute | Input encode, MVM phase, ADC read, reconstruct, digital kernel | Array/range/mode support, current bound, dependencies, buffers, calibration class | Phases, saturation, ADC range, cycles, output checksum |
| Health | Read sensors, self-test, calibrate, refresh | Privilege, idle state, thermal and power envelope, accepted procedure version | Calibration ID, temperature, weak resources, health vector, expiry |
| Control | Barrier, event, cancel, timeout, reset | Queue ownership, dependency validity, safe reset point, outstanding DMA/programming | Final state, canceled/completed work, data validity, reset reason |
Framework integration
Add framework plugins only after the standalone contract works
| Stage | Integration | What to implement | Why this order |
|---|---|---|---|
| MVP | Standalone ONNX CLI and Python compiler | Checker, shape inference, MatMul/Gemm import, static shapes, compile, inspect, run | Smallest dependency surface; deterministic artifacts; easy golden testing |
| Stage 2 | ONNX Runtime plugin EP | GetCapability, Compile, allocator/stream if needed, compatibility info, session options | Provides heterogeneous CPU fallback and a production application API without owning the whole framework |
| Stage 3 | PyTorch export through ONNX | Document supported export profile, dynamic-shape limits, QDQ handling, and reproducible model preprocessing | Reuses the proven ONNX path instead of creating a second importer |
| Stage 4 | StableHLO/PJRT plugin | PJRT C API client/device/buffer/executable/event behavior and StableHLO compiler entry | Useful for JAX/PyTorch-XLA, but larger ABI, async, sharding, and packaging surface |
Since ONNX Runtime 1.23,
its official guidance is to implement a new provider as a plugin EP. The separate
plugin development contract
asks GetCapability() which graph regions are supported and Compile() to produce
executable compute functions, while another provider can handle fallback. That is a good second integration
after the standalone compiler is stable.
GetCapability(graph, target):
validate shapes and dtypes
ask libacim_compiler for legal candidate regions
subtract regions whose boundary cost or error budget fails
return reason-coded supported subgraphs
Compile(subgraphs):
compile each accepted subgraph to .acx bytes
retain compatibility metadata and cache key
create compute callback that calls the public acim runtime API
Compute(inputs, outputs):
bind ORT buffers or copy through declared memory rules
submit asynchronously where the EP contract permits
translate acim status/faults to ORT status
never implement quantization, mapping, or driver logic in the EP
First buildable vertical slice
Prove one exact MatMul from ONNX to artifact to simulator
Start with [1,128] x [128,64], INT8 inputs and weights, INT32 accumulation, static shapes,
no analog noise, one functional backend, and no framework plugin. This deliberately tests software contracts
before physical fidelity.
.acxVersion, program, commands, debug mapcmake -G Ninja -S . -B build \
-DACIM_ENABLE_ONNX=ON \
-DACIM_ENABLE_FUNCTIONAL_BACKEND=ON
cmake --build build
ctest --test-dir build --output-on-failure
python examples/matmul/create_model.py --output build/matmul.onnx
acim-compile build/matmul.onnx \
--target testdata/virtual.atarget \
--characterization testdata/ideal.achar \
--emit-ir build/ir \
--report build/matmul.compile.json \
-o build/matmul.acx
acim-inspect build/matmul.acx --all
acim-run build/matmul.acx \
--backend functional \
--inputs testdata/matmul_input.npz \
--golden testdata/matmul_golden.npz \
--trace build/matmul.atrace
acim-profile build/matmul.atrace --summary
Definition of done: a clean checkout builds without device hardware; the same .acx
is inspected and executed through the public C API; output is bit-exact; malformed artifacts and incompatible
targets fail with stable status codes; IR, compile report, command list, and trace IDs identify the original ONNX node.
Second vertical slice: extend this exact artifact and HAL with capability, live-health,
calibration, binding, and topology epochs. Compile row- and column-parallel linear variants, bind around
injected row/column/tile faults, execute collectives on a fake AcimMesh, and recover from one link
failure. The scale-out implementation plan defines
the schemas, sequence, and acceptance metrics.
Physical-board vertical slice: the PCIe board bring-up guide implements the hardware HAL boundary with host/device requirements, UMD and KMD APIs, BAR and queue schemas, DMA/MSI-X ordering, firmware boot, analog program/verify, and a staged YOLO path.
Profiling vertical slice: the host Tracy and device cycle-trace guide defines optional CMake integration, scoped host zones, a firmware-safe trace ABI, UMD/KMD drain ownership, clock correlation, ACiM counters, profiling modes, and measurement gates.
Implementation roadmap
Expand only after each boundary has an executable test
Testing and CI
Separate fast deterministic gates from statistical and hardware qualification
| CI lane | Runs | Required evidence | Failure blocks |
|---|---|---|---|
| Every change | Formatting, static analysis, schema conform, unit tests, IR verifier, exact golden operations | Deterministic logs, seeds, artifacts, and compiler pass failures | Merge |
| Nightly simulator | Model corpus, statistical sweeps, cycle/resource regressions, memory/concurrency stress | Accuracy distributions, latency/energy deltas, coverage, first divergence | Release candidate |
| Hardware presubmit | Small device pool, load/run/reset, firmware compatibility, differential ops, telemetry | Device IDs, firmware/calibration versions, traces, fault and reset reports | Hardware-facing merge |
| Qualification | Chip/lot population, PVT, thermal cycling, drift, retention, endurance, long-run service load | Mean/tails, rejected devices, remap, recalibration, recovery, accuracy and performance distributions | Product release |
| Benchmark | Same application path for accuracy and performance, single-stream and throughput scenarios | Complete system configuration, preprocessing, datasets, calibration, power, latency percentiles | Published claim |
Functional backend must be bit-exact before analog tolerance is introduced.
Model, data, target, characterization, compiler, schema, seed, and device state.
Framework, quantized, statistical, cycle, RTL/C++, firmware, and silicon checkpoints.
Programming, conversion, fallback, memory, NoC, host, idle, and recovery costs.
Primary references and explicit limits
Use these projects for contracts and evidence, not as proof of an iHW implementation
ttrtOfficial artifact inspect/run/check/perf/debug and strict-versioning workflow.flatc --conform compatibility rules.