Macros to enhance debuggability

Go To Last Post
9 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Over the past year, I've spent a lot of time using a JTAGICE debugging pod to watch the execution of some firmware I've developed for an ATmega324A. Most of the time, I have to say that the WinAVR/AStudio 4.19/JTAGICE combination's performance is at least mediocre. One of its more common nuisances, though, is the dismally low probability that one can inspect the values of program variables by hovering the mouse pointer over one of them in one of the debugger's sourcecode-browsing window. In an effort to enable the debugger to show the values of interesting variables, I've resorted to the fairly sleazy practice of adding extra code to copy the variables of interest into some global that the debugger will usually be willing to display (although the debugger often claims that even globals are "out of scope"). Other than temporarily adding a fair bit of unnecessary code, the technique works well enough, and with the aid of some macros, is not terribly painful to use. In case it would be of use to anyone, here is an #include file that can be pulled into source modules to make them "more debuggable" :

// debugTrace.h - Macros to force visibility of intermediate results
//
// When debugging optimized code, computed results are very seldom
// visible to the debugger, even if the sourcecode tries to assign
// them to specific variables.  The macros in this package simply
// copy their arguments into a volatile global variable, thus forcing
// the compiler to put the values somewhere that (at least temporarily)
// the debugger can "see".
//
#ifndef traceFloat
#include 

#ifdef FORCEDEBUGVISIBILITY
union Signed   {  int8_t Byte;  int16_t Short;  int32_t Word; };
union Unsigned { uint8_t Byte; uint16_t Short; uint32_t Word; };
//
union VisibleDebug { Signed s; Unsigned u; float real; };

// This global will hopefully allow the debugger to examine
// intermediate results:
//
extern volatile VisibleDebug Exposed;
//
//
#define traceInt8(x)   Exposed.s.Byte = x;
#define traceUint8(x)  Exposed.u.Byte = x;
#define traceInt16(x)  Exposed.s.Short = x;
#define traceUint16(x) Exposed.u.Short = x;
#define traceInt32(x)  Exposed.s.Word = x;
#define traceUint32(x) Exposed.u.Word = x;
#define traceFloat(x)  Exposed.real = x;
//
#else
#define traceFloat(x) 
#define traceInt32(x)
#define traceUint32(x)
#define traceInt16(x)
#define traceUint16(x)
#define traceInt8(x)
#define traceUint8(x)
#endif

#endif 

And here's the small static library module that defines the inspectable global:

// debugTrace.cpp - The definitiion of the "visible to debug" global
//
#ifndef FORCEDEBUGVISIBILITY
#define FORCEDEBUGVISIBILITY 1
#endif

#include "debugTrace.h"
volatile VisibleDebug Exposed;

A typical use of it would look something like this:

#include "debugTrace.h"
// ...
    int16_t anAnswer = someFunction(withAnArgument, orTwo);
        traceInt16(anAnswer)
    nowUse(anAnswer);
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

There are various tricks you can use.
1. build with CodeVision. you get a better view of variables in the debugger.
2. ditto ImageCraft, Rowley, IAR, ...
3. use AS6
4. use avr-gcc -O1
5. conditionally declare your local as static
6. or even volatile static.

Any system needs to be easy to manage. If you like using avr-gcc non-portable "features", then (1), (2) are not practical.
MACROS need to be visible but not obfuscate the code.
Ideally, they remain there permanently. You just turn them on and off via Debug / Release build.

I suspect that it will all come down to personal choice.

Cliff will soon comment that he does not believe in either -O1 or MACROS.

In practice, you only ever step through tiny sequences. The bulk of your statements never need special debuggability.
Any final testing has always got to be with the Release build because the timing will be very different to the "low optimisation" Debug version.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Having just done battle with AS6 I feel your pain. Not only is there a problem with debugging -Wl,-relax code (which I need because I'm trying to write a "tight" bootloader) but the optimiser is so aggressive that I simply cannot "see" any of the intermediate variables but I am at 2040 bytes out of 2048 (with UART debug turned on) so there simply is no room to use any of the tricks such as making variables volatile and such. I simply have to breakpoint the code and examine the CPU registers after studying the .lss to work out where values will be held and the fact that the source annotation and .lss are out of step because of the -Wl,-relax thing just makes things worse!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Cliff,

Surely you get it up and running in BOOTSIZ=4096 bytes.

Then do final tests with full optimisation. It is when you get anomalies here that the fun begins! But at least you have got your main logic ironed out in the initial debugging.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Surely you get it up and running in BOOTSIZ=4096 bytes.

If only that were possible:

C:\Program Files\Atmel\Atmel Studio 6.0\devices>grep BOOT_SECT ATmega16.xml
          
          
          
          

If I had an AVR here that had 32K and a 4K BLS I'd be using that - but sadly I don't.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Go on. A man of your means could acquire a mega32.

At a pinch you could do initial debug with a different family, but they all have 'worse' memory maps.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You prompted me to delve into my boxes. I have any number of 168, 88, 8, even 8151 and 163 but nothing with a larger boot section.

Sorry for off-topic. One thing I will add is that I was using:

lba = ((firstclust - fat_offset) * SecPerClus) + RootDir;

which generated quite a long complicated bit of code that was hard to follow. So I broke this down into:

	lba = firstclust - fat_offset;
	lba *= SecPerClus;
	lba += RootDir;

which gave me three distinct, annotated blocks of code making it easier to follow in the debugger and the really curious thing is the code got 4 bytes shorter! So maybe breaking things up for simplicity (at least so it's easy to see that SecPerClus was being held in R5 for example) might not be such a bad idea.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
If I had an AVR here that had 32K and a 4K BLS

You should not DEBUG on targeted device.
It is not worth the effort.
Pick ATMega128 and you will probably never hit any constraint with RELEASE designed for ATMega32. Several #ifdefs are needed but that is much easier than wasting time with "tinys".
Considering debugging with -O0, then 4x flash, 2x RAM, 2xIOs is a good proportion. I did that with ATMega162 -> ATTiny2313 and that did the job.

david.prentice wrote:
2. ditto ImageCraft, Rowley, IAR, ...

Cannot see "Eclipse + AVARICE"

No RSTDISBL, no fun!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
Considering debugging with -O0

I seriously disagree.

Quote:
Cannot see "Eclipse + AVARICE"

I was only considering non crash-prone systems.

David.