Exp 2026 06 23 Chaining Resolution - oRKLLM/ork-driver GitHub Wiki
Date: 2026-06-23
Status: ✅ Resolved — both dtypes chain correctly; full make test green.
Commit: ork-driver npu: fix multi-matmul chaining (run_chain_i4 / run_chain_i8)
The earlier Hardware-Chaining-Zero-Overhead
"breakthrough" (regcmd concatenation via CORE_CTRL=0x000f, single task, regcfg_amount=S*108)
did not survive integration: ork_mm_run_chain_i8 built that way timed out in test_matmul
(require mask 0x300, task counter 0x0 → NPU soft-reset). The task_number=1 PC-jump variant used by
ork_mm_run_chain_i4 failed the same way. Treat the concatenation result as not reproducible in
the integrated path.
Both run_chain_* diverged from the only validated multi-task path in the tree, i4_mcworker
(the multicore M-chunk scheduler, which chains up to 16 tasks per submit and passes test_speed /
the W4A4 suite).
rknpu_job_subcore_commit_pc for a submit batch:
task_end = task_start + task_number - 1;
first_task = &task_base[task_start]; last_task = &task_base[task_end];
REG_WRITE(first_task->regcmd_addr, RKNPU_OFFSET_PC_DATA_ADDR);
REG_WRITE((first_task->regcfg_amount + RKNPU_PC_DATA_EXTRA_AMOUNT + pc_data_amount_scale-1)
/ pc_data_amount_scale - 1, RKNPU_OFFSET_PC_DATA_AMOUNT);
REG_WRITE(last_task->int_mask, RKNPU_OFFSET_INT_MASK);
REG_WRITE(((0x6|task_pp_en) << pc_task_number_bits) | task_number, RKNPU_OFFSET_PC_TASK_CONTROL);
REG_WRITE(0x1, PC_OP_EN); REG_WRITE(0x0, PC_OP_EN);- The kernel programs only
first_taskandlast_task; the HW PC-chains through the middle. -
PC_DATA_AMOUNT(how many words the HW fetches for the FIRST task) is derived solely fromfirst_task->regcfg_amount— get it wrong and the HW mis-fetches task 0, never reaches its boundary, the task counter never increments → the exacttask counter: 0x0timeout. - Completion: the kernel polls a HW task counter (
pc_task_status & pc_task_number_mask) until it reachestask_number. Sotask_numberMUST equal the number of chained regcmds. - RK3588 config:
pc_data_amount_scale=2,pc_task_number_bits=12,pc_task_number_mask=0xfff,pc_task_status_offset=0x3c,max_submit_number=4095.
- Synthesize S complete regcmds (one per matmul,
synth_i4/synth_i8) into one persistent buffer at stridei*REGCMD_*_N*4. - Patch each non-final regcmd's chain-control tail (words
rc[216..219], both dtypes) to jump to the next regcmd:rc[216]=0x0010|(next&0xffff)<<16; rc[217]=0x0101<<16|(next>>16)&0xffff; rc[218]=0x0014|0x0037<<16; rc[219]=0x01010000;. The final regcmd keeps the template default (0,0,0x14,0x01010000= raise completion interrupt). - One
rknpu_taskper regcmd:regcfg_amount = 116(i4, 232-word template) /108(i8),regcmd_addr = base + i*REGCMD_*_N*4,enable_mask=0xd,int_mask=0x300,int_clear=0x1ffff. - Submit:
task_number = S,subcore_task[0..2] = {0,S},flags=0x5,fence_fd=-1,core_mask = 1<<core,timeout=60000(leaving it 0 was a direct cause of the instant timeout), a cold-buffer warmup rep, andrknpu_submit_ioctl(self-healing reset on failure).
-
test_chain_i4(same weight, S=2) →OK: chain_i4 output verified. -
test_matmulork_mm_run_chain_i8(four different weights, varying M, S=4) →ok chained S=4 (varying M, independent matmuls) mism=0. Confirms different-weight chaining. - Full
make test: ALL TESTS PASSED (W4A4 API, layer, decode, model 1/12, test_speed 3-core 1.88x, test_chain_i4). No normal-path regressions.
The chaining experiments can soft-lock the NPU. A wedged NPU is not cleared by a warm reboot
(reloads the driver but not the NPU IP power/clock domain) — it needs a full power-cycle. rknpu
is built into the vendor kernel (no module reload).
Wire run_chain_i8/run_chain_i4 into the ggml-ork decode path (MoE / multiple small M=1 matmuls per
layer) and measure end-to-end vs the per-matmul submit floor.