Skip to content

XDNA: pin upstream amd/xdna-driver (and its XRT), build it privately, keep it current - #12

Merged
bong-water-water-bong merged 2 commits into
mainfrom
xdna-pin
Sep 23, 2026
Merged

bong-water-water-bong merged 2 commits into
mainfrom
xdna-pin

Conversation

@bong-water-water-bong

@bong-water-water-bong bong-water-water-bong commented Sep 23, 2026 •

Copy link
Copy Markdown
Collaborator

Pins the XDNA userspace stack the NPU lane runs on to upstream amd/xdna-driver, with a daily job to follow new upstream commits.

What

  • The pin: third_party/xdna-driver = upstream amd/xdna-driver 5d302c9 (2026-09-22). Its own xrt submodule pins XRT d8ececf.
  • What it covers: XRT and the XDNA shim plugin (the external runtime library). The kernel driver amdxdna ships in the kernel (drivers/accel) and isn't built from the pin.
  • scripts/build-xdna.sh <prefix> builds both privately:
    • XRT (NPU package) and then the shim, staged with DESTDIR under <prefix>/root. XRT hard-codes /etc/OpenCL/vendors, so nothing touches system paths.
    • The shim goes next to XRT, which is where XRT loads plugins from.
    • XRT's OpenCL layer (xocl) is excluded: the NPU doesn't use it, and it doesn't compile against this distro's newer ocl_icd.h.
    • Using it: build the engine with -DONEBIT_XRT_ROOT=<prefix>/root/opt/xilinx/xrt.
  • Keeping current: .github/workflows/bump-xdna.yml runs daily and opens a PR when upstream main moves. It uses the same HRX_BUMP_TOKEN secret as bump-hrx.yml (HRX: follow AMD's live ggml-hrx, pinned to AMD's tested pair and kept current #11).
  • Docs: docs/npu.md gains a section, "The XDNA stack".

Verified on Strix Halo

The box had been running XRT 2.21.75 and a hand-built shim (in /usr/local/xrt-runlist) from an xdna-driver checkout 73 commits behind upstream. Against the pinned stack:

  • The right libraries load: strace shows the external runtime library.so.2 and the external runtime library.so.2 coming from the pinned prefix.
  • Output is unchanged: tests/npu_lane_e2e.sh gives logits bit-identical to the reference lane, 24/24 steps.
  • Speed: 10.8 ms/token (92.6 tok/s), against 11.0 before, with a 327 ms warm load.

CI has no NPU, so run the build script and the lane test on Strix Halo before merging each bump PR.

🤖 Generated with Claude Code

bong-water-water-bong and others added 2 commits September 23, 2026 13:33
… keep it current

third_party/xdna-driver pins upstream amd/xdna-driver 5d302c9, whose xrt submodule pins XRT
d8ececf: the userspace the NPU lane runs on (XRT and the XDNA shim plugin libxrt_driver_xdna).
The kernel driver ships in the kernel (drivers/accel/amdxdna) and is not built from the pin.

- scripts/build-xdna.sh <prefix>: builds XRT (NPU package, without its OpenCL layer: it does not
  compile against newer distro ocl_icd.h headers and the NPU does not use it) and the shim,
  staged with DESTDIR under <prefix>/root (XRT hard-codes /etc/OpenCL/vendors), and puts the shim
  next to XRT, where XRT loads plugins from. Nothing is installed system-wide.
- .github/workflows/bump-xdna.yml: daily, opens a PR when upstream main moves.
- docs/npu.md, "The XDNA stack".

Strix Halo: the engine built with -DONEBIT_XRT_ROOT=<prefix>/root/opt/xilinx/xrt loads
libxrt_core and libxrt_driver_xdna from the pinned prefix (strace); tests/npu_lane_e2e.sh gives
logits bit-identical to the reference lane, 24/24 steps, 10.8 ms/token, 327 ms warm load.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@bong-water-water-bong
bong-water-water-bong enabled auto-merge (squash) September 23, 2026 19:21
@bong-water-water-bong
bong-water-water-bong merged commit 1e1264e into main Sep 23, 2026
1 check passed
@bong-water-water-bong
bong-water-water-bong deleted the xdna-pin branch September 23, 2026 19:31
bong-water-water-bong pushed a commit that referenced this pull request Sep 26, 2026
…oints and three HRX fixes

Our llama.cpp #7 (HRX MoE router route-ids binding length), #9 (decode-split
flash-attention capacity bound), #11 (ZAYA sliding-window attention, ZAYA1-74B),
#12 (MoE router experts-per-wave launch geometry) and #14 (ZAYA legacy
checkpoints; --remote fetches chat_template.jinja).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
bong-water-water-bong pushed a commit that referenced this pull request Oct 13, 2026
…ep, plus defect #12

The pre-registration requires a null result be discarded as INVALID unless the sanitizer was provably active, so
this records the proof before any faulting arm is read, with every step quoted from source or a running process.
(1) The knob reaches the process: read from the arm server's own /proc/<pid>/environ, both
GGML_HRX_LOG_DEVICE_EVENTS=1 and HRX_HAL_SANITIZER=asan are present. (2) runtime.c:576 sets FEEDBACK|ASAN from the
knob. (3) logical_device.c:241-260 applies runtime features to options->asan.enabled and ->feedback.enabled via
create_params->runtime_features, so HRX_HAL_SANITIZER=asan does set asan.enabled rather than silently doing
nothing. (4) The device accepts it: arms create their device and serve requests, so verify_supported_features
passed with ASAN requested. (5) The sink is installed inside create_registry_context gated on
GGML_HRX_LOG_DEVICE_EVENTS and called BEFORE hrx_gpu_initialize, which the API demands since
set_device_event_sink returns FAILED_PRECONDITION after accelerator init; log_hrx_device_event prints executable,
export, site, access, address, length, workgroup, workitem, shadow, value, dispatch. (6) Every libhrx.so on the box
carries the knob string and the ASAN report path. Conclusion: the channel is live end to end and is device-side,
the one class of attribution this campaign has never had, so a run with no HRX ASAN line is a meaningful exclusion
rather than an INVALID one.

Also records that the first arm (315asan-r1q) is discarded as OUT OF REGIME: gtt_start 47GiB against the [95,110]
gate, caused by my killing leftover servers while the rig was composing, so GTT fell away from the projected
gtt_before_arm=82G. Recorded rather than hidden because a silenced OOR arm looks identical to a clean one in a
summary line.

Carries defect #12: killing the top-level probe script does not stop the run. pkill -f 315-idprobe killed only
the wrapper; 315-attrib.sh -> 315-paired.sh -> llama-server kept running ~25 more minutes because NOSTOP=1, and was
found only because it pinned the box at 1577% CPU holding tctl at 91C and blocking the ASAN run's gate. Kill the
process group, not the script name - and a leftover can block a later run's gate, not merely slow it.
bong-water-water-bong pushed a commit that referenced this pull request Oct 14, 2026
To run three probes back to back I wrote three scripts each shaped as 'while ps | grep <predecessor>; do sleep; done;
clean; launch the next one'. Three disc sessions then appeared at once, including two copies of 315lout4, and
315chain-run.log was empty - stage 1 never started - while stage 2 launched anyway, because the guard is 'wait WHILE
the predecessor is running', which is satisfied immediately when the predecessor does not exist at all. The guard
answers 'is it running now?' rather than 'did it ever run and finish?'. Two sessions then composed the regime
simultaneously, so neither arm's cell means what its label says and every arm in that window is unusable.

Why this is the same defect one layer up: it is defect #14 with the sign flipped. There, absence of output was read
as a clean result; here, absence of a process is read as completion. Both are a predicate on a proxy standing in for
the event of interest, and both silently proceed. The campaign now has four instances of this shape - #13, where an
EXTRA_ENV named a feature the build does not implement and absence of effect was read as absence of an effect; #14;
#16; and the pkill-on-name traps of #11/#12 where killing the wrapper was read as stopping the run.

Records the fix and what I did instead: the correct guard records evidence of the predecessor's completion via a
sentinel line written after its last arm, and additionally requires that it started, but chaining is not worth that
machinery here, so I abandoned it and ran the stages directly one at a time, verifying each launch by its own log
header before starting the next - slower, but it cannot silently skip a stage, which is the failure mode that cost
two composed regimes and about twenty minutes of box time this session is already over budget on. Cost: two
simultaneous compositions whose arms are discarded, the section-40 chain probe never ran and must be re-issued, and
one duplicate 315lout4 session, with no conclusions drawn from any arm in that window.
bong-water-water-bong pushed a commit that referenced this pull request Oct 20, 2026
…, G8 #12); --parallel on qwen35 family

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
bong-water-water-bong added a commit that referenced this pull request Oct 20, 2026
…ily (#480)

* serve: the placement report covers every graph, not just the warmup

--placement-report passed GGML_HRX_REPORT=1, and the backend's 1 is the
first graph only, so serve printed a clean report for a model that leaves
nodes on the CPU in later graphs: the 20 Oct census found LFM2 --parallel 4
(20 nodes, 5 splits) and ZAYA1-8B (1-3 nodes) reading "all nodes on HRX".

Ask for every graph (2), keep the worst one instead of the last, and print
serve's line again whenever a graph is worse than every graph before it,
with the graph count. --strict-device reads the same worst graph. Without
--placement-report serve keeps 1, so no log volume is added by default.

tests/hrx_placement_test.cpp covers a clean warmup followed by a par4 graph
and a later clean graph.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* serve: --strict-device reads the report it needs

--strict-device only passed GGML_HRX_STRICT=1 to the backend. It did not pass GGML_HRX_REPORT=2 and it
did not add the -lv 4 that lets llama-server's log level carry the placement report through, so the graph
that would have been refused was never reported and serve fell through to "the backend printed no
placement line" while starting normally. Both flags are now treated the same way: GGML_HRX_REPORT=2 and
-lv 4 whenever either is set, so the refusal reads the same worst graph that --placement-report prints.

Found on device: `1bit serve ... --strict-device --parallel 4` (LFM2-1.2B) answered requests instead of
refusing, with that "no placement line" message in its log.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* serve: --parallel on qwen35 / qwen35moe / qwen3next

These architectures were refused with "--parallel on --device hrx does not run <arch> yet (no
multi-sequence gated delta-net on HRX)". That was true when the HRX gated-delta-net matchers held
kMaximumSequences 3; they hold 8 now (llama.cpp 1bit/hrx-gdn-multi-sequence), and a four-sequence run keeps
every node on the device.

Measured on Qwen3.8-27B UD-Q4_K_XL, `--parallel 4`, four concurrent chats: 4 of 4 answered, placement "all
3655 nodes on HRX, 1 graph splits", zero error lines. The forced `-np 1` for these architectures when
--parallel is not given stays: llama-server's own default opens several slots, and that is a different
question from the explicit flag.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Pin llama.cpp cefbfc07 (fork #117, G9) and gpu-kernels 421d7a4e (G7 #11, G8 #12); --parallel on qwen35 family

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* registry: regenerated at llama.cpp cefbfc07

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Pin llama.cpp dcf8c1ec (fork #118: narrower GDN claim) and gpu-kernels 449a809e (#13: v_f16 ABI + f16-V read fixes)

Fixes this PR's first hw-gate run (ops 618/649, Coder-30B HTTP 500).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* registry: regenerated at llama.cpp dcf8c1ec

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: bcloud <bcloud@localhost>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: bcloud <bcloud@1bit.local>
Co-authored-by: bong-water-water-bong <bong-water-water-bong@1bit.gg>
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.

1 participant