Skip to content

Fix: run SPMD paged attention on A5 at the a2a3 shapes - #1898

Merged
ChaoWao merged 1 commit into
hw-native-sys:mainfrom
doraemonmj:fix/issue-1832-spmd-sim-compat
Aug 20, 2026
Merged

ChaoWao merged 1 commit into
hw-native-sys:mainfrom
doraemonmj:fix/issue-1832-spmd-sim-compat

Conversation

@doraemonmj

@doraemonmj doraemonmj commented Aug 19, 2026 •

Copy link
Copy Markdown
Contributor

Summary

The A5 port of the SPMD paged-attention MIX kernel produced wrong results for any
non-zero query, and hung outright once a workload spanned more than FIFO_DEPTH
KV blocks. Both are defects in how the kernel drives the PTO-ISA TPUSH/TPOP pipes
on A5. With them fixed, A5 runs the a2a3 shapes at the unchanged FIFO_DEPTH = 2,
so the A5 cases become Case1 / Case2 / SmallCase1 with the same parameters
and names as a2a3.

The two kernel defects

1. Pipe direction — the GM ring was never used. The three FIFOs are GM ring
buffers the orchestration allocates, but the pipes were declared with DIR_C2V /
DIR_V2C. On A5 those select the on-chip VEC_FIFO path, which ignores the GM base
entirely — popTileFromVecFiFo only ever reads C2V_CONSUMER_BUF. That path also
strides local slots by a full SLOT_SIZE, while the kernel budgets its UB at
2 * SUB_QT * MAX_BLOCK_SIZE * sizeof(float). For q_tile = 16 both are 8192 B,
so slot 1 starts exactly at OI_UB_BASE and overlaps the entire oi buffer.

A5 has DIR_C2V_GM / DIR_V2C_GM for GM-backed rings; they are behind
#ifdef PTO_NPU_ARCH_A5 and a2a3 has no such distinction, which is how the port
inherited the wrong spelling. examples/a5/tensormap_and_ringbuffer/bgemm is the
correct use of the on-chip form — it passes nullptr as the GM base.

2. Split axis on the Cube's pij pop. The Cube consumes the whole Q_TILE
tile, so TILE_NO_SPLIT looked right. But the split axis also selects the
two-lane sync: setIntraBlockBySplit / waitIntraBlockBySplit only touch the
second AIV lane's flag space (flagId + VEC_CORE_ID_OFFSET) when the axis is not
NO_SPLIT. So free() released only lane 0's slot, and wait() observed only
lane 0's data-ready — letting the Cube read a pij tile lane 1 had not finished
writing. popMatTileFromGMFiFo ignores the axis for addressing, so TILE_UP_DOWN
changes synchronization only, not the data path.

That single omission produced both symptoms:

  • The hang. Lane 1 never saw a slot freed, so it blocked on its third push —
    shouldWaitFree returns false while tileIndex < SlotNum, which is why the
    boundary sat exactly at FIFO_DEPTH. The predicate is cumulative per hardware
    block, since the pipe's tileIndex is not reset across the stride loop:
    (logical blocks on that hw block) * n_blocks > FIFO_DEPTH. At the stock depth
    of 2 the a2a3 shapes need 704 / 384 / 64 pushes.
  • The wrong values. The Cube racing lane 1's pij write.

Coverage changes

  • The A5 cases become the a2a3 shapes and names. The previous A5 case zeroed
    the query, which makes sij zero regardless of how K is loaded, so it exercised
    only the PV path and the final normalization — it passed throughout while every
    non-zero query was wrong. It is replaced rather than kept. Case1 is
    non-manual, mirroring a2a3; Case2 and SmallCase1 stay manual.
  • host_build_graph mirrors tensormap_and_ringbuffer on both architectures,
    instead of carrying a single small zero-query case. All HBG cases stay manual.
  • Simulator coverage is dropped for this workload. The MIX kernel does not
    build for the CPU simulator: PTO-ISA rejects the ColMajor TMULS in the softmax
    tail. a2a3sim / a5sim are removed from the case lists, and the __CPU_SIM
    compatibility shim, which only existed to get that build further, goes with it.
  • The FIFO tensor shapes in both orchestrations get an explicit cast: the division
    by sizeof yields size_t, and narrowing it inside a braced initializer is an
    error for Clang.

Verification

Onboard on a2a3 (Ascend910 / 9392, CANN 9.0.0) and a5 (Ascend950PR / 9579,
CANN 9.2.0), every run wrapped in task-submit. All 12 declared cases pass:

Platform / runtime Case1 Case2 SmallCase1
a2a3 tensormap_and_ringbuffer pass pass pass
a2a3 host_build_graph pass pass pass
a5 tensormap_and_ringbuffer pass pass pass
a5 host_build_graph pass pass pass

Both selections were exercised on each architecture: --manual include (the full
sweep above) and the default Per-PR selection, which collects exactly one node per
architecture — the non-manual Case1 — and passes.

Because the second defect was a race, the three shapes were re-run individually on
a5: 5/5 pass each, 15/15 total.

The diagnosis rests on these measurements, all on a5:

  • Hang boundary follows FIFO_DEPTH. At depth 2, n_blocks 1 and 2 passed while
    3 and 4 returned 507014 and 8 returned 507018. At depth 4, n_blocks 3 and 4
    passed and 8 returned 507014. At depth 8, batch = 64 — 6 cumulative pushes —
    stopped hanging. It is not "merely slow": it still stalled under
    PTO2_SCHEDULER_TIMEOUT_MS=60000, reported as sched_error_code=100,
    sub_class=S1:running-stalled, completed=0/1.
  • The race, measured at depth 8 so nothing hung, n_blocks = 2: batch 2 and 4
    passed 5/5; batch 8 passed 2/5; batch 10 passed 1/3; batch 9 failed 3/3; batches
    12, 14, 16, 18, 20, 24 and 64 failed every run, max_diff 0.12–0.22. The
    non-monotonic, reproducibly flaky pattern is what identified it as a race rather
    than a size limit.

Ruled out along the way, each by measurement rather than inspection: porting the
a2a3 setEntryOffset lane-split fix-up (made it worse, 0.44 → 6.18); passing
sub_block_id through the TPUSH/TPOP overloads (bit-identical output, so A5
already returns the correct lane id); flag-ID overlap between the three pipes (the
_GM directions use only FlagID and FlagID + 1); a missing TFREE (the _GM
pop paths return reqFree = true and TPOP_IMPL already frees); SPMD_BLOCK_NUM
over-subscribing A5; and per-hardware-block GM FIFO regions overlapping.

Closes #1816
Refs #1832

@coderabbitai

coderabbitai Bot commented Aug 19, 2026 •

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

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: Pro Plus

Run ID: 5a5276c7-12b4-4275-ac9a-3bb7f9491c29

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

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a22a0750-a549-437f-987c-442f5ef89ad3

📥 Commits

Reviewing files that changed from the base of the PR and between 34ecd7f and a20b28d.

📒 Files selected for processing (6)
  • tests/st/a2a3/host_build_graph/spmd_paged_attention/test_spmd_paged_attention.py
  • tests/st/a2a3/tensormap_and_ringbuffer/spmd_paged_attention/kernels/orchestration/spmd_paged_attention_orch.cpp
  • tests/st/a5/host_build_graph/spmd_paged_attention/test_spmd_paged_attention.py
  • tests/st/a5/tensormap_and_ringbuffer/spmd_paged_attention/kernels/mix/paged_attention_parallel.cpp
  • tests/st/a5/tensormap_and_ringbuffer/spmd_paged_attention/kernels/orchestration/spmd_paged_attention_orch.cpp
  • tests/st/a5/tensormap_and_ringbuffer/spmd_paged_attention/test_spmd_paged_attention.py

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The change aligns A5 MIX paged-attention pipes with GM ring buffers, updates PV-step synchronization, adds explicit FIFO count casts, and revises A5 and A2A3 test selection for supported onboard platforms.

Changes

SPMD paged-attention alignment

Layer / File(s) Summary
GM FIFO configuration and PV synchronization
tests/st/*/tensormap_and_ringbuffer/spmd_paged_attention/kernels/orchestration/spmd_paged_attention_orch.cpp, tests/st/a5/tensormap_and_ringbuffer/spmd_paged_attention/kernels/mix/paged_attention_parallel.cpp
FIFO element counts now use explicit uint32_t casts. A5 MIX pipes use GM-backed directions, and the pij pop uses TILE_UP_DOWN synchronization.
Platform-specific test coverage
tests/st/a5/host_build_graph/spmd_paged_attention/test_spmd_paged_attention.py, tests/st/a5/tensormap_and_ringbuffer/spmd_paged_attention/test_spmd_paged_attention.py, tests/st/a2a3/host_build_graph/spmd_paged_attention/test_spmd_paged_attention.py
A5 tests exclude a5sim and use three A5 cases. A2A3 HBG tests reuse TMR cases on a2a3 and mark them manual.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to a20b2

This change corrects A5 paged-attention FIFO handling and expands coverage to the shared a2a3 shapes; the reported cases pass across both architectures, so no actionable merge-blocking risk remains beyond normal checks and review.

Possibly related PRs

Poem

A rabbit hops through GM with care,
FIFO shapes align in air.
Pipes now guide the tiles just right,
Tests choose hardware in their sight.
“Hop, hop!” says Bunny, “sync is bright!”

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes address non-zero-query, multi-block, multi-core failures and add validated TMR and HBG coverage required by [#1816].
Out of Scope Changes check ✅ Passed The test, simulator, orchestration, and cast changes support the linked issue and stated A5 paged-attention objectives.
Title check ✅ Passed The title clearly identifies the A5 SPMD paged-attention fix and the affected a2a3 shapes.
Description check ✅ Passed The description directly explains the kernel fixes, coverage changes, linked issues, and verification results.

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.

@doraemonmj
doraemonmj force-pushed the fix/issue-1832-spmd-sim-compat branch 3 times, most recently from afb286c to db02f6e Compare August 20, 2026 01:32
The A5 port of the SPMD paged-attention MIX kernel produced wrong results
for any non-zero query, and hung outright once a workload spanned more
than FIFO_DEPTH KV blocks. Two kernel defects, both in how the kernel
drives the PTO-ISA TPUSH/TPOP pipes on A5:

- The three FIFOs are GM ring buffers the orchestration allocates, but
  the pipes were declared with DIR_C2V / DIR_V2C, which on A5 select the
  on-chip VEC_FIFO path and ignore the GM base entirely. That path also
  strides local slots by a full SLOT_SIZE while the kernel budgets its UB
  at half that, so the second slot ran past SIJ_UB_BASE into OI_UB_BASE.
  Use DIR_C2V_GM / DIR_V2C_GM.

- The Cube popped pij with TILE_NO_SPLIT. The split axis also selects the
  two-lane sync: setIntraBlockBySplit / waitIntraBlockBySplit only touch
  the second AIV lane's flag space when the axis is not NO_SPLIT. So free()
  released only lane 0's slot, stalling lane 1's third push, and wait()
  observed only lane 0's data-ready, letting the Cube read a pij tile lane
  1 had not finished writing. The Mat-from-GM pop ignores the axis for
  addressing, so TILE_UP_DOWN changes synchronization only.

With both fixed, A5 runs the a2a3 shapes unchanged at the stock
FIFO_DEPTH, so the A5 cases become Case1 / Case2 / SmallCase1 with the
same parameters and names as a2a3, and Case1 joins the Per-PR sweep as
a2a3's does. The previous A5 case zeroed the query, which makes sij zero
regardless of how K is loaded and therefore exercised only the PV path
and the final normalization; it passed throughout while every non-zero
query was wrong, so it is replaced rather than kept.

The host_build_graph cases now mirror the tensormap_and_ringbuffer shapes
on both architectures instead of carrying a single small zero-query case.

Simulator coverage is dropped for this workload: the MIX kernel does not
build for the CPU simulator, because PTO-ISA rejects the ColMajor TMULS in
the softmax tail. The a2a3sim / a5sim platforms are removed from the case
lists, and the __CPU_SIM compatibility shim, which only existed to get
that build further, goes with them.

The FIFO tensor shapes in both orchestrations get an explicit cast: the
division by sizeof yields size_t, and narrowing it inside a braced
initializer is an error for Clang.

Verified onboard on a2a3 (Ascend910/9392) and a5 (Ascend950PR/9579): all
12 cases pass, and Case1 / Case2 / SmallCase1 each pass 5/5 on A5.

Closes hw-native-sys#1816
Refs hw-native-sys#1832
@ChaoWao
ChaoWao merged commit f65b83c into hw-native-sys:main Aug 20, 2026
17 checks passed
@doraemonmj
doraemonmj deleted the fix/issue-1832-spmd-sim-compat branch August 25, 2026 09:30
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.

SPMD paged attention fails with multi-block and multi-core A5 workloads

2 participants