14.1.1.3. Simulators#
Two TARGET values build the firmware to run inside a simulator on your host, with no hardware attached. They exist so the firmware can be run and tested anywhere.
| Core | NPU | Simulator |
|---|---|---|---|
| Cortex-M7 | None | QEMU |
| Cortex-M55 | Ethos-U55 (256 MACs) | Arm FVP – Corstone SSE-300 |
MPS2_AN500 is the baseline Cortex-M7 target with no NPU. It runs under QEMU, which is fast to start and is the lightweight way to exercise the platform-independent code and the test suite.
MPS3_AN547 is a Cortex-M55 with the Ethos-U55 NPU, run on Arm’s Fast Models – the FVP, which models the Corstone SSE-300 reference subsystem. This is the target for exercising the NPU and the ML model path in simulation.
Both provide the ROM filesystem and plenty of RAM (and the NPU on the M55), so vision and ML scripts run unmodified.
There is no real sensor, but the csi module still works against a virtual one – csi.CSI.snapshot() returns a synthetic, animated test pattern (a scrolling checkerboard in grayscale, a gradient in RGB565), not a live scene. To process real image content, load it from a file instead: an image.Image from a fixture in the ROM filesystem, or an image.ImageIO stream for recorded frames.
14.1.1.3.1. Building and running#
Each simulator is a host tool you install yourself. You build a target like any other (see Building the firmware), then run it under the matching simulator – run builds the ROMFS image, boots the firmware, and leaves it running with a serial connection you can attach to.
14.1.1.3.1.1. MPS2_AN500 (QEMU)#
QEMU ships in most package managers as the Arm system emulator:
Linux (Debian / Ubuntu) – install it with
apt:sudo apt install qemu-system-armmacOS – install it with Homebrew:
brew install qemu
Then build and run the target:
make -j$(nproc) TARGET=MPS2_AN500
make TARGET=MPS2_AN500 run
14.1.1.3.1.2. MPS3_AN547 (Arm FVP)#
The FVP is Arm’s Fixed Virtual Platform for the Corstone SSE-300, shipped as part of Arm Virtual Hardware. It is available for Linux only – on a Mac, use the QEMU target above instead.
Download the bundle, extract it, and put its bin/ directory on your PATH:
wget https://artifacts.tools.arm.com/avh/11.31.28/avh-linux-x86_11.31_28_Linux64.tar.gz
mkdir -p ~/fvp
tar --strip-components=1 -xzf avh-linux-x86_11.31_28_Linux64.tar.gz -C ~/fvp
export PATH="$HOME/fvp/bin:$PATH"
The FVP also needs the SDK’s Python libraries on LD_LIBRARY_PATH when it runs. Export it once per shell from the repo root – reading SDK_VERSION keeps it correct as the repo updates the pinned SDK:
export LD_LIBRARY_PATH="$HOME/openmv-sdk-$(cat SDK_VERSION)/python/lib:$LD_LIBRARY_PATH"
Then build and run the target:
make -j$(nproc) TARGET=MPS3_AN547
make TARGET=MPS3_AN547 run
14.1.1.3.2. Running the test suite#
The unit tests live in scripts/unittest/tests/, with their fixture images and data in scripts/unittest/data/. They are run with mpremote, which mounts scripts/unittest/ onto the running simulator and executes its run.py.
The simulator link is slow with stock mpremote, so use the copy bundled in the repo with the serial patch applied (apply it once):
patch -N -p1 -d lib/micropython < tools/mpremote-qemu-serial.patch
For MPS2_AN500 under QEMU, start the target – it prints the pseudo-terminal its serial port is on (for example /dev/pts/5):
make TARGET=MPS2_AN500 run
Then, from another shell, point the patched mpremote at that device:
python3 lib/micropython/tools/mpremote/mpremote.py connect /dev/pts/5 \
mount scripts/unittest/ run scripts/unittest/run.py
For MPS3_AN547 under the FVP, the serial port is a telnet socket on port 5555 instead:
make TARGET=MPS3_AN547 run
python3 lib/micropython/tools/mpremote/mpremote.py connect socket://localhost:5555 \
mount scripts/unittest/ run scripts/unittest/run.py
run.py discovers and executes every test, printing a PASSED / FAILED line per test with timing and a summary at the end.
14.1.1.3.3. Adding a test#
Tests are discovered automatically. To add one, drop a file in scripts/unittest/tests/ that defines a unittest(data_path, temp_path) function returning True when the test passes and False when it fails:
# scripts/unittest/tests/circles.py
def unittest(data_path, temp_path):
import image
img = image.Image(data_path + "/shapes.ppm", copy_to_fb=True)
circles = img.find_circles(threshold=5000, x_margin=30, y_margin=30, r_margin=30)
return len(circles) == 1 and circles[0][0:] == (118, 56, 22, 5856)
The data_path and temp_path arguments point into the two filesystems the simulator sees while the suite runs:
Path | Backed by |
|---|---|
| The host’s |
| The board’s read-only ROM filesystem, built into the firmware image from |
So a new test needs no rebuild, but a new fixture does: the ROM image is keyed off romfs_config.json rather than the individual data files, so force the rebuild by touching the board’s romfs_config.json (or make
TARGET=<TARGET> clean) and running again.
Expected values are asserted inline in the test, so there is no separate expected-output file to maintain. To skip a test at runtime – for example when it needs hardware the simulator does not model – raise an exception whose message contains "SKIPPED".