Debugging your PRU C code

Recently I decided to turn my attention to the fairly unique Programmable Realtime Units on the BeagleBone's TI Sitara processor. My earlier blog post covered Coding for the BeagleBone PRU with C in 2019 and hopefully showed how it's become easier to work with the PRUs over the last few years. Whilst it's great to be able to write C code in a nice IDE before deploying, it's even better if you can debug your code and see what's happening when things don't quite go to plan (i.e. 90% of the time). If you're looking a working with the PRUs and you haven't read that post then I suggest you do, as I'll be starting where that blog post left off.

 

XDS 110 debuggerWhat you'll need

To follow along with this guide you'll need:

  • A BeagleBone. I've worked with the black and green but I'm pretty sure any one will do.
  • A debugger. I used a standalone XDS100. There are more expensive options like the XDS200, etc. but this will do. The XDS110 from a Launchpad might work. It failed when i tried before, but I know a bit more now!
  • A JTAG headerheader to solder to your beaglebone. There are a few options, I went with FTR-110-51-L-D-06, but the important bit is FTR-110-**-*-D-06

 

Get your code deploying manually

There's no point trying to get this setup unless you have some simple code that you know you can build, deploy and run on the PRU. I suggest something like blinky to start with. Follow my previous guide to get it running from a manual deployment. Use the same code for debugging and you can be sure that it's the process that you're tweaking and not your code.

 

FTR-110 headerSolder on your header

I hope you're up for a little surface mount soldering. The header is 0.05" pitch with is small, but definitely manageable with a reasonable soldering iron. I considered using hot air, but decided just to go with an iron instead. The pads should be fairly obvious on the bottom of the BeagleBone. I found that they were pre-tinned on the green but left clear on the black (both the Element14 and BeagleBone.org versions). As I mentioned earlier there are a few different versions of the header. Provided it's a FTR-110-nn-p-D-x then you will have 0.05" pitch pins, and each rows will be 0.1" apart. The 110 stands for 10 pins per row and D for a double row header. I think the correct length is the nn=51 version but I know other people have used slightly different lengths without a problem. The p is for the plating - user preference on that one. The x should be 06 meaning that pin 6 is deliberately missing to match up with TI's cTI-20 IDC connector on the XDS110. If you're not using this programmer then I don't suppose it will matter. Otherwise, if you couldn't find the 06 version then yank out pin 6 now. Personally, I have almost connected this the wrong way round, so a polarised connector is probably worth the effort!

 

I also found that with the header in place you'll probably want some stand-offs on the board. Mine are just 20mm M3 nylon screws and they seem to work just fine.

 

Set up CCS

Setup target configuration

You will need to add a target configuration. This just tells CCS what debugger to use and what processor to expect it to be connected to. Go to the target configurations window, add a new one, select "BeagleBone_Black" and you're done. There is an advanced tab that I spent a while messing around with, but you don't need to. You can also click "Test Connection" to verify that everything is connected OK.

 

{gallery} Target configuration

Target configuration

Target Configuration for the BeagleBone: This is all you need to connect to your BeagleBone

Target configuration (Advanced)

Target Configuration (Advanced): Leaving everything here as default works fine.

Target configuration (Text Connection)

Target Configuration (Test Connection): Check that your debugger is working and you can see the BB.

 

Setup debug configuration

Setting up the Target Configuration isn't too tricky. The bit that had me stumped for a while was the Debug Configuration. This tells CCS what to do when you want to debug your code. I was banging my head again the wall with errors like "Cannot access the DAP" and "Verification failed". TI's PRU training was fairly helpful but didn't mention these problems. "Just click debug" and everything should be fine.

 

initialization scriptDebug configurationHowever, if Linux has ever been running on your BeagleBone, you need to undo some of the things it has configured. There is a small snippet of JavaScript that you need to run as the debug configuration starts - the magical bbb_pru_startup,js which I stumbled across referenced here and available here. This is the missing link. It's too important to be hidden away and isn't mentioned at all in TI's PRU training. I can only assume that the training is done on a virgin Sitara processor. It's not included in the otherwise excellent PRU software support package. I have discovered that as long as you run this script once before debugging that is enough. Further debuggging session don't need it - until you boot to Linux again. It doesn't get in the way though, so I suggest you leave it as part of your debug configuration.

 

To get this set up, click the down arrow next to the green bug and select "Debug Configurations...". Click the button to create a new Launch Configuration (different name, same thing). Select the Target Configuration from the next step. Were you to go ahead and try to debug now, you'd get the "Cannot access the DAP" error that plagued me for ages. Just to the right of the empty initialization script box you can click "File System..." or "Workspace..." and reference bbb_pru_startup.js from wherever you saved it. I put it in the workspace but it doesn't matter where it is.

 

You don't need to poke around in the JavaScript, but of course I did. It sets up a few pins related to the PRU Cape which is great if you have one. The interesting thing though seem to be this:

print("Enabling ICSS clock");

debugSessionDAP.expression.evaluate("*((unsigned int*) 0x44E000E8 ) |= 0x02;");

 

 

print("Resetting ICSS");

debugSessionDAP.expression.evaluate("*((unsigned int*) 0x44E00C00 ) |= 0x2;");

debugSessionDAP.expression.evaluate("*((unsigned int*) 0x44E00C00 ) &= 0xFFFFFFFD;");

This is documented on the PRU_ICSS Debug page as follows:

Because the PRU-ICSS cores are deeply embedded inside the AM335x, there are multiple registers that need to be configured in order to perform JTAG debug. This includes:

  • ICSS clocks
  • ICSS reset
  • Pin muxing for PRU-ICSS signals
  • And in some cases even enabling various JTAG clocks so you can connect. (Linux turns many clocks off.)

A script was developed in large part from the work in Debug Configuration Initialization Scripts to handle this automatically.

.

Let's debug!

Prevent Linux from booting

You need to make sure the Linux isn't currently running on the BeagleBone. Unfortunately I don't think you can debug the PRU whilst the Sitara's A8 core is busy running Linux. Obviously this means you can't debug any interaction between your Linux code and the PRU. Maybe it's possible, but I've not heard of it. (jancumps has an excellent post on using CCS to debug just the Linux bit by the way.)

 

The easiest way is to make sure you have no micro SD card inserted and hold down the "Boot" button (S2) whilst powering on. I read other guides that suggested formatting the eMMC but you don't have to go this far.

 

Debug your code

With Linux temporarily disabled, and the startup script in your debug configuration, all is right with the world. Debug your PRU project and you should see it fire up in CCS, deploy and stop at a breakpoint in the first line of your PRU code. We've cracked it! Happy PRU debugging.

Debugging the PRU!