This repo’s main demo is Linux ↔ R5F0_0 RPMsg (at the repo root: LINUX_SIDE/, R5_SIDE/, SHARED_CODE/). Additional PRU/RTU demos live under extra-examples/.
For the examples to work, use a recent BeagleBoard.org Debian image with a newer TI 6.12 kernel (v6.12.x-ti). R5 remoteproc stop/restart needs the graceful RP_MBOX_SHUTDOWN / ACK path in that kernel — older kernels may not have this.
Recommended images:
- eMMC flasher: BBAI64 Debian 13.3 2026-02-12 Minimal Flasher (v6.12.x-ti)
- SD card / runtime: BBAI64 Debian 13.5 2026-05-19 Minimal (v6.12.x-ti)
- To test quadrature encoder reading, connect IO P8_33<-->P8_34 and P8_35<-->P8_36.
- To see PWM work, connect an LED to P9_25.
- To see SPI7, connect a logic analyzer to P9_28 (CS), P9_31 (CLK) and P9_30 (MOSI).
- To try the R5 UART polling self-test, connect P9_16 (UART6_TX) to P8_28 (UART8_RX).
- For PRU LED extras, connect an LED to P8_11 (see
extra-examples/). - SCROLL DOWN BELOW for build and execution instructions
Build, flash, and run steps are below under Setup board and Setup and build.
- Remote Processor Resource Table Initialization: Remote-proc resource table with trace log.
- FPU Initialization for R5 Core: TI AM64 sdk code...
- MPU and Cache Configuration: TI AM64 sdk code...
- Exception and Interrupt Handling: TI J721e SDK/PDK exception/interrupt handlers.
- PWM Signal Generation: Flashing LED on pin P9_25.
- RPMsg: Basic Linux–R5 communication with RPMsg, including cross-core function calling.
- R5 SPI output: SPI7 transfers on P9_28 (CS), P9_31 (CLK), P9_30 (MOSI).
- R5 EQEP Encoder Reading: Reading quadrature encoder EQEP_1 from R5 core.
- R5 GPIO: Shown with quadrature encoder simulation and bit-banged SPI.
- R5 UART TX, and polling RX: P9_16 (UART6_TX) to P8_28 (UART8_RX) polling example.
- pru0_0-hello — remoteproc trace hello-world
- pru0_0-rpmsg-led — blink-count on P8_11 via
rpmsg_char - rtu0_0-pru0_0-rpmsg-led — RPMsg on RTU, blink on PRU via shared DMEM
- Shared Memory: Linux-R5 memory sharing (
SHARED_CODE/include/shared_mem.h,SharedMemoryRegion* sharedMem). Warning: 16-bit aligned reads/writes required to avoid crashes; standardmemcpy()will crash.
- GPIO Linux: Tested and working with
gpiodlibrary, need to write nice example code. - SPI Linux: Tested, working; need to write nice example code.
- UART Linux: Tested, working; pending nice example code, symlink bug fix for 6.12 firmware.
- I2C Linux: Tested, working; need to write nice example code.
- CAN Bus: No progress.
- R5 I2C: Planned, stuck on interrupt routing issues (
R5_SIDE/r5_code/include/io_test_functions/i2c_tests.h). - R5 SPI reading: Have not attempted yet.
If anybody wants to contribute random stuff, please do.
- Flash eMMC with the Debian 13.3 Minimal Flasher (v6.12.x-ti)
- Flash an SD card with the Debian 13.5 Minimal runtime image
- Power cycle the board several times
- Run
df -hto confirm you are booting from the SD card - Update packages:
sudo apt update && sudo apt-get dist-upgrade -y - Run
sudo systemctl mask serial-getty@ttyGS0.serviceto work around a bug where the UART debug login sometimes does not appear - Compile and install
custom_overlays/our-custom-bbai64-overlay.dtso(see Device tree info) - Add the overlay
/overlays/our-custom-bbai64-overlay.dtboto/boot/firmware/extlinux/extlinux.conf - Power cycle the board several times
- Verify the overlay is loaded:
sudo beagle-version | grep UBOOT - Check pin muxing:
sudo ./scripts/show-pins.pl - Optional (Linux SPI only):
sudo modprobe spidev— the main demo does SPI from the R5, not Linux - Connect EQEP_1 loop wires: P8_33↔P8_34 and P8_35↔P8_36
All builds run inside Podman or Docker using two images:
| Image | Used for |
|---|---|
localhost/debian13-bbai64-build:latest |
Linux aarch64 cross-build (gpiod v2) |
localhost/ti-bbai64-build:latest |
R5 firmware, TI SDK, PDK libraries |
See docker/README.md for details on why two images are used.
Requirements: Podman or Docker on the host. No host-installed cross-compilers required.
Processor SDK RTOS 11.02.01.03 for J721E, plus the PRU Software Support Package:
# Downloads ~3 GB SDK to ~/ti, builds PDK debug+release libs, and fetches/builds PSSP
./scripts/build.sh --setupManual SDK URL if needed:
https://dr-download.ti.com/software-development/software-development-kit-sdk/MD-bA0wfI4X2g/11.02.01.03/ti-processor-sdk-rtos-j721e-evm-11_02_01_03.tar.gz
After extract, the SDK lives at:
~/ti/ti-processor-sdk-rtos-j721e-evm-11_02_01_03/pdk_jacinto_*
PDK libraries used by this project (.aer5f extension), under both debug and release profiles:
~/ti/ti-processor-sdk-rtos-j721e-evm-11_02_01_03/pdk_jacinto_*/packages/ti/[LIBRARY]/lib/j721e/...
PSSP is cloned to ~/ti/pru-software-support-package/ (next to the SDK) and lib/rpmsg_lib.lib is built there.
# After --setup: build everything (main demo + extras)
./scripts/build.sh --all
# Main demo (Linux + R5); optional side filters
./scripts/build.sh --main
./scripts/build.sh --main --linux
./scripts/build.sh --main --r5
# Extra examples (PRU / RTU); optional side filters
./scripts/build.sh --extras
./scripts/build.sh --extras --pru
./scripts/build.sh --extra pru0_0-hello
./scripts/build.sh --extra rtu0_0-pru0_0-rpmsg-led --rtu
# Release-flavored application + matching PDK release libs
./scripts/build.sh --all --release
# Clean artifacts
./scripts/build.sh --clean --all
./scripts/build.sh --clean --main
./scripts/build.sh --clean --extras --pruMain demo outputs: build/linux/LINUX_SIDE_aarch64, build/R5_0/r5f_r5f0_0.elf.
Extra-example firmwares: build/extra-examples/<demo-name>/. See extra-examples/README.md.
BUILD_MODE affects compiler flags for both Linux and R5 application sources, and for R5 also selects the matching PDK library profile for most drivers:
debug(default):-Og -g3for Linux,-g3 -Ogfor R5, links PDK debug.aer5flibs where availablerelease:-O3 -DNDEBUGfor both, links PDK release.aer5flibs
Note: TI only builds sciclient and IPC for mcu2_0 under the release profile, so those two always link from .../release/ even in a debug app build.
--setup / --build-pdk build both PDK profiles so either mode can link. --setup also fetches PSSP and builds rpmsg_lib.
There is no supported x86 local-run build for this example app. The Linux build target is the BeagleBone aarch64 binary.
./scripts/build.sh --build-pdkUse a custom SDK location:
./scripts/build.sh --r5 --ti-sdk-dir /path/to/tiThe SDK directory must be writable by your user (required for --fetch-sdk) and must contain the extracted SDK folder (do not use symlinks that point outside the mounted directory).
sudo chown -R "$(id -u):$(id -g)" ~/ti./scripts/compile_and_push.sh --ssh [SSH_DEST]SSH_DEST is an SSH host alias from ~/.ssh/config (e.g. bbai64) or user@host (e.g. kevinc@192.168.7.2).
sudo [SCRIPT_DIR]/debug_run.sh (RUN FROM BOARD, NOT YOUR DEV MACHINE!)
On a newer TI 6.12 kernel, remoteproc stop is not a hard halt: Linux sends RP_MBOX_SHUTDOWN, waits for RP_MBOX_SHUTDOWN_ACK, then expects the R5 to enter WFI. This firmware implements that path. If stop times out with can't stop rproc: -16, the core never ACKed (or the board kernel is too old).
Readiness for reconnect is simple: Linux watches remoteproc sysfs (running), waits a short grace period, opens RPMSG, and pings once. When R5 goes away, the client abandons the fd without RPMSG_DESTROY_EPT (avoids a known 6.12 rpmsg_char race). There is no shared-memory lifecycle handshake.
R5 and Linux restart independently.
# Combined session (tmux LINUX_AND_R5):
sudo ./scripts/debug_run.sh # launch + attach
sudo ./scripts/debug_run.sh restart-r5 # R5 only
sudo ./scripts/debug_run.sh restart-linux # Linux only
sudo ./scripts/debug_run.sh status
sudo ./scripts/debug_run.sh stop-session # tmux only; leaves R5 running
# Or control each side directly (also fine while the combined session is up):
sudo ./scripts/debug_r5.sh start|stop|restart|status
sudo ./scripts/debug_linux.sh start|stop|restart|statusQuick verify: idle debug_r5.sh start → stop → start → stop with status showing offline and no -16 in dmesg. Trace should show SHUTDOWN_ACK status=0. Linux can stay running across those R5 restarts.
- Copy the overlays from our
custom_overlays/folder to/opt/source/dtb-6.12-Beagle/src/arm64/overlayson the board. - Run
git pullfrom/opt/source/dtb-6.12-Beagleto get latest source. - To compile overlays source on board run
sudo make cleanandsudo makefrom/opt/source/dtb-6.12-Beagle. - To install the overlays, from
/opt/source/dtb-6.12-Beaglerunsudo make arm64_install. - On the board, in the config file
/boot/firmware/extlinux/extlinux.conf, replace the line#fdtoverlays /overlays/<file>.dtbowithfdtoverlays /overlays/our-custom-bbai64-overlay.dtbo - Connect to debug uart and have fun. If things go bad you'll be down for some trial and error. You may end up re-flashing your board several times to recover it...
If your boot works correctly, you will see the following in your debug uart output on boot:
Retrieving file: /ti/k3-j721e-beagleboneai64.dtb
Retrieving file: /overlays/our-custom-bbai64-overlay.dtbo
## Flattened Device Tree blob at 88000000
Booting using the fdt blob at 0x88000000
Working FDT set to 88000000
Loading Device Tree to 000000008ffde000, end 000000008fffffff ... OK
Working FDT set to 8ffde000
- Alternatively, you may run
custom_overlays/setup_dtb.shon the AI64 to do the overlay install automatically. Warning:setup_dtb.shreplaces/boot/firmware/extlinux/extlinux.confon the board — there is potential for breakage.
-
You could make use of https://www.ti.com/tool/download/SYSCONFIG to figure out pin muxing when making your own overlays.
-
v6.12.x-Beagle/src/arm64/ti/k3-j721e-main.dtsi is a very important file. If you want to use some IO device that is not defined in here, you will have to dig into the TDA4VM TRM and write your equivalent fanciness in your own overlay. Defining power-domains, clocks, so on. For example for eqep...
-
To figure out which SoC pad numbers go with which BB header pins, look at columns A and B in the following spreadsheet. To figure out mux modes, look at row 10. https://drive.google.com/file/d/15NLaUeMBy-iT8s6rFrP4Esf0Qh57T4xu/view?pli=1
-
To figure out the addresses of SoC pads, look at table "Table 5-125. Pin Multiplexing" in the TDA4VM Processors datasheet https://www.ti.com/lit/ds/symlink/tda4vm.pdf?ts=1741890214437&ref_url=https%253A%252F%252Fwww.ti.com%252Fproduct%252FTDA4VM
From the spreadsheet we see that pad AC22 is the first pad on BB header pin P9_22. AC22 is thus known as P9_22a. The alternative pad, U29, is known as P9_22b. From Looking at the TDA4VM datasheet, we see that AC22(aka P9_22a) has the address 0x00011C09C. For the J721E_IOPAD() pin-mux macro, we need the bottom 12bits of the address, so 0x09c, or 0x9c.
To mux AC22(aka P9_22a) to be SPI6_CLK, as figured out from the spreadsheet, we need to set AC22 to mux-mode 4. And since
a SPI clock is an output signal, the pin should be put set to PIN_OUTPUT mode.
So J721E_IOPAD(0x9c, PIN_OUTPUT, 4)
We will also need to disable the second SoC pad that shares the same BB header pin. The SoC pad known as pin P9_22b, or
pad U29. Using the same process as walked through with pad AC22, you get the following:
J721E_IOPAD(0x170, PIN_DISABLE, 7)
So to mux SPI6_CLK on BB pin P9_22:
&main_pmx0 {
whatever_name_you_feel: and-so-on-pins {
pinctrl-single,pins = <
J721E_IOPAD(0x9c, PIN_OUTPUT, 4) /* AC22, aka P9_22a */
J721E_IOPAD(0x170, PIN_DISABLE, 7) /* U29, aka P9_22b */
/* And so on for other pins... */
>;
};
};Side Note: You can use TI SysConfig for Pin Muxing
TI SysConfig can aid the process of configuring pin muxing, but it comes with a couple of limitations you should be aware of when using it:
-
Conflicting Pads: TI SysConfig does not automatically disable conflicting pads. These are SoC pads that share the same physical header pins on the BeagleBone as the pads you’re configuring. If conflicting pads remain enabled, there is potential for erratic behavior. Realistically, you'll be fine in most cases.
-
Signals with several pad options: Make sure to explicitly select the correct SoC pad for each pin. Some signals have multiple SoC pad options. Normally only one of these options will go to the BB header. For example, with UART4_RXD, there are 3 options, pads AG28, P24, and W23. Ti SysConfig will by default pick the pad P24. Of these 3 pad options, AG28 is the only pad connected to the BB header. Look back at the pin mux spreadsheet linked above to figure out which SoC pads connect to which pins.
-
Default Pin Direction: TI SysConfig sets all pin muxes to "PIN_INPUT" by default, even for pins that should be outputs. For example, for the SPI6_CLK pin config (which should be an output), TI SysConfig will by default generate
J721E_IOPAD(0x170, PIN_INPUT, 7)instead of the correctJ721E_IOPAD(0x9c, PIN_OUTPUT, 4). PIN_INPUT gives the pin both RX and TX perms. PIN_OUTPUT only gives TX.
You can debug using OpenOCD with GDB. With the right setup, graphical debugging in VS Code is also possible (see the video link below).
Some useful links:
- Very helpful video https://www.youtube.com/watch?v=n3u3QgnAvV8
- Debug probe setup / related info: https://nmenon.github.io/k3ocd/#j721e-beaglebone-ai64
- OpenOCD config is here https://git.beagleboard.org/beagleboard/beaglebone-ai-64/-/issues/31
- OpenOCD and GDB setup and install https://u-boot.readthedocs.io/en/latest/board/ti/k3.html#common-debugging-environment-openocd
- https://forum.beagleboard.org/t/debugging-options-for-bbai64/33583/5
- https://forum.beagleboard.org/t/minimal-cortex-r5-example-on-bbai-64/32443/10
-
Setup WSL Debian https://www.microsoft.com/store/productId/9MSVKQC78PK6?ocid=pdpshare
-
Install usbipd from PowerShell:
winget install usbipd -
Use the usbipd CLI or the VS Code extension to connect the debug probe to WSL: https://marketplace.visualstudio.com/items?itemName=thecreativedodo.usbip-connect
After successful forwarding to WSL you should see the following when you run
lsusb:kevin@computer-name:~/openocd$ lsusb Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub Bus 001 Device 002: ID 0403:6010 Future Technology Devices International, Ltd FT2232C/D/H Dual UART/FIFO IC Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub -
Install dependencies
sudo apt-get install libtool pkg-config texinfo libusb-dev libusb-1.0.0-dev libftdi-dev libhidapi-dev autoconf automake usbutils
-
Install and configure OpenOCD
git clone https://github.com/openocd-org/openocd.git openocd cd openocd git submodule init git submodule update ./bootstrap ./configure --prefix=/usr/local/ make -j`nproc` sudo make install
-
Start OpenOCD
From the OpenOCD
tcl/directory (e.g.~/openocd/tcl), run:sudo ../src/openocd -f /path/to/ti_bbai64.cfg
OpenOCD will print the GDB server ports (one per core).
If you get
Error: Invalid ACK (0) in DAP response, reseat the JTAG connection.
Note: I have not done this yet, these instructions might be a little off.
-
Install dependencies on BeagleBone
sudo apt-get install libtool pkg-config texinfo libusb-dev libusb-1.0.0-dev libftdi-dev libhidapi-dev autoconf automake
-
Install and configure OpenOCD on BeagleBone
git clone https://github.com/openocd-org/openocd.git openocd cd openocd git submodule init git submodule update ./bootstrap ./configure --enable-dmem --prefix=/usr/local/ make -j`nproc` sudo make install
-
Copy the udev rules to the correct system location on BeagleBone
sudo cp ./contrib/60-openocd.rules ./src/jtag/drivers/libjaylink/contrib/99-libjaylink.rules /etc/udev/rules.d/ sudo udevadm control --reload-rules sudo udevadm trigger
Make sure BeagleBone bootloader firmware version is 8.6.3 or greater. This can be checked
with sudo k3conf dump processor.
From the OpenOCD tcl/ directory (e.g. ~/openocd/tcl), run:
sudo ../src/openocd -f ./board/ti_j721e_swd_native.cfgOpenOCD will print the GDB server ports.
Connect to the GDB server for a core on local port #### (OpenOCD prints which core is on which port):
gdb-multiarch \
-ex 'target extended-remote localhost:####' \
-ex 'set arch armv7' \
-ex 'file ~/PATH_TO_ELF/r5f_r5f0_0.elf'WARNING, The ELF passed to -ex 'file ...' must match the ELF currently running on that core — GDB needs its debug info. Do not mix an on-board build with a differently built ELF from your development machine.
The debugger can only connect while the core is on and not halted/crashed. After a crash you may need to restart the R5 via remoteproc from Linux; keep a known-good firmware on hand for reset.
On boot the R5 cores start halted, so OpenOCD/GDB cannot attach until firmware has been started.
Graphical debugging is possible; VS Code setup is lightly covered in this video: https://www.youtube.com/watch?v=n3u3QgnAvV8.
| Command | Description |
|---|---|
sudo k3conf show clocks |
Displays all clock information. |
sudo k3conf dump clocks <device ID> |
Check status of clocks for device. |
dmesg | grep -i "reserved mem" |
Shows memory mapping information from logs. |
sudo cat /proc/iomem |
More memory mapping info. |
sudo beagle-version | grep UBOOT |
Displays loaded device tree overlays. |
ls /sys/devices/platform/bus@100000/ |
Platform devices visible to Linux |
dtc -I fs /sys/firmware/devicetree/base > dt.txt |
For dt debugging |
sudo journalctl -k |
View kernel logs |
sudo dmesg |
View kernel logs |
- Pin Mappings: Pin mapping spreadsheet.
- Device and Clock IDs: TI documentation detailing device and clock IDs.
- TDA4VM Processor Page: Official TI page for the TDA4VM processor.
- TDA4VM TRM: Technical Reference Manual for the TDA4VM.
- TDA4VM datasheet: Useful for SoC pad/pin stuff.
- Cortex R5 TRM: Technical Reference Manual for the Cortex R5.
- TI RTOS SDK Documentation: Overview of the TI RTOS SDK.
- TI PDK Documentation: Links to API guide and user guide (SDK 11.02.01.03).
- Processor SDK Linux Software Developer’s Guide: Yet another source of documentation.
- UBoot documentation for the board: How booting works.
- IPC for J721E: J721e sdk documentation explaining how IPC works.
- IPC workings explanation: AM64X pdk documentation explaining how IPC works.
- TI AM64x MCU+ SDK Documentation: Sometimes you can find gems here.
- Flashing eMMC: Forum guide on how to flash the eMMC on the BeagleBone AI-64.
- More on Flashing: Additional forum discussion on flashing-related issues.
- K3 OCD Guide: A guide on using OpenOCD for debugging on the BeagleBone AI-64.
- Debugging Options Forum Thread: A discussion on various debugging options available for the BeagleBone AI-64.
- Debugging Tutorial: A video tutorial covering debugging techniques.
- OpenOCD Debug Setup: OpenOCD and debug hardware configuration.
-
This guy is doing PRU and DSP stuff https://github.com/loic-fejoz/beaglebone-ai64-tutorial
-
Using SDK10 with kernel 6.6 for R5 (These guys are using the TI build system) https://forum.beagleboard.org/t/bbai64-now-can-use-ti-sdk10-0-and-debug-r5/39459
-
Zephyr (currently work in progress) https://docs.zephyrproject.org/latest/boards/beagle/beaglebone_ai64/doc/index.html
- The PRU Development Kit: TI’s PRU software support package for development.
- ti-rpmsg-char: TI rpmsg-char utility library with example code. (The rpmsg_char source we are using on the linux side was taken from here)
- Beagle Images: Release images.
- Random Beagle Images: Random images.
This repo was started/forked from Fred Eckert's Cortex-R5 example.
The PRU examples are based off loic-fejoz's beaglebone-ai64-tutorial
