Infrastructure reveals a novel approach to re-use
By Jan-Erik Frey, Chief Executive Officer and Anders Rosvall, Chief Technical Officer, Embedded Artists AB, Vasteras, Sweden, Jan-Erik.Frey@EmbeddedArtists.com, EE Times
March 8, 2002 (10:47 a.m. EST)
Recent surveys show that 80 percent of all embedded systems are delivered late. Much of the delay arises in the software infrastructure of the system rather than the applications. The software infrastructure is comprised of functionality common to a large group of application domains; typically two-thirds of the development is devoted to the infrastructure.
While the functionality is common, the requirements differ greatly. This has led to a very low level of standardization and reuse in resource-constraint embedded systems. This would suggest that embedded applications have become increasingly complex over the last decades. However, a closer analysis shows that the bulk of this increase in functionality and complexity is related to infrastructure functionality rather than the actual applications.
For example, a central application of a mobile phone is the address book functionality. To implement an address book, a large set of infrastr ucture functions is required: Flash file system (to persist data), database (to accomplish structured storage and retrieval), sorting (to organize views), search (to locate entries), and graphical user interface (user interaction). In fact, the address book application is nothing other than the integration of these functions, the design of the user interface and the connection to the target hardware for connecting the buttons of your phone to the correct functions.
Many functions that are implemented in the infrastructure are taken for granted by a typical user. A few examples are non-volatile storage of product settings, a graphical user interface, responsiveness, and communication with external systems. Most products are soon expected to have Internet and/or Bluetooth connectivity.
Infrastructure functionality is largely implemented by the embedded-product suppliers themselves, with a few exceptions such as TCP/IP protocol stacks and Web servers. Also, infrastructure functionality is often tightly coupled to the application due to pressed time schedules that do not permit a proper design where application and infrastructure are clearly separated. This monolithic approach, although often a necessity, greatly hinders software reuse to any larger extent. Why is that?
The infrastructure constitutes the majority of the development effort today, sometimes as high as 90 percent, but typically between 40-70 percent. Take the mobile-phone address book example. The majority of the functionality required to implement an address book are infrastructure functions, such as flash programming, file system, database, sorting, searching, and GUI. The application consists only of tying keystrokes with appropriate actions.
Since the infrastructure is such a large part of most systems, and the only portion of the system that is somewhat generic in its nature, we should turn our focus toward reusing the infrastructure rather than complete applications. An address book application is difficult to reu se, but a file system, database, or graphical user interface is not.
The infrastructure, although related, should not be confused with a platform. A platform consists mainly of infrastructure functions, which have been selected and tied together based on the requirements of a specific application domain. In other words, a platform is an infrastructure implementation for a specific application domain.
There are many plausible ways of categorizing infrastructure functionality. However, since the spectrum is so broad, ranging from simple data structures to databases, we find that a categorization according to level of abstraction is suitable.
Every embedded product does not contain all of the functions, but at least a few of them can be found in every system. The similarities between products in a given application domain is large, but completely different products contain many common infrastructure functions as well. The infrastructure functions in the address book application could ju st as well be used in an industrial controller such as storing command sequences in a file system, logging diagnostic samples for trend analysis in a database, and having a graphical user interface in a small front LCD.
Highly optimized solutions impede reuse, since generality is traded for efficiency. A Web server on top of a TCP/IP stack can store data in precomputed Internet Protocol (IP) frames in order to minimize TCP/IP protocol processing. Such implementations will be very specific and cannot easily be moved to another platform operating under different conditions.
If a little more general implementation can be done, the chances are much higher that an existing implementation can be modified for the new operating conditions without excessive redesign and programming.
Many functions can be bought today, but certainly not all of them, and not from a single vendor. Even though typical infrastructure functionality may appear similar between applications from a high-level view, the finer implementation details most likely differ greatly. It's not as simple as just creating a standard library of infrastructure functions that can be reused directly. If this would be a feasible approach, it would have been done a long time ago. Also, most functions can be implemented in many different ways, depending on what trade-offs are made. The interfaces to the specific functions can also be implemented in many different ways for example, how to exchange data, blocking or call-back interface, or how to signal errors.
A universal conclusion is that most infrastructure functions don't have one best implementation. Take sorting. No superior sorting algorithm performs best in every situation. When implementing infrastructure functions, you need to make many trade-offs:
- Speed vs. code size /memory consumption;
- Specific vs. general implementation;
- Good mean performance vs. guaranteed worst-case performance;
- Memory usage.
Every solution has its pros and cons. The specific problem in mind must dictate the trade-offs to make: such as cost, responsiveness and system architecture.
The key to reuse of infrastructure is a careful selection of infrastructure functionality that is common within your product family. Based on a thorough requirements analysis, you then need to make the necessary trade-offs in the implementation of the selected functions. And, you must define proper interfaces for your infrastructure.
The most attractive alternative is to find an off-the-shelf code package that implements the required functionality. If you are really fortunate, you will find a perfect match, or at least a package that requires minimal interface/wrapper design to fit into the existing system. However, the most likely scenario is that no suitable package can be found, and you will have to implement the infrastructure functionality yourself.
Also, it's not merely a question of deciding whether a function belongs to the infrastructure or not. Many times, it's a matter of abstraction level you might have several applications that make use of a database. However, the way you persist the data might vary. A suitable infrastructure function could thus be a database, where the persistence level is kept open and implemented in each application. Therefore, having deep knowledge of one single product is insufficient. To be able to achieve effective reuse, it's essential to have a good idea of the big picture.
If you have very diverse requirements on your infrastructure, implementing one standard function might not be feasible. There are a couple of ways to tackle such a situation. Try to break down the function in mind into smaller building blocks and see if there is a common denominator among your applications. A common mistake is that we take reuse to the extreme. It can be much more efficient to stop halfway and make application-specific implementations for the remaining functionality. If this does not work, t ry going back to square one, and redefine your infrastructure. Maybe there is some lower-level block that is common.
Another way to handle diverse requirements is to make multiple implementations. There might not be one universal solution. This is of course more costly, but it's anyway better to have two versions of a specific infrastructure function than a separate implementation for each individual application. The additional work of providing multiple implementations can be alleviated through the use of #defines and #ifdef constructs. This is very suitable for specific, internal functions.
Just as important as the internal implementation, is the definition of the interfaces toward your infrastructure functions. In fact, the selection of a specific type of interface can alter the internal implementation of a function. In the same manner as described in the previous section, the definition of the interfaces has to be guided by your entire product family. Be careful not to make the interface s too specific for one application. Again, often it's better to stop halfway and make the final connection (glue code) for each application.
The work of defining a proper interface resembles that of making trade-offs in the implementation. You need to look at both how the application or other high-level functions, as well as hardware/low-level functions access the infrastructure. Very diverse applications might require multiple interfaces. Apply the same reasoning as above. Supplying hooks or callbacks at strategic places in your infrastructure code can help accommodate a larger variety of applications. This enables you to clearly isolate application-specific processing from your infrastructure code.
This article is based on excerpts taken from ESC class # 553, Identifying your infrastructure - a novel approach to re-use.