Refresh the kernel-mode integration line: target call-flow, #2190 alignment, 8192 ids - #2241
Merged
YunjiQin merged 14 commits intoSep 15, 2026
Conversation
…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>
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Advanced Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
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. Comment |
…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
merged commit Sep 15, 2026
29a1cd4
into
hw-native-sys:feat/kernel-mode-integration-test
3 checks passed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Brings the integration line up to date with
mainand with the kernel-mode PRsas they now stand, and rebases it onto the target call-flow design. Base is
feat/kernel-mode-integration-testat5b2a5a5c7.What changed
Sync and PR refresh
upstream/main(4 already-merged commits ride along: Refactor: name the per-run H2D copy-in instead of overloading "staged" #2213, Fix: check args-dump payload truth at every level, on both arches #2214,Refactor: unify scalar reads across host_build_graph and TaskArgsTpl #2197, CI: bump pinned pto-isa to 03e45c4b #2219).
including its event topology, Add: TMR kernel-mode resource contracts and init admission #2177, Add: kernel-mode entry layer — give the four C entries callers (⑩a) #2185. Refactor: [H1] measure HBG graphs before device upload #2171 and Add: [2B] bind HBG capacity to the runtime layout #2172 were force-pushed
onto a different decomposition after the 2026-09-11 audit and are deferred;
this line still carries the audited shape.
Rebase onto the target call-flow design (
.docs/vllm/vllm-call-flow.md)Start→AicoreStart→AicoreDone→AicpuDone→SerialTail. Caller and AICore share no event,so capture propagates in two hops;
compensate()always drives the AICPUbranch to completion.
Follow #2190's restored device entry
SimplerKernelDispatchArgscarrieschip_callable_addressandchip_callable_bytesdirectly;KernelCallableDeviceResidencyand its headerare 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.
Align with #2190 at
08f7f9218728budget, replacing the single 512 MiB arena committed on first use.
validate_kernel_callable_imageinstead of being duplicated between the entry and the cache.
packet_byteswas bounded againstSIZE_MAX— never true for auint64_ton a 64-bit AICPU, so the check was dead and
arg + packet_bytescould wrap.It is now bounded by the space left above
arg.func_idis bounded byKERNEL_MAX_FUNC_ID, tied toRUNTIME_MAX_FUNC_IDby astatic_assertin bothdevice_runner_base.cppfiles. 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_IDSraised to 8192. The AICPUorch_so_table_growsfrom 450 KiB to 2.73 MiB of
.bss. Which admission limit binds first nowcrosses over at 256 KiB, since 2 GiB / 8192 is exactly that.
Cleanup
HOST_RUNTIME_SOURCESnamedkernel_device_resources.cpptwice; the other three platform lists name itonce. CMake dedups, so no build output changes. (This was briefly Fix: drop the duplicated kernel_device_resources entry in the a2a3 sim list #2240,
closed in favour of this PR.)
Deliberate divergences
prepare_callablefallback validates the image beforereporting that no kernel context is live, so a malformed image reports an
argument error on both platforms. Add: bounded kernel callable registration #2190's fallback reports
INVALID_STATE.kernel_arena_change_is_forbiddenis deleted here (D14 item 4) in favourof K3's
commit_static_arena_bank. That is a difference againstmain, notagainst Add: bounded kernel callable registration #2190 — it is mainline code from the merged K1 (Add: kernel-mode C ABI skeleton and wire headers (K1) #2064), so every
branch off
maincarries it. The consolidation applies the same rule toonboard and simulation from one place, but
static_arena_bank.hexists onlyon this line and on the unmerged Fix: kernel-mode capacity refusals no longer free what they protect (⑤K3) #2193. If Fix: kernel-mode capacity refusals no longer free what they protect (⑤K3) #2193 does not land in that shape,
the rule has no carrier here. Recorded as a risk, not a resolved question.
Validation
Full sweep against the binaries this branch ships, hardware acquired through
task-submitafter the architecture precheck:SIMPLER_ENABLE_HARDWARE_TESTS=ON, no hardware^requires_hardware(_a2a3)?$tests/ut -m "not requires_hardware"-m requires_hardware --platform a2a3tests/ut/py/test_kernel_mode_c_api.pytests/st/a2a3/kernel_capture-m "not sdma" --exclude-level 4Every 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 andthe remaining boundaries are in
docs/kernel-integration-validation.md.Known gaps
tests/st/a2a3/kernel_capturevalidates the chained topology underaclmdlRICaptureBegin/Endwith 100 replays, but it is a standalone driver onKernelExecutionStateprimitives — it never callssimpler_kernel_mode_prepare_callableorsimpler_kernel_mode_launch. Two-hopcapture propagation through the real entries is still unproven, exactly as the
target design's own note says.
UNSUPPORTED.🤖 Generated with Claude Code