PyPRUSS – A simple PRU python binding for BeagleBone

Replicape was announced as the winner of the BeagleBone cape contest! In honor of that, here is a very special blog post for you: A long expected library!

Update 31/01-2014: PyPRUSS is now available as a package for easy installation:

http://wiki.thing-printer.com/index.php?title=PyPRUSS_on_BeagleBone

The Programmable Real-time Units (PRU) in the BeagleBone are perfect for controlling steppers motor movements. With the 200MHz system clocks on these bad boys you get some 5ns resolution,
more than enough for some good old fashion acceleration.

I could not find any Python bindings for controlling the PRUs, so I decided to start one myself.
The library is called PyPRUSS and is meant to be a wrapper for the C-library that already exists for controlling the PRUs.

So far, the library is experimental at best, but it does implement the basic functionality for initializing, loading a binary file into the program memory of the PRUs, catching events and exiting.

The functions that are mapped are the functions described on the Texas Instruments wiki:

Some of the functions are missing, especially for mapping as DDR memory shared between the ARM and PRUs. Instead, it is possible to map the memory directly from Python by using mmap:

import pypruss
import mmap

DDR_BASEADDR		= 0x70000000					# The actual baseaddr is 0x80000000, but due to a bug(?), 
DDR_HACK			= 0x10001000					# Python accept unsigned int as offset argument.
DDR_FILELEN			= DDR_HACK+0x1000				# The amount of memory to make available
DDR_OFFSET			= DDR_HACK						# Add the hack to the offset as well. 

with open("/dev/mem", "r+b") as f:					# Open the memory device
	ddr_mem = mmap.mmap(f.fileno(), DDR_FILELEN, offset=DDR_BASEADDR) # 

data = "".join(map(chr, [5, 0, 0, 0]))				# Make the data, it needs to be a string
ddr_mem[DDR_OFFSET:DDR_OFFSET+4] = data				# Write the data to the DDR memory, four bytes should suffice
ddr_mem.close()										# Close the memory 
f.close()											# Close the file

pypruss.modprobe()							       	# This only has to be called once pr boot
pypruss.init()										# Init the PRU
pypruss.open(0)										# Open PRU event 0 which is PRU0_ARM_INTERRUPT
pypruss.pruintc_init()								# Init the interrupt controller
pypruss.exec_program(0, "./ddr_write.bin")			# Load firmware "ddr_write.bin" on PRU 0
pypruss.wait_for_event(0)							# Wait for event 0 which is connected to PRU0_ARM_INTERRUPT
pypruss.clear_event(0)								# Clear the event
pypruss.pru_disable(0)								# Disable PRU 0, this is already done by the firmware
pypruss.exit()										# Exit, don't know what this does.

The firmware to accompany this is:

.origin 0
.entrypoint START

#define PRU0_ARM_INTERRUPT 	19
#define GPIO1 				0x4804c000		// The adress of the GPIO1 
#define GPIO_CLEARDATAOUT 	0x190
#define GPIO_SETDATAOUT 	0x194
#define CONST_DDR 			C31
#define CTPPR_1         	0x2202C 	

START:

    MOV r0, 0x00100000			// Configure the programmable pointer register for PRU0 by setting c31_pointer[15:0]
    MOV r1, CTPPR_1    			// field to 0x0010. This will make C31 point to 0x80001000 (DDR memory).
    SBBO r0, r1, 0, 4

    LBCO r1, CONST_DDR, 0, 4     //Load values from external DDR Memory into R1
BLINK:
    MOV r2, 7<<22
    MOV r3, GPIO1 | GPIO_SETDATAOUT
    SBBO r2, r3, 0, 4
    MOV r0, 0x00a00000
DELAY:
    SUB r0, r0, 1
    QBNE DELAY, r0, 0
    MOV r2, 7<<22
    MOV r3, GPIO1 | GPIO_CLEARDATAOUT
    SBBO r2, r3, 0, 4
    MOV r0, 0x00a00000
DELAY2:
    SUB r0, r0, 1
    QBNE DELAY2, r0, 0
    SUB r1, r1, 1
    QBNE BLINK, r1, 0

    MOV R31.b0, PRU0_ARM_INTERRUPT+16   // Send notification to Host for program completion
HALT

The firmware is basically a mashup of the blinkled program found on Boxysean’s website and the examples found in the Git repository for the PRU C driver.

I have given the library it’s own repository: It is here.

If you are having trouble with this, please write a comment. If you get the message:

ImportError: libprussdrv.so: cannot open shared object file: No such file or directory

That means the .so file is not found. Either your LD_LIBARY_PATH is wrong or the file is not in theĀ  /usr/local/lib as it should.

If you are getting a segmentation fault, make sure the .bin file is in the same directory as your .py-file and that you have modprobed uio_pruss.

Also, if you have any idea how to extend the available DDR memory beyond 0×4000 bytes, I am VERY interested in hearing about it.. (That seems to be the amount of available DDR memory now. Coincides with /sys/class/uio/uio0/maps/map1/size, so maybe that has something to do with it. )

56 thoughts on “PyPRUSS – A simple PRU python binding for BeagleBone

  1. Elias,

    This library is huge, in theory your Python library will extend realtime functionality to the BeagleBone without having to patch the kernel with Xenomai or RT_PREEMPT. You should let the LinuxCNC guys know about this…

    Have you done any performance testing yet to see how many times per second the PRU can be modified using your Python bindings? And does the entire firmware have to be reloaded into the PRU each time you modify the timing intervals, or can pulse timing changes just be mmaped to the PRU from Pythonin realtime without having to reload different firmwares?

    I just got everything compiled now and hope to get some testing done with it in the next few days.

  2. cd PyPRUSS/PRUSS
    make
    export LD_LIBRARY_PATH=/usr/local/lib

    …probably should be “make install” to get pypruss.so installed.

  3. GP, thank you! With the DDR memory mapping there should be plenty of time for the ARM processor to plan out the next path segment and load it into memory without disrupting the operation of any of the PRUs so that there will be a continous flow. A part of the problem with the 8K of “local” data ram available to the PRUs is that you need to stop the PRU in order to write new data to the memory. This effectively hinders any continous operation. With shared DDR memory that is not a problem and you can use the memory as a ring buffer.
    And yes, “make install” must be a part of the installation. Have you had any success trying the examples?

    • Yes I sure did, the blinking example works just like the original example code. Taking a look at ddr_write and mem_write now.

      Any idea how writing 0x1C00000 via the 22 Left Bit Shift activates all three LEDs?

      I’ve been talking with the BeagleBone team about the PRU and pin muxing, it looks like eight pins can be controlled per PRU. In theory this would yield a total of 16 pins that could be hard realtime via the PRU, does your research coincide with that? There is some confusion right now about which pins are actually accessible via the expansion headers, as some of them were not routed to the expansion headers on the BeagleBone from what I understand.

    • This is incorrect. The only thing you can’t write while the pru is running are the registers. The other two pru data memories, 8k per pru and 12k shared, you can read and write to with the pru running. The pru does not and *should not* do something like poll ddr since you’ll flood the memory bus used by the processor.

  4. Hey do you think the Replicape would be sufficient for driving a 4-axis CNC hot wire cutter? Maybe with your themistor capabilities controlling the hot wire temperature?

  5. Try writing “7 in binary” into google and you should see that it becomes 111. It is shifted 22 spaces left since it is gpio1_22..24 I would reckon. If you just want the bottom led try 1<<22. Most if the pru pins coincide with the LCD pins, so since I have space for an LCD cape I need to keep away from them. The 6 remaining pins are working fine!

  6. Thanks for sharing with the community. Your work has been very helpful.
    2 questions:
    1. Using a replicape inspired device tree overlay, I am able to toggle a GPIO using the PRU. However if I comment out
    LBCO r0, C4, 4, 4 // Load Bytes Constant Offset (?)
    CLR r0, r0, 4 // Clear bit 4 in reg 0
    SBCO r0, C4, 4, 4
    then the c host program run from the bbk never returns. I’m guessing its some kind of segment violation, but I am not smart enough to figure out exactly what the above code does.
    2. I want to keep the PRU toggling the GPIO after the host program returns. How to do this? If I send the notification to the host program, then try to continue toggling with the PRU, the toggling seems to stop.

  7. I am having some issues with the “make install”

    cp pyruss.so /usr/lib/python2.7/site-packages/
    cp; cannot stat 'pypruss.so' : No such file or directory

    the makefile lists pypruss.o but it is pypruss.c that came with the bundle. any help would be great.

    • Dave, have you installed the python package with “python setup.py install”? Any problems with that? Also, you probably have to recompile the pasm compiler. Go into PASM/source/code and write ./linuxbuild (or whatever) and then try the blink led example again.

      • I started with fresh Ubuntu 13.04 install on BBB.
        Here is what I have done:
        apt-get update
        apt-get upgrade
        apt-get install build-essential python-dev python-pip
        (before that i was getting arm-linux-gnueabihf-gcc errors)
        apt-get (pypruss)
        python setup.py install (i get the complete message so should be ok)
        export LD_LIBRARY_PATH=/usr/local/lib (I do not get an messages when this completes. Is there something I should check?)
        Recomplied PASM

        Now from /pypruss/examples/blinkled$ make install I get the error I mentioned before.

        When I try to run blinkled.py i get: ImportError: libprussdrv.so No such file or directory

        I am still somewhat new to Linux so I appreciate your help. I am trying to get your software on a system so I can attempt to adapt a Python GUI I wrote for a RaspPi/Arduino/GRBL dot peen engraver. Then once your boards are available play with the hardware.

          • In a fit of frustration I have reflashed the eMMC with Ubuntu again. Before I run through all of the steps I did last time do you have a list or directions on what order/items need to be done to get a new Beaglebone Black ready for a Replicape?

          • Dave, the official recipe for installing the software is here: http://wiki.replicape.com/index.php?title=Replicape_rev_A3_setup
            It’s a series of Makefiles that should install what you need. Look at what they do for debugging. It’s a work in progress, so no promises. Also, you need a Replicape installed, though, but you can probably bypass that on the command line. But if all you need is a working version of PyPRUSS you should not have to go through all that.

  8. Hello, I tried to install the library, but when I try to run the examples, I get:

    prussdrv_open open failed
    Traceback (most recent call last):
    File “blinkled.py”, line 8, in
    pypruss.open(0) # Open PRU event 0 which is PRU0_ARM_INTERRUPT
    SystemError: error return without exception set

    Is there any workaround for this error?

    • Hi! Im working on getting pypruss into the opkg system for angstrom. Hopefully that will improve stability. I’ll let you know when it’s done.

        • I just gave that a try (I am running latest angstrom) and got a very long list of messages after entering “opkg install pypruss”. Here are the last dozen lines of those:

          Package xproto-dbg version 1:7.0.23-r0.0 has no valid architecture, ignoring.
          Package xproto-dev version 1:7.0.23-r0.0 has no valid architecture, ignoring.
          Package xtrans-dbg version 1:1.2.7-r0.0 has no valid architecture, ignoring.
          Package xtrans-dev version 1:1.2.7-r0.0 has no valid architecture, ignoring.
          Package xtrans-doc version 1:1.2.7-r0.0 has no valid architecture, ignoring.
          Package xz-dbg version 5.1.2alpha-r0.0 has no valid architecture, ignoring.
          Package xz-dev version 5.1.2alpha-r0.0 has no valid architecture, ignoring.
          Package xz-doc version 5.1.2alpha-r0.0 has no valid architecture, ignoring.
          Package xz-locale-cs version 5.1.2alpha-r0.0 has no valid architecture, ignoring.
          Package xz-locale-de version 5.1.2alpha-r0.0 has no valid architecture, ignoring.
          Package xz-locale-fr version 5.1.2alpha-r0.0 has no valid architecture, ignoring.
          Package xz-locale-it version 5.1.2alpha-r0.0 has no valid architecture, ignoring.
          Package xz-locale-pl version 5.1.2alpha-r0.0 has no valid architecture, ignoring.
          Package xz version 5.1.2alpha-r0.0 has no valid architecture, ignoring.
          Unknown package ‘pypruss’.
          Collected errors:
          * opkg_install_cmd: Cannot install package pypruss.
          root@beaglebone:~#

          • I used the link you pointed me at to make sure I dowloaded the same image you had. After booting that image, I did this:

            root@beaglebone:~# lsb_release -a
            Distributor ID: Angstrom
            Description: Angstrom GNU/Linux v2012.12 (Core edition)
            Release: v2012.12
            Codename: Core edition

            root@beaglebone:~# uname -a
            Linux beaglebone 3.8.13 #1 SMP Tue Jun 18 02:11:09 EDT 2013 armv7l GNU/Linux

            So I don’t know how to get the same results as you did with “lsb_release -a”

            Hmmm – I am running a Beaglebone Black and now that I read the heading of this article, I think you are working with the white Beaglebone? If so, sorry for the thrashing – is a black version in the cards?

          • Black and white are the same SoC, so dont worry about that. Are you sure you are flashing it right? You holdt the boot button while applying power, activity on both eMMC and sdcard LEDs for a while then eventually after 20 odd minutes all lights glow steady? I want to find out about this too.

          • Well, after doing to many things to remember the order, I rebooted and rechecked:

            root@beaglebone:~# uname -a
            Linux beaglebone 3.8.13 #1 SMP Wed Nov 20 08:28:37 CET 2013 armv7l GNU/Linux
            root@beaglebone:~# lsb_release -a
            Distributor ID: Angstrom
            Description: Angstrom GNU/Linux v2013.06 (Core edition)
            Release: v2013.06
            Codename: Core edition

            Now ,strong>opkg install pypruss,/strong> runs – I get this at the end:

            Configuring libprussdrv.
            Configuring pypruss.
            Configuring ntpdate.
            Job for ntpdate.service failed. See ‘systemctl status ntpdate.service’ and ‘journalctl -xn’ for details .
            Collected errors:
            * pkg_run_script: package “ntpdate” postinst script returned status 1.
            * opkg_configure: ntpdate.postinst returned 1.

            Could this be that I manually installed ntp?

            Now – how do I test? I can’t find the examples directory and blinkled.

          • Thanks – after installing pasm, setting up the sym link, entering the two source files, and assembling the pru code, I ran the python program and got this:

            root@beaglebone:~# pasm -b blinkled.p

            PRU Assembler Version 0.84
            Copyright (C) 2005-2013 by Texas Instruments Inc.

            Pass 2 : 0 Error(s), 0 Warning(s)

            Writing Code Image of 26 word(s)

            root@beaglebone:~# python blinkled.py
            prussdrv_open open failed
            Traceback (most recent call last):
            File “blinkled.py”, line 8, in
            pypruss.open(0) # Open PRU event 0 which is PRU0_ARM_INTERRUPT
            SystemError: error return without exception set

          • So I reflashed the OS and without any of my normal setup process, ran through the sequence of commands on you latest install page – everything seemed to install fine, so I tried running blinkled.py

            Here is what I got:

            root@beaglebone:~# python blinkled.py
            modprobe: ERROR: could not insert ‘uio_pruss’: Numerical result out of range
            modprobe failed
            : No such file or directory
            Traceback (most recent call last):
            File “blinkled.py”, line 6, in
            pypruss.modprobe() # This only has to be called once pr boot
            SystemError: error return without exception set

          • OK – I think I’ve figured it out. I had to establish a PRU.dtbo file in /lib/firmware. I followed Step 2 at http://analogdigitallab.org/articles/beaglebone-black-introduction-pru-icss

            Only deviation to those instructions was to :

            export SLOTS=/sys/devices/bone_capemgr.9/slots

            where as he specified:

            export SLOTS=/sys/devices/bone_capemgr.8/slots

            After I entered echo BB-BONE-PRU > $SLOTS, Here is the last few steps showing the PRU overley is loaded:

            echo BB-BONE-PRU > $SLOTS
            root@beaglebone:/lib/firmware# cat $SLOTS
            0: 54:PF—
            1: 55:PF—
            2: 56:PF—
            3: 57:PF—
            4: ff:P-O-L Bone-LT-eMMC-2G,00A0,Texas Instrument,BB-BONE-EMMC-2G
            5: ff:P-O-L Bone-Black-HDMI,00A0,Texas Instrument,BB-BONELT-HDMI
            7: ff:P-O-L Override Board Name,00A0,Override Manuf,BB-BONE-PRU

            I then ran python blinkled.py and got this great message:

            root@beaglebone:~# python blinkled.py
            AM33XX
            File ./blinkled.bin open passed

            And the far left user led (right next to the ethernet connector) flashed ten times!

            Thanks for the great libraries and gracious help.

  9. Hi!
    i tried that blinkled example and got the following error

    ../../PASM/pasm -b blinkled.p
    make: execvp: ../../PASM/pasm: Permission denied
    make: *** [all] Error 127

    any thoughts?

  10. Cannot load 2013 image

    Good morning.
    I have embarked on using the PRUs and hit a problem very early.
    I cannot load the PyPruss package because I’m running the 2012 image…all my efforts to implement the september 2013 image simply dont work and the result is apparently the 2012 image again.
    Any help would be appreciated :)

    Regards Andrew

      • Hello Elias.

        The problem is this: I downloaded the lastest Angstrom image Sept 2013 and reflashed and get 2012 image identification using lsb-release -a.
        resulting in the same errors as Maddox above Jan 31 2014
        I repeated the whole process 3 times. If i was doing soething stupid i continued to do it no matter how carefully i tried.
        I emailed another forum member and he gave up on angstrom and went to ubuntu.
        I have given up on Angstrom too and now very close to completion using debian…
        So my problem is moot. I must say my experience with debian thus far is better than with Angstrom.

        Thanks for your reply and I am under control for the moment :)

        For anyone’s info: I have documented my steps to implement PRU control with debian into a word document as carefully as I can ( a document that I could use in a year’s time )

        • Elias, thanks for making this. Andrew, do you have a link to somewhere I can read your documented steps for debian. I would really appreciate it. I’m a linux noob and am barely getting around as it is. I could really use your help. Thanks again.

    • Thanks Elias but my experience with debian so far is better, its smaller and feels smoother in its operation.
      Importantly I also have blinkled.py running under debian, thank you for your posted material :)
      I really cannot understand how an Angstrom image remains available on the offical Beaglebone site that has the issue I encountered. My guess would be that Angstrom has lost its driving force.

      Separately; what flaws in debian/ubuntu was angstrom supposed to address? …so I can proceed with my eyes open.

      Thanks, Andrew

      • Andrew, I think the strength of Angstrom is that it’s built on Yocto so adding new software becomes a breeze, but I guess that is really a maintainers argument. It boots quickly, but the latest news is that Ubuntu is also getting systemd, so I guess that will even out. Angstrom has lost some driving force after Koen Kooi left CircuitCo, yes. He is the maintainer still, but does not have the same drive for keeping the BBB image up to date.
        I have included PyPRUSS in my latest (soon to be announced) Replicape Image, so if you just want something that works on Angstrom, you can try that: http://wiki.thing-printer.com/index.php?title=Thing

        It’s a minimal image, though so it’s probably lacking whatever else you need :)

  11. Pingback: Beaglebone Black SPI and Micron 25LC512 | Zephyr-Labs

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>