Skip to content

feat(bpu): D-Robotics RDK BPU backend with on-board compilation - #69

Open
lvyufeng wants to merge 1 commit into
flagos-ai:mainfrom
lvyufeng:feat/s600-bpu-backend
Open

feat(bpu): D-Robotics RDK BPU backend with on-board compilation#69
lvyufeng wants to merge 1 commit into
flagos-ai:mainfrom
lvyufeng:feat/s600-bpu-backend

Conversation

@lvyufeng

@lvyufeng lvyufeng commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator

Summary

Merges D-Robotics RDK BPU support into torch-fl as ACCELERATOR=bpu — one torch-fl package, no separate torch_bpu repo, no VM dependency.

Key changes

  • Build system: CMake + setup.py recognize ACCELERATOR=bpu, skip CUDA/kernel requirements
  • Runtime: UCP allocator (hbUCPMallocCached) for real device memory, device/stream/event stubs
  • Eager mode: All ops fall back to CPU via cpu_fallback; convolution_overrideable has explicit wrappers (the boxed fallback cannot serve it — it redispatches the same op and lands back on the raising stub)
  • Compile backend: torch.compile(backend="bpu") auto-registered, partitions aten graph → ONNX → hbdk4 → .hbm → hbm_runtime
  • Quantization: Q/DQ insertion is mandatory (hbdk4 lowers float conv to CPU); default scale 0.05, overridable via calibration
  • Weight freezing: Parameters baked into artifact to avoid crossing the boundary on every call
  • Zero-copy: flagos tensors wrap UCP storage in numpy arrays in place (D→H copy eliminated; quantization and output alloc still copy)
  • On-board hbdk4, on the stock kernel: the x86_64 compiler runs under box64. No VM, no cross-compile host, and no kernel rebuild — the earlier conclusion that a 4 KB-page kernel was required turned out to be wrong. The real blocker was box64's version: the packaged 0.2.6 hard-codes a 4 KB page size, while current box64 (0.4+) reads it at runtime and maps 4 KB-aligned x86 segments onto 64 KB pages itself. scripts/setup_bpu_hbdk4.sh automates the setup.

Testing

  • 65 BPU unit tests pass (partition, qdq, decompose, freeze, splice, eager device, zero-copy, x86 env)
  • Eager: elementwise, matmul, reduction, conv2d forward+backward all match CPU
  • Compile: correct graph partitioning, graceful CPU fallback when hbdk4 unavailable
  • Clean process exit (caching allocator deliberately skips block release at exit to avoid a double-free race with libhbucp teardown; _empty_cache() before exit frees the same blocks cleanly, which confirms only the ordering is at fault)
  • ruff check / ruff format --check clean
  • 4 tests in test_profiler_privateuse1.py fail on this board — they need libcupti and real GPU kernel events. That file is untouched by this branch; the failures come from feat(profiler): torch-cuda parity for PrivateUse1 — flows, device time, kernel metadata #55 and are not reproducible without a GPU.

torch-2.13 compat fix (affects all platforms)

PyTorch 2.13 removed named tensors (at::DimnameList no longer exists), but checked-in generated files still declared 16 named-tensor factory variants. This blocked every platform on 2.13. Fixed by adding them to codegen_skip_ops.txt and surgically deleting from the four generated files (pure deletions, 0 additions, verified with git diff --numstat). Did not re-run full codegen: CI pins torch 2.10, and a full regen would bake in 2.13-only signature drift + silently empty flaggems_python_kernels.cc when flag_gems is not installed.

⚠️ This board has no CUDA, so I could not verify CUDA compilation after the generated-file edits. Recommend running CI before merge.

Docs

  • docs/bpu.md: architecture rationale, on-board hbdk4 setup, quantization, calibration, env vars, known limits
  • README.md: BPU build instructions, new env vars

Benchmark

6-layer conv stack @ 224×224, measured on-board with artifacts compiled on-board: 3.75 ms (BPU) vs 72.06 ms (eager CPU) = 19.2×, int8 quantized with ~3% relative error. Toy nets are a wash — submission overhead dominates.

Dependencies

None beyond the board image. libhbucp/libbpu ship at /usr/hobot/lib with headers at /usr/include/hobot, so there is no SDK root to configure. hbdk4 is optional: without it the backend warns and runs every partition on the CPU, so the install stays usable.

@lvyufeng
lvyufeng force-pushed the feat/s600-bpu-backend branch from 57740f7 to 6a676be Compare August 8, 2026 00:17
Merge the RDK BPU support into torch-fl as ACCELERATOR=bpu. This is a
graph-compile backend (torch.compile(backend="bpu")) rather than per-op
kernels: the BPU executes whole .hbm artifacts produced by hbdk4, so there
are no PrivateUse1 operator registrations and eager ops reach cpu_fallback.

Key decisions:
- Eager: real device memory (hbUCPMallocCached) + CPU compute (no per-op BPU
  kernels). Tensors genuinely live in UCP memory so the compile path can be
  zero-copy where dtype matches.
- Compile: on-board via box64-wrapped x86_64 hbdk4. No VM, no separate host,
  and no kernel rebuild — the earlier conclusion that a 4KB-page kernel was
  required turned out to be wrong; current box64 handles 64KB pages itself.
- One torch-fl package: no separate torch_bpu repo. ACCELERATOR=bpu builds
  it, import torch_fl registers the backend.

Build system:
- CMakeLists.txt: bpu branch bypasses CUDA requirements, links Horizon
  runtime (libbpu, libhbucp, libhbdnn).
- setup.py: bpu turns off all kernel sets (CUDA/FlagGems/MetaX/Ascend),
  installs torch_fl/backends/bpu/*.py.
- csrc/runtime/accelerator/bpu/: UCP allocator + device/stream/event stubs.

PyTorch 2.10 pin:
- pyproject.toml + setup.py: require torch>=2.10,<2.11. The checked-in
  csrc/aten/generated/* is version-sensitive; a mismatch surfaces as
  compile errors, not resolver failures.
- torch_fl/codegen_skip_ops.txt: add 16 named-tensor factory variants
  (empty.names, zeros.names, ...) removed in PyTorch 2.13. Delete their
  declarations from the four generated files (pure deletions, verified via
  git diff --numstat). These route to cpu_fallback, which is fine since a
  named tensor cannot live on a custom device anyway.

Convolution registration (csrc/aten/register.cc):
- aten::convolution dispatches PrivateUse1 to convolution_overrideable, and
  the only other kernel is a CompositeExplicitAutograd stub that raises. The
  boxed cpu_fallback cannot help: it moves args to CPU and redispatches the
  *same* op, landing back on the stub. Register explicit wrappers that call
  at::convolution_symint on CPU tensors.

On-board hbdk4 (torch_fl/backends/bpu/compiler.py):
- x86_emulator() probes box64/qemu-x86_64-static with a source-built box64
  as FLAGOS_BPU_X86_EMULATOR. Distro box64 0.2.6 fails; current versions
  handle 64KB pages at runtime.
- x86_env() sets BOX64_LD_LIBRARY_PATH (_mlir_libs for the hbdk4 .so tree),
  PYTHONPATH (stubs for numba/torch — both cause segfaults under box64 but
  are only imported, never executed on the ONNX path), and LD_PRELOAD
  (libhbtl.so with RTLD_GLOBAL — _hbdk.so needs hbtl symbols but doesn't
  list libhbtl in DT_NEEDED).
- _compile_via_x86_emulator() tolerates post-compile AllocError (hbdk4 loads
  the artifact back for validation, which can fail in emulation after a
  complete .hbm is written).
- scripts/setup_bpu_hbdk4.sh: idempotent setup (box64 build, x86 python,
  hbdk4 wheels, stubs).

Zero-copy (torch_fl/backends/bpu/runtime.py):
- _device_view(): wrap a flagos tensor's UCP storage in a numpy array
  in-place (verified: address == data_ptr(), writes through the view are
  visible to torch). No D2H copy inbound. Quantization still copies (dtype
  changes float32->int8), and hbm_runtime.run allocates its own outputs.

Exit-time leak (csrc/runtime/allocator/caching_device_allocator.cc):
- Skip block release on BPU, same as tsingmicro. The allocator is a
  function-local static, so its dtor runs from __run_exit_handlers after
  libhbucp's FINI_ARRAY may have released the heap. Calling
  torch_fl._C._empty_cache() before exit frees cleanly, confirming only the
  ordering is at fault.

Tests (tests/unit/bpu/):
- 8 files, 65 tests covering partition, qdq, decompose, freeze, splice,
  eager device ops (elementwise, matmul, reduction, conv2d forward/backward),
  zero-copy numpy views, and x86 environment construction.
- All BPU tests pass (65); the 4 profiler failures on this board predate
  this branch (they need libcupti and real GPU kernels). ruff clean.

Measured performance: 6-layer conv stack @224x224, 72.06ms eager -> 3.75ms
BPU (19.2x), int8 quantized with ~3% relative error.

Docs: docs/bpu.md covers architecture, quantization, weight freezing,
on-board hbdk4 setup, calibration, environment variables, and known limits.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@lvyufeng
lvyufeng force-pushed the feat/s600-bpu-backend branch from 6a676be to 5d27f3b Compare August 8, 2026 05:21
@lvyufeng lvyufeng changed the title feat(s600): D-Robotics RDK S600 BPU backend with on-board compilation feat(bpu): D-Robotics RDK BPU backend with on-board compilation Aug 8, 2026
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