Docs / API reference / syn_infer.h
syn_infer.hv0.5.0
<synaptic/syn_infer.h> is the inference engine and pipeline scheduler API. A pipeline chains pre-processing stages, a registered model, and post-processing stages into a single unit; jobs are submitted against a pipeline with a priority, an optional deadline, and a completion callback, then awaited, cancelled, or polled for results. The header also declares a synchronous convenience call, a scheduler concurrency knob, and a set of built-in pre- and post-processor stages. The design is discussed in Inference pipelines & scheduler and Pre- & post-processing.
As of v0.2.0, src/core/syn_infer.c implements the pipeline engine and the priority job scheduler, and the built-in pre/post-processor sources under src/preprocess/ and src/postprocess/ are implemented as well. The header itself is unchanged — the frozen v0.1.0 contract is what the implementation now fulfills. Everything is exercised by the QEMU test suite (syn_pipeline_suite, syn_sched_suite, syn_process_suite) and verified live on the FRDM-MCXN947 (2026-07-12): the scheduler path measures 1130 µs wall on the board with the stub NPU backend. The stage configuration structures live in the new syn_process.h header.
Two syn_infer_params_t fields that were accepted-but-inert since v0.1.0 now do what they promise, with the header still byte-identical. deadline_us drives dispatch: jobs order by priority first, earliest absolute deadline within a priority (no deadline sorts last), FIFO on ties — and misses are counted at completion (visible in syn infer stats), not enforced. preemptible is honored for models in the layered execution format (CONFIG_SYNAPTIC_LAYER_EXEC): when a higher-priority job arrives, a preemptible running job parks its context at the next layer boundary (10 µs context save, measured on the board, stub NPU) and later resumes bit-exactly. One documented limitation: quiesce drains only the running job, so unloading or OTA-updating a model that still has a suspended layered job is undefined.
Types & constants
| Name | Kind | Description |
|---|---|---|
syn_priority_t | enum | Job priority: SYN_PRIORITY_BEST_EFFORT (0), SYN_PRIORITY_NORMAL (1), SYN_PRIORITY_REALTIME (2). |
syn_job_id_t | typedef | uint32_t — identifier for a submitted inference job. |
SYN_JOB_INVALID | define | ((syn_job_id_t)0) — the invalid-job sentinel; syn_infer_submit() returns it on failure. |
syn_infer_cb_t | typedef | void (*)(syn_job_id_t job, const syn_tensor_t *output, void *user_data) — completion callback invoked when a job finishes. |
syn_infer_params_t | struct | Job submission parameters. Fields: syn_priority_t priority; uint32_t deadline_us (since v0.5.0: orders dispatch within a priority, misses counted); bool preemptible (since v0.5.0: allows suspension at layer boundaries for layered models); syn_infer_cb_t callback; void *user_data. |
syn_preprocess_fn_t | typedef | int (*)(const syn_tensor_t *in, syn_tensor_t *out, const void *config) — pre-processing stage signature. |
syn_postprocess_fn_t | typedef | int (*)(const syn_tensor_t *in, syn_tensor_t *out, const void *config) — post-processing stage signature. |
syn_pipeline_t | typedef | Opaque pipeline handle (struct syn_pipeline). |
syn_resize_config_t | struct | Config for the image-resize pre-processor: uint16_t w, uint16_t h. Configs for the other built-in stages are defined in syn_process.h. |
syn_preprocess_image_resize | extern | Built-in pre-processor: image resize (configured with syn_resize_config_t). |
syn_preprocess_image_normalize | extern | Built-in pre-processor: image normalization. |
syn_preprocess_quantize_int8 | extern | Built-in pre-processor: INT8 quantization. |
syn_preprocess_audio_mfcc | extern | Built-in pre-processor: audio MFCC feature extraction. |
syn_postprocess_softmax | extern | Built-in post-processor: softmax. |
syn_postprocess_argmax | extern | Built-in post-processor: argmax. |
syn_postprocess_top_k | extern | Built-in post-processor: top-k selection. |
syn_postprocess_nms | extern | Built-in post-processor: non-maximum suppression. |
syn_postprocess_dequantize | extern | Built-in post-processor: dequantization. |
Functions
| Signature | Description |
|---|---|
syn_pipeline_t *syn_pipeline_create(const char *name) | Creates a named, empty pipeline. Returns the pipeline handle, or NULL on failure. |
int syn_pipeline_add_preprocess(syn_pipeline_t *pipe, syn_preprocess_fn_t fn, void *config) | Appends a pre-processing stage with its config to the pipeline. Returns 0 on success, negative errno on failure. |
int syn_pipeline_add_model(syn_pipeline_t *pipe, syn_model_handle_t model) | Appends a registered model (see syn_model.h) as the pipeline's inference stage. Returns 0 on success, negative errno on failure. |
int syn_pipeline_add_postprocess(syn_pipeline_t *pipe, syn_postprocess_fn_t fn, void *config) | Appends a post-processing stage with its config to the pipeline. Returns 0 on success, negative errno on failure. |
int syn_pipeline_build(syn_pipeline_t *pipe) | Finalizes the pipeline, making it ready for job submission. Returns 0 on success, negative errno on failure. |
void syn_pipeline_destroy(syn_pipeline_t *pipe) | Destroys a pipeline and releases its resources. |
syn_job_id_t syn_infer_submit(syn_pipeline_t *pipe, const syn_tensor_t *input, const syn_infer_params_t *params) | Submits an asynchronous inference job on a built pipeline with the given input tensor and parameters. Returns a job id, or SYN_JOB_INVALID on failure. |
int syn_infer_wait(syn_job_id_t job, uint32_t timeout_ms) | Blocks until the job completes or timeout_ms elapses. Returns 0 on success, -EAGAIN on timeout, -ECANCELED if the job was cancelled, -ENOENT for an unknown job id, or the job's own error code. |
int syn_infer_cancel(syn_job_id_t job) | Cancels a job. A queued job is cancelled and returns 0; a running job cannot be stopped and returns -EBUSY; an already-finished job returns -EALREADY. |
int syn_infer_get_result(syn_job_id_t job, syn_tensor_t *output) | Retrieves the output tensor of a completed job into *output and frees the job slot. Returns 0 on success, -EBUSY while the job is still queued or running, negative errno otherwise. The result data lives in the ephemeral arena — consume or copy it before syn_mem_reset_ephemeral(). |
int syn_infer_run_sync(syn_model_handle_t model, const syn_tensor_t *input, syn_tensor_t *output, syn_priority_t priority) | Synchronous convenience: builds a temporary single-model pipeline, submits it at the given priority, and blocks until the output is ready. If the caller provides an output buffer (output->data non-NULL and large enough) the result is copied into it; otherwise *output is set to the arena-backed result descriptor. Returns 0 on success, negative errno on failure. |
int syn_infer_set_max_concurrent(uint8_t max_jobs) | Sets the scheduler's maximum number of concurrently executing jobs. Returns 0 on success, negative errno on failure. |
Usage
Build a classify pipeline from built-in stages and a registered model, then submit a prioritized job:
/* resize -> quantize -> model -> softmax */
syn_resize_config_t resize_cfg = { .w = 96, .h = 96 };
syn_pipeline_t *pipe = syn_pipeline_create("classify");
if (pipe == NULL) {
return -ENOMEM;
}
syn_pipeline_add_preprocess(pipe, syn_preprocess_image_resize, &resize_cfg);
syn_pipeline_add_preprocess(pipe, syn_preprocess_quantize_int8, NULL);
syn_pipeline_add_model(pipe, model_handle); /* from syn_model_register() */
syn_pipeline_add_postprocess(pipe, syn_postprocess_softmax, NULL);
int ret = syn_pipeline_build(pipe);
if (ret != 0) {
syn_pipeline_destroy(pipe);
return ret;
}
/* Submit asynchronously with a 5 ms deadline, then wait */
syn_infer_params_t params = {
.priority = SYN_PRIORITY_REALTIME,
.deadline_us = 5000,
.preemptible = false,
.callback = NULL,
.user_data = NULL,
};
syn_job_id_t job = syn_infer_submit(pipe, input, ¶ms);
if (job == SYN_JOB_INVALID) {
return -EIO;
}
ret = syn_infer_wait(job, 100);
if (ret == 0) {
syn_tensor_t output;
syn_infer_get_result(job, &output);
}Note that syn_preprocess_quantize_int8 and syn_postprocess_softmax take config structures from syn_process.h (softmax's is optional). Pipelines are drawn from a static pool of 4; stages must be added in canonical order — pre-processors, then exactly one model, then post-processors — and syn_pipeline_build() validates the chain and computes a worst-case memory estimate. The direct NPU HAL path — syn_hal_npu_set_input(), syn_hal_npu_invoke(), syn_hal_npu_get_output() — remains available, as shown in Hello Inference in 20 lines.
Notes
- Implementation status: implemented in Phase 2 (v0.2.0, released) — pipeline engine, priority scheduler (REALTIME > NORMAL > BEST_EFFORT, FIFO within a class, dedicated scheduler thread, per-job completion semaphores), and all built-in stages, verified on the FRDM-MCXN947. The
deadline_usandpreemptibleparameters are recorded but not acted on yet; deadline-aware dispatch and layer-granular preemption come later (see the implementation notes insyn_infer.c). - Tensors: inputs and outputs are
syn_tensor_tdescriptors from syn_mem.h; models are handles from syn_model.h. - Ownership: a pipeline created with
syn_pipeline_create()is owned by the caller and must be released withsyn_pipeline_destroy(). Stageconfigpointers are stored, not copied — they must stay valid for the pipeline's lifetime. - Callbacks:
syn_infer_cb_tcompletion callbacks run in scheduler context; keep them short and defer heavy work to an application thread. - Custom stages: any function matching
syn_preprocess_fn_t/syn_postprocess_fn_t(return 0 on success, negative errno on failure) can be added alongside the built-ins. - Results: job outputs are allocated from the ephemeral arena; they stay valid until
syn_mem_reset_ephemeral()is called. Reset between jobs once results are consumed. - Related: syn_process.h for the stage config structures, syn_hal_npu.h for the execution backend, syn_prof.h for per-stage timing, Inference pipelines & scheduler for the design.