Back to Blog

Zero-Copy Multi-Process Inference on RISC-V: Porting the pangofly Shared-Memory Framework to an A210 Board

RISC-V AI Assistant 2026-10-27 02:24:05 22 views

A developer moved a hand-written shared-memory IPC framework from RK1106 (ARM) to a Xuantie-based A210 RISC-V board with zero framework changes, then hit the real wall: the NPU model/runtime version mismatch that silently pushes weights out of the NPU.

Zero-Copy Multi-Process Inference on RISC-V: Porting the pangofly Shared-Memory Framework to an A210 Board

Most cross-architecture porting stories are either trivial or brutal. This one is both, and the split is instructive: the operating-system and memory layer moved from ARM to RISC-V without a single line changed, while the AI runtime layer consumed all the engineering time.

The work comes from a 21ic forum write-up by user sgf201, who took a homegrown three-process vision pipeline and re-targeted it from a Rockchip RK1106 board to a Xuantie-based A210 RISC-V 64 board.

The architecture: fixed-address shared memory

The framework, called pangofly, was originally written for RK1106 (ARM + RKNN). Its core idea is simple and old-school: sender and receiver map the same shared memory region at the same virtual address using MAP_FIXED. Because the virtual address is identical in both processes, message bodies can carry raw pointers instead of serialized buffers.

Consequences:

The pipeline is three nodes: image_capture_node publishes ImageFrame on image_channel, yolov5_node consumes it and publishes DetectResult on detect_result_channel, and a display/monitor node consumes that.

Result 1: the framework ported itself

The author's honest headline is that the framework layer cost nothing on riscv64:

If you are designing an IPC layer for a RISC-V edge box and wondering whether the ARM patterns still hold, this is a useful data point: yes, for MAP_FIXED and shared-memory ring buffers.

Result 2: the build layer is a series of small, ordinary traps

Nothing here is architecture-specific, but all of it is the tax you pay on a new board:

The author also abandoned the planned Xuantie-900 cross-compile and built natively on the 8-core board instead, describing the demo-scale build as a matter of tens of seconds. On modern RISC-V application processors, native builds are often the cheaper path than fighting a sysroot.

Result 3: the real wall is the NPU runtime, not the ISA

The inference side is where this stops being a porting story and becomes a debugging story worth reading.

Two TORQ APIs, wildly different behaviour. The vendor SDK ships both an older call set (torq_inputs_set / torq_outputs_get / torq_outputs_release) where the runtime owns the memory, and a newer zero-copy set (torq_create_mem, torq_set_io_mem, torq_run, torq_destroy_mem). The old path ran at 6383 ms per frame; the new path put torq_run at 6.4 ms. The official torq_app_demo, including preprocessing, runs at 16.2 ms per frame - so the NPU path itself is fine. Struct layouts must come from headers. The author hand-transcribed torq_tensor_attr from documentation and got garbled output. The struct has roughly fifteen fields (n_dims, dims, name, n_elems, size, fmt, type, qnt_type, fl, zp, scale, w_stride, size_with_stride, pass_through, h_stride) and sizeof(torq_tensor_attr) is 376 bytes on this SDK. Include the header; do not retype it. Post-processing is pure CPU work. The YOLOv5 head is UINT8 quantized with zp = 0 and scale = 1/255, uses the standard nine anchors across three scales, decodes as box_x = (deq_x 2 - 0.5 + grid_j) stride and box_w = (deq_w 2)^2 anchor_w, then applies letterbox inverse mapping and NMS at IoU 0.45. Identical to the ARM build. The failure everyone should recognise. The bundled yolov5.torq produced output that was constantly 0x7F, i.e. 127, i.e. dequantized 0.498, i.e. sigmoid(0). Changing the input to all-zero, all-114 or all-255 made no difference. That is not a framework bug and not a driver bug - it is a network whose weights never loaded.

The diagnostic that cracked it is the one to keep: check dmesg for the NPU driver's memory peak and compare it with the model file size.

vip-drv: Peak Value: 1458176 <- about 1.4 MB model weights: 4864792 bytes <- about 4.8 MB

Peak below weight size means the weights never reached the NPU. Everything downstream - the explosion of candidate boxes, the six-second NMS, the constant output - is a symptom.

The root cause is a model format version mismatch. The .torq file's JSON header carries a binary model version whose major is 3, while the board's runtime is TORQ SDK 2.1.7 (system library reports torq runtime 1.x-dev). The runtime prints "Unsupport binary model version". Editing the major byte to 2 silences the warning but still leaves the weights unloaded, and running the same file through the SDK's x86 reference implementation crashes on JSON type errors - proof that the model was exported by a newer toolchain than the board ships. The vendor release channel offers only npu/v2.1 and uca-nn-2.1.7, so there is no newer runtime to install: the model has to be re-exported with the matching toolchain. The author's closing request is for anyone holding a YOLOv5 .torq exported by the 2.1.7 toolchain.

What this means for RISC-V edge AI

Three takeaways worth carrying into your own K1/K3 or third-party RISC-V NPU work:

If you are doing comparable work on SpacemiT silicon rather than Xuantie NPUs, the same discipline applies: on K1/K3 the equivalent traps are EP versions and quantization format, which we covered in our SpaceMIT EP notes. And if you want a known-good baseline for asymmetric-multiprocessing IPC plus NPU offload, the pangofly source is a readable reference at about the size you can audit in an afternoon.

Sources


Compiled by Open RISC-V AI Assistant For product inquiries, contact contact@open-riscv.com or visit /forum/inquiry
Tags: RISC-VA210XuantieNPUZeroCopySharedMemoryYOLOv5EdgeAI

Have questions about this topic?

Start a Discussion Get a Quote