Keith CurtisLet’s face it, all of our product design cycles are accelerating. To stay competitive, your company needs the next big thing, they need it yesterday, and it can’t cost any more than it has to if you are going to compete. There is just one problem; you can’t hire an army of code-savvy programmers to create it. All you have is your current team with their current work load. So, how do you cope? Well, it may sound like a management self-help seminar, but it is true—you have to work smarter, not harder.

First of all, let’s clearly define the scope of a new product development project:
1. You need a product with a specific list of features.
2. The product has to hit a specific price point.
3. It has to be reliable.
4. It has to be expandable for bug fixes, for feature creep during design, and for upgrade options, down the road.
5. It has to have a short development cycle, meaning quick development with few, if any, nasty and time-consuming surprises in the test phase.

The way to make this happen is very simple—spend time up front doing a good system-level design before you start writing the first line of code. The best approach to this is typically a top-down design, as follows:

Step 1: Get a clear and comprehensive requirements document. The document should contain a list of all the system functions, their timing requirements, data-storage requirements, what functions have priority, and when that priority changes. A close review of this document should also point out any holes or ambiguities in the product definition. Filling in these holes prevents misunderstandings later on, and saves hours of rewrites when the product doesn’t meet someone’s preconceived expectations.

Oh and, if possible, get management to sign off on the document—it will help slow down feature creep.

Step 2: Use the information from the requirements document to define a clear set of requirements for your microcontroller. Functions and timing will give you an estimate of the MIPS you will need, and the memory requirements will translate directly to system data and program memory. Remember to add a healthy fudge factor to both numbers—20% to 30% should do. The idea is to have some extra room in both numbers for expansion. This is also a good time to make any design trade-off decisions, such as software versus hardware peripherals, and low versus high overhead display and input systems.

Step 3: Black-box design the individual modules of the design. Each module should be reasonably stand-alone, with a clear definition of what its input/outputs are, its timing requirements, and any specific algorithms. All of this information, including a reasonable set of testing requirements and scenarios, should be pulled together into a set of module specifications; preferably one specification per module.

Step 4: Once you have clear specifications for each of the modules, design a framework for your design that includes a comprehensive module-to-module communications variable dictionary, specifying the source and destination modules, typical range of values, related variables, and the format of the data to be handled by each of the variables. You will also need a timing/priority system to trigger the various functions when, and if, they should be running. Remember, a timing system determines when a module should run. The priority system determines if a module should run.

Step 5: Clearly and verbosely document everything about the design. This will be the blueprint for your design, so be complete and descriptive of how the system will work, and how the modules tie together. The more clearly and completely the system is documented, the easier it will be to write, test and modify. This document will also serve as a great source of verbiage for code documentation.

Once you have all of your system-/component-level designs documented, then the actual coding can begin. You should have a clear definition for each block, making it easy to write and test code. The design will also be very modular, again making it very easy to write. An added benefit of a clearly documented, modular design is that there will be very little left that is open to interpretation by individual coders, making it easy to hand off sections of the design to others when time gets tight at the end of the project. Finally, a clearly documented, modular design will also give you a convenient framework for testing the modules individually.

NOTE, and this is important—the individual modules should be tested within the system framework. This will help eliminate bugs in the integration phase, as well as make it easier to test the modules individually. When all the modules are complete and tested, the only problems in integration should be those related to the asynchronous timing of the modules, and minor disagreements about how data is passed back and forth.

The resulting design should be a three-tier setup:
1. The system-level design with all the original requirements and the resulting design decisions that drove the framework, and module design.
2. A component-level design with specific interface documentation, and a clear definition of how each module works and how it was tested.
3. Finally, the actual code, which was clearly documented and well tested prior to integration. You will also have a clear description of each module’s interface into the framework, which will simplify the addition of new modules, or the replacement of any existing system modules.

In addition to the actual design, you will end up with a library of well-defined, well-tested, stand-alone modular blocks, which will speed up any future design that uses any of the same functional blocks.

Now, up-front design is a good idea, but does it meet our original requirements for product development?

1. You need a product with a specific list of features. Up-front design gives us a list of the features/modules required, with a clear list of how they operate and interact.
2. It has to hit a specific price point. Step 2, discussed earlier, enables us to select a suitable range of 2 - 4 microcontrollers that are pin and code compatible, and that provide a path for expansion, and short time to market, at reduced costs.
3. It has to be reliable. The modular nature of the design promotes testing throughout the design process, fixing bugs early and simplifying the final test phase in the integration phase.
4. It has to be expandable for bug fixes, feature creep during design and, later, for upgrade options. Step 2 gives us some extra capability for expansion, and the framework, modular nature of the design, and well-documented module specifications provide an easy means for adding hooks into new features.
5. It has to have a short development cycle, meaning quick development with few if any nasty, time-consuming surprises in the test phase. The modular nature of the design facilitates multi-designer work and allows early testing. It also provides a clear definition of the project and prevents inevitable rewrites due to misunderstandings of specifications and feature creep. 

Figure 1: Good System-Level Design Can Save You From the Horror of Schedule Slips

Even with the additional up-front design work, the final design should require less time to complete. Testing at the module level will be more comprehensive, and result in fewer final-test problems. The time to fix the integration will also be reduced, due to the limited number of suspects. And, the resulting design will be simpler to upgrade in future designs. As Figure 2 shows, even if the next design starts from scratch, many of the modules will provide a basis for the new system and will eliminate much of the reinvention that is typical of a scratch design.