Before deploying the Topo Template, confirm that the FRDM i.MX 93 board is reachable from your host and that it’s ready for deployment:
topo health --target <user>@<target-ip>
Replace <target-ip> with the IP address or hostname of your board.
Resolve any errors before continuing.
The target section includes successful checks similar to:
Host
----
Topo: ✅ (topo)
SSH: ✅ (ssh)
Curl: ✅ (curl)
Container Engine: ✅ (docker)
Target
------
Destination: ssh://<target-ip>
Connectivity: ✅
Container Engine: ✅ (docker)
Remoteproc Runtime: ✅ (remoteproc-runtime)
Remoteproc Shim: ✅ (containerd-shim-remoteproc-v1)
Hardware Info: ✅ (lscpu)
Subsystem Driver (remoteproc): ✅ (imx-rproc)
If remoteproc-runtime is missing, install it with Topo:
topo install remoteproc-runtime --target <user>@<target-ip>
Run the health check again:
topo health --target <user>@<target-ip>
The web application and Cortex-M33 firmware exchange data through reserved physical memory. The target device tree must reserve memory for the model/input buffer and for the Ethos-U65. This prevents Linux from allocating memory that the firmware and Ethos-U65 need to access by physical address.
You’ll now modify the device tree and reboot the target so that these modifications take effect.
Back up the board’s original device tree before modifying it. The exact boot partition can differ between Linux images, so check the paths on your board before copying files.
On your host, create a working directory and dump the live device tree from the target:
mkdir -p devicetree
ssh <user>@<target-ip> 'cat /sys/firmware/fdt' > devicetree/live.dtb
dtc -I dtb -O dts -o devicetree/live.dts devicetree/live.dtb
Open devicetree/live.dts in a text editor of your choice. Then, under remoteproc-cm33, add the CM33 power domain if it’s not already present:
power-domains = <0x61>;
Under reserved-memory, add the model memory range:
model@c0000000 {
reg = <0x00 0xc0000000 0x00 0x400000>;
no-map;
};
Update the Ethos-U reserved-memory node so it’s reserved and not reusable:
ethosu_region@A8000000 {
compatible = "shared-dma-pool";
reg = <0x00 0xa8000000 0x00 0x8000000>;
no-map;
phandle = <0x60>;
};
Add iomem=relaxed to chosen.bootargs. For example:
bootargs = "clk-imx93.mcore_booted console=ttyLP0,115200 earlycon root=/dev/mmcblk1p2 rootwait rw iomem=relaxed";
Return to your host machine terminal and build the patched device tree:
dtc -I dts -O dtb -o devicetree/patched.dtb devicetree/live.dts
Copy it to the board:
scp devicetree/patched.dtb <user>@<target-ip>:/tmp/patched.dtb
Install it on the board. Adjust the boot partition path if your image uses a different location:
ssh <user>@<target-ip>
cp /run/media/boot-mmcblk1p1/imx93-11x11-frdm.dtb \
/run/media/boot-mmcblk1p1/imx93-11x11-frdm.dtb.bak
cp /tmp/patched.dtb \
/run/media/boot-mmcblk1p1/imx93-11x11-frdm.dtb
sync
reboot
After the board reboots, run the Topo health check again from the host and verify everything is still correct:
topo health --target <user>@<target-ip>
You can choose to deploy from the original Topo Template, or from the template you built from scratch. If you haven’t already cloned the original template, clone it now:
topo clone https://github.com/Arm-Examples/topo-imx93-npu-deployment.git
Topo prompts for optional build cache image arguments. Accept the defaults unless you have your own cache images.
If you build without cache images, the first build can take a long time and requires about 25 GB of free disk space. The first build involves downloading and building ExecuTorch, the Arm GNU toolchain, MCUX SDK components, RPMsg-Lite, and the Cortex-M33 runner sources. Later builds are faster when Docker can reuse local cache layers or import the configured GHCR cache layers.
Then cd into the correct directory:
cd topo-imx93-npu-deployment
Or:
cd new-topo-npu-template
Deploy the project to your target:
topo deploy --target <user>@<target-ip>
During deployment, Topo builds the required images, transfers them to the target, starts the Cortex-M33 firmware through remoteproc-runtime, and starts the web application.
When deployment succeeds, the output includes a successful service startup. You can also check the deployed services:
topo ps --target <user>@<target-ip>
The output shows a process on both the Cortex-M33 and the Linux Host, and is similar to:
Image Status Processing Domain Address
topo-imx93-npu-deployment-cm33-runner Up 50 minutes imx-rproc
topo-imx93-npu-deployment-webapp Up 50 minutes Linux Host imx93-scorpio.cambridge.arm.com:3001, [::]:3001%
Open the web application in a browser:
http://<target-ip>:3001
If you need to use a different target port, set WEBAPP_PORT when deploying. For example:
WEBAPP_PORT=3002 topo deploy --target <user>@<target-ip>
Then open:
http://<target-ip>:3002
The application shows:
Image classification web app showing correctly classified German Shepherd
When you choose an image in the browser and select Classify, the web application:
.pte program and input tensor into reserved physical memory.RUN command to the Cortex-M33 runner over RPMsg.Try this out with an image from an ImageNet-supported class.
You’ve prepared an FRDM i.MX 93 board for shared-memory NPU inference, deployed the topo-imx93-npu-deployment template with Topo, and started Cortex-M33 firmware through remoteproc-runtime. You used a browser-based application to stage the ExecuTorch .pte program and input tensor for MobileNetV2 classification with Ethos-U65 acceleration.
You can now use the deployed application as a reference for your own heterogeneous Arm applications, or adapt the model, firmware runner, web interface, or Topo metadata for another target.