Docs / API reference / syn_hal_dsp.h
syn_hal_dsp.hv0.2.0
<synaptic/syn_hal_dsp.h> is the DSP hardware abstraction layer, targeting the MCXN947's PowerQuad co-processor. It provides the small set of numeric kernels that inference pre- and post-processing needs — INT8 normalization, float32 softmax, argmax, FFT, and Q15 matrix multiply — behind a backend-neutral API, so the same pipeline code runs on hardware and on the QEMU software stub.
Types & constants
This header declares functions only — it defines no public enums, structs, or macros. All parameters use standard fixed-width types from <stdint.h> and <stddef.h>.
Functions
| Signature | Description |
|---|---|
int syn_hal_dsp_init(void) | Initialize the DSP backend. Returns 0 on success. On the MCXN947 (v0.2.0) this initializes the PowerQuad and runs a self-calibration against the silicon — impulse-FFT probes to establish the FFT gain model, plus a Q15 known-answer and saturation check for the matrix multiply. If a check fails, that operation permanently falls back to the software kernel for the session; init itself still returns 0. |
int syn_hal_dsp_normalize_int8(const uint8_t *in, int8_t *out, size_t len, float scale, int32_t zero_point) | Quantize len unsigned bytes to INT8: each output is in[i] * scale + zero_point, clamped to [-128, 127]. Typical use is preparing camera pixels for an INT8 model. Returns 0 on success, -EINVAL for NULL pointers or len == 0. |
int syn_hal_dsp_softmax_f32(const float *in, float *out, size_t len) | Numerically stable float32 softmax: subtracts the max before exponentiation, then normalizes so out sums to 1. Returns 0 on success, -EINVAL for NULL pointers or len == 0. |
int syn_hal_dsp_argmax(const int8_t *data, size_t len, uint32_t *index) | Write the index of the largest INT8 element to *index (first occurrence wins on ties). The usual last step of a classification pipeline. Returns 0 on success, -EINVAL for NULL pointers or len == 0. |
int syn_hal_dsp_fft_f32(const float *in, float *out, size_t len) | Complex float32 FFT (implemented in Phase 2). in/out are interleaved re/im pairs (2 × len floats); in-place operation (out == in) is supported. len must be a power of two between 2 and 1024. On the MCXN947, lengths 8–512 run on the PowerQuad (when calibration passed); other lengths and other targets use the radix-2 software kernel. Returns 0 on success, -EINVAL for NULL pointers or an invalid length. Returned -ENOTSUP in v0.1.0. |
int syn_hal_dsp_mat_mult_q15(const int16_t *a, const int16_t *b, int16_t *out, uint16_t rows, uint16_t cols) | Q15 fixed-point matrix × vector multiply (implemented in Phase 2): a is rows×cols, b is a cols-element vector, out gets rows results. On the MCXN947, dimensions up to 16×16 run on the PowerQuad (when calibration passed); larger matrices and other targets use the software kernel, which accumulates in 64-bit, shifts by 15, and saturates to [−32768, 32767]. Returns 0 on success, -EINVAL for NULL pointers or zero dimensions. Returned -ENOTSUP in v0.1.0. |
Backends
mcxn947 — PowerQuad (src/hal/mcxn947/syn_hal_dsp_pq.c). As of v0.2.0 this driver drives the real PowerQuad: the FFT (8–512 points) and the Q15 matrix multiply (up to 16×16) execute on the co-processor, guarded by the boot-time self-calibration described under syn_hal_dsp_init(). The PowerQuad FFT engine is fixed-point internally, so the wrapper scales float inputs into the engine's range and undoes the measured output gain (confirmed 1/N on silicon). Measured on the board with syn dsp bench: 5.51× vs software for a 256-point FFT and 1.66× for a 16×16 matrix × vector multiply, including the wrapper's float↔fixed conversion, per-call configuration, and locking — honest note: the phase plan's ≥10× acceptance criterion was not met as stated (larger transforms/batches and persistent PowerQuad configuration are the known paths to closing the gap, tracked on the backlog). Accuracy vs the software reference: 976 ppm of peak (FFT), 1 LSB (matmul). Normalize, softmax, and argmax remain software on all targets.
stub — software fallback (src/hal/stub/syn_hal_dsp_stub.c). Pure-software implementations used on QEMU, and the reference against which the PowerQuad driver is validated. It uses the shared syn_dsp_soft.c kernels for FFT and matrix multiply — the same kernels the MCXN947 driver falls back to when a size is out of hardware range or a calibration check fails.
Usage
A minimal post-processing step — softmax over raw logits, then pick the winning class from INT8 scores:
#include <synaptic/syn_hal_dsp.h>
int8_t scores[10]; /* INT8 output from the NPU */
uint32_t winner;
syn_hal_dsp_init();
/* Normalize raw camera bytes into the model's INT8 input range */
syn_hal_dsp_normalize_int8(pixels, model_input, n_pixels,
1.0f / 255.0f, -128);
/* ... run inference (see syn_hal_npu.h / syn_infer.h) ... */
if (syn_hal_dsp_argmax(scores, 10, &winner) == 0) {
printk("predicted class: %u\n", winner);
}Notes
syn_hal_dsp_fft_f32() and syn_hal_dsp_mat_mult_q15() returned -ENOTSUP on every target in v0.1.0. As of v0.2.0 they are implemented everywhere — PowerQuad hardware on the MCXN947 (with per-operation software fallback), software kernels elsewhere — and validated by syn_dsp_fft_suite (9 tests) plus the on-board syn dsp bench cross-check. They still return -EINVAL for invalid arguments — check the return value before consuming the output buffer.
- All functions are synchronous and run in the caller's thread; on the MCXN947 the FFT and matrix multiply serialize access to the PowerQuad with a mutex and wait for the engine to finish before returning.
- The FFT works on interleaved complex data: for a real signal, zero the imaginary slots. The MFCC pre-processor (
syn_preprocess_audio_mfcc) does exactly this internally. normalize_int8saturates rather than wraps: values outside [-128, 127] after scaling are clamped.softmax_f32guards against a zero exponent sum — if the sum is not positive, the output is left unnormalized rather than divided by zero.- In-place operation (
in == out) is safe forsoftmax_f32, since each element is written after it is read.