Take the 2-minute tour ×
Electrical Engineering Stack Exchange is a question and answer site for electronics and electrical engineering professionals, students, and enthusiasts. It's 100% free, no registration required.

I typically program PICs in C, usually for switched-mode converters. I've heard of various static analysis tools and standards like MISRA C that can be used to help improve the reliability of code. I'd like to know more. What standards or tools might be appropriate for my context?

share|improve this question
    
How set are you on the C language? –  Brian Drummond Jan 21 at 15:30
    
I could be persuaded to switch to something else if there was a very good case to be made for it. But it would have to be a very good case. –  Stephen Collings Jan 21 at 15:46
    
"A very good case" for switching from C cannot be made quickly, and for the PIC, possibly not at all yet. For AVR, ARM or MSP430, Ada would be worth a serious look (despite the negativity it attracts, as you can see!) and for high rel, SPARK is worth a look. –  Brian Drummond Jan 21 at 18:05
    
You may find these interesting as background information : SPARK vs MISRA-C spark-2014.org/entries/detail/… and this ongoing case study: spark-2014.org/uploads/Nosegearpaper_1.pdf –  Brian Drummond Jan 22 at 11:13
    
Might be better time invested to make a case for switching away from the PIC to something modern... Especially if you are designing the kind of mission-critical systems that MISRA and SPARK were originally intended for. –  Lundin Jan 29 at 14:34
add comment

5 Answers

Embedded code validation is tricky, especially when dealing with limited-resource parts like PICs. You often don't have the luxury of coding in test cases due to the memnry constraints of the part and the often "real-time" programming done on these sorts of devices.

Here are some of my guidelines:

  1. Write a spec if there isn't one: If you're not coding against a spec, document what your code is supposed to, what are valid inputs, what are expected outputs, how long should each routine take, what can and cannot get clobbered, etc. - a theory of operation, flowcharts, anything is better than nothing.

  2. Comment your code: Just because something is obvious to you doesn't mean that it's obvious (or correct) to someone else. Plain-language comments are necessary for both review and code maintainability.

  3. Code defensively: Don't just include code for normal inputs. Handle missing inputs, inputs that are out of range, mathematical overflows, etc. - the more corners you cover by your code design, the fewer degrees of freedom the code will have when deployed.

  4. Use static analysis tools: It can be humbling just how many bugs tools like PC-lint can find in your code. Consider a clean static analysis run as a good starting point for serious testing.

  5. Peer reviews are essential: Your code should be clean and well-documented enough that it can be efficiently reviewed by an independent party. Check your ego at the door and seriously consider any criticism or suggestions made.

  6. Testing is essential: You should do your own validation, as well as have an independent validation of the code. Others can break your code in ways you can't possibly imagine. Test every valid condition and every invalid condition you can think of. Use PRNGs and feed garbage data in. Do whatever you can to break things, then repair and try again. If you're lucky, you'll be able to run your code in debug mode and peek at registers and variables - if not, you'll need to be crafty and toggle LEDs / digital signals to get an idea of the state of your device. Do whatever is necessary to get the feedback you need.

  7. Look under the hood: Don't be afraid to look at the machine code generated by your C compiler. You may (will?) find places where your beautiful C code has blown up into tens if not hundreds of operations, where something that should be safe (since it's only one line of code, right?) takes so long to execute that multiple interrupts have fired and invalidated the conditions. If something becomes horribly inefficient, refactor it and try again.

share|improve this answer
    
+1 All sound advise. I would expect any professional firmware developer to just smile and nod when reading this. –  Lundin Jan 29 at 14:36
1  
An important aspect of peer reviews is that the review is about the code, not about the programmer. If you analyze your code with terms like "first I do this, then I do that", you're probably in trouble. "First the code does this, then it does that" is the right way to think about it. And the same applies to reviewers: not "why did you do this?", but "why does the code do this?". –  Pete Becker Jan 29 at 16:13
add comment

Most of the same techniques for creating reliable software on a PC are also applicable to embedded development. Its helpful to separate your algorithms from the hardware-specific code, and test those separately with unit tests, simulations, static analysis, and tools like Valgrind. That way there is much less code that only gets tested on the hardware.

I wouldn't abandon C. While languages like Ada can offer some minor guarantees, it's easy to fall into the trap of thinking the language promises more than it really does.

share|improve this answer
    
Valgrid might be a tad bit more relevant for PC than for an 8-bit MCU, however :) –  Lundin Jan 29 at 14:38
    
Unfortunately some techniques for creating good PC-level software are very unsuited to small micros, and some practices considered Bad And Wrong in PC land are perfectly acceptable in an embedded environment. –  John U Jan 29 at 17:27
add comment

MISRA-C is indeed very useful to improve the general code quality and minimize bugs. Just make sure you read and understand every rule, most of them are good, but a few of them doesn't make any sense.

A warning here. The MISRA document assumes that the reader is someone with extensive knowledge of the C language. If you have no such hardened C veteran on your team, but decide to get a static analyser and then blindly follow every warning given, it will most likely result in lower quality code, since you might be reducing readability and introduce bugs by accident. I have seen this happen plenty of times, converting code to MISRA compliance is no trivial task.

There are two versions of the MISRA-C document that may apply. Either MISRA-C:2004, which is still the current embedded industry de facto standard. Or the new MISRA-C:2012 which supports the C99 standard. If you have never used MISRA-C before, I would recommend you to implement the latter.

Be aware however that tool vendors usually refer to MISRA-C:2004 when they say that they have MISRA checking (sometimes they even refer to the obsolete MISRA-C:1998 version). As far as I know, the tool support for MISRA-C:2012 is still limited. I think only some static analysers have implemented it so far: Klocwork, LDRA, PRQA and Polyspace. Might be more, but you definitely need to check what version of MISRA it supports.

Before deciding, you can of course start by reading the MISRA document and see what it entails. It can be bought for £10 from misra.org, quite affordable compared to the prices for ISO standards.

share|improve this answer
add comment

Mathworks (the MATLAB folks) have a static code analysis tool called Polyspace.

As well as static code analysis, lint and such like, I would suggest careful definition and design of interfaces (with a formal review process) and code coverage analysis.

You might also want to look at guidelines for safety-critical code design, including MISRA, but also the UL1998, and IEC 61508 standards.

share|improve this answer
    
I not recommend going near IEC 61508 unless you have to. It does mention software, but lacks modern, scientific sources for its claims. That standard came 30 years too late - had it been released in the 70s like most of its so called "sources", it might have been useful. –  Lundin Jan 29 at 15:28
add comment

For a complete answer to this question, I'd suppress the thought about "code reliability" and instead think about "design reliability", because the code is just the final expression of the design.

So, start with the requirements and write and inspect those. If you don't have a requirements document, point at a random line of code and ask yourself "why is that line needed?" The need for any line of code should eventually be traceable to a requirement, even if it's as simple/obvious as "the power supply shall output 5VDC if the input is between 12-36VDC." One way of thinking about this is that if that line of code can't be traced to a requirement, then how do you know it's the right code, or that it's needed at all?

Next, verify your design. It's OK if it's completely in the code (e.g., in comments), but that makes it harder to know if the code is doing what is really meant. For example, the code may have a line that reads output = 3 * setpoint / (4 - (current * 5)); Is current == 4/5 a valid input that could cause a crash? What should be done in this case to prevent the divide by zero? Do you avoid the operation altogether or degrade the output instead? Having a general note in your design document on how to handle such edge cases makes it much easier to verify the design at a higher level. So, now code inspection is easier because it's a matter of checking if the code correctly implements that design.

Along with that, code inspection should check for common errors that your IDE doesn't catch (you are using an IDE, right?) such as '=' when you meant '==', missing braces that change the meaning of 'if' statements, semicolons where they shouldn't be, etc.

As I write this, it occurs to me that it's really difficult to summarize years of software quality training/experience in a single post. I write code for medical devices and the above is an extremely simplified summary of how we approach it.

share|improve this answer
    
My understanding is the the code portion in a medical device is tested almost as if it's a separate device. Is that accurate? –  Scott Seidman Jan 29 at 15:29
    
@ScottSeidman More likely, it is tested on requirement basis, as mentioned in this answer. For each requirement, you should have a code module, and for each such code module you should have a test. So essentially, each requirement has a corresponding test and the code is the mean to fulfill the requirement. This kind of requirements tracing is common practice in any mission-critical systems, long before the "TDD" buzzword popped up. –  Lundin Jan 29 at 15:34
    
I was referring specifically to FDA guidance, such as fda.gov/downloads/RegulatoryInformation/Guidances/ucm126955.pdf Software actually requires more than you might think if its part of a medical device, starting from the planning stage and design controls. –  Scott Seidman Jan 29 at 15:43
    
Scott, I've never thought of it in that way, but you're correct. Our Software Quality Assurance people verify the software separately from the rest of the system (as much as possible) before turning it over to a different group that is responsible for System Verification and Validation. –  lyndon Jan 29 at 18:46
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.