-Kipling
The Beaglebone Black Programmable Real time Units (PRUs) have the ability to directly manipulate some of the I/O pins that appear on the P8 and P9 Headers. This method of access is much faster than the GPIO subsystem and also, because a PRU is real time and deterministic, the timings of the I/O can be relied upon to be accurate. For example, if you output a 1 KHz square wave on a pin on the P8 or P9 Header using the GPIO system there may well be quite a bit a variability in the length of the pulses as the process is swapped in and out of memory by the Linux kernel. With the PRU there is no such variability as code executing in the PRU is designed to execute with consistent timing and it certainly does not ever get swapped in or out. I/O via the PRU is also much faster and read or write speeds of many tens of megahertz are possible.
The following discussion is generic and does not rely on any particular method of compiling or launching a PRU binary. However, if you wish to view a specific example, the PRU Data In Out page describes how to launch a compiled PRU binary using the BBBCSIO PRU_Driver class. This binary is designed to access certain pins on the Beaglebone Black P8 header for the purposes of input and output. The associated PASM Assembly Language source can be found in the file BBBCSIOHelp_PRUPinInOutExamplePASMCode.html.
The PRU reads certain bits in its R31 register to determine the high/low state of input pins and sets or clears certain bits in its R30 register to write to the state of an output pin. With respect to PRU input/output this is always the way it works - R31 is for reading and R30 is for writing. If you get them mixed up (writing to bits in R31) you can cause some very odd effects indeed as these two registers are also used for other things besides I/O. For example, writing to certain bits of R31 will trigger interrupts. Furthermore, sometimes the same bit in both registers refers to the same pin on the Beaglebone Black Headers and sometimes it does not. In other words, just because you read register 31 bit 14 (R31.14) to determine the high/low state of the P8 Header pin 16 (P8_16) it does not necessarily mean that if you set the pin as an output you can write to R30.14 in order to set the P8_16 pin state high or low. Sometimes this is the case and sometimes it is not. Some PRU I/O lines are just not mapped into the R30 and R31 registers as pairs like that and there is nothing you can do about it. This concept will be discussed in more detail later on in this page.
There are several factors which must be considered when deciding which PRU pins to use for any particular application. One thing to note is that not all pins which are available for GPIO input/output operations on the P8 or P9 headers are mapped into the PRU's R30 and R31 registers. This means you have to choose from the subset of available P8 or P9 header pins which are so mapped. In addition, the two Beaglebone Black PRUs do not have access to the same I/O lines. For example, if a P8 or P9 Header pin is available to PRU1 for I/O in its R30 or R31 registers then it will only be ever be available to PRU1. This means that, in general, PRU code that deals with I/O's is necessarily specific to PRU0 or PRU1. This is usually not the case for most PRU programs in which the code can run in either PRU without modification. The number of possible inputs and outputs per PRU is fixed and each has 16 possible inputs and 15 possible outputs. Those are just the theoretical numbers - many inputs and outputs are not routed out to the P8 or P9 headers on the Beaglebone Black and the actual number of usable inputs and outputs is quite a bit lower.
Another issue is that there are many more input/output requirements on the Beaglebone Black's CPU than there are physical pads to which they can be routed. This means the I/O lines of various devices (GPIOs, PRU I/O, SPI, I2C, UART, HDMI, USB &etc) inside the CPU have to share physical CPU pads and, thus, if something else is using that pad then the PRU cannot use it. Coordinating this sharing is performed by a device called the PinMux and there is a page on this site which provides much more information on that topic. The upshot is that since the pins on the P8 or P9 headers are ultimately connected to a CPU pad you must ensure that the PinMux is appropriately configured to route that pad to a PRU and not to some other internal device. If the PinMux is not set correctly, you can read from or write to the bits in the R31 or R30 registers all you wish and you will accomplish nothing. Reading will always return some constant value and writing will have zero effect. In other words, if the setting of the PinMux (called the PinMux Mode) is incorrect then the pin on the P8 or P9 header is in use for something else and it will not be connected to the PRU's R30 or R31 registers.
On the Beaglebone Black, the PinMux mode for the pins can be set by adjusting the Device Tree source, recompiling and rebooting or, rather more simply, by building and executing a Device Tree Overlay which does the job without the necessity of a reboot. The About the Device Tree document on this site discusses in detail the process of de-compiling, editing, re-compiling and installing a new Device Tree and such a method will not be discussed further here. The discussion below will cover Device Tree Overlays and provide some representative examples.
A PRU has 32 general purpose registers (0 to 31) - of which only register 30 (R30) and register 31 (R31) are of interest to this topic. In general, a bit in a register is referred to by appending a dot and the bit number - for example register 30 bit 10 is referenced as R30.10 or sometimes R30.t10. As mentioned above, a specific pin on one of the Beaglebone Black headers is usually referenced by appending an underscore character and then the pin number. Thus pin 16 on the P8 header would be referred to as P8_16.
The Technical Reference Manual (TRM) manual has a distinct way of naming each I/O line and, although the naming convention varies somewhat depending on the device (UART, HDMI, SPI, GPIO etc), that name is universally used in the documentation for the various PinMux Modes. An example of the naming format for the PRU I/O lines is
pr1_pru0_pru_r31_14
Recall that I/O lines are specific to the PRU. The "pru0"
means the line is only available in PRU0.
The "r31"
means it is an input only because R31 is always used for inputs ("r30"
would be used if the I/O line was usable as an output).
The "14"
means it is mapped to bit 14 in R31. If you look in the PinMux Mode documentation
you can see that that I/O line pr1_pru0_pru_r31_14
will actually be accessible on Header 8 pin 16 if the PinMux
mode is set to Mode6. You should probably have a look at that page
(or the P8 Header Quick Reference pdf) now and,
if you do, you will see that the I/O line pr1_pru0_pru_r31_14
shares an I/O pad with a number of other devices. The relevant section of the P8 Header Quick Reference is reproduced below...
The above information is actually much less complicated than it looks. The P8 header pin is in the left hand column. Thus for P8_16 we see that there are
eight PinMux modes and that the pr1_pru0_pru_r31_14
line is Mode6. This means that we have to put the PinMux in mode 6 and also set it as an input
in order to make the I/O line useable to the PRU. This, as is discussed below,
is done dynamically via a Device Tree Overlay (or by editing the Device Tree directly).
Before deciding to use this pin, it is also important to consider the other devices could need (or indeed already be using) the PinMux line. As discussed on
this
page, it is not possible (for example) for the PRU to be using P8_16 in mode6 while another device (gpio1_14
for example) is using it in mode 7. The
usage of the P8_16 pin must necessarily be coordinated with other hardware requirements on the Beaglebone Black.
The settings of the PinMux device (PinMux modes) are configured via the Device Tree at boot time or subsequently (on the Beaglebone Black) by Device Tree Overlays which dynamically
reproduce the effect of configuration changes as if the settings had actually been present in the Device Tree at boot time. The PinMux configuration
is presented to the Linux user space environment as device files via the PINCTRL subsystem.
Ultimately you will see the PinMux configuration presented (as files) in the /sys/kernel/debug/pinctrl/44e10800.pinmux
directory. The reference to the number
44e10800
in the path is just the memory address of the PinMux device in the CPU's memory.
This directory contains a number of useful files - most of which present variations on the same information in different ways. It should be noted that these are not normal files -
they are device files the contents of which will dynamically change to reflect configuration changes in the PinMux device. The file that will indicate the current
PinMux Mode usage can be found at the path /sys/kernel/debug/pinctrl/44e10800.pinmux/pins
. This file is quite large and, so
to sift out the information we wish from the remainder, we can grep
for the address of the particular PinMux pin we wish to view. Note on the above
graphic that the P8_16 Header is associated with offset 0x838
(the full address would be 0x44e10838
) in the PinMux.
We can use this to our advantage. If we issue the command
cat /sys/kernel/debug/pinctrl/44e10800.pinmux/pins | grep 838
we see something like the following returned (it may be different in your situation)
pin 14 (44e10838) 00000027 pinctrl-single
The current PinMux configuration at address 0x44e10838
is represented by the hex value 00000027
. The last 3 bits of this hex number are the mode. Since this number is 7
we can see that we are in mode 7. In other words, no matter what we do in the PRU, we are never going to be able to read a value from the P8_16 header because that
line is currently routed to some other device and not to the PRU's I/O system. The P8_16 header pin is simply not connected to the PRU. We will need to change this
by setting the PinMux mode to mode 6.
In the above discussion we said that the 7
meant that the PinMux pin was set to mode7 - but what do the other bits mean? It turns out
that the number returned by the pins
file encodes other information in various bit positions - the usage of which is given below...
Bit Number 8 7 6 5 4 3 2 1 0 c r s p m m m m = 3 mode bits [0-7] p = 0 pullups/pulldowns enabled, 1 pullups/pulldowns disabled s = 0 pulldown selected, 1 pullup selected r = 0 pin is output, 1 pin is input c = 0 fast slew control, 1 = slow slew control
We can see that the PinMux setting of 0x00000027
above represents a binary number - the bottom 8 bits of which are 0010 0111
. Using the
above key we can see that this means the pin is in mode 7 (0010 0111
),
the pulldown is selected (0010 0111
) and is enabled (0010 0111
)
and the pin is set as an input (0010 0111
). The encoding mechanism enables you to compose (or interpret) a PinMux configuration for
any combination of mode, input/output state and pullup/pulldown resistor requirements.
Knowing the current state of the PinMux for a pin is sometimes not enough. Often, we also need to know whether we can change the mode. There are 8 modes
and the PinMux pin will always be in one mode or another. The question
we now need to address is "is the PinMux pin at offset 0x838
in use by anything else"? It may well be that the PinMux pin has just been
placed in that mode as a default and nothing else really needs it. To answer this question we have to look at another file (pinmux-pins
). Issue the
command
cat /sys/kernel/debug/pinctrl/44e10800.pinmux/pinmux-pins | grep 838
and we see something like the following returned (it may be different in your situation)
pin 14 (44e10838): (MUX UNCLAIMED) (GPIO UNCLAIMED)
This tells us that neither the MUX nor the GPIO system has claimed that PinMux pin and we can reasonably assume that we can re-direct it for our purposes. If we
try a similar command looking (for example) for the offset 8b4
which is associated with
header pin P8_42 we see something like
hdmi.13 (GPIO UNCLAIMED) function nxp_hdmi_bonelt_pins group nxp_hdmi_bonelt_pins
Oh my, clearly if we were to change the PinMux mode on that pin we would mess up our HDMI video feed. This implies, after referring back to the
P8 Header PinMux Mode Quick Reference guide, that
we cannot use the PRU I/O lines pr1_pru1_pru_r30_5
(mode5) or pr1_pru1_pru_r31_5
(mode6) without first disabling the
HDMI video because they all share the same PinMux pin. Disabling things like the HDMI (called running headless) or the EMMC memory or various other things is commonly done to make more I/O available - but whether
it is a good idea or not depends entirely on the usage requirements of the Beaglebone Black.
If you have been following the above discussion it is now clear that we can use the PRU I/O line pr1_pru0_pru_r31_14
which is connected to
the P8_16 header for the purposes of input and output. For reference, here is the section of the
P8 Header Quick Reference pdf) again
We can see from the above image that we need to put the PinMux in Mode6 so the PRU can access it and also that, because its name is
pr1_pru0_pru_r31_14
, it is only usable as an input (via R31) and only on PRU0. There is no PinMux mode on either PRU which
can use P8_16 as an output because there is no pr1_pru?_pru_r30_14
line listed in any mode for that pin.
This is not to say that pr1_pru0_pru_r30_14
does not exist. In fact it does exist, it is just associated with P8_12 not P8_16.
Have a look at the BeagleboneBlackPRUOutputPinMuxModes.pdf
document and you will see that P8_12 is indeed connected to pr1_pru0_pru_r30_14
and the PinMux mode in that case is also Mode 6.
Given all the information that is available in the standard P8 and P9 Header PinMux Mode documents, it can be quite difficult to easily visualise which I/O pins are available for any one PRU and which ones are inputs or outputs. In order to simplify things, two additional Quick Reference guides dedicated to the PRU I/O usage on the P8/P9 Header have been compiled. These pdf files detail the PRU I/O pins (and only the PRU I/O pins) usable for inputs and the I/O pins that can be used as outputs.
Prior to writing any application which uses the PRU I/O lines as inputs or outputs you should make a number of decisions (perhaps with the assistance of the above P8 PRU I/O and P9 PRU I/O Quick Reference guides). Decide which PRU will run the code (remember each PRU uses different I/O pins), make sure that the PRU you have selected has access to sufficient inputs and outputs for your requirements and also that the pins they use do not conflict with each other or other devices (EMMC, HDMI, GPIOs, SPI etc) on the system.
Once you have selected the header pins you wish to use (and ensured there are no conflicts) you will need to set the PinMux mode so they can be used by the PRU. This is done by editing the Device Tree (not discussed on this page) or by creating and compiling a Device Tree Overlay which can be executed at runtime to set up the PinMux in the way you wish.
Creating an overlay for any one PinMux pin is not too hard. There are lots of samples on the Internet
however, typically, I just go to the excellent Device Tree Overlay Generator at
KiloBaser.com and
generate the basic structure of the Device Tree Overlay there. The instructions for compiling and installing the Device Tree Overlay are also helpfully contained on that page. Here is a summary of what I would do to enable the
P8_16 pin (pr1_pru0_pru_r31_14
) as an input.
First set up the form with the information you need...
... and if you look below the form you will see the Device Tree Overlay is dynamically generated every time you make a change. Here is the code it generated for P8_16 Mode6
/* * This is a template-generated file from BoneScript */ /dts-v1/; /plugin/; /{ compatible = "ti,beaglebone", "ti,beaglebone-black"; part_number = "BS_PINMODE_P8_16_0x26"; exclusive-use = "P8.16", "pr1_pru0_pru_r31_14"; fragment@0 { target = <&am33xx_pinmux>; __overlay__ { bs_pinmode_P8_16_0x26: pinmux_bs_pinmode_P8_16_0x26 { pinctrl-single,pins = <0x038 0x26>; }; }; }; fragment@1 { target = <&ocp>; __overlay__ { bs_pinmode_P8_16_0x26_pinmux { compatible = "bone-pinmux-helper"; status = "okay"; pinctrl-names = "default"; pinctrl-0 = <&bs_pinmode_P8_16_0x26>; }; }; }; };
A bit of copy and paste later and the file is saved (as instructed) to /lib/firmware/bspm_P8_16_26-00A0.dts
. Note the
/lib/firmware
location is important
otherwise the command that later loads the compiled .dtbo
binary will not be able to find it. The .dts
is compiled with command...
dtc -O dtb -o /lib/firmware/bspm_P8_16_26-00A0.dtbo -b 0 -@ /lib/firmware/bspm_P8_16_26-00A0.dts
... and the .dtbo
binary is installed in the slots with a command like...
echo bspm_P8_16_26 > /sys/devices/bone_capemgr.?/slots
The overlay code looks complicated - but mostly it is just fixed boilerplate text in format and structure. Note, however, the references to the number
0x26
. The bits forming the 0x06
part of that number are a reference to the mode (mode6) and the bits which compose
the remainder of the number (0x020
)
are references to the fact that it is an Input and is using a built-in Pulldown resistor (refer to the PinMux State
digression above for more information). The number 0x038
is just the
offset of that PinMux pin from the base PinMux address of 0x44e10800
. Remember when we were using the grep
command above
on the PinMux device file how we looked for the
text "838
". Really this was just a part of the full address of 0x44e10838
and the true offset from the base address of 0x44e10800
is
0x38
which is why the value of 0x38
is used in the Device Tree Overlay. Fundamentally, the Device Tree Overlay needs
an offset (0x38
) and a configuration (0x26
) pair for each pin it is to configure.
If you have a lot of pins to configure you are probably not going to want to have a separate .dts
file for each one. PinMux mode
changes can easily be combined in a single Device Tree Overlay. That technique will not be discussed here, however, a quick look at an
example from the Internet
will show you it is pretty straightforward to do once you know the offset and PinMux setting for each pin.
Of course, whatever Device Tree Overlay Binary you generate will need to be loaded each time you reboot (it will not persist). You can do this automatically with a script - or just decompile, edit, and recompile the full Device Tree and eliminate the need for overlays.
Once your PRU I/O lines have been "muxed" in and you are sure they are available in the PRU you have chosen, you can begin to write code which runs in the PRU to read (from bits in R31) and write (to bits in R30) to manipulate the P8 and P8 Header pin states. The comments in the BBBCSIOHelp_PRUPinInOutExamplePASMCode.html example code will illustrate how this is done.
The contents of this web page are provided "as is" without any warranty of any kind and without any claim to accuracy. Please be aware that the information provided may be out-of-date, incomplete, erroneous or simply unsuitable for your purposes. Any use you make of the information is entirely at your discretion and any consequences of that use are entirely your responsibility. All source code is provided under the terms of the MIT License.
Thank you KiloBaser for your really useful Device Tree Overlay Generator.