cobaltowl

We'll cross that bridge when we find it

Beaglebone PRU compilation

29-07-2023


Prerequisites

All commands should be ran as sudo.

Update the kernel by moving to the /opt/scripts/tools/ folder, running git pull, and then running ./update_kernel.sh. If you're not running a TI kernel, make sure to include \--ti-kernel or \--ti-channel in the command. An example:

cd /opt/scripts/tools
git pull
./update_kernel.sh \--lts-5_10\--ti-kernel

Make sure you've got PSSP 6.0.0 or later. You can check by viewing the README or the HTML page in /usr/lib/ti/pru-software-support-package. If it's not 6.0.0, download the PRU support package and extract it to the folder mentioned. For some reason, 6.1.0 has some quirks too, and headers used in 6.0.0 might not work.

You'll also need the TI compiler, version 2.3.3 or later. Download the installer (click on "Download Options"), chmod +x it and run.

Compiling it with clpru

The following Makefile will compile all C and ASM files in the folder, so they can be linked further on.

You also don't have to set the PSSP location inside the Makefile, the compiler looks for it in the PRU_SDK_DIR environment variable, but I'm doing it for clarity purposes.

However, you should set PRU_C_DIR to the location of your libraries. The value /usr/share/ti/cgt-pru/include;/usr/share/ti/cgt-pru/lib works in default installations.

PSSP = /usr/lib/ti/pru-software-support-package
CC = clpru
CFLAGS = \--include_path=\$(PSSP)/include \\
\--include_path=\$(PSSP)/include/am335x \\
-v3 -O2 \--opt_for_speed=5 \--endian=little
SRCS = \$(wildcard \*.c)
PROGS = \$(patsubst %.c,%,\$(SRCS))
ASM_SRCS = \$(wildcard \*.asm)
ASM_PROGS = \$(patsubst %.asm,%,\$(ASM_SRCS))
all: \$(PROGS) \$(ASM_PROGS) pru1.out
%: %.c
\$(CC) \$(CFLAGS) \$\< \--output_file \$@
%: %.asm
\$(CC) \$(CFLAGS) \$\< \--output_file \$@

As for each flag:

  • -v3 stands for silicon version 3, which is the one in all am335x Beaglebones.
  • \--opt_for_speed=5 just means we're maximizing speed in detriment of file size. You can change that as well.
  • -O2 uses optimization level 2, but feel free to use more agressive optimizations. Valid values are O0, O1, O2, O3 and O4.
  • -endian=little Just sets the endianness to little, as is the case with the PRU. This is implicit, but avoids confusion. Little endian just means we're going to store the least significant bytes before the most significant ones.
  • \--include_path is where we're telling the compiler to search for include files, you might need to change or add more paths depending on your use case.

Other useful flags:

  • -g enables debugging flags. Useful for, well, debugging.

Linking with lnkpru

First of all, I'm going to recommend having a .cmd file to orchestrate the linking process. You can find examples in /usr/lib/ti/pru-software-support-package/examples/<your architecture>. This part of the Makefile assumes your .cmd file is placed inside the same folder as the rest of the code.

LD = lnkpru
LDFLAGS = AM335x_PRU.cmd
pru1.out: \$(ASM_PROGS) \$(PROGS)
\$(LD) \$(LDFLAGS) \$\^ -o \$@*

The only flag here is describing where we can find our linker command file, change it if you rename or move it.

After that, you should have a valid, ready-to-go PRU binary. Here's the completed Makefile:

PSSP = /usr/lib/ti/pru-software-support-package
CC = clpru
LD = lnkpru
CFLAGS = \--include_path=\$(PSSP)/include \\
 \--include_path=\$(PSSP)/include/am335x \\
 -v3 -O4 \--opt_for_speed=5 \--endian=little
LDFLAGS = AM335x_PRU.cmd
SRCS = \$(wildcard \*.c)
PROGS = \$(patsubst %.c,%,\$(SRCS))
ASM_SRCS = \$(wildcard \*.asm)
ASM_PROGS = \$(patsubst %.asm,%,\$(ASM_SRCS))
all: \$(PROGS) \$(ASM_PROGS) pru1.out
%: %.c
 \$(CC) \$(CFLAGS) \$\< \--output_file \$@
%: %.asm
 \$(CC) \$(CFLAGS) \$\< \--output_file \$@
pru1.out: \$(ASM_PROGS) \$(PROGS)
 \$(LD) \$(LDFLAGS) \$\^ -o \$@
clean:
 rm -f pru1.out \$(ASM_PROGS) \$(PROGS)*

What else?

As it might not be clear enough, some of these Makefile tricks that I've used make life easier. We're using wildcard to grab all C and ASM files, and then patsubst to create our output locations by replacing our suffix (%.c or %.asm) with no suffix at all (%).

Then, we compile all of those (all) by setting the targets of all to our expected output files, then our final compiled binary, pru1.out.

\$\< is going to replace itself with the first dependency (a single file in this case), and \$@ with the target (our output). Then, \$\^ replaces itself with all dependencies, space separated.