Embedded software development tips
Time for a few more tips for embedded software developers. This time, the focus of all the tips is ultimately the readability and clarity of code. This is very much a “hobby horse” for me, as I feel strongly that the efficient use of engineers’ time is more important than the last bit of code execution efficiency …
Following a coding standard, like MISRA C, is a good way to improve and assure code quality
There are quite a few coding standards around and each has its own emphasis. Generally, there are two main goals: stop the developer using unreliable or ambiguous constructs and encourage them to write code in a way that avoids some possible errors. Some standards also look at style issues and, if that helps make code more readable, I am all in favor. MISRA C is a well recognized and widely used standard that started out in the automotive software space, but is now much more broadly applied, as other sectors actually face many of the same challenges. The focus of MISRA is all about writing reliable code and uses an approach where the standard can be adapted – by means of a well-defined deviation process – to accommodate the needs of a specific project.
The larger the piece of code that uses a variable, the more descriptive the variable name needs to be
Variable names should always be reasonably descriptive; there is no benefit in making them excessively short. For example, X is not a great identifier, but X_axis might be a whole lot better. It is good practice to comment the declaration of a variable with some information about its use. In a modest sized piece of code, this is good enough, as the reader can easily refer to the declaration. However, in a much larger project, referring to the declaration may be harder, so this is a time when a more verbose, descriptive name is a benefit. Having said this, I should remind you that extensive use of global variables is inadvisable.
Never be afraid to use an intermediate variable to hold the result of as expression, if it makes the code more readable
Many embedded developers are seduced by the idea that, if they write the code in certain ways, it will be more “efficient” – i.e. faster or smaller or both. Whilst, occasionally, they might be right, the gain from being “smart” in this way is generally marginal at best. Here is an example of such “optimized” code:
if ((*((unsigned *)0x80000004)) & 0x4) != 0)
...
This could be rewritten thus:
#define MYDEVICE (*((unsigned *)0x80000004))
if ((MYDEVICE & 0x4) != 0) // test error bit
...
which is certainly a little clearer. But this would be better:
#define MYDEVICE (*((unsigned *)0x80000004))
unsigned DeviceError;
DeviceError = MYDEVICE & 0x4; // test error bit
if (DeviceError != 0)
...
Some developers would object to the extra variable, but an optimizing compiler will simply eliminate it. The resulting machine code from all 3 variants above is likely to be identical.
Consider whether a single return point from a function is best
A number of standards suggest that each function should have a single exit/return point and that should be at the bottom of the code. This sounds reasonable, as it should make reading the code easier and there is less chance that any function exit preparation code is inadvertently skipped. However, it may also be argued that many functions do some initial checks [on parameters, for example], which may result in a premature exit. If the exit point is at the bottom, some convoluted conditional code [probably nested if statements] may be required, which is hard to read and understand.
MISRA C, that I mentioned above, suggests that single exit point may be best. Other coding methodologies, like IEC 61508 and ISO 26262, mandate a single exit point.