C Static Backend
The c_static backend is a specialized C code generator for TVM that produces
standalone static binaries for Relax VM execution. It generates portable C/C++
code suitable for embedded deployment, including optimized support for TI C66x
and C7x DSP processors.
Table of Contents
- Overview
- Target Configuration
- Quick Start
- C66x DSP Quick Start
- C7x DSP and DLOAD Deployment
- C7x DMA Tiling
- Architecture
- C++ API for VM Operations
- Building and Testing
Overview
When to Use c_static
Use the c_static backend when you need:
- Static binary deployment: Self-contained executables without shared library dependencies
- Embedded systems: Resource-constrained environments (microcontrollers, DSPs)
- Cross-compilation: Portable C code that compiles on any target toolchain
- DSP deployment: Optimized code generation for TI C66x/C7x processors
Key Features
- Complete C code generation for TVM Relax VM
- Automatic wrapper function generation for easy integration
- Parameter serialization in binary or C source formats
- Multi-input/multi-output model support
- TI DSP support with cycle-accurate profiling
- C++ API mode for reduced FFI overhead (12% faster on DSP)
Target Configuration
Basic Usage
import tvm
from tvm import relax
# Basic c_static target
target = tvm.target.Target("c_static")
# Target TI C66x DSP
target = tvm.target.Target("c_static -mcpu=c66x")
# Target TI C7x DSP (AM67A/J722S via DLOAD)
target = tvm.target.Target("c_static -mcpu=c7x")
# Compile model
mod = relax.transform.LegalizeOps()(mod)
ex = relax.build(mod, target=target)
Target Attributes Reference
See Compilation -- Target Attributes
Reference
for the full attribute table (including tidl-kernels/tidl-runtime,
not reproduced here).
TI DSP Runtime
When -mcpu=c66x or -mcpu=c7x is specified, the generated code targets
the TI DSP runtime (src/runtime/ti_dsp/) instead of the standard TVM VM
runtime. The code generator handles this switch automatically: it emits
DSP-specific headers, TVM_DSP_EXPORT on entry points, TI compiler
pragmas for loop optimization, and calls to TVMDSPBuiltin* functions
instead of the standard VM FFI dispatch.
The DSP runtime is a lightweight, self-contained C++14 library (~100 KB) designed for bare-metal and RTOS environments. Key differences from the standard TVM runtime:
- No VM class -- c_static emits direct function calls to builtin stubs instead of bytecode interpretation
- Static memory pools -- pre-allocated L2 SRAM (fast) and L3/DDR
(main) pools with bump-pointer allocation; no
malloc()at runtime - No exceptions or RTTI -- error handling via
ModelErrorenum return codes, compatible with TI CGT compiler constraints - C struct FFI -- 16-byte
TVMFFIAnywith manual ref counting instead of C++tvm::ffi::Anywith smart pointers - Platform abstraction -- implementations for host emulation (PC), C66x (AWRL6844), and C7x (AM67A/J722S)
See DSP Runtime Internals for build
instructions, memory architecture, and platform configuration. The full
model.h C++ API reference is in src/runtime/ti_dsp/MODEL_API.md in
the source tree.
Quick Start
1. Compile a Model
import tvm
from tvm import relax
import numpy as np
# Load or create your model
# Example: Simple MLP
from tvm.script import ir as I, relax as R
@I.ir_module
class MLPModule:
@R.function
def main(x: R.Tensor((1, 784), "float32")):
# ... model definition
pass
# Compile with c_static backend
target = tvm.target.Target("c_static")
mod = relax.transform.LegalizeOps()(MLPModule)
ex = relax.build(mod, target=target)
# Export artifacts
ex.export_library("model_lib.so")
2. Generated Files
After compilation, you get:
- lib0.c - Main computation kernels
- lib0.h - Header with function declarations
- weights.bin - Model parameters (if binary mode)
3. Integration Example
#include "lib0.h"
#include <tvm/runtime/crt/ndarray.h>
int main() {
// Allocate input/output tensors
NDArray input = /* ... */;
NDArray output = cg_main(input);
// Process output
return 0;
}
C66x DSP Quick Start
Target Configuration for C66x
import tvm
# C66x with default optimizations (recommended)
target = tvm.target.Target("c_static -mcpu=c66x")
# Defaults enabled: -use-cpp-api=1, -skip-runtime-checks=1
# With layer profiling
target = tvm.target.Target("c_static -mcpu=c66x -profile-layers=1")
# Disable optimizations for debugging
target = tvm.target.Target(
"c_static -mcpu=c66x "
"-skip-runtime-checks=0 "
"-use-cpp-api=0"
)
Build Environment Setup
# Required environment variables
export TI_CGT_C6000_PATH=/path/to/ti-cgt-c6000_8.5.0.LTS
export CCS_ROOT=/path/to/ccs
# Build DSP runtime
cd $TVM_HOME/src/runtime/ti_dsp
mkdir build-c66x && cd build-c66x
cmake -DCMAKE_TOOLCHAIN_FILE=../cmake/toolchain-awrl6844.cmake ..
cmake --build .
Run on C66x Hardware
# Deploy and run
$TVM_HOME/src/runtime/ti_dsp/scripts/run_on_c66x.sh program.out
# With extended timeout (milliseconds)
$TVM_HOME/src/runtime/ti_dsp/scripts/run_on_c66x.sh program.out --timeout 120000
DSP Test Framework
cd $TVM_HOME
export PYTHONPATH=$TVM_HOME/python:$PYTHONPATH
# Run on C66x host emulation
pytest tests/ti-dsp-runtime/dsp-tests/ -v --dsp-mode=c66x_host
# Run on C66x hardware
pytest tests/ti-dsp-runtime/dsp-tests/ -v --dsp-mode=c66x
# With layer profiling
pytest tests/ti-dsp-runtime/dsp-tests/ -v --dsp-mode=c66x --profile-layers
C7x DSP and DLOAD Deployment
The c_static backend supports deploying TVM-compiled models to the TI C7x DSP on the AM67A (J722S) SoC via runtime dynamic loading. Instead of linking the model into the DSP firmware at build time, the generated code is compiled into a relocatable C7x ELF module that the firmware's DLOAD dynamic linker loads at runtime over RPMessage IPC from Linux.
End-to-End Flow
Dev Host AM67A / J722S Board
───────── ─────────────────────
┌────────────────────────┐
│ TVM Compiler │
│ (Python) │
│ │
│ target = "c_static │
│ -mcpu=c7x" │
└──────────┬─────────────┘
│
│ relax.build()
▼
┌──────────┴─────────────┐
│ ├───▶ lib0.c (computation kernels)
│ c_static codegen │
│ ├───▶ weights.bin (model parameters)
└──────────┬─────────────┘
│
│ TI CGT C7000 compiler
│ + DLOAD linker script
▼
┌──────────┴─────────────┐ Relocatable C7x ELF:
│ │ .text compiled lib0.c
│ lib0.out │ .rodata.weights embedded weights.bin
│ (DLOAD module) │ --dynamic=lib relocatable
│ │ --import= symbols from firmware
└──────────┬─────────────┘
│
│ scp to AM67A
│
══════════╪═════════════════════════════════════════════════════
│
▼
┌──────────┴─────────────┐ ┌────────────────────────────┐
│ │ RPMsg │ │
│ c7x_compute CLI ├─────────▶│ C7x DSP Firmware │
│ (ARM Linux) │ IPC │ (FreeRTOS) │
│ │ │ │
│ 1. load lib0.out ├─────────▶│ DLOAD: parse ELF, │
│ │ │ allocate in DDR heap, │
│ │ │ resolve exported syms, │
│ │ │ apply relocations │
│ │ │ │
│ 2. infer ├─────────▶│ Call cg_main_dsp(): │
│ --input X.bin │ │ build DLTensors from │
│ │◀─────────┤ shared DDR, run model, │
│ │ │ write output to DDR │
│ │ │ │
│ 3. unload ├─────────▶│ Free module segments │
│ │ │ │
└────────────────────────┘ └────────────────────────────┘
Target Configuration for C7x
import tvm
# C7x with default optimizations (recommended)
target = tvm.target.Target("c_static -mcpu=c7x")
# With layer profiling
target = tvm.target.Target("c_static -mcpu=c7x -profile-layers=1")
When mcpu=c7x is set, the following defaults apply:
| Attribute | Default | Effect |
|---|---|---|
use-cpp-api |
true |
Direct C++ calls instead of FFI dispatch (~12% faster) |
skip-runtime-checks |
true |
Skip tensor shape/type validation (~5% faster) |
constants-byte-alignment |
64 |
64-byte alignment for cache-line efficiency |
The code generator emits #include <c7x.h> (vs <c6x.h> for C66x),
TVM_DSP_EXPORT __declspec(dllexport) on wrapper functions for DLOAD
symbol visibility, TI #pragma MUST_ITERATE / #pragma UNROLL for loop
optimization, and a cg_main_dsp entry point that the firmware resolves
by name at load time.
Weight Handling
TVM serializes model parameters (convolution filters, biases, batch norm
statistics, etc.) into weights.bin using its binary parameter format.
For C7x DLOAD deployment, these weights are embedded directly into the
ELF module:
weights.bin lib0.out (ELF)
┌───────────┐ ┌────────────────────────┐
│ TVM param │ bin_to_asm.py │ .text │
│ format ├──────────────────────▶│ .rodata.weights: │
│ (binary) │ converts to TI │ _binary_weights_.. │
└───────────┘ assembly with │ _binary_weights_.. │
.sect directive │ _binary_weights_.. │
└────────────────────────┘
The bin_to_asm.py script converts weights.bin to a TI assembly file
with a .rodata.weights section directive, exposing three symbols:
- _binary_weights_bin_start -- pointer to weights data
- _binary_weights_bin_end -- end marker
- _binary_weights_bin_size -- total size in bytes
The linker script places .rodata.weights alongside other read-only data.
At load time, DLOAD allocates space for the entire ELF in the TVM DDR
heap (352 MiB), and the firmware's TVM model manager locates the weights
via the exported symbols and constructs DLTensor descriptors from the TVM
binary parameter format.
Embedding weights avoids a separate model-load IPC step and keeps the
deployment as a single file (lib0.out). For ResNet-18, this produces a
~47 MB ELF containing ~1 MB of code and ~46 MB of weights.
Building a DLOAD Module
The build process uses a two-stage link with TI CGT C7000:
Stage 1 -- Build a pseudo-firmware (dsp_syms.out) containing stub
__declspec(dllexport) declarations of the symbols the firmware
exports (127 in the current symbol list, spanning the C library, TVM
runtime, VM builtins, math, and MMALIB wrappers). This provides
link-time symbol definitions so the TI linker can resolve references in
lib0.c without the actual firmware binary.
Stage 2 -- Compile lib0.c with the TI C++ compiler and link it
against dsp_syms.out using the DLOAD linker script (c7x_dynmod.cmd):
- --dynamic=lib -- produce a relocatable shared library ELF
- --relocatable -- emit C7x dynamic relocation entries
- --import=<symbol> -- declare symbols resolved by DLOAD at runtime
- .rodata.weights section with embedded weights.bin
The output lib0.out is a standard C7x ELF that DLOAD can parse and
relocate into DSP memory at runtime.
The dynmod build infrastructure lives in the tvm repo, under
src/runtime/ti_dsp/dynmod/:
- dynmod/c7x_dynmod/c7x_dynmod.cmd -- DLOAD linker script
- dynmod/c7x_dynmod/dsp_syms.c -- pseudo-firmware symbol stubs
- dynmod/CMakeLists.txt -- standalone cmake project, target c7x_dynmod
- src/runtime/ti_dsp/scripts/bin_to_asm.py -- weights embedder
The tests/ti-dsp-runtime repository's dsp-cpp/CMakeLists.txt can also
drive this build (via -DC7X_DYNMOD=ON), referencing the same files in
the tvm repo rather than a local copy.
Running on AM67A Hardware
The C7x firmware and host CLI are in src/runtime/ti_dsp/firmware/c7x/.
See Deploying Firmware for
build, deploy, and usage instructions, and
Firmware Design Deep-Dive for
architecture and DLOAD internals.
# On dev host: compile model and build DLOAD module
target = tvm.target.Target("c_static -mcpu=c7x")
# ... (produces lib0.c + weights.bin)
# ... (TI CGT C7000 build produces lib0.out)
# On dev host: copy to board
scp lib0.out root@am67a:/tmp/
# On AM67A: load, infer, unload
c7x_compute load /tmp/lib0.out
c7x_compute infer <handle> 0 --input in.bin --output out.bin --dtype float32
c7x_compute unload <handle>
Automated Testing (pytest)
The tests/ti-dsp-runtime repository provides end-to-end pytest tests that
automate the full pipeline: TVM compilation, C7x ELF build, SCP to board,
and inference verification.
cd $TVM_HOME
export PYTHONPATH=$TVM_HOME/python:$PYTHONPATH
# Conv2D on C7x via DLOAD
pytest tests/ti-dsp-runtime/dsp-tests/test_conv2d_dsp.py -v --dsp-mode=c7x_dload
# ResNet-18 on C7x via DLOAD
pytest tests/ti-dsp-runtime/dsp-tests/test_resnet_dsp.py -v --dsp-mode=c7x_dload --use-cpp-api
Cross-Repository Structure
The C7x DLOAD flow spans two repositories:
| Component | Repository | Path |
|---|---|---|
| c_static code generator | tvm |
src/target/c_static/ |
| TI DSP runtime | tvm |
src/runtime/ti_dsp/ |
bin_to_asm.py (weights embedder) |
tvm |
src/runtime/ti_dsp/scripts/ |
| DLOAD linker script + stubs | tvm |
src/runtime/ti_dsp/dynmod/c7x_dynmod/ |
| C7x DLOAD build scripts (test harness) | tests/ti-dsp-runtime |
dsp-cpp/ |
| DSP firmware + host CLI | tvm |
src/runtime/ti_dsp/firmware/c7x/ |
| pytest integration tests | tests/ti-dsp-runtime |
dsp-tests/ |
C7x DMA Tiling
When targeting c_static -mcpu=c7x, the compiler automatically applies
DMA-based double-buffered tiling to conv2d layers whose working set
exceeds the L2 SRAM budget. This overlaps data movement (DDR to L2)
with computation using the C7x DMA engine.
How It Works
The DMA tiling pipeline has three stages:
Stage 1 -- Scheduling (Relax pipeline)
ScheduleC7xDMATiling runs after FuseTIR. For each PrimFunc it
tries three strategies in order, applying the first one that matches:
- NHWC H-tiling (preferred,
conv2d_nhwcblocks) -- splits the output height (H) loop so each tile's input activation strip (double-buffered) fits in the L2 SRAM budget (default 384 KB). Works with fused quantized kernels because per-channel post-conv ops (requantize, bias, relu, clip, cast) are independent of H. Also caches weights into L2 (invariant across H-tiles) when they fit alongside the double-buffered input strip. - NCHW OC-tiling (legacy,
conv2d_nchwblocks) -- splits the output-channel loop intooc_outer/oc_innerso input + weight tiles fit double-buffered in the L2 budget. Only applied to standalone conv2d (skipped if the PrimFunc has fused post-conv blocks, since OC-tiling breaks per-output-channel fused ops). - N-tiling (
dequantize_matmul_accblocks) -- caches the weight matrix into L2 if it fits; SW-pipelined N-tiling for weights that don't fit is not yet implemented.
Whichever strategy applies, matching cache_reads are inserted into
global.l2sram scope, copy loops are fused into a single flat loop per
cache block, and the outer loop is annotated with software pipeline
metadata (e.g. for OC-tiling: software_pipeline_stage = [0, 0, 1]
(DMA, DMA, compute), software_pipeline_order = [0, 1, 2],
software_pipeline_async_stages = [0]).
Stage 2 -- TIR lowering (custom C7x TIR pipeline)
_c7x_dma_tir_pipeline() is a stripped-down fork of the default TIR
pipeline tailored for a single-core CPU DSP (no GPU, no threads, no
shared memory). The key difference: LowerAsyncDMA and
LowerDMAToExtern run immediately after FlattenBuffer, before
NarrowDataType(32).
NarrowDataType(32) converts int64 loop indices to int32, which
changes the index arithmetic enough that IdentifyMemCpy (used by
LowerAsyncDMA) can no longer prove contiguity of the copy regions.
Running DMA lowering while indices are still int64 avoids this.
The pass ordering:
InjectSoftwarePipeline -- expand annotations into async prologue/body/epilogue
LowerOpaqueBlock
FlattenBuffer -- flatten multi-dim buffers to 1D
LowerAsyncDMA -- convert copy loops to dma_copy/dma_wait intrinsics
LowerDMAToExtern -- convert intrinsics to call_extern("tvm_dsp_dma_copy", ...)
NarrowDataType(32) -- safe now, DMA calls are opaque externs
StorageRewrite
MakePackedAPI
Stage 3 -- C codegen and linking
The c_static codegen emits tvm_dsp_dma_copy() and
tvm_dsp_dma_wait() as regular C function calls. The DSP runtime
header (dma/tvm_dsp_dma.h) is included via kDSPHeaders in the code
template. At link time, the calls resolve to:
- Host emulation (
tvm_dsp_dma_host.c): synchronousmemcpy - C7x hardware (
tvm_dsp_dma.c): synchronousmemcpy(Phase 1); TI DmaUtilsAutoInc3d async DMA planned for Phase 3
Generated Code Structure
For a conv2d with OC=128 tiled at oc_tile=64 (2 tiles):
// Prologue: DMA tile 0 (input + weight) as a group
tvm_dsp_dma_copy(0, &l2_buf[0], &ddr_input[0], 460800, 0);
tvm_dsp_dma_copy(0, &l2_buf[921600], &ddr_weight[0], 73728, 0);
// Body: DMA tile 1, overlapped with compute on tile 0
tvm_dsp_dma_copy(0, &l2_buf[115200], &ddr_input[0], 460800, 0);
tvm_dsp_dma_copy(0, &l2_buf[995328], &ddr_weight[73728], 73728, 0);
tvm_dsp_dma_wait(0, 1); // wait for tile 0 DMA (allow 1 group in flight)
// ... compute on tile 0 using l2_buf[0..] ...
// Epilogue: compute on tile 1
tvm_dsp_dma_wait(0, 0); // wait for tile 1 DMA (all complete)
// ... compute on tile 1 using l2_buf[115200..] ...
The dma_wait(queue, max_inflight) calls synchronize at the group
level. Each async commit group contains 2 copies (input + weight).
max_inflight=1 means "at most 1 group still in flight", so the
prologue group must be done before compute starts, while the body
group can overlap with tile 0 computation.
Tiling Decisions
The pass skips tiling when the full working set fits in L2. For a standalone (non-fused) 4-layer conv2d stack -- the scenario the legacy NCHW OC-tiling strategy targets:
| Layer | IC | OC | KH | Working set | Tiled? |
|---|---|---|---|---|---|
| conv0 | 3 | 64 | 3 | 12 KB | no |
| conv1 | 64 | 64 | 3 | 246 KB | no |
| conv2 | 64 | 128 | 3 | 282 KB | no |
| conv3 | 128 | 128 | 3 | 256 KB | yes (oc_tile=64) |
Only conv3 exceeds the 192 KB half-budget (384 KB / 2 for double-buffering) and gets tiled.
Note: fused quantized conv2d stacks (requantize/bias/relu/clip/cast fused into the same PrimFunc) are skipped by NCHW OC-tiling and instead go through NHWC H-tiling (see "How It Works" above), which tiles on output height rather than output channel.
DMA Runtime API
// Initiate async 1D DMA transfer (DDR <-> L2 SRAM)
int tvm_dsp_dma_copy(int queue_id, void* dst, const void* src,
int size, int bypass_cache);
// Wait until in-flight transfers on queue <= max_inflight
int tvm_dsp_dma_wait(int queue_id, int max_inflight);
The API is defined in src/runtime/ti_dsp/dma/tvm_dsp_dma.h.
Phase 1 provides synchronous (memcpy) implementations for both host
emulation and C7x hardware.
File Inventory
| File | Role |
|---|---|
python/tvm/relax/transform/schedule_c7x_dma.py |
ScheduleC7xDMATiling pass |
python/tvm/tir/pipeline.py |
_c7x_dma_tir_pipeline with reordered DMA lowering |
python/tvm/tir/transform/lower_dma_to_extern.py |
LowerDMAToExtern pass (TIR intrinsics to call_extern) |
src/target/c_static/codegen_c_static_templates.h |
kDSPHeaders includes dma/tvm_dsp_dma.h |
src/runtime/ti_dsp/dma/tvm_dsp_dma.h |
DMA API header |
src/runtime/ti_dsp/dma/tvm_dsp_dma.c |
C7x hardware stub (memcpy, Phase 1) |
src/runtime/ti_dsp/dma/tvm_dsp_dma_host.c |
Host emulation stub (memcpy) |
src/tir/transforms/lower_async_dma.cc |
LowerAsyncDMA (upstream TVM, uses IdentifyMemCpy) |
src/tir/analysis/identify_memcpy.cc |
IdentifyMemCpy (upstream TVM, contiguity proof) |
Architecture
Directory Structure
src/target/c_static/
|-- codegen_c_static.h # Core code generator class
|-- codegen_c_static.cc # Main implementation (~1800 lines)
|-- codegen_c_static_dsp.h # DSP extension class
|-- codegen_c_static_dsp.cc # TI DSP pragmas, profiling
|-- codegen_c_static_wrapper.h # Wrapper generator class
|-- codegen_c_static_wrapper.cc # C++ wrapper generation
|-- codegen_c_static_templates.h # Code templates (headers, helpers)
|-- weight_packer.cc # Weight/constant serialization to weights.bin
Modular Components
| Class | Responsibility |
|---|---|
CodeGenCStatic |
Core TIR-to-C code generation, inherits from CodeGenC |
DSPCodeGenExtension |
Emit TI DSP pragmas, headers, profiling infrastructure |
WrapperGenerator |
Generate C++ wrapper functions for exported functions |
Code Generation Flow
- IR Analysis: Examine TVM IR to detect function signatures and return types
- VM Builtin Emission:
EmitAnylistVMBuiltinCallconverts compact anylist intrinsics to C++ API - Register Allocation: Calculate register file requirements per function
- Parameter Processing: Handle serialization (binary or source format)
- DSP Optimization:
DSPCodeGenExtensionemits TI-specific pragmas - Wrapper Generation:
WrapperGeneratorcreates C++ wrapper functions - Output: Produce compilation units suitable for static binary generation
Key Data Structures
// Function metadata
struct CGFunctionInfo {
int64_t max_register_index = -1; // Maximum register usage
int64_t num_args = 0; // Number of input arguments
bool returns_tuple = false; // Multi-output detection
int64_t num_outputs = 1; // Number of outputs (N for tuple)
uint64_t total_params = 0; // Parameter count
bool was_private = false; // Visibility control
};
// DSP configuration
struct DSPConfig {
bool enabled = false; // Targeting TI DSP
std::string mcpu; // Target CPU (c66x, c7x)
bool profile_layers = false; // Per-layer profiling
bool tidl_runtime = false; // Emit tidl_bridge_init_all() in cg_main_dsp
std::vector<std::string> profiled_layer_names;
};
C++ API for VM Operations
The c_static backend includes an optimized C++ API mode (-use-cpp-api=1) that
bypasses the FFI layer for VM operations, providing significant performance gains
on embedded targets.
Performance Benefits
On CLISTA-DoA model (C66x DSP @ 450 MHz):
| Metric | Improvement |
|---|---|
| Cycle reduction | 12% (62K cycles) |
| Memory reduction | 9% (1.8KB L2 peak) |
| Code size | 22% reduction |
How It Works
The C++ API replaces verbose FFI dispatch sequences with direct function calls:
Before (FFI mode):
TVMBackendAnyListSetPackedArg(r, 2, stack_ffi_any, 0);
SetFFIAnyInt(&((stack_ffi_any)[1]), (long)0);
TVMBackendAnyListSetPackedArg(c, 5, stack_ffi_any, 2);
// ... 4 more lines
TVMBackendAnyListMoveFromPackedReturn(r, 3, stack_ffi_any, 4);
After (C++ API mode):
_r.SetNDArray(3, vm::AllocTensor(_r.GetStorage(2), 0, _c.GetShape(5), _c.GetDType(6)));
Optimization Attributes
| Attribute | Default | Effect |
|---|---|---|
use-cpp-api |
true |
Direct C++ calls (~12% faster) |
skip-runtime-checks |
true |
Skip tensor validation (~5% faster) |
Both optimizations are enabled by default for all c_static targets.
Building and Testing
Build TVM with c_static Backend
# Configure build
mkdir -p build
cp cmake/cstatic_config.cmake build/config.cmake
cd build
# Build (requires LLVM 15+)
cmake -G Ninja ..
ninja
# Set up Python environment
cd ..
export TVM_HOME=$(pwd)
export PYTHONPATH=$TVM_HOME/python:$PYTHONPATH
Build Static Runtime (Optional)
# Enable static runtime in config
# Set BUILD_STATIC_RUNTIME=ON in build/config.cmake
cd build
cmake -G Ninja ..
ninja
Run Tests
cd $TVM_HOME
export PYTHONPATH=$TVM_HOME/python:$PYTHONPATH
# C static backend tests
pytest tests/cstatic/unit-tests/test_conv2d.py -v
pytest tests/cstatic/unit-tests/test_resnet.py -v
pytest tests/cstatic/unit-tests/test_matmul.py -v
pytest tests/cstatic/unit-tests/test_mlp.py -v
# DSP tests (host emulation)
pytest tests/ti-dsp-runtime/dsp-tests/ -v --dsp-mode=c66x_host
# DSP tests (C66x hardware)
pytest tests/ti-dsp-runtime/dsp-tests/ -v --dsp-mode=c66x
Build DSP Runtime
Host emulation:
cd $TVM_HOME/src/runtime/ti_dsp
mkdir build && cd build
cmake ..
cmake --build .
C66x hardware (AWRL6844):
cd $TVM_HOME/src/runtime/ti_dsp
mkdir build-c66x && cd build-c66x
cmake -DCMAKE_TOOLCHAIN_FILE=../cmake/toolchain-awrl6844.cmake ..
cmake --build .
C7x hardware (J722S/AM67A):
cd $TVM_HOME/src/runtime/ti_dsp
mkdir build-c7x && cd build-c7x
cmake -DCMAKE_TOOLCHAIN_FILE=../cmake/toolchain-j722s-c7x.cmake ..
cmake --build .
Verify Installation
# Run VM builtins test (host)
./src/runtime/ti_dsp/build/test_vm_builtins
# Run VM builtins test (C66x)
$TVM_HOME/src/runtime/ti_dsp/scripts/run_on_c66x.sh \
src/runtime/ti_dsp/build-c66x/test_vm_builtins_c66x.out