Getting started with compiler-backed emulation
The emule backend compiles TT-Lang programs and executes their generated
kernels through tt-metal and tt-emule inside Docker. Install the environment
once, then run programs with ./bin/tt-lang-sim --backend=emule.
Relative command paths in this guide assume the TT-Lang checkout root as the working directory, except where a container shell is specified.
The repository provides a pinned environment through
config/tt-lang-emule-stack.json. This manifest records the required compiler
baseline, tt-emule revision, tt-metal revision, base image, and hardware profiles.
Installation builds the current TT-Lang checkout, which must contain that
compiler baseline.
Host prerequisites
Git and Python 3.10 or newer on
PATH.A Docker-compatible daemon, running and accessible without
sudo. The Docker installation guide covers Docker Desktop on macOS and Docker Engine on Linux.Support for
linux/amd64containers: tt-emule JITs x86-64 shared objects. On Apple Silicon, see Docker Desktop’s virtualization and Rosetta settings for x86-64 emulation support and acceleration.The approved tt-emule repository URL and Git access to that repository. The installer uses the host’s existing Git credentials.
Check the host tools before installation:
git --version
python3 --version
docker info
Obtain TT-Lang
Start from a TT-Lang source checkout containing the compiler-backed backend:
git clone https://github.com/tenstorrent/tt-lang.git
cd tt-lang
The installer uses the container’s prebuilt LLVM toolchain and pinned tt-metal source, so the checkout’s LLVM and tt-metal submodules can remain uninitialized. Keep the TT-Lang Git history available for the compiler baseline ancestry check.
The installer validates that the checkout contains the compiler baseline recorded in the stack manifest. It also fetches the exact tt-emule revision and verifies that tt-emule pins the same tt-metal revision as the manifest.
Install the environment
Set the emulator repository location supplied by the tt-emule owners, then run the installer from the checkout root:
export TTLANG_EMULE_RUNTIME_SOURCE_URL=REPOSITORY_URL
./scripts/install-tt-lang-emule.sh
The URL supplies the source for the manifest’s pinned emulator commit. The installer uses the recorded emulator, tt-metal, and base image together in a Linux/x86-64 environment and verifies their source revisions before building.
Maintainers can evaluate other combinations with the candidate manifests and runtime overrides described in Updating the supported stack.
Installation builds the pinned tt-emule/tt-metal Docker image and compiles this TT-Lang checkout into a persistent Docker volume. It can take substantial time, CPU, memory, and disk space on its first run. The installer prints the runtime image, compiler build volume, and runtime cache volume names. On success it also prints the compiler commit for which the environment was installed.
Installation prepares the compiler and runtime before the first program run.
Selecting another target reuses the runtime image and compiler
build. Default runtime cache volumes are separate for each target. Subsequent
tt-lang-sim runs reuse the installed environment, which Docker retains in its
image and volume caches. Run the installer again after changing compiler
commits or build inputs, or to restore a missing image or incomplete compiler
environment. The launcher checks the installation before executing a program
and reports when reinstallation is needed. The compiler build and the tt-metal
and tt-emule JIT caches live in named Docker volumes.
Image contents and disk usage
The image uses a separate build stage to prepare tt-metal and tt-emule. Its final stage contains one toolchain Python environment, the LLVM SDK, Clang, and the emulator runtime’s shared libraries, headers, JIT sources, and firmware. The LLVM development libraries support compiling the current TT-Lang checkout; pytest and lit support the existing compiler test commands. Documentation, lint, and hardware-management tools remain available in the development images.
Image builds install the compiler and test dependencies from
requirements-test.txt, including its nested build/runtime requirements.
The image retains those requirement files under
/opt/tt-emule-runtime/requirements. Installation and program execution verify
that they match the checkout, so the compiler uses the image’s prepared Python
environment. Requirement changes select a new runtime image identity.
The toolchain’s src/python directory supplies installed MLIR Python resources
referenced by its CMake package exports. These files support building TT-Lang’s
Python bindings. The virtual environment links to the final image’s system
Python interpreter. Both Ninja and make support the existing tests, including
packaging probes that use CMake’s default Makefiles generator.
The Docker build context is limited to .github/containers; the pinned
emulator sources, stack manifest, and Python requirement files use separate
named contexts. Local compiler and dependency source checkouts remain outside
the image build context.
Disk budgeting includes the final image, compiler build volume, and JIT cache volume. Building the image locally also requires space for the larger build stage and Docker’s build cache. CI workers can consume a prebuilt image to avoid that build-stage cost. Docker’s compressed download size and unpacked image size measure different parts of this budget.
Run a program
After installation:
./bin/tt-lang-sim --backend=emule examples/eltwise_add.py
The program imports the real ttl and ttnn packages from the installed
environment. The compiler generates kernels and tt-emule executes them on the
default emulated P150 target.
Select emulated hardware
The emule backend accepts --target=NAME or --target NAME before the program
argument separator (--). Available profiles are recorded in the stack manifest:
Target |
Emulated hardware |
Devices |
|---|---|---|
|
Blackhole P150, unharvested (13 x 10 compute grid) |
1 |
|
Blackhole P100 (11 x 10 compute grid) |
1 |
|
Blackhole P150, harvested (11 x 10 compute grid) |
1 |
|
Blackhole P300, both chips accessible over PCIe |
2 |
|
Blackhole 4 x P150 mesh |
4 |
|
Blackhole LoudBox, harvested |
8 |
|
Blackhole LoudBox, unharvested |
8 |
|
Blackhole Galaxy, unharvested single-tray torus |
32 |
|
Wormhole N150 |
1 |
|
Wormhole N300 |
2 |
|
Quasar Q1 (experimental; runtime and compiler support pending) |
1 |
These entries cover all 11 YAML descriptors shipped in the pinned emulator’s
cluster_descriptors directory. Harvesting disables a subset of chip resources;
the harvested and unharvested entries select the corresponding resource layouts.
For example, run the same program on P100:
./bin/tt-lang-sim --backend=emule --target=p100 examples/eltwise_add.py
Target selection configures the cluster descriptor and, where defined, the mesh-device setting together. Q1 uses the descriptor without a mesh-device override. Compiler, tt-metal, and tt-emule revisions remain pinned. Programs must fit the selected devices’ resources and open the devices required by the workload; selecting a multi-device profile does not distribute a single-device program automatically. This option selects hardware for the emule backend; Python simulation uses its existing grid and memory options.
Profile selection and workload support are separate. The 2026-09-28 smoke checks
at checkout 6bdcb630 passed device opening, examples/eltwise_add.py, and
examples/compiler_only_external_call.py on all ten Blackhole/Wormhole profiles.
The examples execute on device 0, including on multi-device profiles. Separate
mesh-opening checks passed for P300, P150x4, both P150x8 profiles, and N300.
Galaxy testing covered device 0 only; full 32-device opening was skipped because
its memory requirements exceeded the local Docker VM’s memory. These checks do
not qualify collective operations or distributed workloads.
The pinned runtime currently rejects Q1 device opening: its 4 MiB worker L1 exceeds the emulator memory pool’s 2 MiB slot size. Q1 also requires TT-Lang compiler support; the current compiler supports Blackhole and Wormhole targets.
Arguments belonging to the program follow --:
./bin/tt-lang-sim --backend=emule program.py -- --program-option value
The launcher mounts the checkout and the current working directory. Use relative paths for program inputs beneath the current working directory. Absolute paths are passed unchanged and must refer to locations visible inside the container.
The launcher can also run from another working directory through its absolute
path. In this example, program.py is relative to the current directory:
/path/to/tt-lang/bin/tt-lang-sim --backend=emule program.py
To run programs directly on the host with the default Python backend, first install and activate the environment described in Python backend setup. The emule installer prepares the container environment; native Python simulation uses its own host dependencies. From the activated host environment:
./bin/tt-lang-sim program.py
./bin/tt-lang-sim --backend=python program.py
Run tests with the existing test framework
Select tests and generate reports with TT-Lang’s CMake, pytest, and lit commands from an activated compiler build environment.
Enter the installed Docker environment
Open a shell with the same installed compiler, runtime settings, mounts, and working directory used by the emulator launcher:
./scripts/shell-tt-lang-emule.sh
Pass --target=NAME to this helper to select any profile in the table above.
For example, --target=n150 selects the Wormhole N150 environment. Omitting
the option selects P150, matching the simulator launcher.
The helper verifies the installed compiler and activates its environment before
starting Bash. The compiler build is available at /ttlang-build, and the source
checkout at /workspace. Linked worktrees use the same Git metadata mount as
program execution.
Select tests
Inside the activated container shell, use the commands in Testing
or the detailed
test/TESTING.md guide.
Those references cover full suites, individual cases, pytest filtering, lit,
and report locations. Apply the installed environment’s paths:
Use
/ttlang-buildwherever the testing instructions usebuild.For direct pytest invocations, pass
-c /ttlang-build/test/pytest.inito load the installed build’s generated configuration.Select Python lit cases under
/ttlang-build/test/python, which contains their configured test environment.
A separate native Linux build uses its own build directory. Compiler-only tests exercise compiler behavior; device execution tests exercise tt-emule in the installed Linux environment. Compiler suite on tt-emule records a historical full-suite result and failure triage at the recorded revisions. Validate later revisions with a fresh test run.
Validate and inspect the environment
The installer validates the current TT-Lang checkout against the manifest’s compiler baseline. It also verifies the emulator checkout commit, the default descriptor, and the emulator’s exact tt-metal pin before building. Run the same checks directly with:
python3 scripts/tt-lang-emule-stack.py \
--manifest config/tt-lang-emule-stack.json \
validate --compiler-source . --emulator-source /path/to/emulator
Place --target=p100 before validate to validate the P100 descriptor instead.
Every built image records its resolved inputs and default target as OCI labels and in
/opt/tt-emule-runtime/stack.json. The original supported-stack manifest is
stored beside it as source-manifest.json, and its SHA-256 is verified while
the image is built. These records identify the supported manifest and exact
runtime inputs used to build the image. Before installation, program execution,
or shell access, the launcher checks the image’s provenance labels against the
selected manifest and runtime inputs. This check also applies to images selected
with TTLANG_EMULE_IMAGE. The container runs by its verified immutable image ID,
so changing an image tag after validation does not select a different runtime.
An image with missing or mismatched provenance requires a matching image or a
rebuild with TTLANG_EMULE_REBUILD=1 ./scripts/install-tt-lang-emule.sh.
Inspect an artifact without running a workload with:
docker image inspect tt-lang-emule:TAG \
--format '{{json .Config.Labels}}'
docker run --rm --entrypoint cat tt-lang-emule:TAG \
/opt/tt-emule-runtime/stack.json
Known limitations
The launcher selects the profile’s descriptor and configures tt-metal’s hybrid allocator before opening devices. Selecting a profile establishes the requested hardware configuration, not workload compatibility. Complete models and multi-device workloads require further validation, and Quasar kernels require compiler support beyond this change.
Known compiler-suite failures with the pinned runtime include RISC-V inline assembly rejected by the x86 JIT, a missing RMSNorm SFPU header, incorrect results in some dynamic-buffer reuse and collective tests, and missing DPRINT output. The incorrect-output and DPRINT failures still require isolated reproducers; they are not all established tt-emule defects. Device execution tests provide the evidence for emulated execution; compiler-only tests cover the compiler itself.
Troubleshooting
The environment is not installed
Run the installer from the same checkout:
./scripts/install-tt-lang-emule.sh
The installer prepares the image and compiler build for subsequent program runs.
The compiler checkout changed
The installed compiler is tied to the exact TT-Lang source used during installation. Re-run the installer after switching branches, pulling new commits, or changing compiler/build inputs. The installed compiler can be reused while editing workload scripts and generating output files.
The image’s Python requirements differ from the checkout
Run the installer to build an image for the checkout’s current requirements. For an explicitly selected image tag, request a rebuild:
TTLANG_EMULE_REBUILD=1 ./scripts/install-tt-lang-emule.sh
The rebuild installs the matching compiler/test dependencies into the image and prepares the compiler volume. The configured emulator source location is used again for the rebuild.
Emulator source access fails
Confirm that TTLANG_EMULE_RUNTIME_SOURCE_URL names the approved repository and
that the host can read it. The installer uses the host’s Git credentials to
fetch the manifest’s exact emulator commit.