Beaglebone PRU remoteproc/UIO information
29-08-2023
This is a compilation of useful information regarding moving from UIO to remoteproc and related matters, with all sources linked in their respective relevant tidbits.
Code Composer Studio
Install dependencies
The installation script for Code Composer Studio will not install all dependencies required by the program on Linux, something which you need to do yourself.
These are the required dependencies, as per TI’s instructions:
libc6:i386 libusb-0.1-4 libgconf-2-4 libncurses5 libpython2.7 libtinfo5
After that, you should be ready to begin.
The actual installation process
Download the installation files from TI, extract them to a convenient location and run ccs_setup_XXXX.run
After the dependency check confirms you have everything set, pick a location (the default location will install the compiler in your home directory). After that, everything is straightforward.
To finish up, if you need drivers for debugging or other integrations with the boards you’re developing for, go to your installation directory and execute the script in ccs/install_scripts/install_drivers.sh
.
Depending on your distribution, permissions or how your installation process went, you won’t be able to see the shortcut. You can find it in your installation directory, simply copy it from there
Installing the PRU compiler
CCS does not come with a PRU compiler as well.
You need to download the most recent version of the compiler from TI’s website and install that version. Installing from the app center or from the help menu may result in outdated versions of the compiler, even for the newest version of the CCS IDE.
When the installer asks for a location, pick the ccs/tools/compiler/
folder in your install location, or just move the output of the installer there.
If CCS remained open while you installed it, the compiler will not be available yet. You’ll need to refresh your compiler listing by clicking “More”, then “Refresh” on the “New Project” window, after selecting the PRU tab.
Adding necessary includes
CCS does not come with what you need to properly compile your project.
Download the PRU software support package from TI’s website and add the folders include/
and include/am335x
from the downloaded files to your include paths in the IDE (open the Build Settings/Properties window by selecting Project->Show Build Settings in the top menubar):
Assembly
Moving from pasm to clpru
Moving from pasm to clpru presents a few syntax/styling changes, which can be summed up in this handy table found in the Embedded Linux Wiki and created by Mark. A. Yoder:
pasm | clpru | Notes | |
---|---|---|---|
Comments | \ | ; | Start comments with ; |
Loading constants | MOV r0, 0x0000 | LDI r0, 0x0000 | pasm generalized MOV to not only move registers (MOV r0, r1), but also moving immediate values (MOV r0, 0x0). The MOV in clpru is only for moving registers. |
Loading 32-bit constants | MOV r0, 0xffffffff | LDI32 r0, 0xffffffff | If you are loading more than 16 bits, use LDI32. |
Load/store byte burst | LBBO r2, r1, 0, 4 | LBBO &r2, r1, 0, 4 | An & is needed before the first register of LBBO, SBBO, LBCO or SBCO. |
Substitution Symbols | #define CH1BIT r30.t8 | .asg r30.t8, CH1BIT .define r30.t8, CH1BIT | Be sure the . isn’t in the first column. |
However, some extra unexpected problems appeared (and apparently, won’t get fixed soon). These are documented below:
QBBC/QBBS and related pseudo ops do not work with format 2 from TIs own documentation (page 42). Instead, you are expected to only use format 1, so please replace all code that looks like QBBC somewhere, r31.t3 with: QBBC somewhere, r31, 3
Attempting to use Assembly and nothing else for the firmware is uncharted territory. However, you can call ASM code from C with minimal performance impact, which is described below:
Detecting main CPU “kicks” (new remoteproc message)
Just poll bit 30 of register 31. An example of how that can be done:
QBBS something, r31.b3, 7 ; Branch if message was received
C
Calling ASM code from C
As described by Mark in PRUCookbook, the process is fairly straightforward:
- Create an ASM file
my_delay_cycles:
delay:
sub r14, r14, 1 ; The first argument is passed in r14
qbne delay, r14, 0
jmp r3.w2 ; r3 contains the return address
- Declare it in your C file with the following line:
extern void my_delay_cycles(uint32_t);
Message transmission with remoteproc (firmware)
A lot of the code in this section belongs to RoSchmi’s RPMesg “Hello World” and TI’s own examples.
The following example demonstrates a very basic “echo” program, with the “magic” parts boldened out. All it does is initialize the RP Message transport channel and poll for new messages by checking if bit 30 of register 31 is clear, which represents a “kick” from the main CPU. When a new message arrives, the PRU sends a message back.
volatile register uint32_t __R31;
#define HOST_INT ((uint32_t) 1 << 30)
/* The PRU-ICSS system events used for RPMsg are defined in the Linux device tree
* PRU0 uses system event 16 (To ARM) and 17 (From ARM)
* PRU1 uses system event 18 (To ARM) and 19 (From ARM)
*/
#define TO_ARM_HOST 16
#define FROM_ARM_HOST 17
#define CHAN_NAME "rpmsg-pru"
#define CHAN_DESC "Channel 30"
#define CHAN_PORT 30
#define VIRTIO_CONFIG_S_DRIVER_OK 4
uint8_t payload[RPMSG_BUF_SIZE];
void main(void)
{
struct pru_rpmsg_transport transport;
uint16_t src, dst, len;
volatile uint8_t *status;
/* Allow OCP master port access by the PRU so the PRU can read external memories */
CT_CFG.SYSCFG_bit.STANDBY_INIT = 0;
/* Clear the status of the PRU-ICSS system event that the ARM will use to 'kick' us */
CT_INTC.SICR_bit.STS_CLR_IDX = FROM_ARM_HOST;
/* Make sure the Linux drivers are ready for RPMsg communication */
status = &resourceTable.rpmsg_vdev.status;
while (!(*status & VIRTIO_CONFIG_S_DRIVER_OK));
pru_rpmsg_init(&transport, &resourceTable.rpmsg_vring0, &resourceTable.rpmsg_vring1, TO_ARM_HOST, FROM_ARM_HOST);
while (pru_rpmsg_channel(RPMSG_NS_CREATE, &transport, CHAN_NAME, CHAN_DESC, CHAN_PORT) != PRU_RPMSG_SUCCESS);
while (1) {
if (__R31 & HOST_INT) {
/* Clear the event status */
CT_INTC.SICR_bit.STS_CLR_IDX = FROM_ARM_HOST;
/* Receive all available messages, multiple messages can be sent per kick */
while (pru_rpmsg_receive(&transport, &src, &dst, payload, &len) == PRU_RPMSG_SUCCESS) {
/* Echo the message back to the same address from which we just received */
pru_rpmsg_send(&transport, dst, src, payload, len);
}
}
}
}
Message transmission with remoteproc (userspace)
This one is fairly standard. All you need to do is to write to /dev/rpmsg_pru30
or /dev/rpmsg_pru31
, depending on your channel. This can be done easily in most programming languages, here’s how to do it in C:
pollfd pfd;
pfd.fd = open("/dev/rpmsg_pru30", O_RDWR);
write(pfd.fd,"hello!", 7);
Post-compiling
Pre-running
If you’re running kernel 4.19 or older, you’ll need to enable the remoteproc PRU device tree in /boot/uEnv.txt. Depending on which kernel you use, you must uncomment the line describing your specific kernel, which should be a TI kernel (not a Bone one).
For exemple, if you’re using 4.19, uncomment the following line:
uboot_overlay_pru=/lib/firmware/AM335X-PRU-RPROC-4-19-TI-00A0.dtbo
If you’re running 5.4 or later, you won’t need to load any device tree. However, PSSP (PRU Software Support Package) 6.0.0 or later won’t support with any kernel older than 5.10, as it introduces a number of breaking changes, such as the way it handles vring events.
Running
Send the compiled files to the Beaglebone via your preferred method, then copy the binary to /lib/firmware
.
After that, you must indicate which file contains the firmware by echo’ing the filename to /sys/class/remoteproc/remoteproc1/firmware
or /sys/class/remoteproc/remoteproc2/
firmware.
remoteproc1 refers to PRU0 and remoteproc2 refers to PRU1, as remoteproc0 is the ARM CPU.
echo "firmware.out" > /sys/class/remoteproc/remoteproc1/firmware
To run the code, just echo “start” to /sys/class/remoteproc1/state
or /sys/class/remoteproc2/state
echo start > /sys/class/remoteproc/remoteproc1/state
Stopping
Echo “stop” to /sys/class/remoteproc1/state or /sys/class/remoteproc2/state
echo stop > /sys/class/remoteproc/remoteproc1/state