PRUSS debugging best practices

962 views
Skip to first unread message

Jerrill

unread,
Apr 13, 2012, 7:12:19 AM4/13/12
to beagl...@googlegroups.com
I'm getting started experimenting with the PRUSS on the BeagleBone. I've been trying to run the GPIO toggling example from TI. I finally realized that I was getting a segfault because the PRUSS was being held in reset. Now it's never making it to the HALT statement in the first pinmux binary that's being downloaded.

So my question is whether anyone can offer up any debugging best practices for PRUSS code. Thanks in advance!

Jerrill

Kyle Manna

unread,
Apr 14, 2012, 9:02:14 AM4/14/12
to beagl...@googlegroups.com
It sounds like you're just getting started, so I'd do a few things:

1) Write a program that just writes to the local memory address of the
PRU core you're using.  If the code loads and run, you can read the
memory from Linux and check that your code is running.  Later, you can
re-use this code to insert the ugly equivalent of a printf() to learn
more about some part of your code:

#define CONST_LOCAL_RAM C24
mov    r13, 0xbabec0de
sbco    r13, CONST_LOCAL_RAM, 0, 4


You should then have just enough to write 0xbabec0de to 0x4a300000
(from Linux devmem2) on the AM3359 assuming you are using PRU0, if
it's PRU1 it'll be at 0x4a302000

2) Setup IRQs, the INTC can be overwhelming at first, but once you
reduce it's true capabilities to what you really need it's not that
bad.  Get the PRU to trigger an IRQ to linux, and then the userspace
programs using uio_pruss can display information as it happens, maybe
you might setup a DEBUG IRQ that fires and the userspace program then
immediately reads something from the PRU's local memory and prints it
to your console.

3) Setup macros to do push/pop so you can create assembly functions.
This will enable you to use call/ret safely by saving and restoring
register contexts.  I have the follwoing simple macros in pasm header
file:

#define sp r24
#define lr r23
#define STACK_TOP       (0x2000 - 4)
#define STACK_BOTTOM    (0x2000 - 0x200)

.macro stack_init
    mov     sp, STACK_BOTTOM
.endm

.macro push
.mparam reg, cnt
    sbbo    reg, sp, 0, 4*cnt
    add     sp, sp, 4*cnt
.endm

.macro pop
.mparam reg, cnt
    sub     sp, sp, 4*cnt
    lbbo    reg, sp, 0, 4*cnt
.endm

Now you can define simple functions like:
do_something:
push lr, 1 // save r1
push r1, 4 // save r1-r4

... do stuff ...

pop r1, 4
pop lr
ret

4) Then I would attempt to do something like toggle a GPIO and hook it
up to an o-scope if you so desire (or maybe you can use a LED gpio).
Toggle the GPIO using the PRU firmware you write that sets up and
configures the GPIO blocks. For access to the global system memory
from the PRU core, you need to enable the OCP master ports in the
SYSCFG register (TRM 4.9.1.2), this wasn't obvious to me at the time.

I have updated a kernel space pruss mfd driver that some folks from TI
submitted to the arm kernel mailing list, but was never accepted. If
a few people were interested, I could setup a blog with some quick
demos on the beaglebone and post the code on github.

- Kyle
www.kylemanna.com

> -- To join: http://beagleboard.org/discuss
> To unsubscribe from this group, send email to:
> beagleboard...@googlegroups.com
> Frequently asked questions: http://beagleboard.org/faq

Bas Laarhoven

unread,
Apr 14, 2012, 9:30:47 AM4/14/12
to beagl...@googlegroups.com
On 14-4-2012 15:02, Kyle Manna wrote:
> It sounds like you're just getting started, so I'd do a few things:
>
> 1) Write a program that just writes to the local memory address of the
> PRU core you're using. If the code loads and run, you can read the
> memory from Linux and check that your code is running. Later, you can
> re-use this code to insert the ugly equivalent of a printf() to learn
> more about some part of your code:
>
> #define CONST_LOCAL_RAM C24
> mov r13, 0xbabec0de
> sbco r13, CONST_LOCAL_RAM, 0, 4
>
>
> You should then have just enough to write 0xbabec0de to 0x4a300000
> (from Linux devmem2) on the AM3359 assuming you are using PRU0, if
> it's PRU1 it'll be at 0x4a302000
Yes, seed all memory to see what's happening. Make sure you can control
the PRU running state (not the reset!) and detect it (HALT instruction).
If the PRU is not running, you can dump all registers, cycle counters,
etc. to the console. The PRU even supports single stepping but I've not
used that up to now.

>
> 2) Setup IRQs, the INTC can be overwhelming at first, but once you
> reduce it's true capabilities to what you really need it's not that
> bad. Get the PRU to trigger an IRQ to linux, and then the userspace
> programs using uio_pruss can display information as it happens, maybe
> you might setup a DEBUG IRQ that fires and the userspace program then
> immediately reads something from the PRU's local memory and prints it
> to your console.

Use only if needed.

>
> 3) Setup macros to do push/pop so you can create assembly functions.
> This will enable you to use call/ret safely by saving and restoring
> register contexts. I have the follwoing simple macros in pasm header
> file:

Yes, as soon as your code gets more complex you'll need this. But only
after you got a decent memory layout, proper initalization etc.

Yes, but be carefull ! Linux (kernel) memory is easily corrupted if you
make a mistake.
You can do the enable both from user space or from the PRU. If you use
the PRU you can turn it on and off at need, reducing the risk of memory
corruption if your program faults.

-- Bas

Kyle Manna

unread,
Apr 14, 2012, 10:58:46 AM4/14/12
to beagl...@googlegroups.com
Someone has started a PRU debugger, but it still seems that it's in the early stages of development:





Frequently asked questions: http://beagleboard.org/faq
-- To join: http://beagleboard.org/discuss
To unsubscribe from this group, send email to:

Frequently asked questions: http://beagleboard.org/faq

-- To join: http://beagleboard.org/discuss
To unsubscribe from this group, send email to:

Christopher Piggott

unread,
Apr 15, 2012, 9:27:20 AM4/15/12
to beagl...@googlegroups.com
On Saturday, April 14, 2012 10:58:46 AM UTC-4, Kyle wrote:
Someone has started a PRU debugger, but it still seems that it's in the early stages of development:
 

That's me.  I need to spend time on that.  I'm stuck in the hardware world for another week, after that I should have time.


Rob

unread,
Apr 17, 2012, 4:53:25 PM4/17/12
to beagl...@googlegroups.com
Hi Kyle, did you get the PRU to toggle a GPIO output successfully? 

I've confirmed my code is running on the PRU, however I cant get any outputs to change. 
 

4) Then I would attempt to do something like toggle a GPIO and hook it up to an o-scope if you so desire (or maybe you can use a LED gpio). Toggle the GPIO using the PRU firmware you write that sets up and configures the GPIO blocks.  

 
How did you "configure the GPIO blocks"? 

I've been working with TI's example "PRU_gpioToggle", however all the constants in pinmux.hp, eg "#define PINMUX13   0x54" don't match the AM3359's docs, and especially the offsets from "#define SYS_BASE 0x01C14100".
 

I have updated a kernel space pruss mfd driver that some folks from TI submitted to the arm kernel mailing list, but was never accepted.  If a few people were interested, I could setup a blog with some quick demos on the beaglebone and post the code on github.

 
Some demo code would be awesome! anything would help, even if it wasn't thoroughly documented. 

Cheers, Rob.

Kyle Manna

unread,
Apr 18, 2012, 1:22:43 AM4/18/12
to beagl...@googlegroups.com
Hi Rob,

The first key part is if you intended to use the normal GPIO and need
to access the L3 bus, you'll need to enable access by STANDBY_INIT in
the SYSCFG register. I wasted alot of time trying to access main
system memory until I stumbled on this bit.

Here is some rough code I had laying around that would loop and toggle
a GPIO using the eCAP counter. You'll need to disable the eCAP loop
code or configure eCAP yourself. It's ugly, I'd clean it up if I had
time. Hopefully it'll at least give you ideas as to what to do.

Push and pop are macros I wrote to simulate a stack:

#define STACK_TOP (0x2000 - 4)
#define STACK_BOTTOM (0x2000 - 0x200)
.macro stack_init
mov sp, STACK_BOTTOM
.endm

///////////////////////////////////////////////////////////////
// Stack Push
// reg - starting register to push
// cnt - number of registers to push starting at reg
///////////////////////////////////////////////////////////////


.macro push
.mparam reg, cnt
sbbo reg, sp, 0, 4*cnt
add sp, sp, 4*cnt
.endm


///////////////////////////////////////////////////////////////
// Stack Pop
// reg - starting register to pop
// cnt - number of registers to pop starting at reg
///////////////////////////////////////////////////////////////


.macro pop
.mparam reg, cnt
sub sp, sp, 4*cnt
lbbo reg, sp, 0, 4*cnt
.endm


///////////////////////////////////////////////////////////////
// GPIO test
//
// @param R1 number of loops to run
///////////////////////////////////////////////////////////////

gpio_test:
        push    lr, 1
        push    r1, 4
        mov     R3, R1

        // Set OE to output for GPIO 33 & 34
        mov     r4, #0x4804c100
        LBBO    R1, r4, 0x34, 4
        mov     R2, #0xfffffff9
        and     R1, R1, R2
        SBBO    R1, r4, 0x34, 4

gpio_loop:
        LBBO    R1, r4, 0x3c, 4
        and     R1, R1, R2
        SBBO    R1, r4, 0x3c, 4

        LBBO    R1, r4, 0x3c, 4
        or      R1, R1, #0x6
        SBBO    R1, r4, 0x3c, 4

        sub     R3, R3, #1

        QBGE    gpio_end, R3, #0 // quick branch if 0 > r3
        //QBA gpio_loop

GPIO_POLL_IRQ:
        lbco    r1, c3, 0x2e, 2
        QBBS GPIO_CLEAR_CONT, r1.w0, 6
        QBA GPIO_POLL_IRQ

GPIO_CLEAR_CONT:
        mov     r1, 0xff
        SBCO    r1, c3, 0x30, 2
        QBA gpio_loop

gpio_end:
        pop         r1, 4
        pop         lr, 1
        ret

- Kyle
www.kylemanna.com

Tom King

unread,
May 16, 2013, 11:21:52 AM5/16/13
to beagl...@googlegroups.com
I'd be interested in this.

Thanks

Christopher Piggott

unread,
May 16, 2013, 2:17:13 PM5/16/13
to beagl...@googlegroups.com
On Saturday, April 14, 2012 9:58:46 AM UTC-5, Kyle wrote:
Someone has started a PRU debugger, but it still seems that it's in the early stages of development:




That's me.  I've not gotten very far with it.  The concept is to have a small, ncurses based debugger you can use to load programs into the PRU, set breakpoints, look at and set registers and memory.  It's C++.  If anybody's interested in helping me finish it send me a private message -- otherwise, I plan to get back to the project this summer.  It's still something I very much want.

--Chris

Reply all
Reply to author
Forward
0 new messages