Skip to content

Repository files navigation

NOMMU Linux on Armv8-M (Cortex-M55) + Ethos-U55

Mainline Linux (6.12, NOMMU, XIP) running on the Arm Corstone SSE-300 / Cortex-M55 with 2MB of RAM, with Helium (MVE) usable from userspace and the Ethos-U55 NPU driven directly from a Linux kernel driver. Everything here is verified both on QEMU (mps3-an547) and on real silicon (Arm MPS3 board, AN547 FPGA image).

Measured on the board:

Kernel image (XIP, flash) ~1.0 MB — text/rodata never touch RAM
Boot-time MemFree 724 KB of 1408 KB managed RAM
Userspace busybox + uClibc-ng, FDPIC, text XIP from flash
Helium MVE context-switched per task; 3-thread stress clean
Ethos-U55 DS-CNN KWS inference bit-exact vs TFLite reference; YOLO-fastest face detection; NPU fetches weights in place from QSPI XIP
Warm reboot busybox reboot -f → shell in ~2 s (SYSRESETREQ)

This repository contains only what is needed to build and boot that system: kernel config + patches, the Ethos-U user tooling, a minimal rootfs, a bootwrapper, and a QEMU smoke test. It is meant as a base for porting to other Cortex-M55 silicon (Himax WiseEye2, Infineon, ST — following the layout of rota1001/stm32f429-linux).

Layout

linux.config             kernel config (XIP + NOMMU + slab diet)
buildroot.config         FDPIC uClibc-ng toolchain + busybox
busybox-extra.fragment   applets (top/vi/free/... + setsid/cttyhack)
patches/                 all kernel changes, wired in by apply.sh
init/                    /sbin/init + romfs image builder
bootwrapper/             tiny ITCM loader (DTB copy + HW probe flags)
ethos-u/                 vela-model extractor + on-target runner
qemu/                    run script + expect boot check

What the patches do

File Why the kernel needs it
mps3-an547*.dts No SSE-300/MPS3 board support exists upstream. XIP memory layout, flash partitions, NPU node, arena carveout
timer-sse300.c SSE-300 System Timer clocksource/clockevents (counter is stopped at reset; driver starts it)
vfp-m.c, vfp-m-asm.S M-profile FP/MVE (Helium) context switch (CONFIG_VFP_M). Mainline VFP code is A-profile only — without this, userspace FPU/MVE state is corrupted across task switches
faults-v7m.c/.S Route v7m HardFault/MemManage/BusFault/UsageFault to user signals with CFSR/BFAR diagnostics (mainline: __invalid_entry, i.e. a silent hang)
v7m-reboot.c SYSRESETREQ restart handler for DT-generic boards (mainline only wires it via machine descriptors)
ethosu-npu.c, ethosu_uapi.h /dev/ethosu: direct-register Ethos-U55 driver (see below)

patches/apply.sh <kernel-tree> copies the files and edits the Makefiles/Kconfigs idempotently.

Memory architecture (MPS3 AN547 reference)

QSPI flash 8MB @0x28000000        BRAM 2MB @0x01000000 (all the RAM)
  0x000000 xipImage  (kernel XIP)   kernel .data/.bss   ~121K
  0x1F0000 DTB                      MemTotal            1408K
  0x200000 romfs 6MB partition      carveout@0x1188000   448K (NPU arena)
           (userspace text XIP)     DTB copy              32K

Three mechanisms make 2MB enough: XIP (no code in RAM), NOMMU pointers being physical (framebuffer/model/arena all zero-copy), and a reserved-memory carveout for the NPU arena (a NOMMU malloc(443K) needs one power-of-two 512K buddy block and will fail — carve it out instead).

Ethos-U55 without firmware

Linux runs on the Cortex-M55 that would normally be the NPU's helper MCU, so there is no mailbox and no firmware: ethosu-npu.c programs the U55 registers directly (reset → PROT check → AXI limits → QBASE / BASEP / REGIONCFG → run → poll). Policy lives in userspace:

  1. Compile the model with Vela (--accelerator-config ethos-u55-128).
  2. ethos-u/extract_pack.py model_vela.tflite outdir → a self-contained .m55e pack (command stream + weights + IO map, PACK_FORMAT.md).
  3. On target, npu-run (or your app) mmaps the pack from romfs — on NOMMU that is the flash address, so the NPU reads weights in place from QSPI — puts the arena in the carveout, and issues one ETHOSU_IOC_RUN ioctl per inference.

Bus truths discovered on AN547 silicon (baked into the driver): the NPU only sees real data through the Secure alias (bit 28); QCONFIG=0 and all-zero REGIONCFG selects are the only working AXI ports; ISRAM reads as zero to the NPU; QSPI XIP is reachable (weights need no RAM).

Dependencies

arm-none-eabi-gcc (kernel), clang+ld.lld (bootwrapper), qemu-system-arm ≥ 9 (mps3-an547 model), genromfs, expect, buildroot build prerequisites. For Ethos-U tooling: Python 3.10+ with ethos-u-vela.

Usage

./fetch.sh            # download linux-6.12.43 + buildroot-2025.02.3
make toolchain        # FDPIC uclibc + busybox   (long, once)
make kernel           # patches + xipImage + DTBs -> out/
make rootfs           # out/romfs.img
make boot             # bootwrapper (LLVM=/path/to/llvm/bin if needed)
make check            # boot on QEMU, expect V8M-BOOT-PASS

On the real MPS3: burn boot-an547-xip.bin to ITCM and xipImage / DTB / romfs to QSPI offsets 0 / 0x1F0000 / 0x200000 via the MCC (images.txt with AUTOQSPI).

Porting notes (Himax WiseEye2, Infineon, ST, ...)

Generic (keep): vfp-m (any M-profile FPU/MVE), faults-v7m, v7m-reboot, ethosu-npu (any U55/U65 with register access), linux.config as a baseline, the whole XIP/romfs/FDPIC userspace recipe, extract_pack.py.

Per-board (rewrite): the DTS (memory map, clocks, UART), the timer (SysTick or vendor timer instead of timer-sse300), the UART driver (pl011/vendor instead of mps2-uart), the bootwrapper addresses, the flash mapping.

Pitfalls we hit that you will too:

  • physmap rounds the flash window down to a power of two (fls64); a 6MB reg silently becomes a 4MB MTD and romfs mount fails with a bare EINVAL. Use a power-of-two window + fixed-partitions.
  • NOMMU large allocations round up to a power of two — carve out big buffers (NPU arena, framebuffers) in reserved-memory instead.
  • FDPIC default stack is 32KB: link with -Wl,-z,stack-size=...; a NOMMU overflow is a silent wild pointer.
  • FPSCR.LTPSIZE must be 4 or MVE LE traps INVSTATE; unprivileged VPR access is an architectural NOP (use P0 in asm).
  • A kernel-mode bus fault is a panic on v7m: never enable a DT node whose bus decode you have not proven. Verify on QEMU first — it faults at the same addresses.
  • On Ethos-U55 with a different MAC count (WiseEye2 = U55-64), re-run Vela with the matching --accelerator-config; command streams are not portable across configurations.

Credits / licenses

Kernel patches: GPL-2.0. The Ethos-U register sequences follow Arm's ethos-u-core-driver (Apache-2.0). Layout and approach inspired by rota1001/stm32f429-linux and jserv's Linux-on-STM32 course materials.

About

No-mmu Linux on ARM-v8.1m architecture.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages