SDK for TESAIoT Dev Kit
API reference & tutorials (ModusToolbox)
Loading...
Searching...
No Matches
Diagnostic counters

Functions

uint32_t ai_engine_feeds (void)
 Samples accepted by the model; take the delta over at least 1 s.
uint32_t ai_engine_dq_ok (void)
 Successful dequeues; frozen while dq_calls climbs = NPU stall.
uint32_t ai_engine_dq_calls (void)
 Dequeue attempts; pair with ai_engine_dq_ok() for stall detection.
uint32_t ai_engine_inits (void)
 Successful model initialisations; a packed nibble in the heartbeat word.
uint32_t ai_engine_init_calls (void)
 Model init() calls entered; read together with ai_engine_init_returns().
uint32_t ai_engine_init_returns (void)
 Model init() calls that RETURNED; always read as the pair.
int32_t ai_engine_last_init_rc (void)
 Return code of the last model init(): 0x7FFFFFFF = never called; 0 = ok.
uint32_t ai_engine_stale_drops (void)
 Verdicts discarded past the Ethos-U wait bound; must stay flat while npu_cycles advances.
uint64_t ai_engine_npu_cycles (void)
 NPU cycles accumulated so far; not proof any inference completed.

Detailed Description

Nine plain counter reads. All are cumulative for the boot unless stated; deltas over an interval are the signal, not totals. Every one is vacuous while ai_engine_stack_words() is 0. On-core they feed the page's once-a-second stats line (page_edge_ai.c:1455-1479: push/s feed/s flush/s … init N/M rc R stale S); off-core they ride the heartbeat word (packed nibbles, deepcraft_task.c:913-919, decision table :886-912) and the Q_DIAG pull that becomes edge_ai.diag(). The stated pass condition for a healthy engine (modedgeai.c:432-437): ai_engine_npu_cycles() advancing while ai_engine_stale_drops() stays flat — ml_state alone is untrustworthy. Only the diag "feed" field reports model intake.

Variant
mtb-mpy and mtb-only

Function Documentation

◆ ai_engine_feeds()

uint32_t ai_engine_feeds ( void )

Samples accepted by the model; take the delta over at least 1 s.

Samples accepted by the model, and successful model initialisations. Shown on the page while no verdict exists yet, so a stall is legible.

Contract
Samples accepted by the model. Take the delta over at least 1 s to derive a feed rate in Hz; the packed heartbeat nibble carries only its low four bits. A feed rate of 0 with a running model means the sensor pipeline, not the NPU, is the problem.
Variant
mtb-mpy and mtb-only
Origin
Lifted from page_edge_ai.c:1320-1327 (compiled into the prebuilt archive; not shipped as source).

◆ ai_engine_dq_ok()

uint32_t ai_engine_dq_ok ( void )

Successful dequeues; frozen while dq_calls climbs = NPU stall.

Contract
Successful dequeues (verdicts obtained). Frozen while ai_engine_dq_calls() climbs = NPU stall — the header's own stall signature. Low 12 bits ride the heartbeat word.
Variant
mtb-mpy and mtb-only
Origin
Lifted from deepcraft_task.c:913-917 (compiled into the prebuilt archive; not shipped as source).

◆ ai_engine_dq_calls()

uint32_t ai_engine_dq_calls ( void )

Dequeue attempts; pair with ai_engine_dq_ok() for stall detection.

Contract
Dequeue attempts. No caller exists anywhere in shipped firmware (absent from consumer_must_provide.txt; sim_ai_engine_stub.c:111 is a stub definition, not a call). Pair it with ai_engine_dq_ok() in the stall-detection story: calls climbing with ok frozen over a >= 1 s interval is a stalled NPU; both frozen means the engine is not running — check ai_engine_stack_words() first. Any CM55 task, ~1 Hz.
Variant
mtb-mpy and mtb-only
Example (authored — no shipped call site)
static void bento_ex_ai_engine_dq_calls(void)
{
static uint32_t last_calls, last_ok;
if (ai_engine_stack_words() == 0u) {
return; /* engine never started — no data */
}
/* Call this block at >= 1 s intervals (e.g. the same 1 Hz slot as
* the watchdog); the deltas are the signal, not the totals. */
uint32_t calls = ai_engine_dq_calls();
uint32_t ok = ai_engine_dq_ok();
uint32_t d_calls = calls - last_calls;
uint32_t d_ok = ok - last_ok;
last_calls = calls;
last_ok = ok;
if (d_calls > 0u && d_ok == 0u) {
/* NPU stall signature: dequeues attempted, none succeeded over
* the whole interval. Surface it — do not silently keep polling.
* (ml_state alone is untrustworthy for this; the counters are
* the evidence.) */
}
}

◆ ai_engine_inits()

uint32_t ai_engine_inits ( void )

Successful model initialisations; a packed nibble in the heartbeat word.

Contract
Successful model initialisations. Carried as a packed nibble in the heartbeat word; the reading rules are the deepcraft_task.c:886-912 decision table.
Variant
mtb-mpy and mtb-only
Origin
Lifted from deepcraft_task.c:916 (compiled into the prebuilt archive; not shipped as source).

◆ ai_engine_init_calls()

uint32_t ai_engine_init_calls ( void )

Model init() calls entered; read together with ai_engine_init_returns().

MODEL_INIT_RECOVERY diagnostics. init_calls == 0 means the inference task never reached the cold-load (task absent, or no select/start landed); init_calls > 0 with last_init_rc != 0 means the model's own init() failed with that code; last_init_rc == 0x7FFFFFFF means init() was never called.

Contract
Model init() calls entered. Read together with ai_engine_init_returns(): "2/1" means the inference task entered an init and never left. 0 means the task never reached the cold-load (task absent, or no select/start landed).
Variant
mtb-mpy and mtb-only
Origin
Lifted from page_edge_ai.c:1469 (compiled into the prebuilt archive; not shipped as source).

◆ ai_engine_init_returns()

uint32_t ai_engine_init_returns ( void )

Model init() calls that RETURNED; always read as the pair.

How many model init() calls RETURNED. Less than ai_engine_init_calls() means the inference task went into one and did not come out.

Contract
Model init() calls that RETURNED. Less than ai_engine_init_calls() means the inference task went into one and did not come out. Ditto — always read as the pair.
Variant
mtb-mpy and mtb-only
Origin
Lifted from page_edge_ai.c:1470 (compiled into the prebuilt archive; not shipped as source).

◆ ai_engine_last_init_rc()

int32_t ai_engine_last_init_rc ( void )

Return code of the last model init(): 0x7FFFFFFF = never called; 0 = ok.

Contract
Return code of the last model init(): 0x7FFFFFFF = never called; 0 = ok; anything else is the model's own failure code, and ai_engine_requested() will have dropped to -1 in the same tick.
Variant
mtb-mpy and mtb-only
Origin
Lifted from deepcraft_task.c:376 (compiled into the prebuilt archive; not shipped as source).

◆ ai_engine_stale_drops()

uint32_t ai_engine_stale_drops ( void )

Verdicts discarded past the Ethos-U wait bound; must stay flat while npu_cycles advances.

Verdicts discarded because their dequeue ran past the Ethos-U wait bound and so could only be carrying the previous frame's output tensor.

Read it as "how often a verdict was withheld", not as an NPU health meter. The measurement is wall clock, which cannot separate "the NPU did not answer" from "this task did not run": ai_task sits below the GFX task, and a long render frame or an XIP stall on the shared SMIF can push a perfectly good dequeue past the threshold. It also stops counting once a stall wedges the driver, because dequeue then fails outright and never reaches the check.

The signature of a stalling NPU remains ai_engine_dq_ok() frozen while ai_engine_dq_calls() climbs. Cumulative for the boot — deliberately not cleared on a model switch, unlike the pipeline counters.

Contract
Verdicts discarded because their dequeue ran past the Ethos-U wait bound. Read it as "how often a verdict was withheld", not as an NPU health meter — the measurement is wall clock and cannot separate an unanswering NPU from a starved ai_task (GFX render or an XIP stall on the shared SMIF). Must stay flat while ai_engine_npu_cycles() advances — that is the pass condition. Deliberately not cleared on a model switch.
Variant
mtb-mpy and mtb-only
Origin
Lifted from deepcraft_task.c:375 (compiled into the prebuilt archive; not shipped as source).

◆ ai_engine_npu_cycles()

uint64_t ai_engine_npu_cycles ( void )

NPU cycles accumulated so far; not proof any inference completed.

NPU cycles accumulated so far. A coarse "is the NPU working" reading only — the middleware adds to it on the timeout path as well, so it does NOT prove any particular inference completed. Use ai_engine_stale_drops() for that.

Contract
NPU cycles accumulated so far, a coarse "is the NPU working" reading — the middleware adds to it on the timeout path too, so it is not proof any inference completed; use ai_engine_stale_drops() for that. A u64, split hi/lo over the IPC wire.
Variant
mtb-mpy and mtb-only
Origin
Lifted from deepcraft_task.c:371 (compiled into the prebuilt archive; not shipped as source).