Embedded software design methodologies have been developed by the dozen if not by the hundreds. Methodologies are meant to help guide engineers toward developing software that is more robust, has fewer bugs, and is more maintainable, to name a few coveted characteristics. Methodologies vary in focus from being heavily reliant upon documentation all the way through being focused on cleverly crafting code to act as documentation. One methodology that can be useful for developing embedded software interfaces is Design by Contract.
The term Design by Contract was first used, and later trademarked, by Bertrand Meyer in connection with the object-oriented programming language, Eiffel. The idea behind Design by Contract is that developers should be designing precise and verifiable software interfaces for components in their applications; in essence, they create a contract between the interface and the application code that will use the interface. For example, when creating a hardware abstraction layer interface for a GPIO component, each component interface would contain some method for defining the components pre-conditions, post-conditions, and invariants. In many circumstances, developers using an interface are often left in the dark about these conditions which can be quite dangerous.
|Figure 1 – Example Interface Header using Pre-conditions and Post-Conditions.|
Mastering Modern Debug Techniques. Attendees of this session will walk away with an understanding of the modern debug techniques available to engineers in todays development cycle. Attendees will learn how to quickly setup SWV and how it can benefit their development effort. Finally, a look at ETM and code coverage analysis will be examined. Don't miss "Mastering Modern Debug Techniques" at ESC Silicon Valley, Dec. 6-8, 2016 in San Jose, Calif. Register here for the event, hosted by Design News ’ parent company, UBM.
For a designer to share and identify the pre-conditions and the post-conditions for a component makes sense, yet, very rarely do developers explicitly provide this information. An application developer would be happy to know that they need to enable a clock and initialize the interrupt vector controller before calling the Gpio_Init function rather than spending 30 minutes or more debugging the system only to discover that the initialization function doesn’t do it for them. Design by Contract provides application developers a contract for using the component. If the preconditions are met, then the interface is contractually guaranteed to provide the specified post-condition from calling the interface. An example Doxygen interface function header can be seen in Figure 1 that demonstrates how easily a developer can specify the contract by simply adding comments for the PRE-CONDITIONS and the POST-CONDITIONS.
Since the pre-conditions and post-conditions are fully specified up front, the component developer can use C assertions to validate that the contract is being met by the component user. For example, the component implementer would create an assertion to verify all pre-conditions including function input values. If any precondition is not met or an input value is out of range, this is a bug in the application software and the assertion will stop the application from executing and provide a message stating which file and line number the assertion is located in addition to how it failed. Once an application has been developed and all assertions have been met, the assertions can be turned off to decrease the code size and minimize overhead associated with the assertions.
Design by Contract sounds like a great idea but for embedded software developers simply following Design by Contract doesn’t quite go far enough. For example, in a standard implementation, interface implementers would not be responsible for detecting or handling errors that might occur. If an application developer invokes the component without meeting the pre-conditions, then it is up to the application code to realize this and handle potential errors. In many implementations, this means the system is going to crash or enter into an unexpected state while the component itself just doesn’t care. Using defensive programming techniques is not expected in the Design by Contract environment and often ignored.
Developers looking to improve an interfaces understanding can use techniques from Design by Contract and then take the methodology one step further by applying defensive programming techniques to the implementation. Relying on user application code to “do the right thing” can be dangerous given the speed at which software is developed and the pressure engineers are under to just make something work. Even though assertions can be used to verify the application code is adhering to the contract, error handling code should still be put in place just in case a pointer goes awry or memory becomes corrupted during run-time which then violates the contract.
Design by Contract is by no means a new idea. It was first described in computer science literature as far back as 1986. Developers who are involved in creating API’s and HAL’s may find that borrowing ideas from this methodology can improve clarity and component use. Undoubtedly mixing key concepts with more modern ideas can provide a robust solution that allows developers to decrease the time they spend debugging and focus on delivering new features that their end users will love.
Jacob Beningo is an embedded software consultant who currently works with clients in more than a dozen countries to dramatically transform their businesses by improving product quality, cost and time to market. He has published more than 200 articles on embedded software development techniques, is a sought-after speaker and technical trainer and holds three degrees which include a Masters of Engineering from the University of Michigan. Feel free to contact him at [email protected], at his website, and sign-up for his monthly Embedded Bytes Newsletter.