BeagleBone Tutorial: Accessing Main Memory From the PRU (and visa versa)
This tutorial shows how to access any part of the main memory from within the PRU, and reasons you shouldn’t.
References
PRU Reference:
AM335X PRU Reference Guide from the AM335X PRU Package (usage tutorial)
Technical Reference Manual:
AM335xARM Cortex-A8Microprocessors (MPUs) Technical Reference Manual
PRU Local Memory
Each PRU-ICSS has local memory that, from the perspective of the PRU, spans from address 0x00000000 to 0x0007FFFF, with the memory mapping found in table 5 of the PRU Reference.
Reads and writes to this memory, including the 12kB of memory shared between the two PRUs, are only 1 clock tick (clock runs at 200MHz).
Memory Outside the PRU
All memory available to the CPU (including the PRU memory) can be accessed by the PRU through an interconnect, the L3. For details on the L3, see the Interconnects chapter in the Technical Reference Manual.
For quick summary, the L3 performs data reads and writes to other blocks of the chip using a network-on-a-chip protocol (handles the “send this to” and “get this from” type addressing and handshaking) that’s physically connected with an Open Core Protocol (OCP) socket (a standard way to physically connect subsystems of a chip).
Another way to read that is “There’s lots of overhead”. Where local reads and writes were instant, within one clock tick, reads and writes though the L3 take many cycles. How many? Well, that depends on if the L3 is busy passing data around for some other part of the chip.
Accessing Memory External To The PRU
For the PRU, all addresses from 0x00000000 to 0x0007FFFF are local data. By enabling the OCP socket, the PRU gets to the L3. Connected to the L3, any memory read or write to addresses from 0x00080000 and beyond are routed through the L3 to the main memory, with the main memory address equal to what was requested. For example, reading address 0x0008005 will return the value from main memory address 0x00080005.
To access main memory addresses below 0x00080000, a global memory shit of -0x00080000 can be enabled. With the shift enabled, reading address 0x00080005 with the PRU will return the value from main memory address 0x00000005.
Setting Up The PRU To Access Main Memory
- Enable the OCP socket to connect the PRU to the L3
To enable the OCP port so the PRU can access main memory, set the STANDBY_INIT bit to 0 in the PRUs SYSCFG register, disabling the standby mode, and enabling the port. - Set the address offset field
To read main from 0x00080000 and up, set the address offset bit PMAS_PRU<n> in the PMAS PRU register to 0 to disable the offset. The main memory address is the same as what was specified in the PRU code. Reading PRU address 0x00090200 return the contents from main memory address 0x00090200.
To read main memory addresses 0x00000000 to 0x0007FFFF, set the PMAS_PRU<n> bit to 1, enabling the offset. The main memory address is now 0x00080000 less than that requested by the PRU, so reading PRU memory address 0x00080150 return the value in main memory address 0x00080150 - 0x00080000 = 0x00000150.
GPIO pins can be accessed through the global memory, but they shouldn’t (tutorial on its way).
Accessing PRU Memory From The CPU
There’s nothing special here. The CPU accessing the PRU memory through an L4 interconnect, which is always enabled, so there’s nothing special to do beforehand: the memory is always accessible.
Find the base address for the PRU that you want to access, and look up the offset for the PRU memory you’re interest in using Table 6 in the PRU Reference. Notice that the memory offsets match the PRU local memory, with a few of the “reserved” fields populated.
What NOT To Do
I think these are fairly obvious, but should be stated anyways.
If you need to poll memory, poll local memory
Any pru <—> main memory communication (with the exception of the PRU GPIO) has to go through a memory bus. Be aware that doing something like using the PRU to quickly poll some main memory, rather than a local memory, will flood the memory bus and most likely slow down whatever else interesting you’re doing with the CPU. If you need to poll memory to wait for some rare value, always poll local memory.
- If your CPU is the one polling, poll main memory, and have the PRU set that main memory address.
- If your PRU is needs to poll, have the CPU set the PRU memory (it has full access to all PRU memory, including registers).
- Poll GPIO or use an interrupt! PRU interrupts basically poll some of the PRU GPIO lines. They don’t come through the L3 or L4 interconnect, so will be much quicker. They can also be used to wake up the PRU from a suspended state, if you want to save some power.
From The PRU, Leave Any High Speed GPIO To The PRU GPIO
Don’t toggle the main GPIO pins from the PRU using their host memory addresses. And even worse, don’t toggle the PRU GPIO pins using the host memory address. Use the PRU GPIO pins. They’re one-tick access for reads and writes. That’s what they’re there for! Again, accessing through the main memory will be slow, have unpredictable access/toggle times, and flood the memory bus.
Here’s a list of PRU GPIO pins accessible on the BeagleBone header. See below for a PRU GPIO tutorial.
Related
BeagleBone Tutorial: Configuring GPIO Pins
BeagleBone Tutorial: Toggling GPIO Pins From Within The PRU-ICSS (on its way)
Errors? Something unclear? Leave a comment!
