Header files for non PRU peripherals

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

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.

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

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,

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

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

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.

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.

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

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.

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.

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 ?

    >
    > 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
    charles@steinkuehler.net <mailto:charles@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!

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.

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.

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 ?

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 ?

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

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

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