.xe application without an xTAG or a physical board. You can check console output, record an instruction trace, watch and force pins, and run many test cases at once. Results describe the simulated target. They don’t establish real-board electrical behavior or hardware timing.
Set up the simulator
-
Install XMOS XTC Tools and build your
.xe. Simulated runs needxsim. Simulator debugging also usesxgdbserverandxgdb. -
Make sure Embedder can find XTC Tools. It checks your
PATH,XMOS_TOOL_PATH, and the standard install folder. For any other location, setEMBEDDER_XTC_PATHto the XTC root or itsbinfolder in the environment that launches Embedder. See Where Embedder finds XTC Tools. -
Select the simulator in
.embedder/xmos.json:Replace.embedder/xmos.jsonbin/app.xewith your project-relative.xepath. -
Ask Embedder to check simulator readiness before you start. The simulator is ready only when Embedder finds both
xsimand the Embedder xsim plugin, which Embedder includes for macOS, Linux, and Windows on x64. Finding XTC Tools alone isn’t enough.
Project settings
These keys in.embedder/xmos.json control simulated runs:
If the file has an unknown key or an invalid value, such as
sim.jobs above 64, Embedder ignores the whole file and uses the defaults.
Don’t put paths that contain spaces in sim.args. When you debug under the simulator, such a path doesn’t reach xsim intact, and Embedder drops --max-cycles because the debugger never stops on it.
Run and check output
Ask Embedder to run the application and wait for a message your firmware prints:BOOT_OK with a message your firmware emits. For a repeatable check, save a hardware script such as .embedder/hardware/sim_boot.py:
.embedder/hardware/sim_boot.py
embedder script sim_boot.py from the project directory. If the message doesn’t appear in time, the script raises TimeoutError with xsim console did not emit expected output. Check the console and the simulator state before you assume a firmware defect.
With the default "xscope": "auto", the simulator starts with offline xSCOPE, which needs an image built with -fxscope. For a plain image, call xsim.start(xe="bin/app.xe", xscope=False), or set "xscope": false in .embedder/xmos.json.
Scripts can use these helpers:
When the script ends, Embedder stops a simulation that the script started. It doesn’t stop a simulation the script reused.
After a simulation ends, its full console log is in
.embedder/artifacts/xsim/<session>/, with ports.vcd when sim.vcd is on and xscope.xmt from offline xSCOPE.
A Serial Monitor tab can also show the simulated application’s output. Choose the XMOS xsim (simulator) port; see xSCOPE.
For breakpoints, stepping, tiles, registers, or stack frames, use XMOS debugging with simulator=True. Starting a debugger replaces a standalone simulation that’s already running. While the debugger owns the simulator, xsim.start() fails until you stop the debug session. To find where a workload spends its time, see Profile a session.
Record an instruction trace
An instruction trace records every retired instruction with its tile, logical core, PC, symbol, and tile-clock cycle. xsim names the symbols from the.xe, so you don’t need a separate ELF.
Start the trace before the simulation. If a simulation is already running without tracing, the trace fails with the running simulation was started without instruction tracing. Stop that simulation and start the trace first.
The trace starts the simulator with offline xSCOPE, so build the image with -fxscope. Without it, the run can fail with Unable to find __sodFlag. Add -g when you want source-line results.
From the Trace tab
- Run Embedder: Open Embedder Monitor, select +, and choose Trace.
- In Source, choose the entry that ends in XMOS xsim (simulator).
- Set Capture type to Instruction trace.
- Optional: change Instruction limit, the number of recent instructions to keep. The default is 1,000,000.
- Select Record. Select Stop when the interval you need is complete.
.xe selected in a Serial Monitor tab’s App (.xe) or set as xe in .embedder/xmos.json. The result opens in the Instructions view, and you can watch it fill while it records.
From chat or a script
xsim.trace_start() and xsim.trace_stop(). See Analyze the capture from Python.
Limits
- The instruction limit is a rolling window across all tiles and cores. Older instructions are dropped as newer ones arrive. Execution doesn’t stop at the limit.
- A recording stops on its own after 10 minutes of wall-clock time.
- Boot-ROM instructions aren’t traced. In a script, pass
rom=Truetoxsim.trace_start()to include them. - Stopping a trace also stops a simulation that the trace started. A simulation that was already running keeps running.
Analyze the capture from Python
xsim.trace_start() returns a session ID. A hardware script can query that session while it records or after xsim.trace_stop():
.embedder/hardware/trace_bench.py
All three accept these filters:
tileandcorelimit the result to one tile or logical core.start_cycleandend_cyclelimit it to a cycle range, including the start and excluding the end. A cycle range needstile, because each tile has its own clock.
next_after_index as after_index in the next call. Pages stay stable while the window rolls. If cursor_evicted is true, the instructions you asked for have already left the window. window_truncated means the trace holds only the tail of the run.
Hotspots count retained instructions. Instructions without a function or source line are counted separately as unresolved_count, so the total doesn’t change silently.
Each new trace keeps its own copy of the .xe, so line-level hotspots still resolve after you rebuild. A trace that joined a simulation that was already running, or a trace from an earlier Embedder version, can’t resolve source lines and reports that.
Watch and drive pins
Use the Sim IO tab to watch simulated pins change and to force their levels while the simulation runs.- Run Embedder: Open Embedder Monitor, select +, and choose Sim IO.
- Start a simulation. Until one runs, the tab shows Start an xsim simulation to see GPIO activity.
- Search for pins, pick the ones you want, and select Show N lanes to add them.
- Use H, L, and R on a lane to force the pin high, force it low, or release it.
sim.pins in .embedder/xmos.json.
From a hardware script, use these helpers:
Read and write simulated memory
Scripts can read and write target memory withxsim.read_memory(tile, address, length) and xsim.write_memory(tile, address, data).
xsim fills memory that nothing has written with 0xBADDF00D. The program image doesn’t load until the simulation runs, so before that, the whole program area reads as this fill.
xsim.read_memorywarns when it reads the fill.xsim.write_memoryrefuses to write there, because loading the image would overwrite the write.
- Resume the simulation first, for example with
xsim.resume()and thenxsim.wait_for_output(). - Write at a debugger breakpoint.
- Patch the image with
write_memoryinxsim.run_batch.
force=True.
Run test cases in parallel
Usexsim.run_batch from a hardware script for a test gate with independent cases, such as one run per stimulus, vector set, parameter set, or seed. Embedder starts a separate xsim for each run, runs several at once, and waits for all of them. One failing case doesn’t stop the others.
To debug a failing case with breakpoints or traces, reproduce it on its own with xsim.start() or XMOS debugging.
A batch runs alongside the simulation that xsim.start() manages. It doesn’t use or disturb that simulation, the Sim IO tab, or the Trace tab. Stopping the script cancels the batch.
.embedder/hardware/limiter_gate.py
name that’s unique in the batch, ignoring case. Names use letters, digits, ., _, and -, start with a letter or digit, and are up to 80 characters long. A batch can hold up to 1,000 runs. The batch’s own name follows the same rules, up to 40 characters.
Keyword options apply to every run that doesn’t set them. defaults= sets the same options as a dict. A run can also set its own xe.
Startup zeroes
.bss, so Embedder refuses a write_memory target there and reports the run as error. Give the variable an initializer, such as int32_t stim[N] = {1};, to place it in .data.
How a run ends
Each run reports astatus of passed, failed, max_cycles, timeout, cancelled, or error. ended_by names the condition that ended it:
- Exit code: a single-tile C
mainreturn value becomes the xsim exit code.0ispassed. Any other value isfailed. done_patternordone_memory: the run ispassed. Multi-tile and RTOS images can’t callexit(), so end them this way. Keep the program running after the marker, for example in an idle loop, soread_memorycan read the buffer.fail_pattern: the run isfailed.max_cyclesortimeout_seconds: the run reportsmax_cyclesortimeout.
done_pattern or done_memory is set, a run that exits before the condition matches is failed, and Embedder skips its read_memory. If the program exits after the done condition matches but before Embedder reads memory, the run reports error. Keep the program running after the marker.
passed means the run ended as specified, not that its output is correct. Compare each run’s output with your reference.
keep_going=False cancels the remaining runs after the first one that doesn’t pass. check=True raises RuntimeError if any run didn’t pass.
Each run writes its files to .embedder/artifacts/xsim-batch/<batch>/<name>/, returned as dir. The directory holds the staged files, console.log, result.json, memory dumps, and any xSCOPE, pin, or VCD output. The batch result includes batch_id, batch_dir, jobs, jobs_reason, all_passed, counts, duration_s, and runs.
Set the number of parallel runs
By default, Embedder runs one fewer job than the number of performance cores on Apple silicon, or one fewer than the available CPUs elsewhere. Free memory also caps that count. The count never exceeds the number of runs. To override it, use one of these, in order of precedence. Each accepts 1 to 64:jobs=onxsim.run_batch.sim.jobsin.embedder/xmos.json:
.embedder/xmos.json
jobs_reason in the result reports which setting applied.
Keep cases short
xsim runs hundreds of times slower than real time, so one simulated second can take many minutes. Each run also slows down when several run at once. Time one short run of your image first, then settimeout_seconds from that estimate with margin.
Split a gate into many short cases instead of one long one. Give each case the shortest stimulus that proves it, and get coverage from more cases that run in parallel.
Ask Embedder to run batch scripts in the background. A foreground hardware-script run stops after 60 seconds by default, and Embedder can raise that to at most 10 minutes. The time limit doesn’t apply to a background run. From a terminal, embedder script accepts --timeout <ms> for a longer limit.
Troubleshoot
- No
.xefound: build the project and correctxein.embedder/xmos.json. xsim not found: install XMOS XTC Tools, or pointEMBEDDER_XTC_PATHorXMOS_TOOL_PATHat the installation.the Embedder xsim plugin is not available for this platform: run Embedder on macOS, Linux, or Windows on x64, and check that XTC Tools is complete.… path contains whitespace: xsim can’t take file paths with spaces, and Embedder keeps the simulator’s working files under your user folder. This affects user folders whose names contain a space.the simulator is owned by the debugger session: stop the XMOS debug session before you start a standalone simulation.- The output wait times out: check the console, whether the simulation is paused or running, and the expected message. Increase the wait only when the firmware needs longer.
Unable to find __sodFlag: the image wasn’t built with-fxscope. Rebuild with it, or start a plain image withxscope=False.the running simulation was started without instruction tracing: stop the running simulation and start the trace before the next run.- The beginning of the trace is missing: record a shorter interval or raise the instruction limit.
xsim.write_memory refused: the image isn’t loaded yet. See Read and write simulated memory.- A simulation exits at its cycle limit: check the extra simulator arguments. A standalone run with
--max-cyclesexits when it reaches the limit, which proves only that the limit was reached.