Model-based code generation produces application source code automatically from graphical models of system behavior or architecture. Development tools are moving to model-based
development to raise the level of abstraction at which developers can work. This article discusses different approaches to code generation and how to select the right approach to fit the needs of the application project.
The fast-paced and competitive world of embedded systems technology forces manufacturers to reduce time to market and further differentiate their products. Object-oriented (OO) methods and tools can improve productivity, quality, and reuse. Capabilities for generating code from object
models offer additional help for keeping pace.
Model-based code generation produces application code automatically from graphical models of system objects and behavior. OO methods help developers analyze and understand a system, but the Achilles' heel of analysis and design methods has been the transition to code. Without code generation, the benefits of object modeling seldom extend throughout a product's life cycle, because developers of a pressing upgrade typically bypass the models and just
modify code. Models fall out-of-date and become less relevant. Generating code for object models retains its usefulness.
Model-based code generation continues a long-term trend in development tools. Language abstraction has increased from assembly to high-level languages to graphical models. The abstractions have moved from the system solution space toward the application problem space. Driving this trend are needs for increased productivity, constructing larger applications, and understanding complex
systems. The enduring nature of these needs suggests that model-based code generation is inevitable. The issues for embedded developers are when and how to adopt this technology for competitive advantage while avoiding its pitfalls.
The move from assembly to high-level languages suggests parallels for the use and adoption of model-based code generation, shown in Figure 1. Considerations include:
This article surveys model-based code generation as a key factor in your adoption of methods and tools for developing embedded systems. I'll
consider analysis and design methods and life-cycle methodologies mainly for their relation to code generation. I'll mainly cover tool features and development techniques used to produce code from object models. I'll also present the business case for using model-based code generation and suggest ways to adopt this technology.
Topics such as how to model applications with OO methods, how to construct or control translators (although I survey several approaches), and how to use specific tools are beyond the scope of
this article.
Let's consider a generic development scenario that uses model-based code generation. Using an OO analysis method, embedded systems developers can model their application problem with a related set of diagrams. These typically depict the structure, communication, and behavior of a system's objects. Some methods supplement diagrams with text-based specifications of system actions such as computations. These diagrams are based on
formal syntax and, in some cases, semantics-just as with high-level languages. These formalisms make it possible to generate code that implements the structure, communication, and behavior of the object models.
For exposition purposes, I'll characterize three approaches to model-based code generation that seem to cover the available methods and tools. At a basic level, each extends the techniques used in the older approach, as seen in Figure 2. The oldest approach generates
the framework code for the object structure of a system. Newer approaches model behavior sufficiently to enable generation of system functionality. The most recent approach adds an architecture model that enables user control of all generated code.
When considering model-based code generation, keep in mind the context in which this technology would first be used. Business goals and market opportunities would call for
a new (or re-engineered) product line with expectation of a long-term market. You would be starting on a new embedded application, possibly with the reuse of previously developed software. The complexity of the project and the future of the product would warrant the use of analysis and design methods. Method training and tool acquisition would be part of project initiation.
There would be a base of development skills and tools in which to incorporate code generation. Some changes in development
process would be required. Other technologies might affect or complement model-based code generation. You would address embedded issues within the context of model-based code. These issues include memory layout, hard deadlines, tasking structure, interrupt service, hardware interfaces, cross-development, and assembly code.
A useful way to explore model-based code generation is to consider what the approaches have in common and how they vary. Here are some key
commonalities:
Integrating non-generated code
How Does Model-Based Code Generation Work?
I'll survey the three basic approaches: structural, behavioral, and translative. Each can be seen as building on the concepts of the prior approach, as illustrated in Figure 2. Because our terminology is overloaded,
allow me to define these terms. Structural approaches generate code frames (such as class interface specifications) from models of the static relationships among objects. Behavioral approaches can generate complete code using additional state-machine models and action specifications in a high-level language (HLL). Translative approaches use an application-independent architecture model to give users full control over translating complete models into code.
Structural Approach
Overview.
The structural approach is based on models of object structure (static relationships). From such models, most OO method tools can generate source code for an application framework, such as C++ class hierarchies or Ada package specs. Based on partial models of object dynamics, developers then explicitly program object behavior and communications in target languages, such as C, C++, or Ada. This approach is typically used in an "elaborative" methodology to incrementally develop designs and code in
a gradual transition from analysis models. Methods that support structural code generation include Object Modeling Technique (Rumbaugh), Object-Oriented Software Engineering (Jacobson), Object-Oriented Analysis and Design (Booch), and the recent merger of these methods: the Unified Modeling Language (See "Designing Real-time Systems with UML" by Bruce Powel Douglass on p. 46 of this issue).
Translation
. Models of object structure vary among methods; for example, between object model diagrams
(OMT) and class association diagrams (Booch). Designers model object structure with constructs such as classes, attributes, types, and associations. Some vendors build into their generators the source code that corresponds to object constructs. Other tools use a translation engine and pre-built, customizable templates that specify mappings to particular source code. Written in a scripting language, templates guide the translation of models to code structures, such as class headers or function stubs.
Scripting languages enable designers to follow coding standards, customize the generated object architecture, and create templates for unsupported languages. Translators usually offer options such as which objects, language, or RTOS to generate code for. Designers must usually adapt the simple architectural model implicit in these templates to support multitasking or other embedded designs.
Figure 3
Programming
. By definition of the structural approach, there
is no code generation for object behavior. Methods in this category model behavior as state machines without executable semantics. Consequently, designers program their system dynamics explicitly, including embedded constructs (for example, hard deadlines or interrupt handling). Tools offer mechanisms for integrating hand code with the associated code structure that is generated. Developers can add code for an object's methods to the generated class code. Some tools protect this code from being overwritten
when the objects are regenerated in iterative development. Other tools help reconcile models and code throughout the life cycle. Some tools can reverse engineer OO languages to object (structure) models, providing a migration path for legacy code. Of course, programming environments can always integrate the generated and related explicit source code with commercial components, assembly code, legacy libraries, and subsystems that were not modeled.
Methodology
. Structural code generation is
appropriate to what have been termed "elaborative methodologies." Analysis models are elaborated gradually into design and code. Abstractions are detailed and subsystems are designed and coded in an order to suit project objectives. Once coded, subsystems can be tested and revised, or others can be developed. Because generators can work on partial models, translation from models to code can proceed incrementally. With tools that synchronize and protect code, development can proceed iteratively. Without such
tools, code generation will more quickly build an initial version, but it must be revised at the source level thereafter.
Development
. Structural code generation is incomplete, but it saves hand coding and provides an implementation framework consistent with the models. The approach affords modest reuse of translation templates. Many OO tool vendors (including Rational, Aonix, Cayenne, Select Software, Iconix, Verilog, and Mark V) support structural code generation. Most applications, except
where memory is tight compared to the overhead of class code, would be suitable for this approach.
Behavioral Approach
Overview
. The behavioral approach is based on state-machines augmented with action specifications. Some OO methods that model behavior with state-machines add code (such as C++ or a proprietary language) to represent actions that occur upon a state transition. Coupled with models of object structure and communication, this technique enables tools to generate code
for the entire application model. An added benefit of this technique is the ability to simulate and verify system behavior based on the models-before code is generated. Methods that support behavioral code generation include the telecommunications standard Specification and Description Language (SDL), Harel's hierarchical statecharts, the Real-time Object-Oriented Method (ROOM), and most recently, UML.
Translation
. To model behavior of an entire system, the classic state-machine is extended
in two ways: parallel, communicating state-machines (such as SDL) or hierarchical state-charts (Harel and ROOM). In the state diagrams, users specify the explicit code for handling transition events. Target languages include C++, C, and assembly. Translators use a pre-coded virtual machine (VM) for their version of state machines, either as library routines or built into the translator. This VM implements states, transitions, and communication with other state-machines. Translators integrate event-handling
code (such as action upon a transition) with the virtual state machine.
Methods using this approach also vary in the ways they model object structure and communication. SDL includes block diagrams, process diagrams, signal and data type definitions (with inheritance), and message sequence charts for object communication. Standard SDL modeling may be restricted in order to support code generation (ROOM, Harel). Typical translation mappings are C++ classes (for example, for ROOM actors) and class
members (data or function, such as for ROOM structural and behavioral components). One SDL tool, for another example, maps processes to an RTOS task, signal to messages at a memory location, and time to a timer device.
Programming and Architecture
. In contrast to the structural approach, behavioral coding is done during object modeling. In the behavioral approach, programming is reduced to specifying event handlers (such as state-machine actions) and objects that are hand-coded (for example,
performance). Event handlers can implement embedded requirements such as tight assembly code, hardware access, or interrupts. Code for an event handler is typically small (perhaps five to 10 C++ statements). Translators offer options such as omitting library functions in order to meet memory constraints. Translation tools offer a little user control over the architecture, by the way in which the application's object structure is modeled or by a choice of run-time libraries or RTOS. As with all approaches, entire
subsystems can be implemented outside the scope of the model and integrated with conventional programming environments.
Methodology
. Like the structural approach, behavioral code generation is used to elaborate the analysis and design models. Detail can be added to object structure (for instance, by subclassing actors in ROOM) and behavior (adding new states). Developers can progressively develop an implementation model by adding to design, moving from stubbed-out environment interfaces to
real ones, and from simulated behavior to target execution. Tools don't use reverse engineering, because all code comes from the models. This approach encourages continued use of the models throughout a system's life cycle.
Development
. Developers must adopt a state-machine view of system functionality in addition to an object view of system structure. Fully specified ("executable") behavior enables test and debug to start with simulated models. These models can include environment interfaces
and hardware components, as well as the software under development. Code generation can be relatively complete, with event handlers constituting as little as 5% to 10%. Generated code quality should be comparatively good because translators have matured in response to customer feedback. Some tools support the transition from the development environment to the target prototype or offer features that enable the construction of relatively small systems. Tool vendors offering behavioral code generation include
i-Logix (Harel), Telelogic and Verilog (both SDL), and ObjecTime (ROOM). Typical applications come from the telecommunications industry, although many kinds of systems can be modeled with state machines.
Translative Approach
Overview
. The translative approach is based on application and architecture models that are independent of one another. A complete application model of object structure, behavior, and communication is created using the Object-Oriented Analysis (OOA)
method, first developed by Shlaer and Mellor. As is the case with the behavioral approach, developers can simulate system behavior before generating code. An architecture model (a set of code patterns called templates or archetypes) is developed with a tool that supports this approach. A translation engine then generates code for the application according to the mapping rules in the architecture. The translative approach offers significant reuse because the application and architecture models are independent.
Application Models
. The system is partitioned into domains. A domain is modeled completely to enable simulation and code generation. A domain is modeled by an object information model, state models for each object, and action specifications for each state. Action specifications are written in a language proprietary to each tool, not in an HLL. Simulation is typically performed by interpreting the action specifications. The application model is verified by this simulation before code
generation.
Architectures
. An architecture model is a complete set of translation rules that map OOA constructs onto source code and implementation (run-time) mechanisms. The mapping should be complete; that is, all OOA constructs used in application models are translated. Typical mappings address concurrency (for example, threads, multi-tasking, single task), event handling (queues, inter-process communication, or I/O streams), and data (structures, storage mechanism, and persistence). Templates or
mechanisms address embedded issues such as memory layout, hard deadlines, interrupt service, timers, and hardware access. Any target language can be supported with the translative approach; various projects have used C, C++, Ada, 4GL, SQL, and assembly.
Building an architecture model is a development project in its own right. Translation mappings are written in a proprietary scripting language. These mappings have typical programming constructs: literal string output, substitution of specific model
objects for variables of the translation rules, and iteration and selection of translation rules. While constructing an architecture is an effort akin to building a table-driven compiler, there is help. Tool vendors provide generic architectures which developers can modify. There are some third-party sources for architectures. Developers can reuse an architecture for other products on the same platform, thus amortizing their effort. Vendors are improving their architecture-construction tools, which may
include template libraries, architecture inheritance, and composition aids.
Translation
. Given an application model and an architecture, the translation engine extracts identified objects from the model repository, makes substitutions, and emits code according to the scripted mapping rules. Translation can include code from a run-time library and can vary generated code according to options (such as to omit certain objects) or model annotations about design properties. The developer totally controls
code generation in the translative approach and the code is potentially complete. Developers might choose to hand code performance-critical objects in assembly rather than build translation rules for them. Projects report generating up to 95% of medium-sized systems.
Methodology
. The translative approach produces an application all at once, rather than elaborating an application model gradually into design and code. This approach presumes somewhat independent development of an application
model, which is implementation-independent, and an architecture model, which is application-independent. When the two are put together, translation produces an application. While the application model can be verified by simulation, the architecture model is verified mainly by successful translations. Their independence allows application models to be reused with different architectures (such as for a different price-performance platform) and architectures to translate other application models, such as for
product line variations. With the translative approach, the application model, instead of source code, becomes the principal software artifact. The combination of architectures and translation engine is analogous to a set of HLL compilers.
Development
. As with the behavioral approach, there is only one-way code generation, no reverse engineering. To change the system, developers modify the application and/or architecture model as appropriate and then regenerate the code. While complete code can
be generated, code quality is totally up to the user. This variability contrasts with structural and behavioral approaches, where vendors ensure their translation quality. The trade-off is that code from these approaches is not as flexible in its target architectures, as is translative code. In a translative approach, developers could even change to a new HLL by changing architecture mappings.
Once this approach is fully adopted, an organization might build architecture models for each target
technology. This development could justify separate architecture and application groups, with appropriate specialization of their expertise. Vendors offering tools for the translative approach include Project Technology, Scientific and Engineering Software, and Kennedy-Carter (U.K.). Any embedded application would be suitable for this approach. The key determinant is the potential return from reusing the architectures.
Other Code-Generation Approaches
Some code-generation technologies
available to embedded developers are not based on object models. While they're beyond the scope of this article, I mention them to show that the trend toward code generation is broader than OO methods.
Dataflow diagrams are the basis for generating digital signal processor code in the tool suites from such companies as Mentor Graphics and Cadence for these specialized processors. Generated code implements the overall flow for processing signals, while the processing steps are either hand coded (for
example, in assembly for highly repetitive, processing loops) or implemented by standard library routines. The dataflow concept is used also in iconic diagramming tools that support specific application areas such as control systems (such as Integrated Systems' Matrix), instruments (National Instruments' LabView), and parallel data processing. These tools typically come with libraries of routines that implement the icons, which represent functions unique to the application area.
Tools for older methods,
such as real-time structured analysis and design methods (by Hatley or Ward, for example), also generated code frames for system structure-generally from design models. This approach lessens the utility of the analysis model, which was usually not convertable to design models. The oldest approach to code generation used control flow charts. These diagrams offered nearly a one-to-one mapping to HLL, yielding a simple "code generation" that did not provide much leverage. Nasi-Schneiderman diagrams and AdaZ,
a diagrammatic version of Ada, are two specific examples once supported by tools.
Comparison
As a handy reference for the following sections for management, consider the simplified comparison of the three code-generation approaches shown in Table 1.
TABLE 1: Comparison of three code-generation approaches.
|
Approach:
|
Structural
|
Behavioral
|
Translative
|
|
Models supported:
|
objects
|
objects/states/actions
|
objects/states/actions
architecture
|
|
Methodology:
|
OMT, Booch ...
|
Harel, SDL, ROOM, UML
|
Shlaer-Mellor
|
|
Languages targeted:
|
C++, Ada, . . .
|
C++, C, . . .
|
any (user-defined)
|
|
Extent of code gen:
|
framework
|
complete
|
full, any architecture
|
|
Control of code and architecture:
|
templates
|
user code, RT libraries, generator options
|
complete, separate, architecture model
|
|
Tool support:
|
reverse engineering protected code
|
simulation and debug
|
simulation and debug, construct architecture
|
|
Cost:
|
small
|
tooling, learning
|
building architecture
|
|
Advantage:
|
synch code to model
|
early verification reuse architecture
|
|
A Business Case For Model-Based Code Generation
A decision to adopt any major development technology indicates a change in how your organization will build its products. A change such as this is best considered in terms of how the balance of benefits and costs fits the particular business situation at hand. Beyond this business justification, you should consider how your technology situation may favor or hinder your use of model-based code generation.
Benefits and Costs
The potential benefits and costs of model-based code generation both increase with the type of approach, from structural to behavioral to translative.
Benefits
. A general benefit of all approaches is that they strengthen the advantages of using OO methods. These advantages include the ability to build larger, more complex systems and improve maintainability with models that are more understandable than code. With the capability to synchronize changes in
models and code comes the opportunity to easily iterate application development in support of an elaborative methodology. The increased productivity available from automated code generation extends throughout the product's life cycle.
Tools that generate behavioral code usually offer a facility to also simulate the behavior as modeled. This facility provides the opportunity to verify system behavior during analysis, before designs and code are developed. Tools that support a translative approach improve
time to market, quality, and costs, also by enabling architecture reuse. Both approaches can reduce development cycle time for enhancing the original product. The translative approach can reduce time to market for moving an application to a new platform (reusing the same application model).
Costs
. Investment in code generation comprises adoption and process change and the incremental cost for the initial project. Process change costs include learning to model applications and construct
translators, and buying and learning tools. Automation, reuse, and catching errors earlier cuts the costs of subsequent projects and maintenance. Costs can shift earlier in the project and decline over the life cycle of the product line. The larger positive impact on cost benefits comes from increased competitiveness (as in time to market). Of course, cost benefits would be negatively affected if the adoption and use of model-based code generation are ill-managed.
Marketing Situation
Market opportunities that most highly favor model-based code generation (especially behavioral or translative) have several attributes. The product line is relatively complex and long-lived, has moderate resource constraints, ships in low to moderate volumes, and drives the market by diversity or speedy evolution. This opportunity comes with being an early adopter. Once model-based code generation is widely used, any advantage must come from gaining more leverage than competitors. Code generation from object
models has been used in applications for telecommunications (large switches, satellites, radio, and handheld devices) and networking (video pump), among other areas.
Engineering Situation
Prerequisites for adopting model-based code generation should include experience in analysis and design modeling, a solid knowledge of the application (both user needs and solution architectures), and organizational support for a process change. Organizations considering model-based code
generation should consider how to integrate it with their current development capabilities, including custom embedded design techniques, languages and cross-compilers, commercial software components (such as RTOSs), and legacy software.
Adopting Model-Based Code Generation
Adopting model-based code generation starts with deciding what approach to use in conjunction with choosing OO method and tools. A period of transition incorporates model-based code generation by changing your
engineering organization and process. Adoption ends when you can use the technology effectively to deliver products.
Choosing an Approach
Code generation capability may be a key factor in choosing methods and tools, depending on the extent of benefits you expect. (Vendors continuously improve their tools. Prospective users should seek up-to-date product information.)
Other factors include support for and comfort with the OO methods. One strategy is to pick an approach with
demonstrated success in your application area. For example, the behavioral approach is typified by methods that are intended for telecommunications (like SDL) or were developed in that industry (as with ROOM). An engineering group with expertise in OO modeling and building translators might benefit quickly by going straight to a translative approach.
Another strategy is to adopt code generation incrementally. Start close to your current capability with structural code generation and an elaborative
methodology. Later, generate behavioral code. When appropriate, take full control of code generation with a translative approach. Because methods are associated with particular code generation approaches, this strategy implies either changing methods and vendors, finding a multi-method meta-tool (perhaps from Mark V, Advanced Software Technologies, Protosoft, or Popkin Software & Systems) and focusing on the modeling notation, or waiting for the tool vendor to evolve its support for code generation.
Choosing a
modeling tool for code generation involves factors such as support for desired source languages, how translations can be controlled and customized, reverse engineering and protection of hand-coded portions (in the structural approach), behavior simulation for verification and debug, incremental code generation, and aids for constructing architecture models.
Transition to Model-Based Code Generation
Adopting a new development technology involves engineering changes and management
support. Planning, supporting, and coordinating this transition is key to making the new technology "take." Much of the failure of new technology lies with inadequate management of its adoption. Changes will affect development process, tool environment, and organization. Depending on how extensively you leverage this technology, it may also affect product lines and business models.
Some useful tactics for adopting code generation include pilot projects, planning a sequence of process changes (modeling
expertise, then translator construction, for instance) in order to stabilize and build on a foundation, and a measurement program to assess cost benefits. Pilot projects help manage risks. In pilots, check out how you can generate code structures required for your application and how the development process changes.
Using Model-based Code Generation
Project Management
. Objectives may include design for reuse (of models or architectures, for example) and the extent of
model-based code. You might organize the team into a modeling group with domain expertise and an architecture team with platform expertise (especially if using the translative approach). The work breakdown structure and schedule will be oriented to modeling more than coding. Progress tracking will use measures appropriate to modeling and translator construction. Project managers should plan tactics to avoid or mitigate the risks of process changes, inadequate or faulty code generation, or models falling out of
synch with modified code.
Development
. Design could involve novel tradeoffs such as hand-coding vs. model-based generation and new techniques to meet embedded constraints. In a translative approach, design and coding diffuse into translator construction. The increased translation "distance" (starting from models) compounds tasks such as debugging and getting the code right. Integration is based on interfaces in the models and will involve manual and generated code, and possibly commercial
components or legacy software. Testing starts with simulation of behavioral models. Verification must cover translators as well as models. Under the structured approach, keeping code and models consistent in the face of product evolution involves protection of hand code (from overwriting by code generation) and reverse engineering. All approaches must enable integration of software parts such as commercial C++ libraries.
Top Ten Tips for Using Model-based Code Generation
Here are
some guidelines for implementing model-based code generation:
10. Analyze your business situation to ensure the benefits will make a difference
9. Justify adoption based on a competitive advantage like time to market
8. Pick an approach that suits your technical capabilities
7. Choose OO methods and tools for how well they generate code
6. Develop different specialists for application modeling and architectures
5. Conduct pilot projects to learn gradually
how to use the technology
4. Model your applications with code generation as an objective
3. Maximize your use of code generation; resort to hand coding only if critical
2. Keep your models up-to-date throughout product lifecycle and regenerate code
1. Time your adoption to match your tolerance for risk to the technology's maturity
Future of Model-Based Code Generation
Emerging Technology
. Model-based code generation is an emerging
technology that is quite immature when compared to programming languages and compilers. The design of modeling "languages" specifically to support code generation has relatively little supporting theory. Schemes for code generation show a diversity which is characteristic of an experimental phase in technology evolution. Many ancillary tool capabilities, such as model-based cross debug, do not yet exist.
Most tools generate source (rather than executable) code so that users can modify it to compensate
for possible errors or deficiencies. This feature is similar to early compilers, which let users modify assembly code. The translative approach essentially provides a framework in which the end user constructs translators with efforts that are similar to building table-driven compilers. Early adopters of emerging technology incur risks and costs to gain a competitive advantage from being first on the technology wave.
Technology Trends
. By examining the evolution of HLLs and compilers, I can
project trends in the maturing of model-based code generation. The level of abstraction will continue to approach the problem domain. Tool environments will support the integration of foreign models and HLL programming tools. Simulatable models will evolve into executable ones that prototype system operation and serve as "monitors" for debug and analysis. Target languages will diversify and code quality will improve. Translators will generate machine code directly and support optimizations. Architectures will
be built with special modeling tools. More of a system will be model-based, but HLL and even assembly-based objects will continue to play a role. A commercial market will develop for pre-built architectures and object models.
Model-based code generation is technology of singular importance for software development, embedded systems included. Early adopters can gain a competitive advantage in leveraging models for speedier development, fewer implementation problems, and meeting user needs.
Acknowledgements
I'd like to gratefully acknowledge the information about model-based code generation shared with me by developers (Bradley Frohman of Motorola and Dirk Epperson of Sybase) and product and engineering managers from OO tool vendors: Project Technology, Scientific and Engineering Software, Kennedy-Carter, Interactive Development Environments (now part of Aonix), Cadre Technologies (now part of Cayenne), Rational, ObjecTime, Verilog (also known as Logiscope), SELECT Software
Tools, Iconix, Popkin Software & Systems, Advanced Software Technologies, and Mark V.
Rodney Bell is principal of ASSET Consulting, which works with suppliers and developers on the development and adoption of emerging technologies such as model-based code generation, reuse software processes, and co-design. For 20 years he helped pioneer software development tools (such as embedded CASE tools, cross compilers, source debuggers, and high-level systems languages) with Mentor Graphics, Tektronix, and
Burroughs.