Our team recently completed a project with a client working on an industrial meter, entirely written in C and using the IAR Embedded Workbench IDE as the compiler. I wanted to share a few things that we learned to optimize in IAR, to help your projects go a little smoother.
1. Faster Terminal I/O – “buffered terminal output”
IAR allows you to have several projects in your workspace, and we set up a project to run the unit tests for each of our software modules. We used a Simulator configuration for all projects and an additional Debug configuration for downloading and executing unit tests on the processor (MSP430). We were always frustrated that printing out messages to IAR’s terminal was VERY slow while running the Debug version. Our first attempt was to pass command line arguments into the unit test fixture (Unity) to abbreviate its output to the terminal. It would still be VERY slow to print out failure strings, though. The solution was to turn on “Buffered terminal output” as you can see in the screenshot below. Project -> Options -> Linker -> Output -> Buffered terminal output. Afterwards, you should find that the terminal prints out MUCH faster and you don’t have to worry about making sure that your unit test framework’s “verbose” setting is turned off.
2. Generate code coverage metrics automatically
IAR’s CSPY can automatically generate code coverage metrics for your code modules while running Simulator configurations (but not while running tests on hardware). You simply have to include the option “–code_coverage_file <output file path>” in the PROJ_DIR/settings/PROJ_NAME.<config>.cspy.bat file. Mine is “–code_coverage_file $PROJ_DIR$\$PROJ_FNAME$CodeCoverageFullResults.log”. Note, however, that the *.cspy.bat files are auto generated by the compiler, so we had written a script to add the string to each *.cspy.bat file. However, in the IAR V6.20 release in December 2014, they fixed the IDE so that you can simply specify the option there, without the need for a batch script to edit the *.cspy.bat files. Project -> Options -> Debugger -> Extra Options.
After unit tests for each project/module are executed and the code coverage log files auto generated, you can run these two scripted commands to extract the top-level coverage percentages:
:: Find all module test coverage statistics and put them in a single file findstr /C:"Module " %moduleName%CodeCoverageFullResults.log > %moduleName%CodeCoverageAllModulesOnly.log findstr %modulesOfInterest% "%moduleName%CodeCoverageAllModulesOnly.log" >"%moduleName%CodeCoverageSummary.log"
Then, at the end of all automated unit tests, run this command to collect the code coverage results into a single summary file:
:: Collect all Unit Test Coverage metrics into a single file. Recursively search all files for Summary Logs and concatenate the results. findstr /S /P /C:"Module " *CodeCoverageSummary.log > AllCodeCoverageResults.log
3. When archiving project files, archive *.ewd, *.ewp, *.eww files.
We should only check into a revision control system the necessary files to repeat our setup. Ideally these are also files that the IDE doesn’t unnecessarily change over time. For instance, the *.dep file changes very often and shouldn’t be committed. The *.eww file is the workspace and the *.ewp file contains the project settings. However, we didn’t discover until later that the *.ewd file was necessary to specify the Debug configurations that utilize the FET Debugger option to download to a target. If you don’t check in the *.ewd file, clean checkouts of the project will just use the Simulator to run tests.
4. Turn on several MISRA rules to help enforce coding conventions.
Our client specified that they wanted their codebase to be MISRA C compliant according to the MISRA rules they had chosen in their style guide. For the MISRA 2004 standard, these were rules 2.3, 2.4, 5.2, 5.3, 5.5, 5.6, 5.7, 6.3, 8.3, 8.7, 8.11, 9.1, 10.1, 10.2, 12.2, 12.4, 12.8, 12.9, 13.1, 14.1, 14.4, 14.5, 14.10, 15.1, 15.2, 16.8, 17.1, 17.2, 17.4, 17.6, 19.10, 19.15, 20.4, 20.5, 20.6, 20.7, 20.8, 20.9, 20.10, and 20.11. So we turned them on for the full product build but left them off in the unit test projects, since the Unity unit test framework had lots of warnings of its own. Although we put a few exceptions in which were well documented, this seemed like a good set of rules to use, and better yet the IAR compiler can automatically enforce them. Static analysis tools can later discover further issues with your code, but turning on rules in the compiler makes the feedback loop much shorter and more responsive. This will help your team avoid issues with pointer arithmetic, variable name scope shadowing, initialization, and unintended side effects.
5. Stack manipulation is tricky when using Unity unit test macros.
We were using the Unity unit test framework (written in C), the Segger embOS operating system, and the IAR compiler with all optimizations turned off. Occasionally we needed to either prove the thread safety of certain key communication pathways, or we needed to create unit tests for low-level OS functionality, which required actually running the OS within our unit tests. Unfortunately, embOS has a “Start” but no “Stop” functionality, so there’s no built-in way to cleanly exit the OS. However, IAR and TI provide the “intrinsics.h” header file which has methods for manipulating the program counter, stack pointer, and status registers. This allowed us to run unit tests within the OS and once complete we could jump back to the calling unit test body after the point where the OS had been started. However, we discovered that when “TEST_ASSERT” macros are called, the IAR compiler lazily waits until the end of the enclosing function to reset the stack pointer. So the stack pointer is actually in different place just before and just after the TEST_ASSERT call. Therefore our solution was to create a separate external buffer and save the entire stack contents before entering the OS so that it could be exactly restored afterwards. In the end, although it is tricky to implement these tests correctly, I think they significantly increase confidence in system functionality that is dependent on the OS and protect codebase maintainers from accidently introducing thread safety defects in the future.
And that’s all of the IAR compiler recommendations and gotchas from our last project. In the comments, feel free to propose other IAR compiler settings which have been useful for you and your team, and we’ll post anything else that we find useful in the future. Next up, recommendations when using TI’s MSP430 processor.