Skip to content

Commit 7a55e23

Browse files
authored
Arm backend: Add MobileSAM Ethos-U example (#21397)
Add a MobileSAM prompt segmentation example for Ethos-U85-256. Prepare the pinned official source in an external managed checkout. Apply a small configurable-input patch instead of copying model code. Use int8 globally and A16W8 for TinyViT attention. Lower the complete graph to Ethos-U and compare output with FP32. Emit delegation summaries, validation metrics, and debug artifacts. Build a bare-metal Corstone-320 app using exporter metadata. Validate inference on FVP through the Arm OOTB test. cc @digantdesai @freddan80 @per @zingo @oscarandersson8218 @mansnils @Sebastian-Larsson @robell @rascani Signed-off-by: Usamah Zaheer <usamah.zaheer@arm.com>
1 parent 0129dc9 commit 7a55e23

16 files changed

Lines changed: 2554 additions & 6 deletions

File tree

backends/arm/scripts/docgen/ethos-u/backends-arm-ethos-u-overview.md.in

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,11 @@ how to run `model_export/export_deit.py`, build the sample firmware, and convert
6767
test images into C arrays so the workflow described in this guide can be tried
6868
end to end.
6969

70+
[`examples/arm/mobilesam_prompt_segmentation_example_ethos_u`](https://github.com/pytorch/executorch/tree/main/examples/arm/mobilesam_prompt_segmentation_example_ethos_u)
71+
contains a complete MobileSAM prompt segmentation workflow for Ethos-U85,
72+
including fixed-prompt export, PT2E quantization, transformer lowering, debug
73+
masks and overlays, a bare-metal Corstone-320 runtime app, and FVP execution.
74+
7075
### Ethos-U memory modes
7176

7277
The Ethos-U NPU provides two distinct memory interfaces:

backends/arm/scripts/run_fvp.sh

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ target="ethos-u55-128"
2424
timeout="600"
2525
etrecord_file=""
2626
trace_file=""
27+
semihosting_cwd=""
28+
ethosu_fast=0
2729

2830
help() {
2931
echo "Usage: $(basename $0) [options]"
@@ -35,6 +37,8 @@ help() {
3537
echo " --timeout=<TIME_IN_SEC> Maximum target runtime, used to detect hanging, might need to be higer on large models Default: ${timeout}"
3638
echo " --etrecord=<FILE> If ETDump is used you can supply a ETRecord file matching the PTE"
3739
echo " --trace_file=<FILE> File to write PMU trace output to"
40+
echo " --semihosting-cwd=<DIR> Enable target semihosting with this host working directory"
41+
echo " --fast Use fast Ethos-U model simulation for Ethos-U targets"
3842
exit 0
3943
}
4044

@@ -48,6 +52,8 @@ for arg in "$@"; do
4852
--timeout=*) timeout="${arg#*=}";;
4953
--etrecord=*) etrecord_file="${arg#*=}";;
5054
--trace_file=*) trace_file="${arg#*=}";;
55+
--semihosting-cwd=*) semihosting_cwd="${arg#*=}";;
56+
--fast) ethosu_fast=1;;
5157
*)
5258
;;
5359
esac
@@ -104,9 +110,34 @@ log_file=$(mktemp)
104110
extra_args_u55=()
105111
extra_args_u85=()
106112

113+
ethosu_extra_args=()
114+
if [[ "${ethosu_fast}" == 1 ]]; then
115+
ethosu_extra_args+=("--fast")
116+
fi
107117
if [[ -n "${trace_file}" ]]; then
108-
extra_args_u55+=(-C "ethosu.extra_args=--pmu-trace ${trace_file}")
109-
extra_args_u85+=(-C "mps4_board.subsystem.ethosu.extra_args=--pmu-trace ${trace_file}")
118+
ethosu_extra_args+=("--pmu-trace" "${trace_file}")
119+
fi
120+
if [[ ${#ethosu_extra_args[@]} -gt 0 ]]; then
121+
extra_args_u55+=(-C "ethosu.extra_args=${ethosu_extra_args[*]}")
122+
extra_args_u85+=(-C "mps4_board.subsystem.ethosu.extra_args=${ethosu_extra_args[*]}")
123+
fi
124+
125+
semihosting_args_u55=()
126+
semihosting_args_u85=()
127+
if [[ -n "${semihosting_cwd}" ]]; then
128+
semihosting_cwd=$(realpath "${semihosting_cwd}")
129+
semihosting_args_u55+=(
130+
-C cpu0.semihosting-enable=1
131+
-C cpu0.semihosting-stack_base=0
132+
-C cpu0.semihosting-heap_limit=0
133+
-C "cpu0.semihosting-cwd=${semihosting_cwd}"
134+
)
135+
semihosting_args_u85+=(
136+
-C mps4_board.subsystem.cpu0.semihosting-enable=1
137+
-C mps4_board.subsystem.cpu0.semihosting-stack_base=0
138+
-C mps4_board.subsystem.cpu0.semihosting-heap_limit=0
139+
-C "mps4_board.subsystem.cpu0.semihosting-cwd=${semihosting_cwd}"
140+
)
110141
fi
111142

112143
if [[ ${target} == cortex-m* ]]; then
@@ -154,6 +185,7 @@ elif [[ ${target} == *"ethos-u55"* || ${target} == *"ethos-u65"* ]]; then
154185
-C mps3_board.uart0.out_file='-' \
155186
-C mps3_board.uart0.shutdown_on_eot=1 \
156187
${extra_args_u55[@]+"${extra_args_u55[@]}"} \
188+
${semihosting_args_u55[@]+"${semihosting_args_u55[@]}"} \
157189
-a "${elf_file}" \
158190
${data_file} \
159191
--timelimit ${timeout} 2>&1 | sed 's/\r$//' | tee ${log_file} || true # seconds
@@ -165,8 +197,10 @@ elif [[ ${target} == *"ethos-u85"* ]]; then
165197
-C vis_hdlcd.disable_visualisation=1 \
166198
-C mps4_board.telnetterminal0.start_telnet=0 \
167199
-C mps4_board.uart0.out_file='-' \
200+
-C mps4_board.uart0.unbuffered_output=1 \
168201
-C mps4_board.uart0.shutdown_on_eot=1 \
169202
${extra_args_u85[@]+"${extra_args_u85[@]}"} \
203+
${semihosting_args_u85[@]+"${semihosting_args_u85[@]}"} \
170204
-a "${elf_file}" \
171205
${data_file} \
172206
--timelimit ${timeout} 2>&1 | sed 's/\r$//' | tee ${log_file} || true # seconds
@@ -200,11 +234,21 @@ if [ $? != 0 ]; then
200234
fi
201235

202236
echo "Checking for problems in log:"
203-
! grep -E "^(F|E|\\[critical\\]|Hard fault.|Info: Simulation is stopping. Reason: CPU time has been exceeded.).*$" ${log_file}
204-
if [ $? != 0 ]; then
237+
problem_log=$(mktemp)
238+
grep -E "^(F|E|\\[critical\\]|Hard fault.|Info: Simulation is stopping. Reason: CPU time has been exceeded.).*$" "${log_file}" > "${problem_log}" || true
239+
if [[ "${ethosu_fast}" == 1 ]]; then
240+
filtered_problem_log=$(mktemp)
241+
timing_adapter_fast_mode_regex="Failed to initialize timing-adapter #[0-9]+|Timing adapter has no effect if option --fast/-F is enabled"
242+
grep -Ev "${timing_adapter_fast_mode_regex}" "${problem_log}" > "${filtered_problem_log}" || true
243+
mv "${filtered_problem_log}" "${problem_log}"
244+
fi
245+
if [[ -s "${problem_log}" ]]; then
246+
cat "${problem_log}"
205247
echo "Found ERROR"
248+
rm "${problem_log}"
206249
rm "${log_file}"
207250
exit 1
208251
fi
209252
echo "No problems found!"
253+
rm "${problem_log}"
210254
rm "${log_file}"

backends/arm/test/test_arm_ootb.sh

Lines changed: 138 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ if [[ "$1" == "-h" || "$1" == "--help" ]]; then
2828
fi
2929

3030
if [[ $# -eq 0 ]]; then
31-
TEST_SUITES=(run_ootb_tests_ethos_u run_ootb_tests_tosa run_ootb_tests_vgf run_deit_e2e_ethos_u run_swin2sr_e2e_vgf)
31+
TEST_SUITES=(run_ootb_tests_ethos_u run_ootb_tests_tosa run_ootb_tests_vgf run_deit_e2e_ethos_u run_mobilesam_e2e_ethos_u run_swin2sr_e2e_vgf)
3232
else
3333
TEST_SUITES=("$1")
3434
fi
@@ -164,6 +164,143 @@ run_deit_e2e_ethos_u() {
164164
echo "${FUNCNAME}: PASS"
165165
}
166166

167+
run_mobilesam_e2e_ethos_u() {
168+
echo "$FUNCNAME: Export, build, and run the MobileSAM e2e test"
169+
170+
local example_dir="${et_root_dir}/examples/arm/mobilesam_prompt_segmentation_example_ethos_u"
171+
local work_root="${et_root_dir}/arm_test/mobilesam_ootb_smoke"
172+
local export_dir="${work_root}/export"
173+
local artifact_dir="${work_root}/artifacts"
174+
local debug_dir="${work_root}/debug"
175+
local et_build_dir="${work_root}/cmake-out-arm"
176+
local quantized_aot_build_dir="${work_root}/quantized_ops_aot"
177+
local build_dir="${work_root}/runtime"
178+
local mobile_sam_source="${work_root}/mobile_sam/source"
179+
local image_path="${et_root_dir}/examples/models/dinov2/dog.jpg"
180+
local pte_path="${export_dir}/mobilesam_prompt_smoke.pte"
181+
local metadata_path="${export_dir}/mobilesam_prompt_smoke.json"
182+
local fvp_log="${work_root}/fvp.log"
183+
local toolchain_file="${et_root_dir}/examples/arm/ethos-u-setup/arm-none-eabi-gcc.cmake"
184+
local input_size=448
185+
local fvp_timelimit="${FVP_TIMELIMIT:-300}"
186+
echo "${FUNCNAME}: Work directory: ${work_root}; existing artifacts will be reused if present"
187+
188+
mkdir -p "${export_dir}" "${artifact_dir}" "${debug_dir}" "${build_dir}"
189+
190+
setup_path_script=${et_root_dir}/examples/arm/arm-scratch/setup_path.sh
191+
source ${setup_path_script}
192+
193+
source ${et_root_dir}/backends/arm/scripts/utils.sh
194+
local n_proc="$(get_parallel_jobs)"
195+
196+
echo "${FUNCNAME}: Building ExecuTorch (if needed)"
197+
cmake --preset arm-baremetal -B "${et_build_dir}"
198+
cmake --build "${et_build_dir}" --target install -j"$n_proc"
199+
200+
echo "${FUNCNAME}: Building host quantized AOT library"
201+
local python_executable
202+
python_executable="$(python3 -c 'import sys; print(sys.executable)')"
203+
cmake \
204+
-S "${et_root_dir}" \
205+
-B "${quantized_aot_build_dir}" \
206+
-DCMAKE_BUILD_TYPE=Release \
207+
-DEXECUTORCH_BUILD_KERNELS_QUANTIZED=ON \
208+
-DEXECUTORCH_BUILD_KERNELS_QUANTIZED_AOT=ON \
209+
-DEXECUTORCH_BUILD_XNNPACK=OFF \
210+
-DPYTHON_EXECUTABLE="${python_executable}"
211+
cmake --build "${quantized_aot_build_dir}" --target quantized_ops_aot_lib -j"$n_proc"
212+
213+
local quantized_ops_library
214+
quantized_ops_library="$(
215+
find "${quantized_aot_build_dir}/kernels/quantized" \
216+
-name 'libquantized_ops_aot_lib.*' \
217+
-type f \
218+
-print \
219+
-quit
220+
)"
221+
[[ -n "${quantized_ops_library}" ]] || {
222+
echo "${FUNCNAME}: Missing quantized AOT library under ${quantized_aot_build_dir}"
223+
return 1
224+
}
225+
226+
echo "${FUNCNAME}: Installing example requirements"
227+
pip install -r "${example_dir}/requirements.txt"
228+
229+
echo "${FUNCNAME}: Preparing pinned MobileSAM source"
230+
python3 "${example_dir}/model_export/prepare_mobilesam.py" \
231+
--source-dir "${mobile_sam_source}"
232+
233+
echo "${FUNCNAME}: Exporting quantized MobileSAM PTE"
234+
env EXECUTORCH_QUANTIZED_OPS_AOT_LIBRARY="${quantized_ops_library}" \
235+
python3 "${example_dir}/model_export/export_mobilesam.py" \
236+
--output-path "${pte_path}" \
237+
--calibration-image "${image_path}" \
238+
--eval-image "${image_path}" \
239+
--mobile-sam-source "${mobile_sam_source}" \
240+
--input-size "${input_size}" \
241+
--point 219 193 \
242+
--num-calibration-samples 1 \
243+
--num-eval-samples 1 \
244+
--num-debug-samples 1 \
245+
--minimum-fp32-quantized-iou 0.9 \
246+
--artifact-dir "${artifact_dir}" \
247+
--debug-output-dir "${debug_dir}"
248+
249+
for artifact in \
250+
"${pte_path}" \
251+
"${metadata_path}" \
252+
"${export_dir}/mobilesam_prompt_smoke_delegation.txt" \
253+
"${export_dir}/mobilesam_prompt_smoke_metrics.json"; do
254+
[[ -f "${artifact}" ]] || {
255+
echo "${FUNCNAME}: Missing export artifact ${artifact}"
256+
return 1
257+
}
258+
done
259+
260+
echo "${FUNCNAME}: Configuring the MobileSAM application"
261+
cmake \
262+
-U "LIB_*" \
263+
-U executorch_DIR \
264+
-S "${example_dir}/runtime" \
265+
-B "${build_dir}" \
266+
-DCMAKE_TOOLCHAIN_FILE="${toolchain_file}" \
267+
-DET_PTE_FILE_PATH="${pte_path}" \
268+
-DMODEL_METADATA_PATH="${metadata_path}" \
269+
-DIMAGE_PATH="${image_path}" \
270+
-DMASK_THRESHOLD=0.0 \
271+
-DET_SEGMENTATION_DUMP_MASK=ON \
272+
-DPYTHON_EXECUTABLE="${python_executable}" \
273+
-DET_BUILD_DIR_PATH="${et_build_dir}"
274+
275+
echo "${FUNCNAME}: Building mobilesam_prompt_segmentation_example"
276+
cmake --build "${build_dir}" -j"$n_proc" --target mobilesam_prompt_segmentation_example
277+
278+
local elf="${build_dir}/mobilesam_prompt_segmentation_example"
279+
280+
echo "${FUNCNAME}: Running on FVP"
281+
backends/arm/scripts/run_fvp.sh \
282+
--elf="${elf}" \
283+
--target=ethos-u85-256 \
284+
--timeout="${fvp_timelimit}" \
285+
--semihosting-cwd="${build_dir}" \
286+
--fast | tee "${fvp_log}"
287+
288+
grep -q "Model executed successfully." "${fvp_log}" || {
289+
echo "${FUNCNAME}: FVP run did not report successful execution"
290+
return 1
291+
}
292+
293+
python3 "${example_dir}/runtime/visualize_fvp_output.py" \
294+
--fvp-log "${fvp_log}" \
295+
--input-image "${image_path}" \
296+
--metadata "${metadata_path}" \
297+
--reference-mask "${debug_dir}/dog/quantized_mask.png" \
298+
--minimum-iou 0.9 \
299+
--output-dir "${work_root}/fvp_visual"
300+
301+
echo "${FUNCNAME}: PASS"
302+
}
303+
167304
run_swin2sr_e2e_vgf() {
168305
echo "$FUNCNAME: Prepare demo assets, export FP/INT8, build, and run the Swin2SR VGF e2e test"
169306

docs/source/backends/arm-ethos-u/arm-ethos-u-overview.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,11 @@ how to run `model_export/export_deit.py`, build the sample firmware, and convert
121121
test images into C arrays so the workflow described in this guide can be tried
122122
end to end.
123123

124+
[`examples/arm/mobilesam_prompt_segmentation_example_ethos_u`](https://github.com/pytorch/executorch/tree/main/examples/arm/mobilesam_prompt_segmentation_example_ethos_u)
125+
contains a complete MobileSAM prompt segmentation workflow for Ethos-U85,
126+
including fixed-prompt export, PT2E quantization, transformer lowering, debug
127+
masks and overlays, a bare-metal Corstone-320 runtime app, and FVP execution.
128+
124129
### Ethos-U memory modes
125130

126131
The Ethos-U NPU provides two distinct memory interfaces:

examples/arm/README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,11 @@ For Cortex-M testing, use a Cortex-M target and bundled I/O:
7777
- End-to-end DEiT-Tiny image classification flow for Ethos-U, including
7878
model fine-tuning, export, bare-metal runtime build, and Corstone-320 FVP
7979
execution.
80-
- [image_classification_example_vgf](image_classification_example_vgf/) -
80+
- [mobilesam_prompt_segmentation_example_ethos_u](mobilesam_prompt_segmentation_example_ethos_u/)
81+
- End-to-end MobileSAM prompt segmentation flow for Ethos-U, including
82+
export, quantization, debug mask generation, bare-metal runtime build, and
83+
Corstone-320 FVP execution.
84+
- [image_classification_example_vgf](image_classification_example_vgf/) -
8185
DEiT-Tiny image classification flow for VGF host execution.
8286
- [super_resolution_example_vgf](super_resolution_example_vgf) - Swin2SR image
8387
super-resolution.
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# MobileSAM Prompt Segmentation Example Application
2+
3+
This end-to-end example shows how to use the Arm Ethos-U backend in
4+
ExecuTorch for transformer-based prompt segmentation. MobileSAM predicts a
5+
binary mask for fixed positive point prompts rather than semantic class IDs.
6+
The host debug flow validates quantization by comparing FP32 and quantized
7+
masks, with an optional binary reference mask when one is available.
8+
9+
It covers:
10+
11+
- Loading the MobileSAM `vit_t` checkpoint.
12+
- Freezing one or more positive point prompts into the exported graph.
13+
- Applying post-training quantization with the Ethos-U quantizer.
14+
- Lowering the quantized model to an Ethos-U85-256 ExecuTorch program.
15+
- Producing validation and debugging artifacts such as masks, overlays,
16+
mismatch heatmaps, metrics, and delegation summaries.
17+
- Building a bare-metal Corstone-320 runtime app and running it on FVP.
18+
19+
The default export uses a reduced `448x448` image input and returns one
20+
low-resolution `[1, 1, 112, 112]` mask-logit tensor. The example prepares the
21+
official MobileSAM GitHub source at a pinned revision in an external checkout
22+
and applies a small configurable-input patch there. Neither the MobileSAM
23+
source nor checkpoint is redistributed in ExecuTorch.
24+
25+
The export uses int8 activations and int8 weights globally, and A16W8
26+
quantization for TinyViT attention modules. This keeps the transformer
27+
attention numerically stable while still producing one Ethos-U delegate.
28+
29+
The exported graph intentionally uses `multimask_output=False` and leaves
30+
mask thresholding outside the model. SAM-style candidate-mask selection can be
31+
numerically sensitive after export and quantization, so this example keeps the
32+
target graph focused on the fixed-prompt image encoder and mask decoder.
33+
34+
## Layout
35+
36+
- `model_export/prepare_mobilesam.py` - Prepares the pinned external MobileSAM
37+
checkout and applies the configurable-input patch.
38+
- `model_export/README.md` - Model loading, quantization, lowering,
39+
validation, and debug artifact generation.
40+
- `runtime/README.md` - Bare-metal runtime build, image header generation, and
41+
Corstone-320 FVP execution.
42+
- `runtime/visualize_fvp_output.py` - Decodes the target mask dump, creates an
43+
overlay, and compares FVP output with the host quantized mask.

0 commit comments

Comments
 (0)