EmbeddedRelated.com
Blogs
Memfault Beyond the Launch

Tracing code and checking timings

Richard DorfnerMay 25, 20115 comments

Debugging resource limited systems

Applications writers that write code on large systems have it easy. Well, perhaps not easy, but certainly easier. There are some things that they don't have to worry about and there is a huge array of tools available to them when it comes time to debug. The have choices in their toolsets, lots of choices. They also have a large selection of available methods for getting debugging information out to them such as log files, proc entries, pop up dialog boxes or even something as simple as creating and constantly writing to a journal file. Not so much with the small embedded resource limited applications writer.

It might seem obvious to many, but for some reason, it is often forgotten that when trying to follow the flow of operations of a resource limited system, we have a very strong , and very simple tool available. IO ports. Yup, those little individual IO lines that can be driven high or low as outputs.  These can be easily used, along with a 2 or more channel oscilliscope to follow the cpu's progress through code in real time without adding too much delay to the code.  Thankfully, it's relatively rare to find yourself in a situation where the few machine cycles added impacts the code negatively.

So! "Fine." you might think "I can toggle bits, but why would I want to?"  Which is, ostensibly, a very valid question. After all, you have a device that can likely single step through the code as well as program the device, right?  In most cases, this is true, however,  there are often cases where stopping the code to examine the program counter or internal registers is problematic, such as in the case of receiving data from a serial stream that drives some kind of state machine, such as a communications protocol implementation.  The time it takes to stop and examine the state of the machine means that data will get lost.   Or perhaps in a bit of code that controls a motor where if you pause the code there, the motor driver might overcurrent and fail catastrophically. (Such as any motor driver that uses a pulse width modulation to control the drive current and having the cpu generate the pulse width with code and not, say, a built in PWM controller.) 

You might find that driving IO pins in those situations to flag certain places in the critical section of code an ideal method to try and tease out where and how the code is flowing.

This technique is especially effective for finding timings of sections of code. Not that everyone codes the same way, but with small systems that have no real time OS , you usually find a construct along these lines.

#define EVER 1
main()
{
   init_hardware();
   for(EVER){
       do_thing1();
       do_thing2();
       do_thing3();
       do_some_other_thing();
    }
}

It sometimes becomes a question as to whether or not a particular function is taking up too much time or not.  This is easily measured with an oscilloscope if you add a few more lines of code, making the above look something like this, perhaps.

#define EVER 1
main()
{
   init_hardware();
   for(EVER){
       turn_on_led( 1 );
       do_thing1();
       turn_off_led( 1 );

       turn_on_led( 2 );
       do_thing2();
       turn_off_led( 2 );

       turn_on_led( 3 );
       do_thing3();
       turn_off_led( 3 );

       turn_on_led(  4  );
       do_some_other_thing();
       turn_off_led( 4 );
    }
}

Putting a scope probe on the io pin that drives the various LEDs will allow you to physically measure those signals and measure , within a few microseconds, how long each function is taking.   This is assuming that you don't have some interrupt service routine going off in the midst of things. However, if you do, turning yet another IO port bit on at the start of the isr and then off at the end of the ISR lets you see the event on the oscilloscope if you use another probe on that line driven by the ISR.

The best part of this is that the code is minimally invasive in terms of machine cycles spent flipping IO bits on and off.  You can shrink things even further by directly writing to the ports involved in stead of having a function call.  Also, you can leave the code in place and simply enable or disable it by having the code conditionaly compile using #defines to control the inclusion or exclusion of the supporting code.  It makes turning these kinds of feaatures on and off relatively straight forward and easy.

There are, of course, many different ways to do the same thing, however the general concept tends to work very well and is far less invasive than adding statistics gathering code.

Assuming you have perhaps, 8 bits of IO free, and something fancy to use like a logic analyzer, then suddenly a whole world of possibilities starts to open up, you could do any of the following.

1) Write out 8 bit variables in real time and examine them on the Logic Analyzer.

2) Write out state information for the system.

3) Mark the execution of 8 different functions and examine their timing relationships.

4) Combine several bits into a variable, and use the rest to mark the execution of specific functions.

In the end, it all boils down to what you need, or think you need, in the way of an experiment in order to try and ascertain where the code is running or what it is doing or how it is misbehaving.

Till next time,
Keep coding!

Rick



Memfault Beyond the Launch
[ - ]
Comment by jms_nhMay 26, 2011
A tried and true technique for timing analysis.
[ - ]
Comment by horsedorfMay 30, 2011
Indeed! There are many such little techniques that seem obvious in retrospect, yet all too often I find that they are simply forgotten or not even thought of during debugging by colleagues.
[ - ]
Comment by mba128June 7, 2011
Maybe you could trial UnetICE for your debugging www.armkits.com
[ - ]
Comment by horsedorfJune 7, 2011
It's not really a matter of not having an ICE of some kind. The issue is with how debugging with any of the JTAG ICE's has the deficiency of interrupting timing of the program. Even if you have a relatively intelligent ice that can look to see what values a variable has and only stop the cpu when the variable achieves a certain value or range of values has the short coming that it stops the cpu, and serially clocks out the data it is looking for. All of which is a time hit that sometimes is not wanted or worse, suddenly makes whatever bug you are hunting go away and not be visible anymore due to odd time dependencies. Rick
[ - ]
Comment by tedmarJuly 19, 2012
Maybe, I am really an ancient man. But today, I am still using some tricks from 1980, when no ICE or Logic analyzers were at my disposal. In all my boards with a microcomputer, there is a little LED of status, saying that all work ok. It works blinking and controlled in the same way as a Watchdog timer. If there is place and IO available, maybe 4 or 5 State Leds indicating a state number of the state machine that command my firmware. If there is another pin of IO free, set it to output. In an interrupt, set it to 1 and when interrupt is abandoned, set it to 0 This 'poor man's analyzer' was useful not only for me in development times but also for maintenance people.

To post reply to a comment, click on the 'reply' button attached to each comment. To post a new comment (not a reply to a comment) check out the 'Write a Comment' tab at the top of the comments.

Please login (on the right) if you already have an account on this platform.

Otherwise, please use this form to register (free) an join one of the largest online community for Electrical/Embedded/DSP/FPGA/ML engineers: