BeagleV-Fire quadcopter during an early test flight.
I have been working on a Zephyr-based HAL for ArduPilot and now have ArduCopter booting and flying on a BeagleV-Fire.
This is not upstream yet. Everything below is currently living on my fork:
- Fork: github.com/marcoantonio-ferrera/ardupilot
- HAL source: AP_HAL_Zephyr
The short version is that the HAL is written against Zephyr’s device model and Devicetree aliases rather than board-specific register code. The goal is to make new board bring-up look like board configuration work, not a fresh autopilot port every time.
On the BeagleV-Fire side, Zephyr is running on a U54 hart with no Linux involved. The current setup uses the PolarFire SoC MSS for the CPU-facing side of the flight stack, and FPGA fabric PWM for motor and servo outputs. Sensors are on the MSS I2C bus, and the HAL uses Devicetree aliases to map ArduPilot’s expected peripherals onto Zephyr devices.
Here is the basic alias pattern the HAL expects:
aliases {
ardupilot-serial0 = &uart2;
ardupilot-serial1 = &uart4;
ardupilot-i2c0 = &i2c0;
ardupilot-spi0 = &spi0;
ardupilot-pwm0 = &pwm0;
ardupilot-gpio0 = &gpio0;
ardupilot-rcinput = &uart3;
};
If an alias is not present, that HAL path simply does not come up. That keeps the generic code free of board-specific #ifdef clutter.
What is working today on BeagleV-Fire:
- ArduCopter boots and reaches
ardupilot_entry(). - MAVLink telemetry is running over MMUART2 at 115200 and talks to Mission Planner.
- EKF3 is running.
- IMU, barometer, and magnetometer are up.
- PWM outputs are generated in FPGA fabric.
- Parameters are stored on microSD using FAT32 over SPI.
- Status LEDs are working through
AP_Notify.
One detail that matters on this board is the gateware. The stock BeagleV-Fire gateware does not expose the fabric PWM outputs in the way this setup expects, so this build depends on the ROBOTICS cape variant from this fork:
git clone https://openbeagle.org/marco_ferreira/my-lovely-gateware.git
That bitstream is what exposes the eight PWM outputs on the cape header, routes the expected SBUS input, and matches the I2C and SPI wiring used by the BeagleV-Fire board definition.
Bringing up a new Zephyr target is intentionally small in scope. In practice it comes down to four board-side files:
hwdef.datnaming the Zephyr board target.- A Devicetree overlay mapping
ardupilot-*aliases. - A
.conffragment enabling the needed Zephyr subsystems. - A
hwdef.hwith probe order and board-specific defines.
That is the part I find most promising. If a board already has a Zephyr port and the sensors you need, first boot should be much closer to standard board bring-up than to maintaining a one-off HAL.
Build flow is straightforward once the Zephyr SDK and RISC-V toolchain are installed:
git clone --recursive https://github.com/marcoantonio-ferrera/ardupilot.git
cd ardupilot
export CROSS_COMPILE=riscv-none-elf-
export PATH=/path/to/riscv-none-elf-gcc/bin:$PATH
./waf configure --board beaglev_fire
./waf build
The main outputs are:
build/beaglev_fire/zephyr_build/zephyr/zephyr.elf
build/beaglev_fire/zephyr_build/zephyr/zephyr.bin
For flashing, I followed the standard Zephyr BeagleV-Fire flow with hss-payload-generator. One thing that is easy to trip over: the ArduPilot build entry point is not the same as the default sample app entry point shown in some upstream examples. Before generating the HSS payload, check the ELF header:
riscv-none-elf-readelf -h build/beaglev_fire/zephyr_build/zephyr/zephyr.elf
Use 0x1004000000 as the entry point for exec-addr and u54_4 in the YAML. If those addresses do not match the ELF, HSS jumps to the wrong place and the board looks dead.
There is more detail in the HAL README, including a worked example for another Zephyr target:
- HAL README: libraries/AP_HAL_Zephyr/README.md
I am posting this mainly because I think the Zephyr route opens up some interesting development options for ArduPilot, especially on boards that already have decent Zephyr support but would otherwise need a much heavier porting effort.
I would genuinely love feedback on the approach, the implementation, or the current board bring-up model. If anyone is interested in collaborating, testing on other Zephyr-supported boards, or just comparing notes, please feel free to reach out. I would be very happy to work with anyone who wants to help push this further.
This is still prototype code, not an upstream-supported target. I would treat it accordingly: bench-test everything, assume rough edges, and do not fly it anywhere you cannot afford a bad failure mode.

