Tracing code and checking timings
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
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
turn_on_led( 1 );
turn_off_led( 1 );
turn_on_led( 2 );
turn_off_led( 2 );
turn_on_led( 3 );
turn_off_led( 3 );
turn_on_led( 4 );
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,
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: