Skip to content

Commit 4899130

Browse files
build template
Signed-off-by: Tomás González <tomasagustin.gonzalezorlando@arm.com>
1 parent 920ab36 commit 4899130

4 files changed

Lines changed: 274 additions & 4 deletions

File tree

content/learning-paths/cross-platform/deploy-ml-model-to-npu-with-topo/_index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ learning_objectives:
1111
- Explain how Topo deploys an application that spans Cortex-A, Cortex-M, and Ethos-U
1212
- Prepare an NXP FRDM i.MX 93 board for remoteproc-runtime and shared-memory inference
1313
- Clone and deploy the topo-imx93-npu-deployment template
14+
- Describe how the Template is bootstrapped from Compose services, Remoteproc Runtime metadata, and Topo arguments
1415
- Run image classification from a browser and verify that inference is executed by the Cortex-M33 firmware
1516

1617
prerequisites:
Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
1+
---
2+
title: Build the Topo Template from scratch
3+
weight: 4
4+
5+
### FIXED, DO NOT MODIFY
6+
layout: learningpathall
7+
---
8+
9+
## Start from the application pieces
10+
11+
The `topo-imx93-npu-deployment` repository is a Compose project with Topo metadata at the root. The Topo-specific part is not a replacement for Compose. The services still describe container builds, dependencies, ports, volumes, and runtime settings. The `x-topo` block adds the metadata Topo uses to identify the Template, check target requirements, and prompt for configuration.
12+
13+
The project has three implementation areas:
14+
15+
- `executorch-runner/`: builds the ExecuTorch `.pte` program and the Cortex-M33 firmware ELF.
16+
- `webapp/`: builds the Flask application that stages memory and sends `RUN` commands over `RPMsg`.
17+
- `compose.yaml`: connects the build artifacts, runtime services, Remoteproc Runtime settings, and Topo metadata.
18+
19+
When bootstrapping this Template from scratch, first make the project work as a normal Compose build. Then add the `x-topo` metadata that lets Topo deploy it consistently to an Arm64 target.
20+
21+
## Create the runner build pipeline
22+
23+
The `executorch-runner/Dockerfile` is a multi-stage Dockerfile. It builds two artifacts from one build context:
24+
25+
- `mv2_ethosu65_256.pte`: the MobileNetV2 ExecuTorch program lowered for `ethos-u65-256`.
26+
- `executorch_runner_cm33.elf`: the Cortex-M33 firmware image loaded by Linux `remoteproc`.
27+
28+
The first half of the Dockerfile builds the model artifact:
29+
30+
```Dockerfile
31+
FROM build-base AS executorch-base
32+
...
33+
FROM executorch-base AS pte-builder
34+
...
35+
RUN source /workspace/executorch/examples/arm/arm-scratch/setup_path.sh && \
36+
python /usr/local/bin/export_mv2_imx93.py
37+
38+
FROM busybox:1.36 AS pte-artifacts
39+
COPY --from=pte-builder /workspace/build/mv2-imx93/mv2_ethosu65_256.pte /artifacts/mv2_ethosu65_256.pte
40+
```
41+
42+
The second half builds and packages the firmware:
43+
44+
```Dockerfile
45+
FROM build-base AS runner-base
46+
ARG MCUXSDK_MANIFEST_URL=https://github.com/nxp-mcuxpresso/mcuxsdk-manifests.git
47+
ARG MCUXSDK_MANIFEST_REV=v25.09.00
48+
...
49+
FROM runner-base AS runner-builder
50+
RUN /usr/local/bin/build-runner.sh /artifacts
51+
52+
FROM scratch AS runner-runtime
53+
COPY --from=runner-builder /artifacts/executorch_runner_cm33.elf /executorch_runner_cm33.elf
54+
ENTRYPOINT ["/executorch_runner_cm33.elf"]
55+
```
56+
57+
The `runner-runtime` stage is intentionally a `scratch` image. The only payload is the ELF file. When the service starts with `runtime: io.containerd.remoteproc.v1`, containerd uses Remoteproc Runtime instead of a normal Linux process runtime. Remoteproc Runtime passes the ELF entrypoint to the Linux `remoteproc` driver, and the `imx-rproc` driver loads and releases the Cortex-M33.
58+
59+
The project also applies patches before building the runner. One patch changes the MCUX SDK RAM linker and startup behavior so initialized data is loaded in-place by `remoteproc` rather than copied from a flash-style load address. The runner patches add RPMsg stability fixes and trace output used by the web application.
60+
61+
## Add artifact-only Compose services
62+
63+
At the root of the Template, create normal Compose services for the build outputs:
64+
65+
```yaml
66+
services:
67+
pte-artifacts:
68+
platform: linux/arm64
69+
scale: 0
70+
build:
71+
context: executorch-runner
72+
dockerfile: Dockerfile
73+
target: pte-artifacts
74+
75+
runner-artifacts:
76+
platform: linux/arm64
77+
scale: 0
78+
build:
79+
context: executorch-runner
80+
dockerfile: Dockerfile
81+
target: runner-artifacts
82+
```
83+
84+
These services are not runtime application containers. `scale: 0` keeps them out of the running deployment while still making their build targets available to the rest of the Compose project.
85+
86+
The web application imports the PTE artifact as a BuildKit additional context:
87+
88+
```yaml
89+
services:
90+
webapp:
91+
platform: linux/arm64
92+
build:
93+
context: .
94+
dockerfile: Dockerfile
95+
additional_contexts:
96+
pte_artifacts: service:pte-artifacts
97+
```
98+
99+
The webapp Dockerfile then copies from that context:
100+
101+
```Dockerfile
102+
COPY --from=pte_artifacts /artifacts/mv2_ethosu65_256.pte /opt/mv2-imx93/mv2_ethosu65_256.pte
103+
```
104+
105+
This keeps the model export pipeline separate from the Flask app while still producing one deployable webapp image.
106+
107+
## Add the remote processor service
108+
109+
The Cortex-M33 firmware is represented as another Compose service:
110+
111+
```yaml
112+
services:
113+
cm33-runner:
114+
platform: linux/arm64
115+
build:
116+
context: executorch-runner
117+
dockerfile: Dockerfile
118+
target: runner-runtime
119+
runtime: io.containerd.remoteproc.v1
120+
annotations:
121+
remoteproc.name: imx-rproc
122+
```
123+
124+
This is the key heterogeneous deployment hook. The service is still built by Docker, but it is not launched as a Linux userspace process. The `runtime` selects the containerd Remoteproc Runtime shim, and `remoteproc.name: imx-rproc` selects the i.MX 93 remote processor driver.
125+
126+
After this service starts, Linux exposes the RPMsg device used by the Cortex-A web app. The Flask code waits for `/dev/ttyRPMSG*`, writes the `.pte` file to `0xC0000000`, writes the input tensor to `0xC036D000`, sends `RUN\n` over RPMsg, and parses the `CM33:` response lines into top-1 and top-5 ImageNet results.
127+
128+
## Add the web application service
129+
130+
The web application service extends `webapp/compose.yaml` from the root Compose file:
131+
132+
```yaml
133+
services:
134+
webapp:
135+
platform: linux/arm64
136+
extends:
137+
file: webapp/compose.yaml
138+
service: webapp
139+
depends_on:
140+
- cm33-runner
141+
```
142+
143+
The extended service is privileged and mounts `/sys` and `/dev`:
144+
145+
```yaml
146+
services:
147+
webapp:
148+
privileged: true
149+
ports:
150+
- "${WEBAPP_PORT:-3001}:3000"
151+
volumes:
152+
- /sys:/sys
153+
- /dev:/dev
154+
```
155+
156+
Those mounts are required because the app checks `/proc/device-tree`, reads remoteproc state through `/sys/class/remoteproc`, talks to `/dev/ttyRPMSG*`, writes model and tensor data through `/dev/mem`, and checks for `/dev/ethosu0`.
157+
158+
## Add Topo metadata
159+
160+
After the Compose services are in place, add the root-level `x-topo` block:
161+
162+
```yaml
163+
x-topo:
164+
name: "i.MX93 ExecuTorch runner"
165+
description: "Runs a Cortex-A web application that sends image inference commands to a resident CM33 ExecuTorch runner over RPMsg."
166+
features:
167+
- "remoteproc-runtime"
168+
```
169+
170+
Keep `x-topo` at the root of `compose.yaml`, not under `services`. The `features` entry is what tells Topo this Template needs a target with Remoteproc Runtime support. That is why `topo health` checks for:
171+
172+
```output
173+
Remoteproc Runtime: ✅ (remoteproc-runtime)
174+
Remoteproc Shim: ✅ (containerd-shim-remoteproc-v1)
175+
Subsystem Driver (remoteproc): ✅ (imx-rproc)
176+
```
177+
178+
You can also add a deployment success message so users know exactly what to do after deployment:
179+
180+
```yaml
181+
x-topo:
182+
deployment_success_message: |
183+
The i.MX93 ExecuTorch runner is deployed.
184+
Open http://<target-ip>:3001 and classify an ImageNet image.
185+
```
186+
187+
## Expose project configuration
188+
189+
Topo arguments are metadata for project parameters. Compose still carries the values into the build.
190+
191+
The current Template exposes optional cache image parameters:
192+
193+
```yaml
194+
x-topo:
195+
args:
196+
EXECUTORCH_BASE_CACHE_IMAGE:
197+
description: Optional GHCR image used as a BuildKit cache source for the ExecuTorch PTE build.
198+
required: false
199+
default: ghcr.io/arm-examples/topo-imx93-npu-deployment/executorch-base:et-v1.2.0-ubuntu24.04
200+
IMX93_RUNNER_BUILD_CACHE_IMAGE:
201+
description: Optional GHCR image used as a BuildKit cache source for the CM33 runner build.
202+
required: false
203+
default: ghcr.io/arm-examples/topo-imx93-npu-deployment/imx93-runner-build:mcux-v25.09.00-armgcc14.2-ubuntu24.04
204+
```
205+
206+
Those values are used by Compose interpolation in `build.cache_from`:
207+
208+
```yaml
209+
cache_from:
210+
- ${EXECUTORCH_BASE_CACHE_IMAGE:-ghcr.io/arm-examples/topo-imx93-npu-deployment/executorch-base:et-v1.2.0-ubuntu24.04}
211+
```
212+
213+
For build-time configuration, wire Topo arguments into standard Compose `build.args`. The runner Dockerfile already declares project-specific arguments for the MCUX SDK manifest:
214+
215+
```Dockerfile
216+
ARG MCUXSDK_MANIFEST_URL=https://github.com/nxp-mcuxpresso/mcuxsdk-manifests.git
217+
ARG MCUXSDK_MANIFEST_REV=v25.09.00
218+
```
219+
220+
To expose the SDK revision through Topo, add matching Compose build args to the services that build `runner-base` descendants:
221+
222+
```yaml
223+
services:
224+
runner-artifacts:
225+
build:
226+
args:
227+
MCUXSDK_MANIFEST_REV: ${MCUXSDK_MANIFEST_REV:-v25.09.00}
228+
229+
cm33-runner:
230+
build:
231+
args:
232+
MCUXSDK_MANIFEST_REV: ${MCUXSDK_MANIFEST_REV:-v25.09.00}
233+
234+
x-topo:
235+
args:
236+
MCUXSDK_MANIFEST_REV:
237+
description: MCUX SDK manifest revision used to build the Cortex-M33 runner.
238+
required: false
239+
default: v25.09.00
240+
```
241+
242+
With that wiring, Topo can prompt for the value when the Template is cloned or extended, Compose passes the value into Docker BuildKit, and the Dockerfile consumes it through `ARG MCUXSDK_MANIFEST_REV`.
243+
244+
Use this only for configuration that should be chosen at Template setup time. Runtime-only settings, such as `WEBAPP_PORT`, should remain normal Compose environment interpolation unless you intentionally want Topo to collect them as build-time parameters.
245+
246+
## Lint the Template
247+
248+
Before publishing the Template, validate the root Compose file:
249+
250+
```bash
251+
check-jsonschema \
252+
--schemafile ../topo-template-format/schema/topo-template-format.json \
253+
compose.yaml
254+
```
255+
256+
Then review the Template the same way Topo Template linting does:
257+
258+
- The Template root contains `compose.yaml`.
259+
- `compose.yaml` contains a root-level `x-topo.name`.
260+
- Non-remoteproc services set `platform: linux/arm64`.
261+
- The `cm33-runner` service uses `runtime: io.containerd.remoteproc.v1` and `remoteproc.name: imx-rproc`.
262+
- `x-topo.description` matches the README and the actual Cortex-A to Cortex-M33 RPMsg flow.
263+
- `x-topo.features` includes `remoteproc-runtime`.
264+
- `x-topo.args` entries are either consumed through Compose interpolation, such as the cache image values, or wired into `services.<service>.build.args` and declared as Dockerfile `ARG` instructions.
265+
- `deployment_success_message` tells the user to open the web app on the configured target port.
266+
267+
## What you've accomplished
268+
269+
You now understand how the `topo-imx93-npu-deployment` Template is built from ordinary Compose services plus Topo metadata: artifact-only build stages produce the model and firmware, Remoteproc Runtime starts the Cortex-M33 ELF, RPMsg connects the processors at runtime, and `x-topo.args` provides a path for setup-time configuration without replacing Docker or Compose.

content/learning-paths/cross-platform/deploy-ml-model-to-npu-with-topo/deploy.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ topo health --target <user>@<target-ip>
5252

5353
## Reserve memory in the device tree
5454

55-
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 Ethos-U65. We are now goint to modify the device tree and reboot the target so that this modifications take place.
55+
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 Ethos-U65. You are now going to modify the device tree and reboot the target so that these modifications take effect.
5656

5757
{{% notice Warning %}}
5858
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.
@@ -209,4 +209,4 @@ You should see something similar to:
209209
## What you've accomplished
210210

211211
You have prepared an FRDM i.MX 93 board for shared-memory NPU inference, deployed the `topo-imx93-npu-deployment` Template with Topo, started Cortex-M33 firmware through `remoteproc-runtime`, and used a browser-based application to run MobileNetV2 classification with Ethos-U65 acceleration.
212-
Next, you will review the toolchains used to build the model artifact, firmware runner, and web application.
212+
Next, you will review how this project is structured as a Topo Template.

content/learning-paths/cross-platform/deploy-ml-model-to-npu-with-topo/what-are-the-toolchains.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
title: Understand the toolchains
3-
weight: 4
3+
weight: 5
44

55
### FIXED, DO NOT MODIFY
66
layout: learningpathall
@@ -33,7 +33,7 @@ The firmware runner is built as:
3333
executorch_runner_cm33.elf
3434
```
3535

36-
This firmware runs on the Cortex-M33 core. It waits for commands coming from the Linux web application over `RPMsg`, reads the input imae (tensors) from reserved memory, executes inference through ExecuTorch, and writes classification output back over `RPMsg`.
36+
This firmware runs on the Cortex-M33 core. It waits for commands coming from the Linux web application over `RPMsg`, reads the input image tensors from reserved memory, executes inference through ExecuTorch, and writes classification output back over `RPMsg`.
3737

3838
The Template packages the firmware as the entrypoint of the `cm33-runner` image:
3939

0 commit comments

Comments
 (0)