Add a kernel
Add math in Kernels, not in Runtime. A kernel owns its tensor contract, ordered computation, checked preparation and implementation selection. Runtime supplies memory access, scheduling, device submission and retirement.
This is the canonical authoring guide. Backend skills describe optimization and qualification procedures; they do not define another API or folder template.
Follow production operations
Production operations are the examples. There is no separate teaching kernel to copy or maintain. Inspect the operation closest to your requirements:
| Requirement | Production reference |
|---|---|
| Simple conversion and shared A65/EV74 contract | Cast |
| Multiple ports and prepared parallel resources | SuperPoint |
| Dynamic geometry and reusable scratch | BoxDecode |
| EV codec, registration and device execution | Histogram |
| Specialized image-processing paths | Preproc |
Keep a single-backend A65 operation together:
A65/<name>/
CMakeLists.txt
<name>.h # Installed as simaai/kernels/ops/<name>.h
request.cpp # Cold named parameters -> typed request
prepare.cpp # Admission and checked executable factory
kernel.cpp # Numerical entry; specialized workers may be separate
tests/{correctness,latency}.cpp
docs/index.md
An EV operation has explicit host/device boundaries:
EV74/<name>/
CMakeLists.txt # License-free host build
<name>.h # Public semantic request and checked plan
request.cpp # Cold named parameters -> typed request
prepare.cpp # Semantic admission
host/
codec.h # Installed as simaai/kernels/ev74/<name>.h
codec.cpp # Device representability and packet binding
executable.cpp # Kernel-owned executable factory
route.h # Trusted route schema
device/
CMakeLists.txt # Licensed compiler build
... # Native entry, ABI/control headers and workers
firmware/
Native<Name>Firmware.cmake
registration.{h,cc}
tests/
CMakeLists.txt
correctness.cpp
e2e/CMakeLists.txt
docs/index.md
For an operation supported on both backends, put its one semantic contract in
common/<name>/<name>.h, admission in common/<name>/prepare.cpp and factory in
common/<name>/executable.cpp. Its cold named decoder lives in
common/<name>/request.cpp. A65's backend.h is the direct numerical API;
EV's host/codec.h is the device codec. Keep backend implementations, tests,
documented calls and mathematical execution notes in their respective operation folders.
Do not duplicate semantic validation to make two folders look identical.
File responsibilities are consistent; file counts need not be. Keep substantial SIMD/DMA workers in qualified separate translation units. Add a private header only when it has a real user. A small operation may keep its factory with preparation instead of adding an otherwise empty layer.
Add the operation
- Define the public request and numerical contract before implementing the math.
- Add the backend's explicit sources using the same build-default helpers as the production operation you inspected. Preserve required FP flags and tuning.
- Add
request.cppbeside the semantic public header. Decode only the operation's named fields withcommon/request/reader.h, reject unknown/duplicate/type/range errors, and call the existing typedmake_executable(). Do not duplicate numerical admission or interpret parameters during execution. See the named-request contract and production decoders. - Register membership once: A65 in
cmake/A65Operations.cmake; EV in the localfirmware/Native<Name>Firmware.cmakedeclaration. The firmware composition guide defines readable IDs, generated membership and released compatibility. Do not edit generated catalogues or change the released compatibility snapshot to add an operation. Usesima_register_operation()for package ownership. - Add independent correctness cases, measured latency cases and the canonical mathematical page with a public-call snippet and links to the real tests.
- Run the corresponding host, installed-package and target checks. The
installed Runtime consumers (
tests/runtime_consumer/README.md) exercise production operations across the repository boundary; Runtime source changes are not part of adding ordinary math.
For EV tests, keep routine CPU checks bounded to admission, binding, dispatch, independent layout goldens and instrumented source-safety mechanisms. Put broad numerical coverage in the real device fixture and retain exhaustive CPU sweeps in full mode for weekly/manual validation. Reuse the existing test executable and profile completion records; do not add one binary per case. Follow the profile policy.
Define the contract before the fast path
- Define a typed
Requestinsimaai::kernels::ops::<name>, with explicit input and output specifications and typed parameters. Do not add a shared central operation enum. The request type identifies the operation; parameters select its supported mathematical variants. - Document ordered port roles, supported rank/dtype/layout combinations, parameter defaults and physical storage requirements. Six-axis capacity does not mean every kernel accepts every rank, stride or layout.
- Implement canonical
check_inference(). Check arity, shape, output agreement, parameters, overflow and implementation limits. Retain owned metadata, not spans into a temporary caller request. - State the math: intermediate precision, operation order, rounding, saturation, exceptional values, ties, padding and output initialization. Fusion does not authorize FMA or a different rounding boundary.
- Define binding checks separately. Future addresses and capacities cannot be validated during metadata preparation. Check them before the first write; explicitly declare aliasing and workspace requirements.
The direct numerical entry remains useful for kernel testing and applications that already manage execution. Keep its name/signature operation-specific and its checked contract clear. Do not make direct callers construct Runtime queues, GStreamer buffers or application configuration.
Connect the same math to Runtime
Declare make_executable(const Request &, Target) in the request's namespace.
Runtime can find it by argument-dependent lookup. Typed applications include
<simaai/kernels/ops/<name>.h> and link the operation target explicitly alongside
SimaRuntime::runtime when using the runner.
Applications without operation-specific headers use KernelRequest from
<simaai/kernels/request.h> and link SimaKernels::request. The resolver is
emitted from sima_register_operation() registrations and calls the operation's
decode_request() once. Adding ordinary math does not add a Runtime switch or a
plugin integration file. Both entry points produce the same executable; there is
no generic interpreter in numerical or submission loops.
Typed request
-> check_inference + backend admission
-> owned checked executable
-> Runtime prepare: capability and bounded resources
-> bind current buffers / submit
-> numerical completion
-> Runtime coherency and retirement
The factory selects a numerical function or EV packet-binding function once. It must not move operation-specific decisions into Runtime or repeat selection inside every element's inner loop. A new ordinary operation requires no Runtime operation overload, switch or registration patch.
Use <simaai/kernels/authoring.h> for ordinary factories: make_contract()
copies checked requirements; stateless_host() reuses a direct checked entry;
context_host() adds cold per-in-flight resources; ev_executable() publishes
a checked codec binding entry. EV entry names must be static literals or backed
by retained immutable payload storage, never temporary strings. Do not copy
Runtime internals. The provider implementation interface remains reviewed kernel
code: never publish unchecked state or let application parameters assert that
resources are compatible.
Empty inputs, outputs and scratch
- Mark an invocation as no work only when all required output stores can be omitted. Empty candidates can still require zero counts, statuses or padding.
- Allocate bounded reusable scratch during preparation, or declare explicit per-in-flight workspace. Successful numerical execution must not allocate.
- Resource creation is a cold operation that can fail. Report expected failures
through
Result/Error; cold allocation can still throwstd::bad_alloc, as in the direct API.Resultis not anoexceptguarantee. Do not publish a partially initialized plan, silently create private threads or fall back to another device. - Share prepared resources only through their exact kernel-owned compatibility recipe. Dynamic geometry does not by itself establish scratch compatibility.
- A timeout is not retirement. Runtime retains storage until completion or confirmed recovery; kernels must drain their own DMA events before returning.
The installed Runtime consumers (tests/runtime_consumer/README.md) run
real SuperPoint and submit real Histogram descriptors using a recording
transport. They test package compatibility, not an independent extension ABI.
Recording transport tests do not qualify EV numerical execution.
Add the EV contribution without changing Runtime logic
An EV kernel needs all of these pieces; host admission alone is not execution:
- Codec: encode explicit little-endian fixed-width fields, a version and supported payload length. Leave relocatable addresses unbound. Validate the actual bound regions and their agreement with the prepared plan. Runtime provisions packet capacity; the binder fills its entry, ABI, payload length and bytes without reallocating. Do not assume reserved storage already contains a copied packet.
- Device admission: reject malformed payloads before launch. Specify which input-content checks run on-device and whether an error can leave partial outputs. Host metadata checks cannot replace these checks.
- Workers: implement math, DMA waits, local-memory budgets, tails and physical write ownership. Use the pinned dispatcher's group IDs and launch geometry.
- Registration: contribute the capability probe and numerical entry to Kernels-owned firmware composition. Preserve explicit payload variants; registrations are not all fixed-size copies of one another.
- Trusted schema: put a self-contained
host/route.hinEV74/<name>/. Definenative_routeandNativeProbeinsimaai::kernels::ev74::<name>. The route declares admitted payload sizes, authorized address slots and inactive slots; probe layout is checked at compile time.cmake/GenerateNativeSchemas.cmakeassembles these contributions into the installed trusted catalogue without changing Runtime or the existing route order. Do not edit its generated headers. Applications do not supply authorization schemas; Runtime retains driver and DMA-BUF safety checks. - Build inventory: declare
NAME, a readable decimalGRAPH_IDandPACKAGEin the localfirmware/Native<Name>Firmware.cmake. The composition guide defines accepted families, generated outputs and selection. Do not edit a central membership table, generated headers or the released compatibility snapshot to add an operation. The build rejects duplicates, invalid intervals and stale installed outputs. Productiondevice/CMakeLists.txtandtests/e2e/remain explicit.
Adding an EV operation requires rebuilding the Linux CVU adapter against the updated trusted catalogue and composing matching firmware. No Runtime source change is required; an already-built Runtime binary does not discover arbitrary new EV routes. This is not a stable cross-version C++ plugin ABI. An unknown or unprobed route must fail before submission.
Register the build and keep its evidence reproducible
Use sima_register_operation() from local CMake with the conventional
<name>.h, docs/index.md and tests/ paths shown above. For a single backend, the numerical target is
simaai_kernels_<backend>_<name> (lowercase backend). A dual-backend contract
registers BACKEND SHARED from common/<name>/, with the checked factory target
simaai_kernels_<name>_executable. Shared membership is derived from the two
backend catalogues, not maintained in a third list. All operations expose
SimaKernels::<name>. The registration helper installs the public header and
installs/exports both the numerical target and its facade exactly once. Do not
add a second numerical-target install declaration. Extra public headers and
dependency targets still need explicit install/export declarations; the helper
does not guess sources, numerical flags or private dependencies. Keep the public
header self-contained: private adjacent includes do not become public automatically.
A65 membership lives only in cmake/A65Operations.cmake; EV membership is derived from operation-local firmware declarations. tools/operation_inventory.py feeds downstream tools.
EV tests keep an explicit tests/CMakeLists.txt with their
required compiler flags and host-versus-device distinction; the build must not
silently treat a host fixture as device qualification. Keep
source lists, dependent kernels, FP flags and OpenCL header dependencies visible
in the operation's build definition. The device acceptance project is
tests/e2e/, builds <name>_cvu_e2e and links the installed operation facade and
SimaRuntime::runtime. Shared test utilities may use the package's version-matched
SimaKernels_TEST_SUPPORT_DIR; this is test infrastructure, not an application
API. Numerical E2E ownership stays with the operation, while Runtime owns generic
transport, dispatch, lifetime and recovery tests. Do not infer them from a directory scan.
Use canonical CMake targets for production tests and qualification. Copying a convenient operation's compiler command can silently omit another operation's required FP flags and change its independent oracle. Host builds must remain license-free; private builds verify the pinned runtime/toolchain cohort and archive source identity before firmware composition.
Preserve physical firmware link order, specialized translation units and per-source compiler options. Even a source-preserving relink can change latency.
Test and document the complete contribution
Keep one parameterized correctness executable and one latency executable per
A65 operation. EV qualification may combine both modes in its operation-local
device fixture. Add cases, not one executable per shape. Keep stable case IDs and
independent expected values active in Release builds. Production latency suites
use the standard benchmark support: --list-cases, suite/case selection,
checked execution timing, separate preparation/first-call costs and percentile
reports. Do not replace it with a mean-only timer or count catalogue listing as
a latency measurement.
Production inventory checks enforce local contribution completeness. The root
package_consumer check installs and relocates the real package, compiles each
public operation header and links real factories/codecs through their exported
targets. Documentation checks validate authored call snippets and links to real
operation tests; they do not compile the snippets.
These checks require no teaching operation and do not imply target performance.
Cover admitted contracts, edge shapes, tails, numerical boundaries, malformed specifications and packets, invalid bindings, no-write rejection where promised, reuse, concurrent scratch ownership and successful-path allocation behavior. Retain independent layout/graph-ID goldens; production-generated expectations and round trips are supplementary checks, not independent oracles.
Time the checked execution path. Separate preparation/first-call costs, validate outside timed regions and preserve the median qualification policy. Use canonical standalone device targets linking both Runtime and the operation, not accidental transitive numerical dependencies.
Write the ordered equations, path eligibility, memory movement and complete public call snippet in the operation's canonical page. Do not add standalone demonstration programs or a snippet build framework. Link that page from the support matrix and native catalogue. Follow the documentation guide.
Finish with installed-package consumer checks and the appropriate target tests. Report host contracts, source-model tests, private compilation, actual device correctness and latency qualification separately. An x86 pass or successful firmware link is not EV execution evidence.