Skip to content

Repository files navigation

BeagleBone AI-64 Heterogeneous App Example

demo_image

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:

HOW TO RUN/SETUP

  • 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.

Demonstrated Features (main demo — Linux ↔ R5F0_0)

  • 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.

Extra examples (PRU / RTU)

See extra-examples/README.md:

  • 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

Implemented but not used by test code yet

  • Shared Memory: Linux-R5 memory sharing (SHARED_CODE/include/shared_mem.h, SharedMemoryRegion* sharedMem). Warning: 16-bit aligned reads/writes required to avoid crashes; standard memcpy() will crash.

Planned

  • GPIO Linux: Tested and working with gpiod library, 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.

Setup board

  1. Flash eMMC with the Debian 13.3 Minimal Flasher (v6.12.x-ti)
  2. Flash an SD card with the Debian 13.5 Minimal runtime image
  3. Power cycle the board several times
  4. Run df -h to confirm you are booting from the SD card
  5. Update packages: sudo apt update && sudo apt-get dist-upgrade -y
  6. Run sudo systemctl mask serial-getty@ttyGS0.service to work around a bug where the UART debug login sometimes does not appear
  7. Compile and install custom_overlays/our-custom-bbai64-overlay.dtso (see Device tree info)
  8. Add the overlay /overlays/our-custom-bbai64-overlay.dtbo to /boot/firmware/extlinux/extlinux.conf
  9. Power cycle the board several times
  10. Verify the overlay is loaded: sudo beagle-version | grep UBOOT
  11. Check pin muxing: sudo ./scripts/show-pins.pl
  12. Optional (Linux SPI only): sudo modprobe spidev — the main demo does SPI from the R5, not Linux
  13. Connect EQEP_1 loop wires: P8_33↔P8_34 and P8_35↔P8_36

Setup and build (container-first)

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.

One-time setup (SDK/PDK + PSSP)

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 --setup

Manual 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.

Build commands

# 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 --pru

Main 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 -g3 for Linux, -g3 -Og for R5, links PDK debug .aer5f libs where available
  • release: -O3 -DNDEBUG for both, links PDK release .aer5f libs

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.

Optional: rebuild PDK libraries only

./scripts/build.sh --build-pdk

Use a custom SDK location:

./scripts/build.sh --r5 --ti-sdk-dir /path/to/ti

The 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

To build and copy to board (from your dev machine):

./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).

To run:

sudo [SCRIPT_DIR]/debug_run.sh (RUN FROM BOARD, NOT YOUR DEV MACHINE!)

R5 start/stop/restart (Linux 6.12)

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|status

Quick 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.

Device tree info

  • Copy the overlays from our custom_overlays/ folder to /opt/source/dtb-6.12-Beagle/src/arm64/overlays on the board.
  • Run git pull from /opt/source/dtb-6.12-Beagle to get latest source.
  • To compile overlays source on board run sudo make clean and sudo make from /opt/source/dtb-6.12-Beagle.
  • To install the overlays, from /opt/source/dtb-6.12-Beagle run sudo make arm64_install.
  • On the board, in the config file/boot/firmware/extlinux/extlinux.conf, replace the line #fdtoverlays /overlays/<file>.dtbo with fdtoverlays /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.sh on the AI64 to do the overlay install automatically. Warning: setup_dtb.sh replaces /boot/firmware/extlinux/extlinux.conf on the board — there is potential for breakage.

Resources:

Walkthrough of process to figure out muxing

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 correct J721E_IOPAD(0x9c, PIN_OUTPUT, 4). PIN_INPUT gives the pin both RX and TX perms. PIN_OUTPUT only gives TX.

Debugging R5

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:

Debug from WSL Debian environment connected to TIAO USB JTAG probe:

  • 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.

Self-hosted debugging (no physical debug probe)

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.cfg

OpenOCD will print the GDB server ports.

Using GDB

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.

Using VS Code with GDB

Graphical debugging is possible; VS Code setup is lightly covered in this video: https://www.youtube.com/watch?v=n3u3QgnAvV8.

Useful Commands

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

Useful Links

Documentation

Tutorials

  • 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.

Debugging

Work by others:

Other

Credit

This repo was started/forked from Fred Eckert's Cortex-R5 example.

The PRU examples are based off loic-fejoz's beaglebone-ai64-tutorial

About

An example application showing a heterogeneous app with Linux and R5 cores working together

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages