"... and no one shall work for money, and no one shall work for fame; But each for the joy of the working, and each, in his separate star, shall draw the thing as he sees it, for the god of things as they are"

-Kipling

 

Using Device Trees To Configure PRU IO Pins

Summary

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 and the R30 and R31 Registers

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.

The PRU I/O Pins and the PinMux

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.

The PRU I/O Pins Nomenclature

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 P8 Header Pin 16 PinMux Modes

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.

How to Tell the Current PinMux Setting for a Pin

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.

Digression: What do the Other Bits in the PinMux State Mean

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.

How to Tell Which PinMux Mode is in Use

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.

Choosing the PRU I/O Pins

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

The P8 Header Pin 16 PinMux Modes

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.

Setting the PinMux Mode Using Device Tree Overlays

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

The KiloBaser Device Tree Overlay Generator

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

Reading and Writing to the I/O Pins in the PRU

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.

License

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.

Acknowledgements

Thank you KiloBaser for your really useful Device Tree Overlay Generator.