GitHub

Docs / Getting started / The syn shell

The syn shellv0.5.0

SynapticOS registers a syn command group in the Zephyr shell for inspecting the running system: version, tensor arena statistics, the model registry, NPU capabilities and state, profiling control, triggering an inference, benchmarking the DSP hardware, inspecting the CPU1 link and self-testing the cross-core MPU guard; Phase 4 added the flash-backed model store and a complete OTA model update over the serial console; and Phase 5 adds arena dumps, model load/unload by name, scheduler statistics, activation-placement reports, health and watchdog introspection with fault injection, the DMA ingest benchmark, and a raw binary OTA transport. Unless labeled otherwise, the outputs below were captured live from a FRDM-MCXN947 during the Phase 1 (2026-05-27), Phase 2 (2026-07-12), Phase 3 (2026-07-14), Phase 4 (2026-07-15), and Phase 5 (2026-08-09/10) verifications.

Enabling the shell

The commands are compiled in when CONFIG_SYNAPTIC_SHELL=y (the default) and Zephyr's CONFIG_SHELL=y is set. The shell runs over the same serial console you use for boot logs — on the FRDM-MCXN947 that is the MCU-LINK CDC bridge:

shell
picocom -b 115200 /dev/ttyACM0
# then at the uart:~$ prompt, type e.g.
syn version

Tab completion works on the whole tree: syn followed by Tab lists the subcommand groups version, mem, model, npu, dsp, infer, prof, store, ota (with CONFIG_SYNAPTIC_OTA), health (with CONFIG_SYNAPTIC_HEALTH), dma, and — on dual-core FRDM builds — ipc and mpu. Since v0.5.0, completion is also dynamic where it matters: syn model info/load/unload and syn infer run complete registered model names.

Command reference

CommandDescriptionExample output
syn version Print SynapticOS version SynapticOS v0.2.0
syn mem stats Show memory statistics: arena and scratch usage, peak, allocation and reset counts Arena: 800/114688 bytes (peak 800)
Scratch: 0/16384 bytes
Allocations: 1, Resets: 0
syn mem dump New in Phase 5: show the arena layout — base, persistent and ephemeral extents, free bytes, scratch usage — plus a hexdump of the first 64 bytes at the arena base Arena layout (131072 bytes total):
  base       0x30000860
  persistent 0x30000860 - 0x30000860 (0 bytes)
  ephemeral  0x30000860 - 0x30000860 (0 bytes)
  free       114688 bytes
  scratch    0x3001c860 (0/16384 bytes used)
(FRDM-MCXN947, 2026-08-09; hexdump lines omitted)
syn model list List registered models with handle, name, version, and load state Registered models: 1
  [1] test_classify v1.0.0 (loaded)
syn model info <name> New in Phase 5: show one model's registered metadata — version, load state, tensor shapes and sizes, SRAM requirement. The name argument tab-completes Model 'demo_model' (handle 1):
  version:  ota
  loaded:   no
  input:    27648 bytes int8 [1,96,96,3]
  output:   10 bytes int8 [1,10,0,0]
  sram:     0 bytes required
(FRDM-MCXN947, 2026-08-09)
syn model load/unload <name> New in Phase 5: load a registered model onto the NPU (CRC-gated read from flash for stored models) or unload it, by name Model 'demo_model' loaded
syn npu caps Show NPU capabilities: backend name, throughput, scratch size, async support NPU: neutron
  Max OPS/sec: 100000000
  Scratch: 16384 bytes
  Async: yes
syn npu state Show NPU state: one of IDLE, BUSY, ERROR, SUSPENDED NPU state: IDLE
syn npu plan New in Phase 5: report the activation placement of the last layered session — the planner's peak, the all-live naive baseline, the reduction, and the planning time (see the scheduler page; layered execution is the stub-NPU synthetic model format) Activation placement of the last layered session:
  planned peak: 208 bytes
  all-live sum: 362 bytes (naive baseline)
  reduction:    43%
  planning:     18 us
(values measured by demo preempt on the FRDM-MCXN947, 2026-08-10)
syn dsp bench New in Phase 2: benchmark the DSP HAL against the software kernels — a 256-point float32 FFT (16 iterations) and a 16×16 Q15 matrix × vector multiply (200 iterations), printing per-op times, the speedup, and the maximum hardware-vs-software error. On the MCXN947 the HAL side runs on the PowerQuad; on QEMU both sides are the same software kernel FFT f32 256 pts x16:
  soft: 86184 us (5386 us/op)
  hal:  15641 us (977 us/op)
  speedup: 5.51x
  max err: 976 ppm of peak
MatMul q15 16x16 x200:
  soft: 2948 us (14740 ns/op)
  hal:  1768 us (8840 ns/op)
  speedup: 1.66x
  max err: 1 LSB
(FRDM-MCXN947, 2026-07-12)
syn infer run <model-name> [be|normal|rt] New in Phase 2: run a scheduler-driven inference on a registered model — allocates an ephemeral input tensor with a gradient test pattern, calls syn_infer_run_sync(), prints the top class and elapsed time, then resets the ephemeral arena. Since v0.5.0 the optional last argument selects the priority class (default normal) and the model name tab-completes Model 'test_classify': class 0 (confidence 127), 1130 us
Use 'syn prof last' for the stage breakdown.
(FRDM-MCXN947, stub NPU, 2026-07-12)
syn infer stats New in Phase 5: show the scheduler counters — completed/error/cancelled jobs, deadline misses, preemptions and resumes (with the last and maximum context-save time after a preemption) Jobs: 11106 completed, 0 errors, 0 cancelled
Deadline misses: 0
Preemptions: 0 (resumes 0)
(FRDM-MCXN947, end of the 2026-08-10 soak run)
syn prof last Show last profiling result: total, preprocess, NPU, and postprocess times plus memory peak Last inference:
  Total:       1069 us
  Preprocess:  1 us
  NPU:         1068 us
  Postprocess: 1 us
  Memory peak: 1792 bytes
(FRDM-MCXN947, stub NPU, after syn infer run; Phase 1 firmware answered No profiling data available)
syn prof enable Enable profiling Profiling enabled
syn prof disable Disable profiling Profiling disabled
syn ipc status New in Phase 3: show the CPU1 link — boot and handshake timing, serve counters, and the IPC round-trip statistics that CPU1 measures with its own cycle counter and publishes through the shared control block (CPU1 has no console of its own) CPU1 link: UP
CPU1 boot time: 1514 us (release to ready)
IPC handshake:  2554 us (release to STATUS_REQ)
STATUS_REQ answered: 64
Inferences served: 1267 (errors 0, avg 2290 us)
IPC round-trip (CPU1-measured, 64 samples): last 15 us, min 15 us, max 81 us
(FRDM-MCXN947, dual_model, stub NPU, 2026-07-14)
syn ipc stats New in Phase 5: show the per-direction ring counters, read from the free-running SPSC head/tail indices — pushed, popped, and currently queued messages each way Ring capacity: 16 messages each direction
cpu0 to cpu1: pushed 1473, popped 1473, queued 0
cpu1 to cpu0: pushed 1473, popped 1473, queued 0
(FRDM-MCXN947, dual_model, 2026-08-09)
syn health New in Phase 5: show the health monitor — hardware watchdog state, stale-source episodes, CPU1 heartbeat recoveries, and each registered source's period, last kick, and busy/idle state Watchdog: armed (fed by the monitor)
Stale episodes: 0
CPU1 recoveries: 1
  sched    period 5000 ms, last kick 4 ms ago, idle, 0 stale episodes
(FRDM-MCXN947, 2026-08-10, after the CPU1 hang-recovery demonstration)
syn health hang cpu0|cpu1 New in Phase 5: fault injection for the recovery paths. cpu0 locks the scheduler and spins — only the hardware watchdog gets the board back (reset in under the 2000 ms timeout). cpu1 makes CPU1's heartbeat ISR spin with interrupts off — genuinely dead until CPU0's monitor detects the stall (600 ms measured) and parks + re-releases the core (1.3 ms) hanging CPU0 (watchdog will reset)... (followed by a clean watchdog reboot; FRDM-MCXN947, 2026-08-10)
syn dma bench [frames] New in Phase 5: benchmark the double-buffered DMA ingest pump against a sequential CPU-copy baseline over a synthetic frame source, verifying every frame byte-exactly (stamp check + word checksum). On the FRDM the pump runs on the eDMA; on QEMU the stub backend is CPU work, so no throughput claims there 1000 frames of 8192 bytes:
  cpu copy:  460912 us (460 us/frame), 0 corrupt
  dma ingest:131218 us (131 us/frame), 0 corrupt, 0 dma errors
  frame rate gain: +251%
(FRDM-MCXN947, 2026-08-10)
syn store status New in Phase 4: show the flash-backed model store — registry generation, active and staged slots, per-copy registry wear, the last commit and boot-scan durations, and each slot's occupant (name, payload bytes, CRC32, flash address) generation 5  active slot 0  staged slot 255
registry wear: copy0 3 copy1 2 erases
last commit 2625 us, boot scan 24 us
slot 0: 'demo_model' 442368 bytes crc 0x47c2e332 at 0x00124040
slot 1: 'demo_model' 2000 bytes crc 0x597f9492 at 0x00192040
(FRDM-MCXN947, 2026-07-15; slot 255 = none)
syn ota status New in Phase 4: show the OTA session — state, target slot, received/total bytes, session duration, last error state READY  slot 1  received 2064/2064 bytes
syn ota begin <name> <bytes> New in Phase 4: open an OTA session — erases the staging slot sector-by-sector and, on dual-core builds, parks CPU1 for the duration (its XIP bank hosts the model slots) OTA RX 'demo_model' 442432 bytes (FRDM-MCXN947, 2026-07-15)
syn ota data <hex> New in Phase 4: feed one hex-encoded chunk (up to 1024 bytes per line); each line is acknowledged, which is the transport's flow control. tools/syn_ota_send.py drives this from the host ok 2048/442432
syn ota rawdata <bytes> New in Phase 5: the binary transport — takes the UART raw via the shell's bypass mode for the announced byte count: no hex doubling, no echo, no line parsing. The host waits for the RAW <n> line, streams the bytes, then waits for the ack; tools/syn_ota_send.py --binary drives it in 16 KB blocks. A stalled transfer is abandoned after 5 s and the shell restored. Moved a slot-maximum 432 KB model in 38.88 s — 11.1 KB/s, 2.06× the hex transport, 98.7% of the 115200 line rate RAW 16384
(host streams 16384 raw bytes)
raw ok 16384/442432
(FRDM-MCXN947, 2026-08-10)
syn ota done New in Phase 4: finish the transfer — the image is validated by re-reading it from flash (magic, size, name, payload CRC32) and committed as staged; a staged update survives reboot OTA staged and verified: state READY (FRDM-MCXN947, 2026-07-15)
syn ota activate New in Phase 4: commit the staged model as active (one power-loss-safe registry write) and hot-swap inference to it; releases CPU1 on dual-core builds OTA activated (8889459 us since begin); active slot 1 (FRDM-MCXN947, 2026-07-15)
syn ota rollback New in Phase 4: restore the previously active model with the same commit + hot-swap path rolled back; active slot 0 (FRDM-MCXN947, 2026-07-15)
syn mpu test New in Phase 3: self-test the cross-core MPU guard — verifies the shared region is writable, then deliberately writes into CPU1's RAM and confirms the write faults. The MemManage fault dump is expected output; the offending thread is aborted and both cores continue Running cross-core MPU self-test (a MemManage fault dump below is EXPECTED)...
MPU self-test PASS: shared region writable, cross-core write faulted
(FRDM-MCXN947, 2026-07-14; see the full fault dump below)
Note · What changed in Phases 2, 3, 4 and 5

In v0.1.0, syn prof last always answered No profiling data available because the profiling marks were not yet called from an inference path. As of v0.2.0 the marks fire inside the pipeline executor and syn prof last, syn infer run, and syn dsp bench return real data. v0.3.0 adds syn ipc status and syn mpu test for the dual-core subsystem. v0.4.0 adds syn store status and the syn ota group, which doubles as a complete UART OTA transport. v0.5.0 adds syn mem dump, syn model info/load/unload, the priority argument on syn infer run, syn infer stats, syn npu plan, syn ipc stats, the syn health group with fault injection, syn dma bench, and the syn ota rawdata binary transport — all verified live on the FRDM-MCXN947 (2026-07-14 through 2026-08-10). The NPU numbers are the deterministic stub backend (the Neutron SDK invoke path lands in a later phase); the syn dsp bench hardware numbers are the real PowerQuad.

A Phase 5 hardening session

From the board (2026-08-10 verification, dual_model serving cross-core inference from CPU1 throughout, stub NPU): the on-hardware layer-preemption demo — a NORMAL-priority 8-layer job is preempted at a layer boundary by a REALTIME job and later resumes bit-exactly — then the health monitor after the CPU1 hang-recovery demonstration, the DMA ingest benchmark, and the scheduler counters at the end of the 11,106-job soak:

serial
uart:~$ demo preempt
baseline: 106945 us for 8 layers (stub NPU, synthetic model)
plan: peak 208 vs all-live 362 bytes (-43%), planned in 18 us
rt job: completed BEFORE the normal job, 117254 us from submit to completion
normal job resumed: output MATCHES the baseline
preemptions +1, resumes +1, ctx save 10 us (max 10 us)
verdict: PASS
uart:~$ syn health
Watchdog: armed (fed by the monitor)
Stale episodes: 0
CPU1 recoveries: 1
  sched    period 5000 ms, last kick 4 ms ago, idle, 0 stale episodes
uart:~$ syn dma bench 1000
1000 frames of 8192 bytes:
  cpu copy:  460912 us (460 us/frame), 0 corrupt
  dma ingest:131218 us (131 us/frame), 0 corrupt, 0 dma errors
  frame rate gain: +251%
uart:~$ syn infer stats
Jobs: 11106 completed, 0 errors, 0 cancelled
Deadline misses: 0
Preemptions: 0 (resumes 0)

(Interleaved serving-traffic lines and the demo's internal diagnostic lines abridged; the four captures come from different points of the 2026-08-09/10 session — the final syn infer stats is from the soak run, whose counters were reset at its start. The synthetic layers are deliberately heavy busy-work, so the ~13 ms layer boundary and ~107 ms job times are arbitrary; the scheduler's own contribution is the 10 µs context save.)

A Phase 4 OTA session

From the board (2026-07-15 verification, dual_model serving cross-core inference from CPU1 throughout): a complete OTA update driven by tools/syn_ota_send.py over the shell transport. CPU1 is parked while the staging slot is erased and written, the staged image is CRC-verified from flash, activation swaps the registry, and CPU1 resumes with a normal handshake — 1,514 µs boot + 1,519 µs, the same cost as a cold Phase 3 release:

serial
uart:~$ syn ota begin demo_model 2064
[00:36:12.490] <inf> syn_boot: CPU1 held in reset (bank 1 quiesced)
[00:36:12.490] <wrn> syn_model_ota: OTA session: CPU1 parked, offload paused
[00:36:12.490] <inf> syn_model_ota: Erasing staging slot 1: 1 sectors
[00:36:12.492] <inf> syn_model_ota: OTA begin: 'demo_model' 2064 bytes into slot 1
uart:~$ syn ota data 53594e4d01000000…   (hex lines, ack-paced)
ok 1024/2064
ok 2048/2064
ok 2064/2064
uart:~$ syn ota done
OTA staged and verified: state READY
[00:36:19.373] <inf> syn_model_ota: OTA staged: 'demo_model' 2000 bytes crc 0x597f9492 (slot 1), ready to activate
uart:~$ syn ota activate
OTA activated (8889459 us since begin); active slot 1
[00:36:21.380] <inf> syn_model: Registered model 'demo_model' (handle=3)
[00:36:21.380] <inf> syn_boot: Releasing CPU1 (vector table at 0x00100000)
[00:36:21.381] <inf> syn_boot: CPU1 ready flag after 1514 us
[00:36:21.381] <inf> syn_boot: IPC handshake complete 1519 us after release
[00:36:21.381] <inf> syn_model_ota: OTA session over: CPU1 resumed
uart:~$ syn ota rollback
rolled back; active slot 0
[00:36:52.862] <inf> syn_model_ota: OTA rollback: previous model active again

(Interleaved serving-traffic lines abridged; the hex payload line is truncated for display. The same transport moved a slot-maximum 442,432-byte model in 80.5 s at 5.4 KB/s — ack-paced hex over 115200-baud UART is transport-bound, and we report it as measured.)

A Phase 3 dual-core session

From the board (2026-07-14 verification, dual_model sample serving cross-core inference requests from CPU1): the link status, then the MPU self-test with its expected fault dump — the offending thread is aborted, and the interleaved serving traffic in the raw transcript shows CPU1's requests continuing uninterrupted across the fault:

serial
uart:~$ syn ipc status
CPU1 link: UP
CPU1 boot time: 1514 us (release to ready)
IPC handshake:  2554 us (release to STATUS_REQ)
STATUS_REQ answered: 64
Inferences served: 1267 (errors 0, avg 2290 us)
IPC round-trip (CPU1-measured, 64 samples): last 15 us, min 15 us, max 81 us
uart:~$ syn mpu test
Running cross-core MPU self-test (a MemManage fault dump below is EXPECTED)...
MPU self-test PASS: shared region writable, cross-core write faulted
[00:00:44.729,000] <inf> syn_mpu: Shared region write/readback OK
[00:00:44.729,000] <err> os: ***** MPU FAULT *****
[00:00:44.729,000] <err> os:   Data Access Violation
[00:00:44.729,000] <err> os:   MMFAR Address: 0x30060000
[00:00:44.729,000] <err> os: r0/a1:  0x00000000  r1/a2:  0x00000000  r2/a3:  0xdeadbeef
[00:00:44.729,000] <err> os: >>> ZEPHYR FATAL ERROR 19: Unknown error on CPU 0
[00:00:44.729,000] <err> syn_mpu: MPU violation (reason 19): aborting offending thread, core continues
[00:00:44.729,000] <inf> syn_mpu: Cross-core write to 0x30060000 faulted as expected

(Register lines abridged from the full fault dump. Inference latencies are stub-NPU baselines.)

A full session

A Phase 2 session from the board (2026-07-12 verification), after the hello_inference boot sequence finishes — the DSP benchmark, a scheduler-driven inference, and its profile:

serial
uart:~$ syn dsp bench
FFT f32 256 pts x16:
  soft: 86184 us (5386 us/op)
  hal:  15641 us (977 us/op)
  speedup: 5.51x
  max err: 976 ppm of peak
MatMul q15 16x16 x200:
  soft: 2948 us (14740 ns/op)
  hal:  1768 us (8840 ns/op)
  speedup: 1.66x
  max err: 1 LSB
uart:~$ syn infer run test_classify
Model 'test_classify': class 0 (confidence 127), 1130 us
Use 'syn prof last' for the stage breakdown.
uart:~$ syn prof last
Last inference:
  Total:       1069 us
  Preprocess:  1 us
  NPU:         1068 us
  Postprocess: 1 us
  Memory peak: 1792 bytes
uart:~$ syn mem stats
Arena: 0/114688 bytes (peak 1792)
Scratch: 0/16384 bytes
Allocations: 6, Resets: 2

And the original Phase 1 transcript (2026-05-27 verification, v0.1.0 firmware), kept for comparison:

serial
uart:~$ syn version
SynapticOS v0.1.0
uart:~$ syn mem stats
Arena: 800/114688 bytes (peak 800)
Scratch: 0/16384 bytes
Allocations: 1, Resets: 0
uart:~$ syn model list
Registered models: 1
  [1] test_classify v1.0.0 (loaded)
uart:~$ syn npu caps
NPU: neutron
  Max OPS/sec: 100000000
  Scratch: 16384 bytes
  Async: yes
uart:~$ syn npu state
NPU state: IDLE
uart:~$ syn prof last
No profiling data available
uart:~$ syn prof enable
Profiling enabled
uart:~$ syn prof disable
Profiling disabled

The syn npu state snapshot reads IDLE because the shell can only run after the boot-time inference has completed; during the inference itself the HAL traverses IDLE → BUSY → IDLE.