PRUSS debugging best practices

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

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

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.

#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.

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

Someone has started a PRU debugger, but it still seems that it’s in the early stages of development:

https://github.com/wz2b/prude

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.

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.

  1. 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.

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

I’d be interested in this.

Thanks

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