Header files for non PRU peripherals

149 views
Skip to first unread message

Kirk

unread,
Sep 1, 2016, 7:57:43 PM9/1/16
to BeagleBoard

Are there header files that define the various non-PRU peripherals on the host processor?

I'm learning how to program the PRU and I'd like to be able to access some of the other peripherals.
For example, the eQEP2 registers starting at 0x4830_4180 as defined in section 2 of the technical reference manual.

Thanks,
Kirk

Jason Reeder

unread,
Sep 1, 2016, 8:18:07 PM9/1/16
to BeagleBoard
Check out this header file in the PRU Software Support Package: https://git.ti.com/pru-software-support-package/pru-software-support-package/blobs/master/include/am335x/sys_pwmss.h

The eQEP, ePWM, and eCAP modules make up the PWMSS peripheral.

Kirk

unread,
Sep 3, 2016, 9:30:56 AM9/3/16
to BeagleBoard

OK, I see it now.  Thanks for pointing that out
I overlooked it because I thought he pru_*.h files were specific to the PRU peripherals.
Should have looked closer.

Here's the directory I'm looking in and the header files in it:

/opt/source/pru-software-support-package/include/am335x

pru_cfg.h
pru_ctrl.h
pru_ecap.h
pru_iep.h
pru_intc.h
pru_uart.h
sys_mailbox.h
sys_pwmss.h

I don't see anything for SPI0 and SPI1 on the host processor.
I'll be needing to get at them too.
Any ideas?

Thanks,
Kirk

Kirk

unread,
Sep 3, 2016, 9:30:56 AM9/3/16
to BeagleBoard

OK, I see it now.  Thanks for pointing that out
I overlooked it because I thought he pru_*.h files were specific to the PRU peripherals.
Should have looked closer.

Here's the directory I'm looking in and the header files in it:

/opt/source/pru-software-support-package/include/am335x

pru_cfg.h
pru_ctrl.h
pru_ecap.h
pru_iep.h
pru_intc.h
pru_uart.h
sys_mailbox.h
sys_pwmss.h

I don't see anything for SPI0 and SPI1 on the host processor.
I'll be needing to get at them too.
Any ideas?

Thanks,
Kirk

On Thursday, September 1, 2016 at 5:18:07 PM UTC-7, Jason Reeder wrote:

Jason Reeder

unread,
Sep 7, 2016, 6:55:36 AM9/7/16
to BeagleBoard
Kirk,

Do a 'git pull' on your pru-software-support-package repo and the sys_mcspi.h files should be in the include/am335x/ folder. Let me know if you run into any problems while using it, I didn't have time to test it out.

Jason Reeder

Kirk

unread,
Sep 7, 2016, 2:09:43 PM9/7/16
to BeagleBoard
Jason,
I just grabbed the sys_mcspi.h file.  Much appreciated!  I'll let you know how it goes.  I'm still climbing up the learning curve.

While I have your attention can I ask you one other question:

What is the recommended way to access NON PRU GPIO pins?

I'm asking because I'm working with a custom cape board on the Beaglebone Black that has already been built.
And I'm trying to move some time critical code to the PRU.
I don't think all the GPIO pins that are used are in the group of PRU GPIO pins.

Thanks again,
Kirk

Charles Steinkuehler

unread,
Sep 7, 2016, 5:28:43 PM9/7/16
to beagl...@googlegroups.com
On 9/7/2016 1:02 PM, Kirk wrote:
>
> What is the recommended way to access NON PRU GPIO pins?
>
> I'm asking because I'm working with a custom cape board on the Beaglebone Black
> that has already been built.
> And I'm trying to move some time critical code to the PRU.
> I don't think all the GPIO pins that are used are in the group of PRU GPIO pins.

IMHO it works best if you setup the GPIO banks with Linux, then use
the PRU to twiddle the output bits using the set and clear registers
in the GPIO bank. That way you don't have to worry about any sort of
locking to insure consistent updates. Note the Linux side also needs
to use the set/clear registers for output updates (and not a
read-modify-write sequence) to insure no updates get lost.

--
Charles Steinkuehler
cha...@steinkuehler.net

William Hermans

unread,
Sep 7, 2016, 6:18:17 PM9/7/16
to beagl...@googlegroups.com

On Wed, Sep 7, 2016 at 2:28 PM, Charles Steinkuehler <cha...@steinkuehler.net> wrote:

IMHO it works best if you setup the GPIO banks with Linux, then use
the PRU to twiddle the output bits using the set and clear registers
in the GPIO bank.  That way you don't have to worry about any sort of
locking to insure consistent updates.  Note the Linux side also needs
to use the set/clear registers for output updates (and not a
read-modify-write sequence) to insure no updates get lost.

--
Charles Steinkuehler
cha...@steinkuehler.net

I agree with Charles with one exception. I personally prefer to use DATAOUT directly instead of using SETDATAOUT / CLEARDATAOUT.

I'd actually recommend that people also let the OS setup stuff like the ADC as well. Then just read form the FIFO buffer.

Charles Steinkuehler

unread,
Sep 7, 2016, 8:40:03 PM9/7/16
to beagl...@googlegroups.com
On 9/7/2016 5:18 PM, William Hermans wrote:
>
> I agree with Charles with one exception. I personally prefer to use DATAOUT
> directly instead of using SETDATAOUT / CLEARDATAOUT.

If you write directly to DATAOUT from either the PRU or the ARM side,
you risk data corruption. Example access patterns causing data
corruption:

1:
* PRU reads DATAOUT
* ARM modifies DATAOUT
* PRU writes DATAOUT
* ARM Update is *LOST*

2:
* ARM reads DATAOUT
* PRU modifies DATAOUT
* ARM writes DATAOUT
* PRU update is *LOST*

Both the PRU and the ARM need to use the set/clear registers for
updates or some updtes can get *LOST* (which can be *REALLY* hard to
debug in the RealWorld).

--
Charles Steinkuehler
cha...@steinkuehler.net

William Hermans

unread,
Sep 7, 2016, 8:50:11 PM9/7/16
to beagl...@googlegroups.com
It does not say anything about that in the TRM. In fact as I recall it mentions that SETDATAOUT, and CLEARDATAOUT directly write to DATAOUT. Something like

SETDATAOUT -> |= BITx
CLEARDATAOUT -> &=(~BITx)

I gues I'll have to reread the TRM again.

Charles Steinkuehler

unread,
Sep 8, 2016, 9:39:04 AM9/8/16
to beagl...@googlegroups.com
On 9/7/2016 7:50 PM, William Hermans wrote:
>
> SETDATAOUT -> |= BITx
> CLEARDATAOUT -> &=(~BITx)
>
> I gues I'll have to reread the TRM again.

The set/clear registers only affect the bits that are written with a
'1' value, all other bits remain unchanged, while writing directly to
the DATAOUT register affects the value of all 32 bits in the GPIO
bank. Using the set/clear registers allows a single atomic write
operation to affect the specific bit(s) you want to change without
having to perform a non-atomic read-modify-write cycle.

Since the write to the set or clear register is atomic, if both the
ARM and the PRU both use this method, no locks or other hand-shaking
is required to prevent corruption.

--
Charles Steinkuehler
cha...@steinkuehler.net

William Hermans

unread,
Sep 8, 2016, 1:41:18 PM9/8/16
to beagl...@googlegroups.com
It has long been programing technique to use DATAOUT |= BITx to set a register bit DATAOUT &= (~BITx) to clear a register bit, or something like if(DATAOUT & BITx){} or if((DATAOUT &BITx) == 0 or 1) to read a bit from a register.

So I'm having a very hard time taking what you're saying without a grain of salt. Especially after having read that section of the TRM in detail, and not getting what you got from it. But I do feel that if you read a gpio register bit first, before writing to it there should be no contention as to what the register should be.

With that said, I've only ever used /dev/mem + mmap(), but have never done this with a PRU to date. So, while I do firmly believe in what I say above, I do have respect for what you're saying. Plus I do know you have hands on with the PRU . . . I wonder if anyone could whip up a test case for others to play around with- to demonstrate this ?

Charles Steinkuehler

unread,
Sep 8, 2016, 4:37:04 PM9/8/16
to beagl...@googlegroups.com
On 9/8/2016 12:41 PM, William Hermans wrote:
>
>
> On Thu, Sep 8, 2016 at 6:36 AM, Charles Steinkuehler <cha...@steinkuehler.net
> <mailto:cha...@steinkuehler.net>> wrote:
>
> On 9/7/2016 7:50 PM, William Hermans wrote:
> >
> > SETDATAOUT -> |= BITx
> > CLEARDATAOUT -> &=(~BITx)
> >
> > I gues I'll have to reread the TRM again.
>
> The set/clear registers only affect the bits that are written with a
> '1' value, all other bits remain unchanged, while writing directly to
> the DATAOUT register affects the value of all 32 bits in the GPIO
> bank. Using the set/clear registers allows a single atomic write
> operation to affect the specific bit(s) you want to change without
> having to perform a non-atomic read-modify-write cycle.
>
> Since the write to the set or clear register is atomic, if both the
> ARM and the PRU both use this method, no locks or other hand-shaking
> is required to prevent corruption.
>
> --
> Charles Steinkuehler
> cha...@steinkuehler.net <mailto:cha...@steinkuehler.net>
>
>
> It has long been programing technique to use DATAOUT |= BITx to set a register
> bit DATAOUT &= (~BITx) to clear a register bit, or something like if(DATAOUT &
> BITx){} or if((DATAOUT &BITx) == 0 or 1) to read a bit from a register.

Yes, it has. But those short-hand snippits of C code are hiding an
implicit read-modify-write cycle:

DATAOUT |= BITx

...turns into:

Read : <temp> = DATAOUT
Modify: <temp> = <temp> | BITx
Write : DATAOUT = <temp>

...where <temp> is probably a CPU register (unless your C compiler is
_really_ bad!). But if you use the set/clear registers it's just:

Write : SETDATAOUT = BITx

If you have multi-threaded code (or interrupts or the PRU) that can
modify GPIO output states, you have to protect the read-modify-write
cycle with some sort of lock. With the set/clear registers, no lock
is necessary for reliable operation.

> So I'm having a very hard time taking what you're saying without a grain of
> salt. Especially after having read that section of the TRM in detail, and not
> getting what you got from it. But I do feel that if you read a gpio register bit
> first, before writing to it there should be no contention as to what the
> register should be.

In a multi-threaded system, the time between the read and the write in
the read-modify-write cycle is when corruption can happen. If another
process writes to the GPIO after the read, but before the write, that
update can get lost (this was covered in a previous email with
specific examples). Take with as much salt as required for it to make
sense, but controlling access to shared resources (like a GPIO bank,
or a shared variable) is a very basic part of multi-threaded
programming...but it's still _really_ easy to get wrong!

--
Charles Steinkuehler
cha...@steinkuehler.net

William Hermans

unread,
Sep 8, 2016, 4:52:32 PM9/8/16
to Charles Steinkuehler, beagl...@googlegroups.com
I guess I just tend to think, and do things a bit differently than some, or maybe even most.

1) If I could avoid having writes happen on both sides I would. Normally this isn't an issue, but if it is, see 2.
2) If writes *had* to happen on both sides. I would implement a locking mechanism. A single bit somewhere in the PRU's shared memory.

I did this with an application I wrote that read data out of the CANBUS, and had to write the parsed data to a shared memory file. Where locking semaphores were far too slow. As I was writing around 1400 data parameters out over a websocket a second. Where the second half of the app( separate process ) had to be on the ball, and send that data out in real-time as it was available . . . Anyway, first half of the application would only write if this bit were set to 0 while the second half would only read while bit was set to 1. So in effect, a blocking write, then read, but it turned out to be very fast.

Anyway, I think this is similar how I would approach what's being discussed here.

On Thu, Sep 8, 2016 at 1:38 PM, Charles Steinkuehler <cha...@steinkuehler.net> wrote:
On 9/8/2016 1:40 PM, William Hermans wrote:
> So here is my contention, but note that I'm emailing you directly. Because it is
> not my intention necessarily to argue with you, and feel good or righteous
> about. Especially since I have nearly no hands on with the PRU's or how they
> behave in relation to usrspace, or even kernelspace Linux.
>
> 25.4.1.18 GPIO_DATAOUT Register (offset = 13Ch) [reset = 0h]

Yes, the TRM correctly describes the behavior of the DATAOUT,
SETDATAOUT, and CLEARDATAOUT registers, but I don't think you
understand the potential contention issues in a multi-threaded system
which is what makes the set/clear registers very helpful.  Believe me,
they wouldn't be in the hardware if they didn't have a very useful
purpose!  :)

I replied with more details to your post on the list.

--
Charles Steinkuehler
cha...@steinkuehler.net


Charles Steinkuehler

unread,
Sep 8, 2016, 4:54:22 PM9/8/16
to beagl...@googlegroups.com
On 9/8/2016 3:35 PM, Charles Steinkuehler wrote:
> On 9/8/2016 12:41 PM, William Hermans wrote:
>>
>> It has long been programing technique to use DATAOUT |= BITx to set a register
>> bit DATAOUT &= (~BITx) to clear a register bit, or something like if(DATAOUT &
>> BITx){} or if((DATAOUT &BITx) == 0 or 1) to read a bit from a register.
>
> Yes, it has. But those short-hand snippits of C code are hiding an
> implicit read-modify-write cycle:
>
> DATAOUT |= BITx
>
> ...turns into:
>
> Read : <temp> = DATAOUT
> Modify: <temp> = <temp> | BITx
> Write : DATAOUT = <temp>
>
> ...where <temp> is probably a CPU register (unless your C compiler is
> _really_ bad!). But if you use the set/clear registers it's just:
>
> Write : SETDATAOUT = BITx

Oh...while we're on the subject, there's one more significant reason
the set/clear registers are helpful even if you're not worried about
multi-threaded operation: They're *FAST*

The ARM core on the BBB is running at 1 GHz, but the I/O is only
running at about 100 MHz. So that simple DATAOUT |= BITx stalls the
CPU until the system can provide the current value of DATAOUT to the
processor core, which likely takes a couple hundred nanoseconds (based
on the time it takes for the PRU to read a GPIO register).

The write, however, is very fast (for both the set/clear case and for
writing directly to DATAOUT). Writes are posted and the ARM has weak
memory ordering, so as long as you're not saturating the on-chip
interconnect fabric, the write will appear to complete instantly, the
ARM core will carry on executing instructions, and about 100 nS later
the GPIO register will update with the new value once the write
transaction has worked it's way through the on-chip interconnect fabric.

--
Charles Steinkuehler
cha...@steinkuehler.net

William Hermans

unread,
Sep 8, 2016, 4:57:18 PM9/8/16
to beagl...@googlegroups.com
Oh...while we're on the subject, there's one more significant reason
the set/clear registers are helpful even if you're not worried about
multi-threaded operation:  They're *FAST*

I did notice that it seemed to be that way from your description. But the way I read the TRM. TO me it seems that the SETDATA(direction) registers actually write to DATAOUT. did I read that wrong ?


--
Charles Steinkuehler
cha...@steinkuehler.net

--
For more options, visit http://beagleboard.org/discuss
---
You received this message because you are subscribed to the Google Groups "BeagleBoard" group.
To unsubscribe from this group and stop receiving emails from it, send an email to beagleboard+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/beagleboard/66e45786-85b9-2302-bef4-3213f6848a8d%40steinkuehler.net.
For more options, visit https://groups.google.com/d/optout.

William Hermans

unread,
Sep 8, 2016, 5:00:45 PM9/8/16
to beagl...@googlegroups.com
25.4.1.18 GPIO_DATAOUT Register (offset = 13Ch) [reset = 0h]

GPIO_DATAOUT is shown in Figure 25-24 and described in Table 25-23.

The GPIO_DATAOUT register is used for setting the value of the GPIO output pins. Data is written to the
GPIO_DATAOUT register synchronously with the interface clock. This register can be accessed with
direct read/write operations or using the alternate Set/Clear feature. This feature enables to set or clear
specific bits of this register with a single write access to the set data output register
(GPIO_SETDATAOUT) or to the clear data output register (GPIO_CLEARDATAOUT) address.

Ok thats confusing . . .but I guess I read this and understood the opposite of what it's saying ? e.g. I was thinking DATAOUT would be fast ?

Kirk

unread,
Sep 9, 2016, 5:30:37 PM9/9/16
to BeagleBoard
Makes sense.  Thanks for your ideas.
Is there a header file, along the lines of sys_mcspi.h that Jason provided, that you use for the GPIO?

Kirk

Kirk

unread,
Sep 14, 2016, 3:48:51 PM9/14/16
to BeagleBoard

Jason,

I'm trying to use the new sys_mcspi.h header file which you provided.

I have a simple test program I adapted from some example code that toggles a gpio pin which I monitor with a scope.
I use the toggle rate seen on the scope to determine whether phaseBit is zero or not.
(the code is below)

The code writes a 1 to the phase control bit in the SPI config register.
Then it reads it back into the phaseBit variable.


I figure if it writes a 1 it should read back a 1.
Trouble is it's always reading back 0.
The code does what is expected if I force a 1 or 0 into phaseBit


Not sure what I'm doing wrong. (remember I'm new to PRU work)

Do does the code have to do something to enable access to the SPI registers?

Thanks,
Kirk


/*
 * Based on the examples distributed by TI
 *
 * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
 *
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * * Redistributions of source code must retain the above copyright
 *  notice, this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the
 *  distribution.
 *
 * * Neither the name of Texas Instruments Incorporated nor the names of
 *  its contributors may be used to endorse or promote products derived
 *  from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdint.h>
#include <pru_cfg.h>
#include <pru_ctrl.h>
#include <sys_mcspi.h>
#include "resource_table_empty.h"

volatile register uint32_t __R30;
volatile register uint32_t __R31;

void main(void)
{
volatile uint32_t gpio;

volatile uint32_t phaseBit;


/* Clear SYSCFG[STANDBY_INIT] to enable OCP master port */
CT_CFG.SYSCFG_bit.STANDBY_INIT = 0;

// attempt to write an SPI configuration bit and read it back
CT_MCSPI0.CH0CONF_bit.PHA = 0x1;
phaseBit = CT_MCSPI0.CH0CONF_bit.PHA;

// by forcing phaseBit to 0 or 1 here I get the expected toggle rate below
// phaseBit = 1;


/* Toggle GPO pins TODO: Figure out which to use */
gpio = 0x000F;

// if phaseBit is non-zero toggle fast, otherwise toggle slow
while (1) {
__R30 ^= gpio;
if(phaseBit == 0)
{
__delay_cycles(1000000); // toggle at approx 100Hz
}
else
{
__delay_cycles(500000); // toggle at approx 200Hz
}

}
}




On Wednesday, September 7, 2016 at 3:55:36 AM UTC-7, Jason Reeder wrote:

Jason Reeder

unread,
Sep 14, 2016, 4:23:05 PM9/14/16
to BeagleBoard
Kirk,

You probably need to enable the peripheral (and its clocks) using its CM_PER register in order to read and write its registers.

Let me look up the address in the TRM real quick and I'll post some psuedo-code.

Jason Reeder

Jason Reeder

unread,
Sep 14, 2016, 4:23:09 PM9/14/16
to BeagleBoard
#define CM_PER_SPI0_CLKCTRL        (*((volatile unsigned int *) 0x44E0004C))
#define CM_PER_SPI1_CLKCTRL        (*((volatile unsigned int *) 0x44E00050))

in your main method after you enable the OCP master port using STANDBY_INIT:
// to enable SPI0
while (! (CM_PER_SPI0_CLKCTRL == 0x2) ) {
        CM_PER_SPI0_CLKCTRL = 2;
}
//to enable SPI1
while (! (CM_PER_SPI1_CLKCTRL == 0x2) ) {
        CM_PER_SPI1_CLKCTRL = 2;
}

Jason Reeder

On Wednesday, September 14, 2016 at 2:48:51 PM UTC-5, Kirk wrote:

Kirk

unread,
Sep 14, 2016, 5:13:48 PM9/14/16
to BeagleBoard

That did it!
Now I'm a happy "CM_PER..."  :)

Now it's on to see if I can get some data moving on the SPI.

Thanks for your help,
Kirk
Reply all
Reply to author
Forward
0 new messages