December 12, 2013

Setting things up on the Beaglebone Black to do PRU code development

I am doing all this on a BBB running Angstrom Linux (the 2013.05.20 image). This runs the 3.8.13 kernel. I had no trouble with this kernel or distribution.

I found the information in this tutorial well written and valuable:

Accessing the PRU - uio_pruss

The first step is to load the kernel driver:
modprobe uio_pruss
This "just works", but be haven't tried to do anything with it yet.

Since I don't want to remember to do this modprobe every time I reboot, I edit the file /etc/modules and add one line to the end:

uio_pruss
After a reboot, lsmod shows the driver loaded, so this is fine.

Just for the record, the driver lives at:

/lib/modules/3.8.13/kernel/drivers/uio/uio_pruss.ko
My copy of the 3.8.13 kernel source has:
kernel/kernel/drivers/uio/uio_pruss.c
kernel/kernel/include/linux/platform_data/uio_pruss.h
A quick scan through this tells me that the uio_pruss module does no more than allow access to the PRU, but doesn't really set it up to do anything.

Accessing the PRU - linux device tree

I am running a 3.8.13 kernel -- at least as of this writing. When and if I encounter a true show stopper I will do an upgrade, but I am avoiding that for as long as possible. I have read some things about problems with uio_pruss on some 3.8 kernels, but I have had no problems at all (I wish people would be more specific about kernel versions when they report problems instead of saying things like "this works on more recent kernels" or other nonsense).

In the /boot partition are a bunch of file with ".dtb" extensions. These are inscrutable binary files that can be manipulated using the "dtc" utility. You decompile them to a ".dtc" file, edit that text file, then convert it back to a ".dtb" file as in the following procedure:

su
cd /boot
cp am335x-boneblack.dtb am335x-boneblack.dtb_ORIG
dtc -I dtb -O dts am335x-boneblack.dtb > bb.dts
vi bb.dts
dtc -I dts -O dtb  bb.dts > am335x-boneblack.dtb
reboot
The key thing to edit is a single line below the entry pruss@4a300000 (search for this). Below this make the following change to enable the PRU:
status = "disabled";
   -- change to:
status = "okay";
While you are at it, turn off the heartbeat behavior for the usr0 LED by changing a single line. Find a section that looks like this:
	    led0 {
		label = "beaglebone:green:usr0";
		gpios = <0x9 0x15 0x0>;
                linux,default-trigger = "heartbeat";
		default-state = "off";
	    };
And change the word "heartbeat" to "none". This will make it easier to run a PRU demo later that will blink this LED.

And indeed, after the reboot the LED stays out.

I am told that there is a new scheme that allows you to have small files that are "device tree fragments" in lieu of modifying the entire device tree file like this, but this works and I don't find it bothersome. It is probably of more interest to people who want to have installable packages (such as to support some kind of "cape").

Get the am335x PRU package and set it up

See my notes on getting this package.

Note that this includes all the source, so you can run (the assembler anyway) as a cross-assembler hosted on a system of your choice. There are components that need to be built on the BBB, and as far as I am concerned you might as well do everything on the BBB.

prussdrv

The first thing is the somewhat misnamed "prussdrv" -- it is really a library and not a driver, but it will cause less trouble to just leave it named as it is. And in fact once built it becomes libprussdrv.a which satisfies me. It does need to be compiled, and the Makefile has a bizarre setting for the CROSSCOMPILE variable. I edit the Makefile and change an obvious line near the top to make the value for this a blank string:

CROSSCOMPILE=
You can also do this externally by setting an environment variable. Then I do this:
su
cd am335x_pru_package/pru_sw/app_loader/interface
ntpdate -b -s -u pool.ntp.org
make
cp ../lib/libprussdrv.a /usr/lib
Notice that I set the time (since my BBB is not yet doing this on its own and if you forget you get clock skew warnings from make). Also I am copying the most useful of the output files to a system location where it will be easy to find.

header files

This will make life easier later on:
su
cd am335x_pru_package/pru_sw/app_loader/include
cp *.h /usr/include
This copies the two files:

pasm

This is the assembler itself.
su
cd am335x_pru_package/pru_sw/utils/pasm_source
sh linuxbuild
cd ..
cp pasm /usr/bin
There was already a pre-built pasm_2.arm executable, identical to pasm, but it does no harm to build it again. Also I once again go ahead and copy the assembler to a proper system directory.

build the examples

The Makefile for these also has the odd setting for the CROSSCOMPILE environment variable, and I again edit the Makefile to change it to a blank string.

The Makefile also looks for the PRU assember at ../utils/pasm_2, so I change that to /usr/bin/pasm.

su
cd am335x_pru_package/pru_sw/example_apps
-- edit the Makefile
make clean ; make
This produces a ./bin directory with 7 files:
-rw-r--r-- 1 root root    64 Dec 14 23:14 PRU_PRU0toPRU1_Interrupt.bin
-rw-r--r-- 1 root root    56 Dec 14 23:14 PRU_PRU1toPRU0_Interrupt.bin
-rwxr-xr-x 1 root root 18022 Dec 14 23:14 PRU_PRUtoPRU_Interrupt
-rwxr-xr-x 1 root root 17403 Dec 14 23:14 PRU_memAccessPRUDataRam
-rw-r--r-- 1 root root    56 Dec 14 23:14 PRU_memAccessPRUDataRam.bin
-rwxr-xr-x 1 root root 18092 Dec 14 23:14 PRU_memAccess_DDR_PRUsharedRAM
-rw-r--r-- 1 root root    64 Dec 14 23:14 PRU_memAccess_DDR_PRUsharedRAM.bin

For each example there are two files (well there are three files for
the first example).  The file with the ".bin" extension is the binary
image that will go into the PRU, which is quite tiny.
The other file is an executable, which links to code in libprussdrv.a
and will run on the arm processor and communicate with the PRU code.

The reason why the first example has three files is that it has code images for both PRU cores as well as code that runs on the ARM to talk to them.

Run an example

This is easily done:
su
cd am335x_pru_package/pru_sw/example_apps
cd bin
./PRU_PRUtoPRU_Interrupt
This produces the following output.
INFO: Starting PRU_PRUtoPRU_Interrupt example.
AM33XX
AM33XX
    INFO: Initializing example.
    INFO: Executing example on PRU0.
File ./PRU_PRU0toPRU1_Interrupt.bin open passed
	INFO: Executing example on PRU1.
File ./PRU_PRU1toPRU0_Interrupt.bin open passed
    INFO: Waiting for HALT command.
    INFO: PRU0 completed transfer.
	INFO: Waiting for HALT command.
	INFO: PRU1 completed transfer.
Example executed succesfully.
As near as I can tell this has worked just fine, and when I run the other two examples, it works fine too.

Conclusion

At this point I am done with this stage. I have a working PRU development system.


Feedback? Questions? Drop me a line!

Tom's Computer Info / tom@mmto.org