# Embedded Applications Software Testing Life Cycle

I have no JTAG and I can not afford it for my ARM Cortex M3 Chip.

I'm wondering, how professionals debug their applications ?

1. Do you flash your uC every time, frequently, then you run and test, then change? is that a bad practice ?
2. Do you only use UART or LCD for writing logs on the screen ?

I need tips and tricks from the professionals :)

-
It depends on what you have available. A JTAG is nice. UART works as well. Simply blinking a LED can be used. Whatever gets the job done. –  Peter Karlsen Mar 26 at 8:52
@PeterKarlsen I only have LCD, UART can not be use, its shared with the LCD FSMC bus. I also have no JTAF, I think JTAG would make the process easier? –  Ahmed Saleh Mar 26 at 8:54
A JTAG would probably make it easier. You are in the same situation I was in earlier. For many years I didn't have a JTAG. I did have an oscilloscope, so often i connected it to a couple of spare ports on the device I was working on. Then I would toggle the port bits on certain events in my code to get an idea of what was going on. You can probably use the LCD, but it is not easy to get a grasp of the timing when using the LCD. –  Peter Karlsen Mar 26 at 9:01
Something like a 3D renderer ought to be well tested before getting anywhere near the embedded CPU. –  Brian Drummond Mar 26 at 10:37
Not directly on topic, but... many evaluation boards have built-in JTAG/SWD. The STM32F4 Discovery eval board, for example, includes an embedded STLINK v2 USB JTAG module, and is only US\$15 or so. The JTAG/SWD is slow but perfectly functional and can be redirected off the eval board to another target device. That might solve your "can't afford" problem, at least. –  markt Mar 26 at 10:48

This is more of a blog post, but here goes:

If you write in C, you can compile your code and run it on your desktop. This won't test the low-level hardware drivers, but can test all the logic code.

Recommendations on this approach:

1. If you're on ARM, you can use the same compiler (gcc) for both purposes. Then any compiler-specific extensions will continue to work.
2. But compiler-specific extensions are undesirable, so you might compile the desktop version with Clang/LLVM instead (with the benefit of better error messages).
3. My desktop is a Linux PC. Nothing in this approach is Linux-specific. But Linux sure makes a nice C development environment.
4. Use stdint.h and types like uint64_t, instead of "unsigned long int". This lets you get the same behavior when compiled on different systems.
5. But beware of C's integer promotion. If possible (it is on ARM) use a 32-bit PC to test 32-bit ARM code.
6. Regarding hardware drivers, I've found two approaches to be beneficial.
1. Make a live prototype on the PC. The PC has a real-time clock and a network connection and a display and inputs, so those are taken care of. (My PC even has an accelerometer.) You can use libSDL to animate a picture of your final product and receive keypresses. For other functions of your board connect up development boards, or fake it. The advantage of this approach is that you can use it in the live system and save yourself the trouble of making hardware if you find it doesn't solve the problem you need solved.
2. Make a dead prototype that reads input events from a file and writes output events to a file. Now for testing you can record (or synthesize) events that correspond to specific scenarios you want to test. And then you can verify that the right things are written to the output. The advantage of this is it leaves a full automated test suite that you can run at build time to catch regressions.
7. Use -Wall -Wextra -Werror compiler options and keep your code warning-free. You'll spend some time patching warnings, but it makes the coding go faster by reducing debugging time.
8. Compile the PC version using mudflaps. This catches a lot of pointer mischief. Some folks recommend Valgrind, but it's not as useful for code that never uses malloc() or free().
9. For PC debugging, use GDB or DDD or printf(), to taste. There are a large variety of mature and useful desktop debugging tools available.
10. Even if I don't do the full PC debug setup, I will often include a unit-testing main() function at the end of a file that is #define'd out. This main() tries any tricky functions in the file and returns 0 for all-pass or asserts() for fail. Then I add a "test" target in the makefile that compiles and runs each of the individual files' tests.
11. There are many unit testing platforms out there that you can use. There are so many because they are very easy to write. I don't use them. I don't want a pretty report of "percent tests passed." I want my test to die at the first failure. Visual Basic used to have a laughable feature called 'on error resume next'. I don't know why you'd want that feature for unit tests.

If you do standalone unit-testing this way, you will find that you have very little need for on-chip debugging. In addition, you will find porting to different hardware platforms is a lot easier because the core logic and hardware drivers are well separated.

-
1. Working with embedded systems without OS or remote console, its pretty common to do plenty of flashing for testing changes. When you have a Linux based system, you can potentially just change some python script code on the fly.

2. Personally I find a UART console for debug output and executing debug commands invaluable. Its a good way to trace the program flow and trigger certain functionality.
I tend to use different debug levels, so that I am able to switch between showing errors only or enabling verbose logging to follow everything in detail, for example.

Regarding JTAG / in system debugging: Even if I have JTAG available, I only use it for tracking down algorithm problems (following the calculation) or checking register / hardware configuration values. I rarely use it to follow the program flow, I find that easier and faster using the debug console output.

-

My 5c. Somethings are more speedily done with JTAG debuggers: Looking at serveral variables at a time, stepping through the code, adding breakpoints. Looking at larger memory locations. Catching stupid errors: That interrupt that you forgot to disable.

-

You can get by with almost no IO to debug, but difficulty is inversely proportional to the available IO - watching one IO pin with a scope will take a lot longer to debug something than a JTAG or similar debugger that can halt the CPU & examine everything, step through, trace, etc.

For basic stuff you don't need much debug, for very complex stuff the more the merrier really.

-

For systems that had to run full speed (e.g. controlling something that couldn't be suspended at a breakpoint and shouldn't be allowed to run uncontrolled), and couldn't tolerate or acheive the required serial data rate while executing, I've stuffed raw samples into an array to be dumped at the end of the run and interpreted offline. It adds minimal execution time at the expense of memory space.

-