Skip to main content

Native QuantTess boundary

Use simaai/kernels/ops/quanttess.h for shared admission and simaai/kernels/ev74/quanttess.h for device encoding. Link SimaKernels::quanttess. The host targets require no MetaWare, ConfigManager, JSON or GStreamer.

Contract​

The QuantTess contract applies to A65 and EV74: FP32, IEEE FP16 or BF16 input becomes MlaTiled INT8 output. Input and output have the same explicit logical shape, with one through six axes. Input can be contiguous or an admitted scalar-aligned, nonoverlapping strided view. Negative strides and active-axis broadcast strides are unsupported. Rank-zero scalars are unsupported; empty tensors require no numerical device invocation.

Device admission additionally checks representable storage/address limits. Neither MlaCBlocked nor arbitrary strided output is a substitute for MlaTiled.

Parameters​

  • multiplier: required, finite and strictly positive, including subnormals. It is a multiplier, not a divisor.
  • zero_point: optional, default zero, in [-128,127].
  • rounding: defaults to NearestEven; TowardZero, TowardPositive and TowardNegative are also supported.

Params is a distinct QuantTess request type with Quant's numerical fields. Dtype and output layout select conversion and placement. Callers do not select SIMD code, DMA panel sizes, worker partitions or fast-path flags.

Numerical behavior​

product = round_to_nearest_even_binary32(exact_input × multiplier)
result = saturate_int8(round_selected(product) + zero_point)

The binary32 multiplication rounding boundary precedes integer rounding. Moving zero-point addition before rounding is not equivalent. NaN maps to zero_point; infinities saturate by sign. Signed zero and underflow follow the same equation. BF16 is not FP16.

The native implementation reuses Quant's exact input decoding and checked binary32-product path. Eligible products map NaNs to zero, clamp to [-256,256], and use the selected native conversion into sixteen INT32 lanes. Only then are zero point and INT8 saturation applied. Those bounds preserve all results for admitted zero points and prevent undefined float-to-integer overflow. Narrowing straight into sixteen INT16 lanes is deliberately avoided: the current toolchain scalarizes that conversion, whereas INT32 emits vector conversion. Exceptional/underflow cases retain exact integer significand/exponent handling. Scalar tails use exact bit-domain arithmetic. Fusion does not authorize changed rounding or tolerance. No whole-tensor FP32 or quantized intermediate is allocated.

Preparation normalizes an exact positive power-of-two multiplier to significand one and its exponent, including powers in the subnormal multiplier range. Its exact-fallback vector call supplies literal one to the significand path, eliminating multiplication without moving either rounding boundary. Underflow still rounds to the binary32 subnormal quantum before integer rounding. Output saturation preserves the endpoint when the corresponding binary32 product would overflow. Out-of-line OpenCL helpers use scalar/pointer arguments rather than passing large task structures by value. Validate their generated caller/callee ABI with the target toolchain as well as testing their numerical expressions on the host.

Normal finite powers still use hardware multiplication/conversion; forcing every power-of-two input through integer emulation is not inherently faster.

C++ call​

The A65 call supplies explicit tensors and typed parameters. Prepare shared metadata with ops::quanttess::check_inference(request), then choose A65 numerical preparation or Runtime's EV74 executor. The host codec sequence is:

shared check_inference → EV representability check → encode_relocatable
→ retained DMA-BUF bindings and address relocation
→ asynchronous native submission → confirmed retirement and coherency

bind() is available to transport integrators with resolved device regions. CPU pointers are not device addresses. Runtime's synchronous convenience call submits the same job and waits; it is not another numerical path.

Place the includes and namespace aliases at file scope; the remaining statements belong in a simaai::kernels::Status-returning caller function. It checks metadata and encodes a relocatable descriptor, not EV74 computation. Runtime must map actual buffers, bind addresses and retain inputs, outputs and any workspace until confirmed device completion.

#include <array>
#include <simaai/kernels/ev74/quanttess.h>
#include <simaai/kernels/ops/quanttess.h>

namespace k = simaai::kernels;
namespace op = k::ops::quanttess;
namespace ev = k::ev74::quanttess;
k::MlaTiled tiled;
tiled.axes = {-1, -1, 0, 1}; // Shape [W,C], with an explicit channel axis.
tiled.tile_shape = {1, 1, 2, 3};
const std::array inputs{k::TensorSpec{{2, 3}, k::DType::Float32}};
const std::array outputs{k::TensorSpec{{2, 3}, k::DType::Int8, tiled}};
const op::Request request{inputs, outputs, op::Params{1.0F}};
auto checked = op::check_inference(request);
if (const auto *error = std::get_if<k::Error>(&checked)) return *error;
const auto &plan = std::get<op::Plan>(checked);
auto encoded = ev::encode_relocatable(plan);
if (const auto *error = std::get_if<k::Error>(&encoded)) return *error;
// Encoding leaves device addresses unbound; no device work has run.
return {};

See the host contract tests (EV74/quanttess/tests/correctness.cpp) and device fixture (EV74/quanttess/tests/runtime_e2e.cpp) for binding and execution coverage.

Memory and lifecycle​

INT8 uses one byte per channel, not BF16 byte planes. Physical tile order is C,D,H,W; each tile contains D,H,W,C payload and occupies a 16-byte-aligned span. Unmapped logical axes are independent outer slices. Boundary tiles use actual extents. Channel padding stores zero_point; tile alignment-only gaps store raw zero. Both are written on every invocation, including dirty output reuse.

Physical output capacity comes from the admitted layout, not logical element count alone. Input requires scalar alignment and output requires 16-byte alignment. Accessed input/output ranges must not overlap. Input holes and extra accessible output capacity are not modified.

Runtime owns allocation, mapping, coherency, submission and retirement. It retains buffers, descriptors and mapping leases through confirmed completion. A timeout does not permit freeing in-flight DMA storage. There is no per-tile CPU packing, preclear, hidden A65 fallback or private per-kernel worker pool.

Implementation notes​

Native boundary​

The version-1 little-endian descriptor is 232 bytes. It contains input dtype, rank, six-capacity sizes/strides, storage spans, MLA axis/tile/channel metadata, and multiplier bits, zero point and rounding. Device address slots are at 24 and 32; their 64-bit wire values must fit the EV 32-bit address domain. Host/device compilers assert structure offsets, size and alignment.

The native entry revalidates descriptor integrity and overflow-safe geometry before dispatch. This is a defensive device boundary, not application configuration parsing. The platform explicitly composes NativeQuantTessFirmware.cmake and registers epoch-2 route 49 (0x31) in the native-only image. After transport discovery, Runtime probes this exact capability and fails closed when absent. Legacy QuantTess has a separate ABI; its payloads cannot be sent to this entry.

Fused DMA and local arithmetic​

The general worker owns two source/output slots: 18 KiB input and 6 KiB output per slot, 48 KiB explicit arrays in total. Compiler control/spill space is additional: verify the generated call-chain stack against the actual per-worker VCCM configuration, not just these arrays. A successful execution test is not a stack-capacity proof or evidence of an active hardware stack guard. Large tiles split into bounded panels. Input DMA completes before arithmetic; output DMA completes before that slot is reused and before the entry returns.

Dtype-specialized block dispatch selects normal/subnormal multiplier handling and rounding-specific SIMD leaves outside the ordinary vector loop. Normal leaves process two sixteen-lane vectors per iteration. Their independent arithmetic chains share one underflow-admission reduction and branch; a final complete sixteen-lane vector keeps the single-vector normal path. FP32/BF16 admission checks input magnitudes; FP16 admission checks rounded products.

If either vector in a pair needs repair, the leaf returns the pair's starting element without writing either half. The mixed helper handles that pair and the remaining staged block: normal vectors use hardware arithmetic, while affected vectors use exact arithmetic. A safe first vector can therefore enter mixed handling when only the second vector needs repair. For FP32/BF16, neither half has been multiplied before this handoff. FP16 must compute both products to check for underflow, so mixed handling can repeat a product before producing the same result. Neither path writes the deferred pair before repair. Already completed pairs are never restarted, and no whole-tensor prescan is added. FP32 and BF16 normal leaves conservatively admit only inputs whose product cannot underflow. For a positive normal multiplier with binary32 exponent field E, choose I_min = max(1, 128 - E) once per block. A normal input with exponent field I >= I_min has magnitude at least 2^(I - 127); the multiplier is at least 2^(E - 127). Their exact product is therefore at least 2^-126, so its binary32 rounding remains normal or overflows. Signed zeros, infinities and NaNs follow the multiplication and packing rules described above.

The decoded absolute binary32 bits use the unsigned test (magnitude - 1U) < ((I_min << 23) - 1U). Zero wraps outside that range. For paired vectors, taking the unsigned lane-wise minimum of their two magnitude - 1U values and applying the same comparison detects a risky lane in either vector with one reduction. Nonzero subnormals and conservatively small normal inputs select the mixed fallback before any lane is multiplied. That fallback repairs input-subnormal and product-underflow cases. Some safe tiny normal values may also take this path; their numerical result does not change. Admitted vectors need no second, product-dependent underflow reduction. For example, multiplier 0.5 admits normal exponent fields at least 2; approximately 0.3 requires 3.

Every finite nonzero FP16 input is normal in FP32 (minimum 2^-24), so input-subnormal screening is unnecessary. Small multipliers can still make the binary32 product subnormal or zero. After computing both products, the paired FP16 leaf tests whether either vector has a nonzero input and a zero-exponent product. One combined reduction detects a risky lane in either half before any result is stored. The mixed helper then repairs product-underflow cases. A subnormal multiplier still selects exact block arithmetic before the normal leaf is called.

The conservative FP32/BF16 path introduces no speculative multiplication across a subnormal-input guard. It may avoid a repeated underflowing multiply before mixed repair; this preserves the numerical contract, not trap location or exception-handler invocation counts. No different rounding or FTZ policy is introduced.

Small-channel Pad16 packing uses a scalar/pointer outlined helper so its cold packing choices do not expand each general worker's live vector state.

Keeping repair outside the normal leaf avoids call-induced vector spills in its hot loop. A subnormal multiplier selects exact block arithmetic; scalar tails remain exact. No path assumes caller-promised finite input.

When staged input already matches final output pixel order, quantization writes directly into the output slot without an extra local byte-copy pass. When padding/transposition requires packing, staged input is compressed in place to INT8: both vectors in a pair are loaded before either shorter result is stored; the final single vector follows the same read-before-write rule. No unread source value is overwritten. This private scratch compression does not permit overlapping public input/output tensors. Packing then places those bytes directly into the final tile representation. Small-channel inputs quantize across pixels rather than making a tiny arithmetic call per pixel.

  • Contiguous and pitched rows use DMA and, where eligible, 2D DMA. The transfer threshold is evaluated after eligible adjacent H rows are merged: a 96-byte BF16 RGB row can participate in a 768-byte rectangle rather than forcing scalar DDR gathers for the entire tile.
  • A whole-panel H-contiguous route reads physically contiguous spans, quantizes locally and transposes bytes into output order. Eligible H8 paired-width and H-multiple-of-16 panels use a fixed 16-by-16 local byte transpose. On aligned panels, eight native halfword gathers read two height samples per channel; low/high-byte extraction writes sixteen complete channel vectors. The second eight-row group retains its separate destination offset for paired H8 tiles. Odd local addresses or pitches use bounded byte gathers instead. These paths change data movement only: quantization has already completed. Unsupported shapes and channel tails retain bounded scalar packing.
  • C-contiguous pixels with admitted positive pixel/row pitches can stage all channel tiles together. This avoids tiny separate channel-tail reads while preserving independently addressed C-major output intervals.
  • Giant or awkward compact planes use aligned physical-output block tasks. Regular panel tasks also own complete 16-byte output intervals. Separate workers never race on partial alignment blocks.
  • Eligible small channel-subtiled patches group all C tiles into one task. The patch is read and quantized once. Grouped outputs occupy separate C-major intervals: the writer computes each address instead of assuming adjacency. Eligibility checks bound total input, output and every boundary tile class. Equal stored channel counts across full/final C tiles let grouped tasks reuse decoded spatial coordinates; unequal compact tails retain general decoding.
  • Padding runs after quantization. For dense C3 pixels with four-byte-aligned local source/output addresses, three 32-bit source loads supply four RGB pixels. A single 64-byte vector store writes those four Pad16 pixels, including every zero-point padding byte. This little-endian word-packing path reads exactly twelve source bytes; it does not overread a short tail. C1/C4 pixels and unaligned C3 sources retain byte-vector shuffles. Pitched source pixels and short tails read only their valid scalar bytes and write complete padded pixels. Grouped C-tail packing follows the same rules with its own source pixel pitch.

For a 128×128×33 tensor with H8/W16/C16 tiles and padded channels, each patch reads 128×33 values once and emits 128×48 INT8 bytes. FP32 uses 16,896 source bytes and 6,144 output bytes per slot. There are 128 spatial tasks and 384 independently addressed output tiles. Partial spatial tiles and outer dimensions preserve the same ownership rule. Layouts outside this eligibility retain the general path.

Compact direct-tile workers​

After descriptor admission, a private selector chooses a dtype-specific complete- tile worker when channels and pixels are contiguous, depth tiles contain one plane, and a single task owns each tile. Complete tensors retain the constant-size loop; eligible spatial tails use weighted offsets and exact per-tile extents. Rows and outer slices may be pitched. Multirow DMA requires a representable signed row pitch. Singleton strides do not contribute to addressing.

Two specializations retain the same numerical helpers:

  • Unpadded complete tiles: at most 4,096 input elements, no stored channel padding, and a payload divisible by 16. Two source/output slots require 40 KiB for FP32 or 24 KiB for BF16/FP16, before compiler stack/spill space.
  • RGB to Pad16: at most 128 pixels per tile, three logical channels and sixteen stored channels. Two slots require 7 KiB for FP32 or 5.5 KiB for BF16/FP16. Quantization compresses the completed input slot in place; bounded packed stores then fill a separate output slot, including zero-point padding.

Both require at least 128 source bytes in a regular full tile. Partial tensors must contain a useful complete H/W interior, one logical C tile, and bounded single-task classes with a consistent compact or RGB family. Smaller requests and other unsupported specialization geometry retain the general implementation; channel subtiles may use the compact grouped worker described below. The public support contract does not narrow.

Complete workers decode their starting tile once, then advance W, H, D and outer-slice coordinates incrementally. Partial workers use the checked weighted class suffixes for each exact tile, including its alignment-only edge bytes. They prefetch the next source slot, wait before reusing an output slot, and retire both output events before returning. Separate compiled entries omit the general panel and transpose machinery; this reduces code and scratch requirements without changing arithmetic, public parameters or ABI. The compact workers live in a private source header compiled with the general workers. This keeps one copy of the static numerical helpers while preserving separate worker entry functions; splitting them into separate compilation units would duplicate the arithmetic code and change firmware placement. The scratch sizes above describe payload storage. Each worker also reserves 126 bytes of alignment slack and explicitly aligns the two buffer bases to 64 bytes, independently of the runtime vector-stack alignment. Compiler stack and spill requirements remain additional. Latency improvements still require qualification on the composed firmware.

Compact grouped C16 worker​

An additional private worker handles already-admitted grouped patches with more than 16 and at most 64 logical channels, C tiles of 16, full H/W tiles, and depth tiles of one plane. Every full/final C tile must store exactly 16 channels and fit one existing task. The complete patch must fit 4,608 source elements and 6,144 output bytes; source transfers must meet the existing DMA threshold. A padded final channel tile is supported. Compact final tiles with fewer than 16 stored channels, partial H/W tiles, larger depth tiles, split tasks and other ineligible geometry retain the general path. These are implementation-selection conditions, not new public restrictions.

The worker reads the patch's complete logical channels once, converts that local source slot in place using the same exact dtype/rounding helpers, then packs each C16 plane into a separate region of its output slot. Full channel panels use 16-byte local vector transfers. The final partial channel panel fills padding with zero_point on every invocation and copies only valid channels. Because all stored panels are multiples of 16 bytes, this route has no alignment-only gaps. No arithmetic order, intermediate precision or exceptional-value policy changes.

Eligible contiguous requests may retain quantized bytes across adjacent W tiles in the existing bounded publication cache. Complete C16 planes use exact local vector copies; short planes retain bounded gathers. The cache never quantizes a byte twice. Publication expands required ghosts and keeps each C plane's actual DDR destination, rather than assuming that scratch adjacency implies DDR adjacency. For complete grouped patches, cache redirection also requires the aligned raw input and its extraction-scratch role to fit the second input window at the worst local alignment phase. Otherwise the grouped worker uses independent input/output ping-pong: fewer publication commands do not justify serializing input. This check does not change partial-tile or non-grouped cache eligibility.

Contiguous pixels use one contiguous or row-pitched transfer for the complete H/W tile. Pixel-pitched sources use a chained 2D transfer per H row. Input events cover every row; output events chain all C-plane writes. Each worker owns a contiguous range of spatial groups across every C plane, with no overlapping output blocks between workers. The C planes are adjacent only in private scratch: their device destinations remain separated by the full spatial-plane span. Starting coordinates are decoded once and advanced through W, H, D and outer slices; no general weighted task decode is repeated for each C plane.

The two source/output slots use 48 KiB for FP32 or 30 KiB for BF16/FP16 plus 126 bytes of explicit 64-byte alignment slack and compiler stack/spill space. A slot's complete output chain retires before that output slot is reused; all outstanding writes retire before return. The implementation remains in the same OpenCL translation unit as the general workers, sharing one static numerical closure. Its smaller control path is an optimization candidate, not a measured latency guarantee; qualification still applies to the complete linked firmware.

Performance scope​

Sub-1 ms is a workload-specific target, not an API guarantee. RGB padded output, channel subtiles, multipliers, exceptional values, strides, address phases and worker count materially change cost. Allocation/preparation are separate from individual-call submit-to-confirmed-completion latency. Report host CPU time per operation separately from wall latency. The resident-buffer benchmark excludes preparation, initial CPU input handoff and final CPU output inspection from each sample; it is not camera-ingestion or CPU-to-CPU application latency. Process CPU accounting does not include all driver/IRQ worker cost. No A65 input prescan selects math paths.

Tests and qualification​

ev74_quanttess_contract tests host encoding, malformed native payload rejection, bounded output ownership and mocked dispatch. It does not execute EV arithmetic or DMA. A65's generic correctness fixture covers the shared contract and strided numerical/layout behavior.

The host vector-expression fixture checks paired-prefix admission at every lane position around sixteen- and thirty-two-element boundaries, including output guards, deferred suffixes and same-base scratch compression against an independent oracle. FP16 coverage additionally checks every 16-bit encoding through paired blocks, all rounding modes, zero-point extremes, multiplier underflow boundaries and gradual/FTZ host modes. It also executes the actual compact-worker source with immediate and deferred DMA models, an independent arithmetic/layout oracle, guard bytes and cross-worker output ownership checks. It covers uneven task partitions, dirty slot reuse, pitched and contiguous rows, outer dimensions and special values. The grouped model additionally covers full/final C16 panels, C-major plane destinations, exact staging capacities, rank-six carries and one through four workers. Native-selector tests retain fallback neighbors. This checks software ordering and bounds, not EV instructions or timing.

Runtime's quanttess_cvu_e2e fixture checks an independent arithmetic/layout oracle, full physical output and guards, dirty reuse, rounding/parameter edges, and exhaustive 16-bit inputs. Latency is enabled by default, including RGB, C32/C33 and retained input/output address phases with CPU accounting. --correctness-only explicitly scopes a rerun without latency.

Qualification requires real-device correctness under one through four workers, uninstrumented measurements and all neighboring operation suites on the complete linked firmware. Adding code can affect existing latency despite unchanged neighboring archives. Compilation and host tests alone are not device acceptance.

Fast interiors, compressed edges and two-channel publication​

The general W-run worker stages adjacent owned tiles into existing bounded roles, overlaps next input with current conversion/publication, and derives every physical offset from weighted tile classes. A worker starting on a row's last W tile can publish that singleton prefix before sharing the following regular run; its initial seam does not disable reuse for the entire worker.

Bulk conversion eligibility is tracked for the actual bounded run. A compressed edge containing alignment-only bytes does not disable bulk execution in unrelated complete runs. That edge uses per-task packing, zero-point ghosts and zero alignment bytes. The direct tile entry retains its constant-size loop for complete geometry. A checked partial whole-C request reuses the general W-run only when every useful worker has a bounded, DMA-sized adjacent pair after its optional seam prefix. The proof includes actual compressed H/W extents, source row pitch, publication bytes and the allocated scratch capacity. Other requests retain the direct tile entry. Both routes derive compressed-edge offsets from weighted classes, never job * output_bytes; input prefetch and output retirement remain double-buffered.

Tess and QuantTess share a byte-only C2 Pad16 publisher. Eight packed pixels are read exactly once into a vector, then published as eight complete 16-byte pixels. The scalar tail reads only live channel bytes. QuantTess uses it after exact Quant conversion; Tess uses it after gathering. Every invocation writes its own padding value and all live bytes, including dirty-buffer reuse.

quanttess_cvu_e2e --shared-family-only measures C2 Pad16 bulk and singleton-seam families plus RGB and Compact tensors with full interiors and compressed edges. The default correctness and latency catalogs include the same cases; their IEEE and packing oracle remains test-owned.