Skip to the content.

The Orange Pi RV2 is built on the SpaceMiT K1 SoC: eight SpacemiT X60 cores at ~1.6 GHz (rv64gcv, RVV 1.0, VLEN=256), integrated IMG BXE-2-32 GPU (OpenCL 3.0 / Vulkan 1.3), 8 GB RAM.

Video walkthrough

all videos

Compute paths on this board

The K1 SoC exposes all four paths we benchmark: scalar baseline, RVV OpenBLAS, IME int4 matrix ops (cluster 0), and an integrated GPU.

Compute backends on the Orange Pi RV2

IME (Integer Matrix Extension)

Besides RVV, SpaceMiT’s IME is a dedicated int8 matrix unit via the custom instruction smt.vmadot. One vmadot fuses a 4×4 int32 tile update from two 4×8 int8 operand tiles; this is the hardware behind the part’s quoted AI TOPS rating.

On the K1 the IME sits in cluster 0 only (cores 0–3), with those four cores sharing a 512 KB L2 — cluster 1 (cores 4–7) has no IME. Pin IME workloads to a cluster-0 core (taskset -c 0).

Toolchain support (xsmtvdot)

On X60 the usable IME subset is the vendor extension xsmtvdot / XSMTVDot (SpacemiT Vector Dot Product v1.0) — mnemonics like smt.vmadot. There are no C intrinsics and no auto-vectorization; every real user emits the instruction via inline assembly.

Toolchain First version with xsmtvdot What it gives you
LLVM / Clang ≥ 22 (PR #151706) Assembler recognizes smt.vmadot with -march=…_xsmtvdot
GCC ≥ 16 (spacemit-x60 core includes …_xsmtvdot) -march / -mcpu=spacemit-x60 know the extension
Binutils (gas) ≥ 2.46 Named smt.* mnemonics in the assembler

EESSI foss-2025b on this board is still GCC 14.3 + binutils 2.44 — neither understands smt.vmadot. We get IME working anyway with two patterns (both in benchmarks/ime):

  1. Raw encodings (no special assembler)ime-bench emits smt.vmadot as a .insn / raw word (0xe200302b|… with register fields filled in). Stock binutils 2.42 on the RV2 assembles it; any RVV-capable as is enough.
  2. Named mnemonics + patched binutils — llama.cpp’s SpaceMiT backend uses inline asm. Upstream ggml still wrote bare vmadot; our patch (llama.cpp-x60-ime-upstream-binutils.patch) renames those to smt.vmadot, injects .option arch,+xsmtvdot, and gates IME2 sources (X60 is IME1-only). We deploy a standalone binutils-2.46.1-xsmtvdot and point GCC at only that as via -B…/ (symlink in a private dir) so linking still uses the toolchain ld — see the EasyBuild recipe comments in the same directory. Without that assembler, the CMake FindSMTIME probe fails and the IME backend never builds.

Same hardware and toolchain story apply on the Banana Pi F3 (identical K1 / X60 silicon).

Microbenchmarks in opensolvers/benchmarks/ime (ime-bench): pure s8s8s32 GEMM, bit-exact vs a scalar reference, timed against a plain RVV int8 baseline on this board (core 0, 1.6 GHz):

M×N×K RVV int8 IME (smt.vmadot) IME / RVV
512×512×512 5.2 GOP/s 39 GOP/s 7.5×
768×768×512 ~5.2 GOP/s 42 GOP/s (peak) 8.1×
1024×1024×512 5.2 GOP/s 32 GOP/s 6.2×

Peak ~42 GOP/s single-core — vs ~5 GOP/s for a straightforward RVV int8 path. End-to-end ORT decode (Qwen / SmolLM2 / TinyLlama int4+int8) through ONNX Runtime and isolated MLAS kernel rates use the same IME hardware; see also papers/x60-ime-block-scale-optimization in the benchmarks repo.

End-to-end llama.cpp: 10/10 Q4_0 models (0.5B–7.6B) validated — IME wins prefill ≥1.1B (up to ~2.5×), RVV wins token-gen. Q8_0 hybrid restores decode (6.68 vs 0.83 tg32 @ t4) at ~2× weight RAM. Staging fork: opensolvers/llama.cpp branch x60-ime-rvv.

IME1 scale-build prefill optimization (llama.cpp)

llama.cpp’s block-scaled Q4_0 kernel (gemm_kernel_i8i4) pays a per-block FP scale tax (~31–37% vs raw s8s8s32). Patch llama-ime1-scalebuild-opt.patch rebuilds As×Bs scales with vfmul.vv (LOAD_SCALE_4x16_FP16_OPT) instead of the masked vfmul.vf chain.

Isolated interleaved A/B on this board (30 rounds, cluster-0 pinned, performance governor, pp512 = 512³) — benchmarks/ime:

  stock scaleopt
median GOP/s 22.50 23.47
range 22.4–22.7 23.2–23.6

+4.3% (+0.97 GOP/s); scaleopt faster in 30/30 rounds; zero overlap; paired t = 50.3. Bit-exact (sum/sumsq/max identical across three shapes).

The kernel win is real; end-to-end llama-bench pp512 on this multi-tenant part is not resolvable above ±15–20% noise (see the paper linked above). Decode (tg) uses the untouched M1/GEMV path.

GPU (PowerVR BXE-2-32) — compute closed

The on-die Imagination PowerVR BXE-2-32 (BVNC 36.29.52.182) is compute-capable on paper — OpenCL 3.0 + Vulkan 1.3, and /sys/kernel/debug/pvr/status shows a live CDM (Compute Data Master). Firmware (rgx.fw.36.29.52.182) loads. Source: benchmarks/gpu.

Vendor userspace is a dead end. SpaceMiT’s shipped DDK (libVK_IMG, libPVROCL, libGLESv2_PVR_MESA) embeds only "B-Series BXM-4-64" — none of the blobs know BXE. Measured on this board:

API Result
OpenCL (libPVROCL) Gates on BVNC → refuses 36.29.52.182
Vulkan (libVK_IMG) vkCreateInstanceVK_ERROR_INCOMPATIBLE_DRIVER (−9) after probing /dev/dri/renderD128
GLES 3.1 compute Same DDK / same BXM allowlist

No rebuild path: the CL/VK/GLES runtime and USC shader compiler are proprietary; every public img-gpu-powervr package is the same 24.2@6603887 BXM blob. Graphics rendering works; GPGPU on our SKU does not.

Open stack (deferred). The only self-buildable route is Mesa pvr + mainline drm/imagination — a different kernel uAPI and firmware format (powervr/rogue_36.29.52.182_v1.fw exists on freedesktop). That needs a rebuilt K1 kernel (backport onto 6.6.63-ky; no vendor/Armbian/OpenWrt tree ships the open driver). Explicitly not switching this board’s kernel for now. Even on full success the GPU ceiling is ~20 GFLOPS FP32 — roughly 50–100× below 8× X60 RVV / IME for our int8 LLM GEMM.

Bottom line: GPU compute via the vendor stack is closed. Useful accelerators on this SoC remain RVV and IME (above). Same silicon on the Banana Pi F3.

HPL via EESSI

See also the HPL app overview.

Unlike the scalar VisionFive 2, the X60 already dispatches OpenBLAS’s upstream RVV RISCV64_ZVL256B kernels from the stock EESSI stack — but OpenBLAS 0.3.30 has a bug in gemv_n that zeroes an uninitialized vector register, so stock EESSI HPL fails with residual nan (it can still report a plausible ~8.5 GFLOP/s; only the residual check reveals the answer is wrong).

End-to-end on real Orange Pi RV2 hardware using EESSI 2025.06-001 on dev.eessi.io/riscv. Peak run: N=20000, NB=256, 2×4 grid (8 MPI ranks).

  Before After
HPL (8 cores, N=20000, 2×4) ~8.5 GFLOP/s, FAILED (nan) 10.53 GFLOP/s, PASSED
Residual (N=8000, 1×8) nan 4.04e-03

With the fixed backend, scalar-vs-RVV A/B (benchmarks/hpl): 6.41 → 11.55 GFLOP/s (N=8000, 1×8) and 7.38 → 13.41 GFLOP/s (N=28672, 1×8).

Before — stock EESSI OpenBLAS 0.3.30 (RVV gemv_n bug). After — fixed OpenBLAS built with TARGET=RISCV64_ZVL256B and a backported gemv_n patch (easyconfigs#26444), swapped in via FlexiBLAS — no HPL rebuild.

The fix backports the upstream gemv_n correction from OpenBLAS ≥ 0.3.31 (OpenBLAS#5408). OpenBLAS 0.3.34 fixes this natively — verified end-to-end on RV2 (BLAS: dgemv NaN 0, SYRK/CTRSM PASS, DGEMM 15.54 GFLOP/s @ t8; HPL: 11.04 / 10.97 GFLOP/s PASSED on HPL.dat / HPL-sweep). A future EESSI bump to ≥ 0.3.34 should make the manual patch unnecessary.

Reproducing the fixed run

  1. Set up CVMFS + EESSI on riscv64 (EESSI_VERSION_OVERRIDE=2025.06-001).
  2. Baseline: module load HPL/2.3-foss-2025b → stock HPL fails residual check (nan).
  3. Build fixed OpenBLAS: eb --from-pr 26444 --robot (via EESSI-extend user install, EASYBUILD_OPTARCH='-march=rv64imafdcv_zvl256b').
  4. Register the new backend with FlexiBLAS and re-run the same xhpl.

Full walkthrough: EESSI blog — Chasing a NaN: correct RVV HPL on a RISC-V SpaceMiT X60.

BLIS vs OpenBLAS

See BLIS — FLAME BLIS rv64iv with hand-written RVV assembly vs patched OpenBLAS on the same bench_dgemm.c. Single-thread DGEMM at N=4096: 2.95 vs 2.28 GFLOP/s (1.29× BLIS); 8-thread OpenBLAS still leads at large N. TRSM: 2400 cases, 0 fails on BLIS.

End-to-end HPL on BLIS: all PASSED, but 0.35–0.53× OpenBLAS-RVV (best full-memory 5.87 GFLOP/s at N=25600, 2×4) — the DGEMM win does not carry to Linpack.

FFTW RVV

See FFTW — r5v wins 1.06–1.60× in tests/bench, but ~0% end-to-end in Quantum ESPRESSO (FFTW_ESTIMATE).

GROMACS

See GROMACS — two axes on this board:

Axis Result
FFT (libfftw3f swap) 1.23× on isolated PME 3D-FFT; ~0% whole-app (Force = 90%)
Force (rvv-backend/, SIMD: RISCV_RVV) 4.38× on Force, 3.31× whole-app (0.380 → 1.256 ns/day)

LAMMPS

See LAMMPS — RVV-Kokkos whole-app MD (five upstream bench/ workloads, 32000 atoms / 100 steps):

Regime Best back-end Peak vs serial
Compute-bound pair (lj, eam) Kokkos/OpenMP (8 threads) 6.20× / 7.21×
Comm / bonded-heavy (chain, chute, rhodo) MPI (8 ranks) 5.67× / 4.90× / 5.94×

Speedups are parallel scaling on one RVV-vectorized binary (not RVV-vs-scalar). Pair force ≈ 84% of lj wall. Hand RVV Pair: eam/rvv 1.27× vs stock; LJ in-app ~1.02× — see Kokkos.

Kokkos

See Kokkos — portable OpenMP/Serial for LAMMPS; no RVV SIMD backend in 4.6.2; hand RVV LJ microbench ~1.64×, EAM plugin 1.27×.

GCC mtune

See GCC — EasyBuild-facing SpacemiT X60 patches for stock 14.3.0 and 15.2.0; A/B vs -mtune=generic-ooo:

Probe 14.3 15.2
Canaries fma_chain / div_mix −5.0% / −6.7% −8.7% / −7.7% ns/call
OpenBLAS DGEMM N=512–2048 −3…−7% +2.2–3.8% GF/s
HPL N=3000 +6.8% +0.8% (both PASSED)

Local proof only — not an EESSI PR yet.

OpenFOAM

See OpenFOAM — motorBike simpleFoam (4 ranks): GCC auto-vec ~0%; hand RVV Amul ~50% slower (whole solve ~3–4% worse); GS RVV mild regression. Sparse gather on short rows.

waLBerla

See waLBerla — contiguous auto-vec campaign:

Probe Result
BasicLBM ISA-tag (rv64gcrv64gcv) ~1–4%
HeatEquation Jacobi np1 1.64×
UniformGrid --not-fused collide 1.54× (WALL 1.30×)
SoA auto-vec vs hand simd::double4_t ~ favour auto-vec

PETSc

See PETSc — FlexiBLAS A/B + hand RVV SpMV on overlay PETSc/3.24.0-foss-2025b:

Probe Result
Jacobi-CG AIJ (n=400) patched ~1.06× vs scalar
Dense MatMult n=2048 patched ~1.70×; stock RVV NaN
SuperLU_DIST / UMFPACK stock RVV NaN; patched finite
MUMPS 2D/3D (small) finite on stock; little patched speedup
MUMPS 3D n=80 (512k) RVV ~1.5× vs scalar
Hand RVV CSR SpMV no win vs scalar CSR / trails MatMult
Structured 5-pt stencil RVV ~3.6× vs PETSc MatMult

FlexiBLAS language / ML stack (2026-08-22)

Probe Result
NumPy DGEMM / EIGH patched 3.13× / 1.61×; stock eig fails
Armadillo DGEMM 1.82×, EIG 1.63×
R GEMM 1.80×, EIGEN 1.45×
scikit-learn PCA 1.22×, Ridge 1.90×
MODFLOW ex-gwf-lgrv-lgr FlexiBLAS ~1.00× (flat)

Soft-matter / Coulomb / MPI (2026-08-21)

Probe Result
ESPResSo P3M FFT r5v ~1.12×
ScaFaCoS P3M FFT r5v ~0.99×
MetalWalls tip4p FFT/BLAS ~1.00–1.02×
PLUMED SPRINT patched RVV 1.25×
Voro++ auto-vec ~0.99× (negative)
OSU on-node MPI 1.12 μs @ 1 B; ~2 GB/s uni BW

ScaLAPACK

See ScaLAPACKPDSYEV on 2×4 grid: stock RVV hangs; patched RVV 107.23 s vs scalar 116.87 s (1.09×).