Beaglebone Black PRU Troubles

In trying to test my code for the PRUs is there a good way to check and see if binary is being loaded into the PRU properly and check whether or not it is executing. I have a simple LED circuit running some basic blink code but nothing seems to be occurring. Also with everything activated, the pru_rproc running and the devices showing up as they should I still sometimes get “no such device” when I try to bind pru0, have you ever run into this issue?

When attempting to bind pru0 I get the following outpur from dmesg:

`
[ 697.537395] remoteproc1: 4a334000.pru0 is available
[ 697.537435] remoteproc1: Note: remoteproc is still under development and considered experimental.
[ 697.537450] remoteproc1: THE BINARY FORMAT IS NOT YET FINALIZED, and backward compatibility isn’t yet guaranteed.
[ 697.537926] remoteproc1: Unsupported class: 254
[ 697.546973] pru-rproc 4a334000.pru0: booting the PRU core manually
[ 697.547013] remoteproc1: powering up 4a334000.pru0
[ 697.547250] pru-rproc 4a334000.pru0: rproc_boot failed
[ 697.557946] remoteproc1: releasing 4a334000.pru0
[ 697.559696] pru-rproc: probe of 4a334000.pru0 failed with error -12

`
does this mean it’s an error in the code I am trying to load into it?

Below is a copy of the assembly code that I am attempting to load if it helps

`
Enter code here…; blink.p: demonstration of PRU on the BeagleBone Black
; blink LED connected to P8_11 ten times
.origin 0
.entrypoint TOP

TOP:
MOV r1, 10 ; blink counter

BLINK:
SET r30, r30, 15 ; set GPIO output 15
MOV r0, 0x00a00000 ; delay counter

DELAY:
SUB r0, r0, 1
QBNE DELAY, r0, 0 ; loop until r0 == 0 (delay)
CLR r30, r30, 15 ; clear GPIO output 15
MOV r0, 0x00a00000 ; delay counter

DELAY2:
SUB r0, r0, 1
QBNE DELAY2, r0, 0 ; loop until r0 == 0 (delay)
SUB r1, r1, 1
QBNE BLINK, r1, 0 ; loop until r1 = 0 (blink counter)
MOV r31.b0, 32 + 3
HALT

`

Zach
(Deleted the old one because I forgot to include the error messages)

How did you compile the assembly code? The compilation has to be done with the Remoteproc and linker files.
Remoteproc will look for certain code in the firmwares, and if it is not there, nothing will happen.

ooooh that’s my problem. I was only using
`
pasm -b blink2.asm

`
which doesn’t link against anything. I had missed that because most of the other examples that I had read where using the UIO pru loader, which doesn’t require any linking of the remoteproc. I had planned on switching to C in the future but I was using my simple assembly program to start. I guess I will just try modifying the example pru code gpio_toggle to toggle the pin I want as a first attempt. What is the proper way to link those files in during the assembly compilation process?

Sorry if some of these questions are pretty basic I’m learning as I go through all of this.

Go into the PRU support package examples directory. See if you can get the PRU_Halt to compile. This is the simplest possible C program for the PRU.
All you have to do is the command make, and this will kick off the process.

You will need to have the PRU_CGT environment variable set and also a very simple addition to the PRU compiler library directory (add a bin directory and link to clpru).
I’ve got this covered in my PDF file.

After these two changes, the Makefile included with the project should successfully compile and produce a firmware binary. This will create a gen directory. In the gen directory, there will be a .o file which you rename to am335x-pru0-fw, and then copy this to /lib/firmware. You should be able to rmmod pru_rproc, then modprobe pru_rproc, and then look at dmesg and see a successful start of the firmware.

You can look at the Makefile and reverse engineer the options used with the clpru compiler to create a working firmware.
You can run clpru at the command line with all the options, but it is a real mess!
Get out the PRU C Compiler manual to decode what all of the options do.
Then you can make a shell script to run the compiler. I used a shell script initially, and then made it into a primitive Makefile.
I really could have stuck with the shell script, but I wanted to learn about the make system.

You will be developing your own “tool chain” which I don’t think is too different from that used to develop a “bare metal” C program.
You also get to deal with kernel modules and also the Linux operating system. I think if you work through this, you will have
a good introduction to Linux embedded system design. Be warned that it can really eat up hours and hours of your time!

Thank you for all of the help Greg, I was finally able to get one of the examples to compile and execute. I’m still doing a bit of reading with regards to the reference manuals to figure out the proper registers and values for everything. I did have a few left over questions though.

  1. Is “config-pin” able to override the HDMI allocation on pin header 8 or is that something that needs to be taken care of in the master device tree that gets loaded? It seems whenever I activate the HDMI/emmc disable portion of the uEnv.txt it disables my ability to start and stop the PRUs with the echo command. The device is just no longer found, but if I can simply override those pin settings and switch them to PRU outputs without having to use anything in the uEnv.txt then that works just as well, even better if I don’t have to mess with the device tree again haha.

  2. My second question, is there a better way to execute tasks at specific timing intervals that having one PRU count and set bits in shared memory for the other PRU? I guess I am asking if there are internal timer interrupts that I could use to trigger events or ISRs?

  3. Has anyone ever put a simplified RTOS on the PRUs or are they not capable of handling that? Seems like you could use a combination of both PRUs to accomplish some pretty cool timing or interrupt driven real time tasks.

Thanks again for all of the help, I couldn’t have done it with you guys!

Excellent! It’s challenging to get Remoteproc to work and remember it is still experimental, still evolving. So remember what works today may not work tomorrow when you update your OS.
But I think that is more or less true with all of Linux!

The only question I can even halfway answer is #1. The Universal IO has more than one possible overlay. I think a different overlay might solve this problem.
Looking in /lib/firmware, I see 5 different “univ” overlays. How to change them? I don’t know. My boards are booting up with univ-emmc in slot 4.
That overlay has been sufficient for my projects so far, but it might not be enough for the next.
I tried a few experiments, and only got errors. I’m sure the process is simple, but I can’t tell you what it is.

We really need to find more documentation on how to use the Universal IO. The README on github seems to be a bit behind the current design.

The other questions I have no idea! Hopefully a PRU expert can comment on those.

I’ve made some progress on translating the motor controller to BBG non-CCS and latest interrupt scheme. The code in that project is way more complex than anything I have seen so far.
I wonder if it represents something close to the upper limit of how much code you can jam in a single PRU? I’ll get it to github, hopefully pretty soon.

Thanks for the information Greg.

Robert & everyone,
I was digging into that link you sent about which universal overlay gets loaded at boot. I have tried modifying my uEnv.txt file to enable different overlays but i cant’ seem to get config-pin to work for P9_31 and P9_29. It responds that config-pin isn’t set up for those pins but when I run

`
config-pin overlay cape-univversala

`

it seems to load just fine but when I call either:

config-pin-q P9_31 config-pin P9_31 pruout

it says that it is unable to access the “state” of that pin or set the “pinmux”. What’s the right way to enable config-pin on all of the pins so that I can set the PRU pins I need to “pruout”?

Zach

Most likely this is because the universal IO overlay you're loading does
not have those pins muxed. I have run into this myself when working with a
custom cape( physical hardware ). So, I have to create a custom overlay
based on more than one universal IO overlay. Then once I gleaned how the
structure for each pin had to be in the overlay, I modified the overlay
further to only define the exact mux for each pin. That is, removing all
the extra mux modes I did not need.

So, in short, you're going to have to create your own overlay file based on
multiple universal IO overlays, if you want to use config-pin to mux the
pins. If you just want to use config-pin to load an overlay, then your
overlay does not *have* to be based on a universal IO overlay.

Sorry for the deleted post I forgot a piece of information…

So I’m not exactly sure what modifications I made that fixed my problem because I made a few and somewhere it resolved my issue. I ended up modifying the files listed below. I don’t think I had to modify all of them but I figured I would enable the pru_rproc for all variations of device trees that load for future use (whether this is good or bad idk).

`
Uncomment ‘#include “am33xx-pruss-rproc.dtsi”’ in the following files as desired

/opt/source/dtb-4.4-ti/src/arm/am335x-boneblack-overlay.dts
/opt/source/dtb-4.4-ti/src/arm/am335x-boneblack-audio.dts
/opt/source/dtb-4.4-ti/src/arm/am335x-boneblack-hdmi-overlay.dts
/opt/source/dtb-4.4-ti/src/arm/am335x-boneblack-emmc-overlay.dts

`

be sure to run “make” followed by “make install” after making the above modifications.

I then modified my uEnv.txt file as follows:
Change

`
##BeagleBone Black: HDMI (Audio/Video)/eMMC disabled:
#dtb=am335x-boneblack-overlay.dtb

`
to

`
##BeagleBone Black: HDMI (Audio/Video)/eMMC disabled:
dtb=am335x-boneblack-overlay.dtb

`
this should enable the device tree file “cape-universala-00A0.dts” on boot. The “cape-universala-00A0.dts” has all of the pins pinmuxed and configured for all modes, so it should blow everything wide open for use with config-pin. it worked for all of the pins I wanted to configure. The universal capes can be found (at least for me) at

`
/opt/source/beaglebone-universal-io/

`

or you can check them out on this github link:
https://github.com/cdsteinkuehler/beaglebone-universal-io

Doing those 2 things seems to have allowed me to configure any pin I want and starts the pru_rproc on boot ensuring all of the required devices are there to start and stop the PRUs. I ended up not having to mess around with any custom device trees. Hopefully that information helps someone else struggling with the same thing I was.

There are a few things to note on this subject.

#1
https://github.com/cdsteinkuehler/beaglebone-universal-io/blob/master/config-pin#L522-L527

Of these 3 capes shown, only cape-universala will work unmodified. This is because this pin in the other two cape overlay’s is commented out.

#2

Going by the ball info /* P9_31 (ZCZ ball A13) Audio */, this must be an HDMI audio pin too ? So you’ll probably want to edit /boot/uEnv.txt to load this board file.

##BeagleBone Black: HDMI (Audio/Video) disabled:
dtb=am335x-boneblack-emmc-overlay.dtb

Initially the line dtb=am335x-boneblack-emmc-overlay.dtb will be commented out. Then once you make sure you have that uncommitted, make sure no other board file is left uncommitted after this line. Otherwise it may load at boot instead.

Once that is done, you should be able to configure the pin via config-pin to mux the pin for mode 5, or 6 depending on whether you want GPI, or GPO through the PRU.

Has anyone compiled a straight assembly file for the PRU? I know you can program in C but I can’t accurately predict how long each C command takes to run and I have a time critical portion of my code.

I have been attempting to use the clpru compiler but I keep getting linking errors as it is looking for a main function call and not finding one, since it’s just an assembly file. Is it possible to compile assembly with clpru and link to all of the proper files to ensure it compiles and runs correctly on the PRUs? I know I ran into that issue in the past when tried to use pasm and didn’t add any linked files.

I guess a better question would be, what libraries or other files need to be linked to create a file that will load and execute on the PRU? I can compile my code but the linking is a bit of a black box to me. I have been reading a lot of PRU reference documentation and haven’t come across the answer yet. I’m still working my way through “PRU Optimizing C/C++ User Guide”.

Here is a primer on linker files:
http://processors.wiki.ti.com/index.php/Linker_Command_File_Primer

I haven’t done anything with assembly other than reverse engineer what it is doing.
You can put assembly inline with your C code. That is described in the manual.

There are “intrinsics” which I think are chunks of assembly code which don’t quite fit nicely into the C language. There is a list in the compiler manual.

Also, there is a clpru option which will create a file with the assembly code generated from the C code.
With this you could get an idea of how much more efficiency you can achieve with assembly.
Then there are optimization levels of the C compiler you could experiment with.

You found the assembly compiler asmpru?

Greg,

Thanks for the info. That linker primer helped a bit with what is exactly going on inside of the linker, at least to the level that I can understand. I’m still working through getting a single assembly file to work but for now I figured out a workaround with an empty c file. What is asmpru? I only know about “clpru” and “pasm”.

Is it possible to load firmware into the PRU, start and stop it for testing and then reload new software without having to reboot? Whenever I stop the PRU and go to remove pru_rproc with “rmmod” or “modprobe -r” I get a message that it is still in use. In order to get around that I just restart the system after copying my new firmware file to /lib/firmware, but is there another way around this? As far as I can tell the PRU is not running when I try to unload/remove pru_rproc. Also when exactly is the pru firmware loaded into the PRU, is it when the system boots or when “modprobe pru_rproc” is called and the remoteproc is started?

I believe asmpru is the assembler code compiler which is part of the PRU compilation system which is described in the C/C++ Compiler manual.

pasm is the older assembler which existed prior to the C compiler.
There are differences in the assembly instructions used with pasm versus the more recent asmpru.

I believe clpru actually uses asmpru in the compilation process of C code.

Check out this discussion, especially the link to the pasm to clpru porting guide:

https://groups.google.com/forum/#!searchin/beagleboard/mark$20yoder$20pasm|sort:relevance/beagleboard/vuxsX_oMzkM/UjNIYWiKAQAJ

Greg and Zach,

Check out the two tarballs in this drive link: https://drive.google.com/drive/folders/0B_OVOhSEksP8MDFiT0x6YU1EZG8?usp=sharing

  • PRU_Halt_Assembly.tar.gz

  • Contains a project to drop into /opt/source/pru-software-support-package/examples/am335x/ that is an assembly only project

  • There are only two parts to the main.asm file: an empty resource table at the top (mandatory for RemoteProc loading) and a main function that writes ‘0xdeadbeef’ to address 0x4a310000 and then halts.

  • I created this assembly file by first writing the program in C, compiling it with the ‘-k’ compiler option to keep the generated assembly, and then removing all the unnecessary generated comments

  • Make it on target using the included Makefile (I modified the Makefile to build both .c and .asm files before linking)

  • cd /opt/source/pru-software-support-package/examples/am335x/PRU_Halt_Assembly/

  • export PRU_CGT=/usr/share/ti/cgt-pru

  • ln -s /usr/bin/ /usr/share/ti/cgt-pru/bin

  • make

  • (generated files will be in the ‘gen’ folder- Add a symbolic link from /lib/firmware/am335x-pru1-fw

  • cd /lib/firmware/

  • rm am335x-pru1-fw

  • ln -s /opt/source/pru-software-support-package/examples/am335x/PRU_Halt_Assembly/gen/PRU_Halt_Assembly.out am335x-pru1-fw- Reload the firmware in PRU 1

  • echo 4a338000.pru1 > /sys/bus/platform/drivers/pru-rproc/unbind

  • echo 4a338000.pru1 > /sys/bus/platform/drivers/pru-rproc/bind- Check that the firmware actually ran by seeing if ‘0xdeadbeef’ has been written to 0x4a310000 (I use devmem2 for this and I am logged in as root)

  • cd ~/

  • wget http://free-electrons.com/pub/mirror/devmem2.c

  • gcc devmem2.c -o devmem2

  • ./devmem2 0x4a310000

  • (output should say ‘0xdeadbeef’ is at that address)- PRU_Halt_Mixed.tar.gz

  • Contains a project to drop into /opt/source/pru-software-support-package/examples/am335x/ that is a C program that calls a function that is defined in an assembly file

  • main.c contains a C program that calls a function that takes an integer as a parameter and returns an integer as a result

  • assembly_loop.asm is an assembly file that contains a single function which can be called by a C program (which is what main.c does)

  • the loop is mostly nonsense but shows how to take a parameter in an assembly function as well as how to return a result- Make it on target using the included Makefile (I modified the Makefile to build both .c and .asm files before linking)

  • cd /opt/source/pru-software-support-package/examples/am335x/PRU_Halt_Mixed/

  • export PRU_CGT=/usr/share/ti/cgt-pru

  • ln -s /usr/bin/ /usr/share/ti/cgt-pru/bin

  • make

  • (generated files will be in the ‘gen’ folder- Add a symbolic link from /lib/firmware/am335x-pru1-fw

  • cd /lib/firmware/

  • rm am335x-pru1-fw

  • ln -s /opt/source/pru-software-support-package/examples/am335x/PRU_Halt_Mixed/gen/PRU_Halt_Mixed.out am335x-pru1-fw- Reload the firmware in PRU 1

  • echo 4a338000.pru1 > /sys/bus/platform/drivers/pru-rproc/unbind

  • echo 4a338000.pru1 > /sys/bus/platform/drivers/pru-rproc/bind- Check that the firmware actually ran by seeing if address 0x4a310000 contains the integer passed to the function in the C file minus 1 (I use devmem2 for this and I am logged in as root)

  • ./devmem2 0x4a310000

  • (output should be the parameter passed to the C function in main.c minus 1)
    Let me know if any of this doesn’t make sense. These two projects should cover assembly only projects, as well as C programs where you want to write one of the functions in assembly.

Jason Reeder

Hi Jason, this is awesome, much appreciated! Is it going to become part of the PRU Support Package examples?

I’ll run through the process soon and report on the result.
I also have some questions on the interrupt examples for some time in the future.

Meanwhile, I assume you ran the demo on a recent Debian distribution on a Beaglebone.
I was wondering if you see this in the dmesg log. This is for PRU0, and there is a repeated instance of these messages for PRU1.

It’s not a big deal as the firmwares can be started reliably after boot. I was not seeing this on the Beaglebones a few months ago, as the firmwares would be up and running automatically
after boot. Just curious what is going on…

[ 4.705647] irq: no irq domain found for /ocp/pruss@4a300000/intc@4a320000 !
[ 4.742790] irq: no irq domain found for /ocp/pruss@4a300000/intc@4a320000 !
[ 4.836176] remoteproc1: 4a334000.pru0 is available
[ 4.836201] remoteproc1: Note: remoteproc is still under development and considered experimental.
[ 4.836210] remoteproc1: THE BINARY FORMAT IS NOT YET FINALIZED, and backward compatibility isn’t yet guaranteed.
[ 4.836500] remoteproc1: Direct firmware load for am335x-pru0-fw failed with error -2
[ 4.836520] remoteproc1: failed to load am335x-pru0-fw
[ 4.847402] pru-rproc 4a334000.pru0: booting the PRU core manually
[ 4.847433] remoteproc1: powering up 4a334000.pru0
[ 4.847543] remoteproc1: Direct firmware load for am335x-pru0-fw failed with error -2
[ 4.847559] remoteproc1: request_firmware failed: -2
[ 4.852787] pru-rproc 4a334000.pru0: rproc_boot failed
[ 4.969567] remoteproc1: releasing 4a334000.pru0
[ 4.969746] pru-rproc: probe of 4a334000.pru0 failed with error -2

Greg,

Yes, I’d like to get assembly only and mixed c and assembly examples in the support package using TI’s compiler tools. I’d like to clean it up a bit and make it a little more useful first though. I put those examples together last night for you and Zach and figured I’d get them in your hands first. Let me know if you run into any issues.

I was running on a recent-ish version of the Debian distro on my BBGW. I’m fairly certain that I am seeing the same dmesg output that you are reporting. I don’t think it happens with our TI Processor SDK distro so I haven’t looked too closely at it yet. If I get a chance tonight after work I’ll try to update to the latest Debian and see if I can’t root cause the PRU boot loading issues.

Jason Reeder

Thanks Jason, those were two great examples.

Has anyone had any trouble with the scratch pad between PRUs? I’m using it to pass values between the two systems and it doesn’t appear to be working at all. I even created a test script that has a PRU load a value into bank 0 and then read it back out into a separate register and it doesn’t appear to be working. I get nothing back out of bank0. Is there some control register that you have to set to activate these banks?
Here is the code I am using to test

`
init:
LDI32 R21, 0x55555555
LDI32 R1, 0x00000000
LDI32 R2, 0x00000020
XOUT 10, &R21, 4
NOP
XIN 10, &R1, 4
; MOV R1, R21

SUB R2, R2, 1
MOV R30, R1
NOP

read_loop:
SUB R2, R2, 1
LSR R30, R30, 1
NOP
QBNE read_loop, R2, 0
QBA init
HALT

`

The MOV command works just fine but the XIN command does nothing. The register remains empty. Any thoughts?

Zach

Hi Zach,

Are you compiling with the -V3 switch? This should enable the scratch pad correctly. I’ve had not problems with it this way.

Laurel