GitHub

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.

Phase 2 · implemented and released (v0.2.0)

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.

Phase 5 · the dormant fields wake up (v0.5.0)

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

NameKindDescription
syn_priority_tenumJob priority: SYN_PRIORITY_BEST_EFFORT (0), SYN_PRIORITY_NORMAL (1), SYN_PRIORITY_REALTIME (2).
syn_job_id_ttypedefuint32_t — identifier for a submitted inference job.
SYN_JOB_INVALIDdefine((syn_job_id_t)0) — the invalid-job sentinel; syn_infer_submit() returns it on failure.
syn_infer_cb_ttypedefvoid (*)(syn_job_id_t job, const syn_tensor_t *output, void *user_data) — completion callback invoked when a job finishes.
syn_infer_params_tstructJob 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_ttypedefint (*)(const syn_tensor_t *in, syn_tensor_t *out, const void *config) — pre-processing stage signature.
syn_postprocess_fn_ttypedefint (*)(const syn_tensor_t *in, syn_tensor_t *out, const void *config) — post-processing stage signature.
syn_pipeline_ttypedefOpaque pipeline handle (struct syn_pipeline).
syn_resize_config_tstructConfig 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_resizeexternBuilt-in pre-processor: image resize (configured with syn_resize_config_t).
syn_preprocess_image_normalizeexternBuilt-in pre-processor: image normalization.
syn_preprocess_quantize_int8externBuilt-in pre-processor: INT8 quantization.
syn_preprocess_audio_mfccexternBuilt-in pre-processor: audio MFCC feature extraction.
syn_postprocess_softmaxexternBuilt-in post-processor: softmax.
syn_postprocess_argmaxexternBuilt-in post-processor: argmax.
syn_postprocess_top_kexternBuilt-in post-processor: top-k selection.
syn_postprocess_nmsexternBuilt-in post-processor: non-maximum suppression.
syn_postprocess_dequantizeexternBuilt-in post-processor: dequantization.

Functions

SignatureDescription
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:

c
/* 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, &params);
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_us and preemptible parameters are recorded but not acted on yet; deadline-aware dispatch and layer-granular preemption come later (see the implementation notes in syn_infer.c).
  • Tensors: inputs and outputs are syn_tensor_t descriptors 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 with syn_pipeline_destroy(). Stage config pointers are stored, not copied — they must stay valid for the pipeline's lifetime.
  • Callbacks: syn_infer_cb_t completion 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.