PyPRUSS – One library to rule them all

Ok, yet another revelation for me this is. When designing Replicape rev 0 and rev 1, I spent an inordinate amount of time trying to work around the limitation that only some of the GPIO pins were available to the PRU. As it turns out, this assumption was was not right. In fact, all GPIO pins are available to both PRUs!

The downside is that it does take a bit longer to toggle the pins since the PRU must access the global memory instead of the local memory. How much longer? About four times:


In both the pictures above, the time division is 20ns. The fast method uses one instruction for each update, since it is only a MOV command. The “slow” uses four instructions for one SBBO command to toggle the state of the pin, so there is a significant difference, but still the minimum pulse width for the DRV8825 stepper motor controllers is 1.9ms, so even with the slow method, you can toggle the pin 100 times for each “legal” toggle.

Here are the source codes for the assembly:

// Fast speed test. Each loop iteration takes 3 instruction cycles = 15ns
.origin 0
.entrypoint START

    LBCO r0, C4, 4, 4		
    CLR  r0, r0, 4			
    SBCO r0, C4, 4, 4		

	MOV r30, 1<<14
	MOV r30, 0
// Slow speed test. Each loop takes 4+4+1 = 9 instruction cycles = 45ns
.origin 0
.entrypoint START

#define GPIO1 				0x4804c000	
#define GPIO_CLEARDATAOUT 	0x190
#define GPIO_SETDATAOUT 	0x194

    LBCO r0, C4, 4, 4		
    CLR  r0, r0, 4			
    SBCO r0, C4, 4, 4		

	MOV r2, 1<<12

	SBBO r2, r3, 0, 4
    SBBO r2, r4, 0, 4


7 thoughts on “PyPRUSS – One library to rule them all

  1. Congrats on the Cape Win!

    I’ve been following your blog along with the boxysean blog and also read Lyren Brown’s post and the question that seemed to be difficult to answer was how to map the GPIO pin into the R30 register. Probably not asking that right but basically the “slow method” was the result of not finding a way to direct access through the local memory to access the GPIO pin and thus blink the LED. I’ve gotten the “slow method” to blink the LED going through boxysean’s blog but now try to do the “fast method” which in the visual LED world won’t matter cuz you goto go slow anyway but it’s the method I’m after. So how did you map that?

    Thanks you for great explanations here.

    • Gary, the pins are mostly hardcoded into the R30-register for output and R31 for input. I’m sure you know of the debug file system for muxing pins located in /sys/kernel/debug/omap_mux/. You need to mux that pin to the proper PRU function. In the above example that is:

      echo 6 > /sys/kernel/debug/omap_mux/gpmc_ad12

      If you cat that same file you will see that MODE6 is pr1_pru0_pru_r30_14,
      so register r30, pin 14 of PRU0. This corresponds with

      MOV r30, 1< <14

      in the above example.
      Here is a list for the other pins:
      Beaglebone PRU pins
      As far as I can tell, none of the USR leds have a dedicated pin connected to the PRU and that makes sense since they do not need to be toggled very quickly.. : )

  2. I guess you mean 1.9 microseconds for the minimum pulse width on STEP input of DRV8825.

    A conservative max-speed of 200mm/s on a common Prusa (80steps/mm) will require 16.000 steps/second (or 62.5 microseconds/step).

    Really cool stuff this PRU thing.

    • Miguel you are right, from the datasheet of DRV8825, the minimum step pulse high and low durations are 1.9us. Still the maximum step frequency is 250 kHz giving a minimum pulse duration of 4us.

      The PRU is awesome! Assuming your calculations are correct, you could even have 8 in micro stepping and still be within the limits of 4us..

  3. I’ve been working on similar PRU code to talk to the Replicape and BeBoPr with LinuxCNC. I measured the PRU performance when talking to the GPIO registers and it’s pretty impressive. The maximum sustained write speed I measured is 40 nS (which matches your ‘scope trace), but there is a write buffer that keeps the PRU from stalling for short bursts of writes. Full details in the Beagle G+ group.

  4. I wrote the PRU side of the code to drive the hobby servo outputs of the board I mentioned (GPS/IMU) a month or so ago. A question that I have not seen addressed is how do you protect the inter-processor communication? I would expect to see a spinlock to control whether the main CPU or the PRU can access the shared RAM area to communicate between the two. I looked in the PRU code you posted for your replicape and did not see a spinlock. Is the interrupt / event method you used sufficient? Have you seen any problems with that?

    The stuff I just did was pretty simple, but I am considering using the PRU for a more complex closed loop motion control system that will have a lot more inter-processor communication.

    Thanks for your postings on working with the PRU, they were a lot of help to get stuff running.
    Best Regards,

    • Bob, thanks for this excellent question! I have not had any problems with it that I know of (o_0). Usually the write head is way ahead of the read head, so perhaps I’ve just been lucky.. I should look into it to see if there are any issues with this.

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>