Você está na página 1de 3

e m be dde d.co m http://www.embedded.

co m/design/pro to typing-and-develo pment/4008182/10-tips-fo r-writing-mo re-maintainable-embeddedso ftware-co de

10 tips for writing more maintainable embedded software code


T imothy Stapko Code maintenance is an important aspect of application development, one that is of ten ignored in f avour of a f aster time-to-market. For some applications, this may not pose a signif icant problem, since the lif e span of those applications is short or the application is deployed and never touched again. However, embedded systems applications may have lif e spans that are measured in decades, which means some early mistakes can result in signif icant costs later. When developing an embedded application that will likely have a long lif e, maintenance must be a consideration both in design and in implementation. T he f ollowing tips by no means constitute a complete list, but they address some common issues that can give the maintainers of your application cause to curse your name " and don't f orget that you may be one of them! Tip #1: Avoid assembly code Of course, on a low end PIC you have no choice and on a high end ARM you probably don't need it, but between these two extremes there are a lot of platf orms that use assembly code as a means to increase perf ormance and reduce code size. However, the problem is that simply choosing to use assembly code can derail your project and set you back months. While assembly code allows you direct access to the machine's f unctionality, the perf ormance benef it can easily be overridden by the dif f iculty in understanding just what is happening in a program. It is f or precisely this reason that higher level languages, like C and Java, were conceived. Consider every piece of assembly code to be suspicious when debugging, since violation of a high level language's saf ety f eatures is extremely easy. If you must use assembly, try to be wordy when commenting. In C or Java, comments can clutter the code, but in assembly the comments can save a lot of time and f rustration. You may choose to comment blocks of assembly, but make sure there aren't more than 5 or 6 instructions in a block. Ideally, the algorithms used should be spelled out in pseudo-code directly in the comments (see Tip #8). Tip #2: Avoid comment creep T his is a general programming tip, but one that becomes especially important in long-lif etime applications " manage your comments' association with the code they document. It can be very easy f or comments to migrate as code is updated, and the result can be dif f icult to understand. T he f ollowing example shows just how easily comment creep can happen over time: // T his f unction adds two numbers and returns the result #if __DEBUG void printNumber(int num) { printf ("Output: %d\n", num); } #endif // T his f unction multiplies two numbers and returns the result int multiply(int a, int b) { return a*b; }

int add(int a, int b) { #if __DEBUG // Debugging output printNumber(a+b); #endif return a+b; } Notice that the comment f or the f unction 'add' is at the top of the listing while the actual f unction is f urther down. T his can happen over time if there is a space between the comment and the f unction. T he likely cause was the addition of the printNumber f unction between 'add' and its comment description. Later, someone saw that there was an addition f unction and it seemed logical to put multiply next to it " the comment creep has resulted in disjointed documentation within the code. To f ight this problem, try keeping code inside the f unction it documents, or make the comment block very obvious by putting lines above and below the comments. Tip #3: Don't optimize prematurely. One of the cardinal sins in programming is premature optimisation. However, the rule is of ten broken in practice due to time constraints, sloppy coding, or overzealous engineers. Any program you write should start out as simple as it can possibly be and still provide the desired f unctionality " if perf ormance is a requirement, try to implement the program simply, even if it does not match the perf ormance. Once you have tested and debugged your complete unit (be it a program or a component of a larger system), then go back and optimise. Haphazardly optimising code can lead to a maintenance nightmare since optimised code is usually harder to understand, and you might not get the perf ormance results you need. Ideally, use a prof iler (such as gprof , which works with GCC, or Intel's VTune) to see where your bottlenecks are and f ocus on those areas " what really is slow may surprise you. Tip #4: ISRs should be simple Interrupt Service Routines (ISRs) should be as simple as possible, both f or perf ormance and maintenance reasons. ISRs, being asynchronous in nature, are inherently more dif f icult to debug than "regular" program code, so keeping their responsibility to a minimum is important f or the overall maintainability of your application. Try to move any data processing out of your ISR and into your main program " the ISR can then be responsible only f or grabbing the data (f rom hardware, f or example) and placing it in a buf f er f or later use. A simple f lag can be used to signal the main program that there is data to be processed. Tip #5: Leave debugging code in the source f iles During development, you will likely add a great deal of code that is designed f or debugging " verbose output, assertions, LED blinking, etc. When a project is over, it may be tempting to remove those sections of code to clean up the overall application, especially if the debugging code was haphazardly added. While cleaning up the application is a noble pursuit, taking out the debugging code creates problems later. Anyone attempting to maintain that code will likely reproduce many of the steps created in the original development " if the code is already there it makes maintenance a whole lot easier. If the code needs to be removed in production builds, use conditional compilation or put the debugging code in a central module or library and don't link it into production builds. Initial development of the application should include time to document and clean up debugging code; the extra time spent will be well worth it. Tip #6: Write wrappers f or system calls Try to separate low-level I/O routines f rom the higher-level program logic through interf aces, because a program can be made very dif f icult to manage by developing monolithically. Putting all of the f unctionality of an application into a f ew large f unctions makes code dif f icult to understand and much harder to update and debug. T his is especially true with hardware interf aces. You may have direct access to hardware registers or I/O, or even an API provided by the platf orm's vendor, but there is a lot of motivation to create your own 'wrapper' interf ace.

You usually do not have control over what the hardware does and if you have to change platf orms in the f uture, having hardware-specif ic code in your application (API or direct manipulation, it doesn't matter which) will make it much more dif f icult to port. If you create your own wrapper interf ace, which can be as simple as creating macros that are def ined to the hardware API, your code can be consistent and all the updates needed f or porting will be in a centralised location. Tip #7: Break up f unctionality only as needed Embedded applications will dif f er f rom PC applications in that a lot of the f unctionality will be specialised to the hardware you are working with. Splitting up f unctional units into the smallest pieces possible is not advisable - keep the number of f unction calls in a single scope (f unction) to less than 5 or 6, and make f unctional units of the hardware correspond to f unctional units in the sof tware. Breaking up the program any f urther will create a spider web of a call graph, making debugging and comprehension dif f icult. Tip #8: Documentation Keep all documentation with the code and, ideally, a copy of the hardware too. When documenting your application, try to put as much of the design and application model directly into the source code. If you have to keep it separate, put it in a source f ile as a giant comment and link it into the program. At the very least, if you use a version control system (such as CVS or Microsof t Source Saf e), check the documentation into the same directory as your source " it is really easy to lose the documentation if it is not located with the source. Ideally, put all the documentation and source on a CD (or your choice of portable storage device) seal it in a bag with the hardware and development tools you are using and put that bag in a saf e place " your successors will thank you. Tip #9: Don't be clever! Similar to premature optimisation, clever coding can lead to big trouble. Since C and C++ are still dominant languages in the embedded world, there is a huge number of ways to solve a single problem. Templates, inheritance, goto, the ternary operator (the "?"), the list goes on and on. Really clever programmers can come up with extremely compact and elegant ways to use these tools to solve problems. T he problem is that usually only the programmer understands the clever solution (and will likely f orget how it worked later). T he only solution is to avoid cleverness and keep the use of esoteric language f eatures to a minimum " f or example, don't rely on short-circuit evaluation in C statements or use the ternary operator f or program control (use an if -statement instead). Tip #10: Put all def initions in one place T his one is easy; if you have a lot of constant def initions or conditional def ines, keep them in a central location. T his could be a single f ile or a source code directory, but if you bury a def inition deep within your implementation, it will come back to bite you.

Você também pode gostar