Skip to content

Refresh the kernel-mode integration line: target call-flow, #2190 alignment, 8192 ids - #2241

Merged
YunjiQin merged 14 commits into
hw-native-sys:feat/kernel-mode-integration-testfrom
YunjiQin:feat/kernel-mode-integration-test
Sep 15, 2026
Merged

YunjiQin merged 14 commits into
hw-native-sys:feat/kernel-mode-integration-testfrom
YunjiQin:feat/kernel-mode-integration-test

Conversation

@YunjiQin

Copy link
Copy Markdown
Collaborator

Brings the integration line up to date with main and with the kernel-mode PRs
as they now stand, and rebases it onto the target call-flow design. Base is
feat/kernel-mode-integration-test at 5b2a5a5c7.

What changed

Sync and PR refresh

Rebase onto the target call-flow design (.docs/vllm/vllm-call-flow.md)

  • Callable ids are minted, not caller-supplied; the callable generation is gone.
  • Launch uses the chained stream topology — Start → AicoreStart →
    AicoreDone → AicpuDone → SerialTail. Caller and AICore share no event,
    so capture propagates in two hops; compensate() always drives the AICPU
    branch to completion.
  • This settles D3, D7-D8 and D9, which the earlier survey had left open.

Follow #2190's restored device entry

  • SimplerKernelDispatchArgs carries chip_callable_address and
    chip_callable_bytes directly; KernelCallableDeviceResidency and its header
    are deleted. The descriptor's only real property — keeping the extent out of
    the per-call packet — assumed an untrusted packet, which is not this
    contract's threat model.
  • The AICPU entry validates the span and performs no device read.

Align with #2190 at 08f7f9218728

  • Callable cache is Add: bounded kernel callable registration #2190's block allocator: 2 MiB blocks against a 2 GiB
    budget, replacing the single 512 MiB arena committed on first use.
  • Structural image validation lives once in validate_kernel_callable_image
    instead of being duplicated between the entry and the cache.
  • packet_bytes was bounded against SIZE_MAX — never true for a uint64_t
    on a 64-bit AICPU, so the check was dead and arg + packet_bytes could wrap.
    It is now bounded by the space left above arg.
  • A child func_id is bounded by KERNEL_MAX_FUNC_ID, tied to
    RUNTIME_MAX_FUNC_ID by a static_assert in both device_runner_base.cpp
    files. The previous bound was the extent of ChipCallable::child_func_ids_,
    which is how many children an image may carry, not the size of the table the
    id indexes; they agree at 1024 only by coincidence.

Capacity

  • MAX_REGISTERED_CALLABLE_IDS raised to 8192. The AICPU orch_so_table_ grows
    from 450 KiB to 2.73 MiB of .bss. Which admission limit binds first now
    crosses over at 256 KiB, since 2 GiB / 8192 is exactly that.

Cleanup

Deliberate divergences

Validation

Full sweep against the binaries this branch ships, hardware acquired through
task-submit after the architecture precheck:

Suite Result
Native builds: a2a3, a5, a2a3sim, a5sim, nanobind extension all succeeded
C++ unit tests, no hardware 166/166
C++ unit tests with SIMPLER_ENABLE_HARDWARE_TESTS=ON, no hardware 168/168
C++ hardware tests, ^requires_hardware(_a2a3)?$ 2/2
Python unit tests, tests/ut -m "not requires_hardware" 2428 passed
Python hardware unit tests, -m requires_hardware --platform a2a3 21/21 scheduled
The ten cases that marker run drops, invoked by path 10 passed
a2a3 hardware, tests/ut/py/test_kernel_mode_c_api.py 13/13
a2a3 hardware, tests/st/a2a3/kernel_capture passed, 100 replays
a2a3 onboard scenes, -m "not sdma" --exclude-level 4 167 passed, 1 skipped
a2a3 SDMA scenes 3 passed
a2a3sim scenes 82 passed, 8 skipped
a5sim scenes 78 passed

Every count matches the pre-change baseline, no re-runs for failure. The last
commit deletes one duplicated line from a CMake source list and was not
re-validated.

Adjudications are recorded in INTEGRATION-LOG.md (D15-D18); the run record and
the remaining boundaries are in docs/kernel-integration-validation.md.

Known gaps

  • tests/st/a2a3/kernel_capture validates the chained topology under
    aclmdlRICaptureBegin/End with 100 replays, but it is a standalone driver on
    KernelExecutionState primitives — it never calls
    simpler_kernel_mode_prepare_callable or simpler_kernel_mode_launch. Two-hop
    capture propagation through the real entries is still unproven, exactly as the
    target design's own note says.
  • H4 has no submitted PR. HBG kernel capability is zero; init returns
    UNSUPPORTED.
  • No A5 hardware in this validation environment.

🤖 Generated with Claude Code

chenshengxin2026 and others added 13 commits September 14, 2026 14:08
…w-native-sys#2197)

host_build_graph's Arg::scalar(i) returned InheritableScalar
unconditionally and exposed the raw uint64_t only through a deprecated
implicit conversion, kept for callers that had not yet moved to
args.scalar(i).to<T>(). TaskArgsTpl::scalar(i) (TMR and every other Arg
built directly on it) returned S itself with no template parameter at
all. The two spellings disagreed on what a caller writes for a static
read even though both slots are the same uint64_t.

scalar(i) is now a template on both sides, its parameter spelled
ScalarT in both -- TaskArgsTpl's T is its tensor type. host_build_graph
defaults it to InheritableScalar, so a bare scalar(i) still forwards
with its origin; TaskArgsTpl defaults it to S (uint64_t), so a bare
scalar(i) there is unchanged. Either side accepts an explicit type for
a static value read (args.scalar<T>(i)), and TaskArgsTpl bounds that
read by sizeof(S) -- the slot the value has to fit in -- rather than by
a hardcoded 8. task_args.h includes data_type.h for the from_u64 that
read applies, rather than reaching it through tensor.h.

The deprecated operator uint64_t() is removed, which is stronger than
the deprecation it replaces: with no conversion left to suppress, a
value read is a compile error rather than a warning. That closes the
blind spot the deprecation had, where a read instantiated inside a
system header -- EXPECT_EQ(args.scalar(i), v) -- was silently exempt.
InheritableScalar::to<T>() stays: it is the only way to read a handle
that was passed on as a function argument, where Arg::scalar<T>(i) is
unavailable because the Arg is not in hand.

Every call site that triggered the deprecation warning (98 across 20
orchestration files) moves to the explicit-T spelling. All of them were
already value reads, so no call site changes meaning; which of them
ought to forward instead is a question about each example's semantics
and is tracked separately.

The tensormap_and_ringbuffer orchestrations that read a slot as some
type other than the slot's own move with them, so both runtimes spell
that read the same way and the non-S branch of TaskArgsTpl::scalar has
in-tree instantiations -- a template body is only checked when it is
instantiated, and until now every scalar<T> call site was
host_build_graph, whose Arg hides the base's scalar with its own. A
bare uint64_t read is left alone: that is what scalar(i) already
answers, so naming the type would add nothing.

Python's add_scalar took a pre-encoded uint64_t, pushing
scalar_to_uint64(value) onto every caller. It now takes the value
directly -- int, float, bool, or a ctypes scalar -- and encodes it
natively (encode_scalar in the bindings, exposed to Python as
scalar_to_uint64 for callers that still want the raw bits).
scene_test.py's three add_scalar call sites drop their
scalar_to_uint64 wrapping accordingly.

That encoder matches C++ to_u64() bit for bit, which the previous
Python implementation did not: it read a ctypes scalar through its
`.value`, which ctypes has already sign-extended for a signed type, so
c_int8(-1) produced 0xFFFF'FFFF'FFFF'FFFF where to_u64(int8_t{-1}) is
0xFF. A ctypes scalar is now read through the buffer protocol at its
own width and zero-extended, which is what to_u64's union does.

Reading raw bytes makes the scalar's byte order load-bearing, and its
buffer format is where that order is stated. ctypes admits
byte-order-qualified variants -- c_uint32.__ctype_be__ carries the same
_type_ as c_uint32 and differs only in the format prefix -- whose bytes
for the value 1 are 00 00 00 01, which a raw copy would store as
0x01000000. A reversed-order scalar has no native C++ counterpart to
agree with, so the format decides admission: host byte order plus one
of the integer widths, f, d or ?. The pointer and character types (P,
z, Z, c, u) and long double are refused with it; c_void_p and c_char_p
would otherwise encode a host pointer into a device-bound slot. A
subclass inherits its base's format and so is admitted with the base,
which dispatching on the type's __name__ would not do.

A native Python float still narrows to IEEE-754 single precision, and a
finite value out of that range now raises where a narrowing conversion
would produce an infinity -- struct.pack("<f", 1e100) raised too, and
storing that infinity would silently be a different number. inf and NaN
pass through as themselves. This is the one encoding that cannot align
with its C++ counterpart, because a Python float carries no width where
to_u64(1.5) is a double; ctypes.c_double is the spelling for full
precision.

Two Python C API returns that signal failure are checked rather than
used: PyObject_IsInstance answers -1, which is truthy, and
PyNumber_Index answers nullptr with the caller's own exception pending.
The common Python-int case is tested first, so the hot path costs one
PyLong_CheckExact and no attribute lookup.

test_task_interface.py pins every encoding across scalar_to_uint64,
TaskArgs.add_scalar and ChipStorageTaskArgs.add_scalar, including the
zero-extension widths, a c_double subclass, both byte-order qualifiers,
the single-precision range boundary against struct.pack's own verdict,
out-of-range integers, and an __index__ that raises.
…-native-sys#2214)

The args-dump scene test validated structure only — entry counts, arg indices,
offsets, sizes, the manifest schema — and a dump whose payload is correctly
sized and entirely zero satisfies every one of those. hw-native-sys#1560 is exactly that
failure: a5sim `--dump-args 2` wrote correctly sized, all-zero tensor payloads
and the test reported PASSED.

The a2a3 test did carry one payload-truth assertion, but only under
`if level == 3`, so levels 1 and 2 had none. The a5 test had that block
deleted, its docstring recording that "the A5 payload values remain untrusted
until hw-native-sys#1560 is fixed".

The assertion now runs at every level that writes a payload, in both tests. It
anchors on task 0's `a + b` over the 2.0 / 3.0 inputs, whose bytes are known
before the run, rather than on "not all zero": the case also passes
`torch.zeros` as an argument, so an all-zero payload is a legitimate value
somewhere in this dump and a blanket non-zero rule would be wrong.

a5 also regains the level-3 payload-selection check. The two files now differ
only in `KERNELS_BASE` and their platform lists.

The underlying bug is gone. Running hw-native-sys#1560's own per-record check — payload
sliced by each entry's `bin_offset` / `bin_size` — over a fresh a5sim level-2
run gives 13 tensor records, 0 of them all-zero, matching the a2a3sim control
exactly.

Verified at levels 1, 2 and 3 on a2a3sim, a5sim and a2a3 onboard: 9 runs, all
green. The negative control expects 6.0 instead of 5.0 and fails all six sim
combinations, including levels 1 and 2 — which is the point, since those two
had no payload assertion to fail before.

Note that CI exercises level 1 only: `_st-sim-a2a3.yml`, `_st-sim-a5.yml` and
`run-onboard-dfx-smokes` all pass a bare `--dump-args`, and `conftest.py`
declares that flag `nargs="?", const=1`. So levels 2 and 3 still have no
coverage there, including the level-3 mode `core_swimlane.py` consumes.
hw-native-sys#2213)

"stage" carries four unrelated meanings in this repo and is defined
nowhere. Two of them meet inside one file: host_build_graph's
runtime_maker.cpp reports `staged=%d` for caller tensors it copied to the
device, and a hundred lines away holds a `staging` block that is the host
scratch the Graph Definitions are assembled in. The scheduler adds a third
(`staged_core_mask`, cores held ready before release) and `drain_stage` a
fourth (a step in a sequence). A reader cannot tell which is meant without
following the code, and error text shown to users inherits the ambiguity.

This renames one sense: the per-run copy of a caller tensor into device
memory. The other senses keep the word -- a staging buffer, a pipeline
stage, a staged IPC frame and the args-dump capture stage are all ordinary
uses of it, and the sense renamed here had the weakest claim, since the
device buffer it produces is the live one the kernel reads rather than
somewhere bytes pass through.

The replacement reuses names the repo already has rather than coining any.
A tensor on this path is `child_memory=False`, i.e. `AddressSpace::HOST`,
so it is a host-memory tensor; the operation is an H2D copy-in, and `h2d`
is already how the sibling bind phase spells it (`BindArenaH2d`,
`arena_h2d`). So `bind.args` now reports `h2d=%d bytes=%llu`, and
tensormap_and_ringbuffer's `stage_device_args` becomes
`copy_in_device_args`.

Both runtimes carry this sense, so both move, and each arch sibling moves
with its pair. Nothing parses the attribute string programmatically --
`h2d=` replaces `staged=` in logs and in docs/dfx/hbg-bind-phases.md only.

The remaining occurrences were classified by reading them rather than by
pattern: a regex over the obvious spellings missed tmr's "Failed to stage
tensor" diagnostic, SCALAR_DATA_ACCESS.md's "used to stage", and the
prose in task-flow.md and buffer-abi.md. `docs/investigations/` is left as
written, since those are dated records of past measurements.

docs/testing.md also said the default copies every tensor in on every
round, which overstates it: pure OUT buffers skip the H2D.

No behaviour change.
…tegration-test

# Conflicts:
#	docs/task-flow.md
The heads this integration was audited against were frozen on 2026-09-11.
Since then hw-native-sys#2064 merged to main and six of the thirteen remaining kernel-mode
PRs moved. Four of those moves are adoptable as they stand; the rest reverse
decisions this line already made. D15 in INTEGRATION-LOG.md records the
survey and says what each deferred item needs before it can be taken.

Adopted:

- From hw-native-sys#2177, build_kernel_pipeline_contract_impl rejects an invalid
  CallConfig with INVALID_ARGUMENT and keeps INTERNAL for a null output or a
  contract it generated but cannot validate. This extends the K1 numbering
  D14 adopted for the entry points to the hook behind them.
- From hw-native-sys#2176, KernelContextOps drops event_flag; create_event takes
  (context, event) and the onboard implementation owns ACL_EVENT_SYNC, so the
  platform constant stays with the platform.
- From hw-native-sys#2176, a PersistentKernelArgs rollback whose own release fails now has
  coverage: a host-only test for the block the owner retains, and an rtFree
  fault hook behind the new persistent_free_close lifecycle scenario.
- From hw-native-sys#2185, the Python entry test terminates a hung child instead of
  leaving it for the CI job timeout, and destroying the borrowed stream after
  a refused kernel_init is an assertion rather than a swallowed exception.

persistent_free_close asserts that the retry re-attempts and completes rather
than that it makes exactly one more rtFree call. A kernel context here
releases several device blocks and the injected failure stops the first pass
partway, so the retry covers the failed block plus everything the first pass
never reached: eight calls across the two passes where the first made three.
The count-exact form the source PR uses holds only for its own allocation set.

hw-native-sys#2176, hw-native-sys#2177 and hw-native-sys#2185 also drop kernel_execution_state.cpp from a platform
source list, which does not apply here: kernel_resource_requirements.h calls
bind_resources_for_launch from the H chain, so the simulation host runtimes
need the translation unit too.

Validation is recorded in docs/kernel-integration-validation.md.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The target call-flow design settles three of the four adjudications D15 left
open, and names this line as still carrying the old shape. D16 in
INTEGRATION-LOG.md records the decisions; they are one package rather than
three independent choices.

Registration mints the id. simpler_kernel_mode_prepare_callable takes
(ctx, callable, size, int32_t *out) and writes a context-local id on success,
-1 on every failure. It takes no caller_stream: registration enqueues on the
context's own AICPU stream, which every later launch also enqueues on, so
stream FIFO carries the ordering. Only launch takes a stream, per call.

Registration is pure. There is no deduplication and no lookup, so the same
image registered twice takes two ids, two uploads and two charges, and arena
capacity is spent per registration rather than per distinct image.

No callable generation survives. SimplerCallableHandle,
PTO_RUNTIME_ERR_CALLABLE_STALE, KernelCallableDeviceResidency::generation and
SimplerKernelInvocationHeader::generation are gone; the wire header is 32 bytes
and the device descriptor 24. Three properties replace the guard: an id is
minted once and never reused within a context, close invalidates every id the
context minted, and a closed worker accepts no launch.
PTO_RUNTIME_ERR_CAPACITY_EXCEEDED takes the vacated BASE - 8.

The launch sequence is chained rather than sibling. Events are Start,
AicoreStart, AicoreDone, AicpuDone, SerialTail: the caller records Start, AICPU
waits it, clears the handshake and records AicoreStart, AICore waits that and
records AicoreDone, AICPU launches with HostArgs, joins AicoreDone and records
AicpuDone, and the caller joins that and records SerialTail. Caller and AICore
share no event, so ACLGraph capture propagates in two hops and no wait crosses
a capture boundary. AicoreStart must precede the AICPU launch, since the AICPU
orchestrator spins on AICore's handshake report. Compensation now cancels only
between the AICore and AICPU launches and drives the chain back through AICPU,
which is the caller's only path to a tail.

hw-native-sys#2190's block allocator is not taken: its cache is host-only, while this line's
AICPU entry resolves a descriptor at arena_ + callable_id * sizeof(descriptor).
The single arena and its descriptor prefix stay.

Validation is recorded in docs/kernel-integration-validation.md. The chained
sequence is what the a2a3 capture probe drives, and it completes 100 captured
replays with verified buffers; the public prepare/launch entries are still not
exercised inside a captured graph by any test.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
hw-native-sys#2190 restored its device entry without a residency descriptor: the packet
carries chip_callable_address and chip_callable_bytes, and the consumer
receives the ChipCallable reference. This line follows that shape. D17 in
INTEGRATION-LOG.md records why.

The descriptor bought two things and only one of them was real. Its stated
purpose was revocation — the entry re-read the slot on every invocation
including graph replay, so the host had one small location it could
invalidate. Nothing ever wrote a descriptor after commit: ids are never
evicted, kernel-mode unregister_callable returns INVALID_STATE, and clear()
only drops host metadata. It would not have covered the case that matters
either, since after close the freed arena leaves the descriptor address
dangling exactly as the image address does. What it did buy was keeping the
extent out of the per-call packet, so a packet could name which descriptor but
not widen the window the device parses — an argument that assumes an untrusted
packet, when the packet is built by the host binder from cache.resolve().

SimplerKernelDispatchArgs replaces residency_address with
chip_callable_address and chip_callable_bytes. This line keeps the four
binding fields hw-native-sys#2190 has no source for — binding_address, context_generation,
sm_bytes, arena_bytes — because its TMR consumer reads them, so the prefix is
88 bytes against hw-native-sys#2190's 56. consume_kernel_invocation takes
(args, const ChipCallable &, callable_bytes, payload, payload_bytes), passing
the whole prefix for the same reason.

The entry validates the span — non-null, alignof(ChipCallable), at least
sizeof(ChipCallable), no wraparound — and performs no device read; cache
visibility for the image moves to the consumer, where the image is parsed.
KernelCallableDeviceResidency and its header are deleted, the code arena loses
its descriptor prefix, and KernelDispatchStatus retires NotResident and Stale
rather than reusing 2 and 3.

docs/zh-cn/kernel-mode-integration-test.md also had a stale launch diagram: it
still showed the sibling topology the previous commit replaced with the chained
one. Its device-side section and interface-adjudication table follow the packet
change.

Validation is recorded in docs/kernel-integration-validation.md. Every scene
and unit count matches the pre-change baseline.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This was the one part of hw-native-sys#2190's contract this line had not taken. Both lines
already register purely — no deduplication, no lookup, no reuse before close —
so every prepare spends an id permanently and 64 was the binding limit for a
caller covering all of a model's specializations.

The capacity unit test stops naming 64: it fills MAX_REGISTERED_CALLABLE_IDS
residents and static_asserts that one arena still holds them at that image
size, so the case keeps measuring the count limit rather than silently
becoming a byte-limit test.

The AICPU cost is real and measured, not incidental. The TMR AICPU executor
holds orch_so_table_[MAX_REGISTERED_CALLABLE_IDS] of ~296-byte entries, so
libaicpu_kernel.so's .bss grows from 450 KiB to 2.73 MiB. The device loads and
runs it: the kernel C API and capture suites pass unchanged on a2a3. The entry
is 86% char path[256], which the kernel path never uses since it registers by
dev_orch_so_addr, so moving path out of the resident table would return about
2 MiB. That is left alone here because it is program-mode registration code.

docs/zh-cn/kernel-callable-residency.md records which of the two admission
limits binds first, since that now depends on image size: sizeof(ChipCallable)
is 9376 bytes, so 8192 empty images occupy 73.5 MiB and the count binds, while
1 MiB images exhaust the 512 MiB budget at 512 registrations and the bytes
bind.

Validation is in docs/kernel-integration-validation.md, including the triage of
four cases that failed once on a device that dropped its host channel
(507901, hdc disconnect) and pass 9/9 on re-run.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…sys#2190

Two things come over together. Device memory for callable images is taken in
2 MiB blocks against a 2 GiB budget instead of one 512 MiB arena committed on
the first registration, and the structural image validation moves into
`validate_kernel_callable_image` so the entry and the cache stop carrying two
copies of it.

The block allocator was blocked until now by the residency descriptor, which
indexed a fixed prefix of the single arena by callable_id. That descriptor is
gone, so nothing requires the images to be contiguous from one base. The first
registration now takes max(charged, 2 MiB); a registration larger than a block
gets one sized exactly to it while earlier blocks keep their usable tails;
block slack is charged against the budget through allocated_bytes() while
resident_bytes() still counts only charged image bytes; and a published
device_address never moves as blocks are added. With the id cap at 8192 the
crossover between the two admission limits lands at exactly 2 GiB / 8192 =
256 KiB per image.

The weak stub for consume_kernel_invocation is now linked into both dispatch
test targets, so test_kernel_dispatch's own strong consumer proves the override
resolves rather than colliding. That is the link error a runtime-specific
consumer would hit if the fallback were not weak.

Two deliberate divergences from hw-native-sys#2190 remain.

validate_kernel_callable_image keeps rejecting a child func_id that is out of
range or repeated within one image. hw-native-sys#2190's extraction lost those two checks,
and without them such an image reaches the device consumer, which rejects it at
launch and poisons the context rather than failing the registration cleanly.

The simulation prepare_callable calls the image validator too, so both entries
agree that structural checks precede the lifecycle refusal. Without it an image
whose size clears the header floor but whose variable tail does not add up
reports INVALID_STATE instead of INVALID_ARGUMENT, which is what
test_kernel_entries_reject_a_context_with_no_kernel_claim caught.

kernel_arena_change_is_forbidden is a third difference but not one against
hw-native-sys#2190. It is mainline code from the merged K1 (hw-native-sys#2064), so every branch off main
carries it. This line deleted it in D14 item 4 and routes the same rule through
K3's commit_static_arena_bank, which applies it to onboard and simulation from
one place. That consolidation removes a guard main uses today in favour of a
mechanism that exists only in an unmerged PR — static_arena_bank.h is on this
line and on hw-native-sys#2193 and nowhere else — so it is recorded in the validation doc as
a risk rather than a settled question.

Validation is in docs/kernel-integration-validation.md. Every scene and unit
count matches the pre-change baseline with no re-runs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`simpler_aicpu_kernel_exec` treats `packet_bytes` as the length of the
region starting at `arg`, and derives the payload span from it. The upper
bound compared it against `std::numeric_limits<size_t>::max()`, which a
`uint64_t` field can never exceed on a 64-bit AICPU, so the check was dead
and `arg + packet_bytes` could wrap.

Bound it against the space actually left above `arg` instead, so the
payload span the consumer receives is guaranteed not to wrap the address
space. This is the shape hw-native-sys#2190 carries at `9c085aa0`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`validate_kernel_callable_image` rejected a child `func_id` outside the
extent of `ChipCallable::child_func_ids_`. That array's capacity is the
number of children an image may carry, not the size of the table the id
indexes; the two agree at 1024 only by coincidence, so raising the child
capacity would have silently widened the accepted id range past what the
device function table holds.

Name the real bound, `KERNEL_MAX_FUNC_ID`, next to the registration-id cap
it is independent of, and tie it to `RUNTIME_MAX_FUNC_ID` with a
`static_assert` in both onboard and simulation `device_runner_base.cpp`, so
the two cannot drift apart without a compile error. This is the shape hw-native-sys#2190
carries at `08f7f921`, adopted along with the test it added; the accepted
set is unchanged today.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The frozen-heads table now shows hw-native-sys#2190 at `08f7f9218728`, and the validation
doc carries the sweep run against the binaries this line actually ships: 166
and 168 C++ unit tests, 2428 Python unit tests, 2 C++ hardware tests, 21
scheduled hardware unit cases plus the ten that marker run drops, 13 kernel
C API cases, the capture replay, and all four scene platforms. Every count
matches the pre-change baseline.

D18 records why the child `func_id` bound moved from the extent of
`ChipCallable::child_func_ids_` to `KERNEL_MAX_FUNC_ID`, and the divergence
note in the earlier section is corrected: hw-native-sys#2190 restored those checks, so only
the simulation `prepare_callable` ordering still differs.

Two harness details are written down because each cost a false red: the C++
unit build takes no `CMAKE_BUILD_TYPE`, since `Release` defines `NDEBUG` and
silences the `debug_assert` three tests assert on; and the C++ hardware tests
need a `--resource-spec-file` built from `TASK_DEVICE`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Sep 15, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 5b74b9ad-5946-4430-b5bd-4032a82bbb20

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

…m list

`HOST_RUNTIME_SOURCES` in the a2a3 simulation host list named
`common/platform/shared/host/kernel_device_resources.cpp` on two consecutive
lines. The a5 simulation list and both onboard lists name it once, and CMake
dedups a target's source list, so this changes no build output — it is a
copy/paste slip from the integration commit, removed so the four lists read
the same.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@YunjiQin
YunjiQin merged commit 29a1cd4 into hw-native-sys:feat/kernel-mode-integration-test Sep 15, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants