You know you shouldn’t, but you do. I do. We all do. Though we don’t like to admit it. Yes, I use print statements to debug my code. And I work on debug tools for programmers and hardware engineers. I guess that makes me even guiltier.
Despite having a plethora of debugging tools and scripting languages, I’ll admit it – one of the first things I do to try to get a handle on a bug is to throw in a few printfs. Even though I can use valgrind with the best of ‘em, there is something comforting about seeing exactly where your code is as it makes progress (or doesn’t).
But what about embedded folks? If you’re putting an application together for a desktop machine, you have a file system and terminal windows – you can create log files and printouts galore. In the embedded world you may not have that luxury. You might have a display or a file system, but more likely you don’t. Many of today’s embedded systems do have a serial port where you can connect it to a terminal (usually virtually). One common alternative I’ve seen used – especially with systems based on embedded Linux – is to create a file system in flash memory. This can later be accessed by the debug system.
ARM has put together an approach called “semi-hosting” that they have integrated into their debugger and compiler tools. Semi-hosting allows the embedded developer to put printfs in their embedded code. The linker redirects these calls to the host system (for simulated designs) or to the debugger (for prototype boards) for output. Not only can you use printfs with semi-hosting, but you can call just about any standard I/O function. You can open log files – or even input files. I’m not going to teach you all about ARM’s semi-hosting capability in this post, but you can read about it here.
So ARM has you covered if you are running one of their “programmer’s view” virtual prototypes or one of their development boards, but what about running the processor in a logic simulation? ARM never bothered to add support for the semi-hosting in their logic simulation models. And I don’t think they should – these are silicon sign-off models that need to perform exactly as the real gates will. Tweaking them to support debug features would not be a good idea. But if you’re designing hardware for an ARM processor, at some point you’re going to need to run some code on that simulation model. And – thanks to Murphy’s Law – it’s not going to run the first time. Then you’re going to need to debug it. While my company has a fancy debugger that solves this problem, your first instinct is probably to “throw in a few printfs.”
One common way to support some kind of a print statement is to add a virtual character device to the logic simulation of the design. Basically, this is just an address where you can write a series of characters which, through the magic of HDL, will end up in the simulation transcript. With this you can write a “P” if the test passes or an “F” if the test fails. Of course, you now need to hunt through your simulation transcript to find all your characters.
One clever approach I saw one of my customers using was to grep the log.eis file for writes to that address. In his software he wrote a series of characters to address 0×80000000. This was done with a simple modification to the retarget.c file supplied by ARM. After the program ran in the logic simulation he did a grep of the log.eis file (on newer processors this is now the “tarmac.log” file – I really wish ARM could be consistent with these things).
% grep –i –e “Write Byte.*80000000” log.eis > foo
Then he lined up the characters using cut and a simple filter program.
% cat foo | cut –c 111- | to_string > bar
Now the file “bar” has a nicely formatted set of characters as they were written to address 0×80000000. For reference, here is the listing of the “to_string” program:
% cat to_string.c
while (EOF != scanf(“%x”, &n))
One of my engineers found a way to add a virtual terminal to an ARM processor that works with this same approach – and with the semi-hosting, too – to put the characters in a terminal window live during the simulation (rather than post processing, as above). It’s a pretty neat approach, and it lets you keep track of what that pesky code is doing as the simulation proceeds. It works with no changes in the software at all; it just gives the printf a place to go. However, with a slight modification to the code he can eliminate all the printf processing (which is really sloooow) in the logic simulation. If you’re interested, shoot me an e-mail and I can set you up with it.
Printfs and log files are often used as debug tools. They are particularly useful when you don’t want to stare at a debugger while a system is running for long periods of time. Although the computer science purists may denigrate them, they are simple, easy, and they work. With a bit of ingenuity you can use them on a wide variety of targets.