Docs / API reference / syn_model_ota.h
syn_model_ota.hv0.1.0
<synaptic/syn_model_ota.h> defines the over-the-air model update API: a chunked transfer session (begin → write_chunk → finish) followed by explicit activation of the staged model, with rollback to the previous one. Progress through the update is exposed as a state machine via syn_ota_get_state(). The design intent — validate before staging, stage before activating, always keep a rollback target — is described in Model lifecycle & OTA updates.
The frozen header is implemented over the flash-backed model store: chunks stream through a page buffer into the A/B staging slot, finish() re-reads the staged image from flash and validates magic, size, name, and payload CRC32 before committing a staged record, and activation/rollback are each one power-loss-safe ping-pong registry commit plus a hot-swap under the live scheduler. On dual-core boards, begin() parks CPU1 in reset (its XIP bank hosts the staging slots) and every terminal state releases it through the normal blank-check-guarded boot path — resume measured at 1,514 µs boot + 1,519 µs handshake on the FRDM-MCXN947. Board-verified: a slot-maximum 442,432-byte update staged, CRC-verified, and activated; power loss mid-transfer leaves the previous model serving; a staged update survives reboot and activates from IDLE. See Model lifecycle & OTA updates for the architecture. The header is included by syn_api.h only when CONFIG_SYNAPTIC_OTA is enabled.
Types & constants
| Name | Kind | Description |
|---|---|---|
syn_ota_state_t | enum | OTA session state machine: SYN_OTA_STATE_IDLE — no update in progress; SYN_OTA_STATE_DOWNLOADING — session open, chunks being written; SYN_OTA_STATE_VALIDATING — transfer complete, integrity being checked; SYN_OTA_STATE_STAGING — validated image being placed in the staging slot; SYN_OTA_STATE_READY — staged and awaiting activation; SYN_OTA_STATE_ERROR — the update failed. |
Functions
| Signature | Description |
|---|---|
int syn_ota_begin(const char *model_name, size_t total_size) | Opens an OTA session for the named model, declaring the total image size up front (a .synm image: 64-byte header + payload). Rejects sizes beyond the staging slot's capacity (-EFBIG). Since v0.5.0, first drains the in-flight cross-core inference (1000 ms budget) — refusing with -EBUSY and resuming serving on timeout — closing the Phase 4 gap where a live remote exchange could be killed mid-flight (board-demonstrated under a ~20 req/s offload with zero serve errors). Erases the staging slot sector-by-sector, and on dual-core boards parks CPU1 first (offload pauses; local inference continues). Returns 0 on success, negative errno on failure. |
int syn_ota_write_chunk(const uint8_t *data, size_t len) | Appends len bytes of the incoming model image to the open session, buffered to the 128-byte flash program page. Any chunk size works — the engine is transport-agnostic. Call repeatedly until total_size bytes have been written. Returns 0 on success, negative errno on failure. |
int syn_ota_finish(void) | Closes the transfer and validates the staged image by re-reading it from flash: header magic, version, declared size, model name, and payload CRC32 must all match. On success the staged record is committed to the registry and the session reaches SYN_OTA_STATE_READY — a staged update survives reboot. Returns 0 on success, negative errno on failure (state ERROR, recoverable; the active model is untouched). |
int syn_ota_activate(void) | Commits the staged model as active (one ping-pong registry write) and hot-swaps inference to it; the previous model becomes the rollback candidate. Works from READY or — for a staged update found after reboot — from IDLE. Releases CPU1 on dual-core boards. Returns 0 on success, negative errno on failure. |
int syn_ota_rollback(void) | Reverts to the previously active model with the same commit + hot-swap path. Also recovers service when an activated update turns out to be unloadable. Returns 0 on success, negative errno on failure. |
syn_ota_state_t syn_ota_get_state(void) | Returns the current OTA state machine value. |
Usage
The update flow — receive a .synm image in chunks from any transport, then activate it. This is exactly what the syn ota shell transport and the samples/ota_update demo do:
/* Chunked transfer, then explicit activation */
int ret = syn_ota_begin("test_classify", image_size);
if (ret != 0) {
return ret;
}
while (bytes_remaining > 0) {
size_t len = receive_next_chunk(chunk_buf, sizeof(chunk_buf));
ret = syn_ota_write_chunk(chunk_buf, len);
if (ret != 0) {
return ret;
}
bytes_remaining -= len;
}
ret = syn_ota_finish(); /* validate + stage */
if (ret != 0 || syn_ota_get_state() != SYN_OTA_STATE_READY) {
return ret;
}
ret = syn_ota_activate(); /* switch inference to the new model */
if (ret != 0) {
syn_ota_rollback(); /* revert to the previous model */
}Notes
- Power-loss safety: the previous model stays authoritative until the single registry commit in
activate(); a reset anywhere mid-transfer or mid-commit boots the old model — exercised by fault injection in the QEMU suite and by physical reset on the board. - CPU1 quiescence: the staging slots share flash bank 1 with CPU1's XIP image, so an OTA session parks CPU1; cross-core offload callers time out during the session while local CPU0 inference keeps serving (demonstrated under load).
- Build gating: the module is compiled and included only with
CONFIG_SYNAPTIC_OTA— see the Kconfig reference. - Single session: the API models one global update session (no session handle); one OTA update runs at a time.
- Transport-agnostic: the API only consumes byte chunks — how they arrive (UART, network, USB) is up to the application. The built-in
syn otashell transport offers two modes over 115200-baud UART: ack-paced hex lines (~5.4 KB/s) and, since v0.5.0, therawdatabinary bypass — 11.1 KB/s measured on the board (2.06× hex, 98.7% of the line rate), with a power-loss injection at 81% of a raw 432 KB transfer leaving the store intact. The engine itself is unchanged since Phase 4. - Related: an activated model becomes visible through the syn_model.h registry, and
syn_model_swap()provides the registry-side hot-swap primitive.