Using C Language Extensions for Developing Embedded Software

Download Using C Language Extensions for Developing Embedded Software

Preview text

Delft University of Technology Software Engineering Research Group
Technical Report Series
Using C Language Extensions for Developing Embedded Software:
A Case Study
Markus Voelter, Arie van Deursen, Bernd Kolb and Stephan Eberle
Report TUD-SERG-2015-010

TUD-SERG-2015-010 Published, produced and distributed by: Software Engineering Research Group Department of Software Technology Faculty of Electrical Engineering, Mathematics and Computer Science Delft University of Technology Mekelweg 4 2628 CD Delft The Netherlands ISSN 1872-5392 Software Engineering Research Group Technical Reports: For more information about the Software Engineering Research Group: Note: To appear in Proceedings of the 30th Annual ACM SIGPLAN Conference on Object-Oriented Programming, Systems, Languages, and Applications, OOPSLA 2015, part of SPLASH 2015
Copyright c ACM. Permission to make digital or hard copies of all or part of this work for personal or classroom use is granted without fee provided that copies are not made or distributed for profit or commercial advantage and that copies bear this notice and the full citation on the first page. To copy otherwise, to republish, to post on servers or to redistribute to lists, requires prior specific permission and/or a fee.


Using C Language Extensions for Developing Embedded Software: A Case Study

Using C Language Extensions for Developing Embedded Software: A Case Study

Markus Voelter
independent/itemis, Germany [email protected]

Arie van Deursen
Delft University of Technology, The Netherlands
[email protected]

Bernd Kolb, Stephan Eberle
itemis AG, Germany {kolb|eberle}

We report on an industrial case study on developing the embedded software for a smart meter using the C programming language and domain-specific extensions of C such as components, physical units, state machines, registers and interrupts. We find that the extensions help significantly with managing the complexity of the software. They improve testability mainly by supporting hardware-independent testing, as illustrated by low integration efforts. The extensions also do not incur significant overhead regarding memory consumption and performance. Our case study relies on mbeddr, an extensible version of C. mbeddr, in turn, builds on the MPS language workbench which supports modular extension of languages and IDEs. Categories and Subject Descriptors D.3.2 [Extensible languages]; D.3.4 [Code Generation]; D.2.3 [Program Editors]; C.3 [Real-time and embedded systems] Keywords Embedded Software, Language Engineering, Language Extension, Domain-Specific Language, Case Study
1. Introduction
According to Ebert and Jones [12], 80% of embedded systems companies implement embedded software in C. C is good at low-level algorithms and produces efficient binaries, but it provides only limited support for defining custom abstractions. This can result in code that is hard to understand, maintain and extend. On the other hand, high-level modeling tools make it hard to effectively address the low-level aspects important for embedded software. To address this apparent contradiction, a team at itemis and fortiss has built mbeddr,

an extensible version of C that comes with extensions relevant to embedded software development. At the same time, C’s native constructs are available to write efficient low-level code if needed. For details on mbeddr see Section 2. Contribution To provide empirical evidence to what extent the kinds of language extensions supported by mbeddr are useful, we report on a case study on the development of a smart meter (SMT). Our contribution is to analyze, in a real-life project, how the extensions affect the complexity, testability, and runtime overhead of embedded software, as well as the effort for its development. Audience We target language engineering researchers (interested in empirical data justifying their work or looking to understand problems they may be able to solve) as well as embedded systems developers (seeking to understand how language extensions can help them in practice). Structure We organize the paper according to the structure for case studies proposed by Runeson et al. [41] and Yin [60]. We begin by outlining the background on embedded software, language engineering, MPS and mbeddr in Section 2. In Section 3 we introduce the research questions and the collected data. Section 4 then describes the relevant context of the case study (as suggested by Dyba et al. [11]) including the hardware and software architecture, the initial artifacts and the development timeline. Section 5 provides an overview over the mbeddr-based implementation of SMT and illustrates the use of the extensions. We answer the research questions in Section 6, complemented with a critical discussion in Section 7. We wrap up the paper with related work and conclusions in Sections 8 and 9, respectively.
2. Background
2.1 Embedded Software Engineering Embedded software controls hardware devices, often under time and memory constraints. It can be simple (lighting controls running on an 8-bit microprocessor with a few KB of RAM) or sophisticated (airplanes, missiles and process control). The amount of software embedded in devices is growing and its value for businesses is increasing rapidly [9].



Using C Language Extensions for Developing Embedded Software: A Case Study


According to our own experience, as well as experiences of others [5, 26, 28, 29, 48], developing embedded software poses challenges. These include meaningful abstraction while incurring little runtime overhead (because unit pricing often prohibits an increase in resources), addressing the safety and security issues incurred by C (because many embedded systems are also safety-critical), integration with various metadata (for analysis, deployment or parametrization), support for testing and monitoring (because updating deployed faulty systems is often expensive), and adhering to development processes and standards regarding requirements tracing or documentation. Together with the need for shorter time-to-market and product-line variability this makes for a challenging field.
2.2 Language Engineering with MPS Language engineering refers to building, extending and composing general-purpose and domain-specific languages (DSLs) [54]. Language workbenches [13, 14] are tools for efficiently implementing languages and their integrated development environments (IDEs). The JetBrains Meta Programming System (MPS)1 is an open-source language workbench with comprehensive support for specifying structure, syntax, type systems, transformations and generation, debuggers and IDE support (see Figure 2). MPS relies on a projectional editor. Projectional editors avoid parsing the concrete syntax of a language to construct the abstract syntax tree (AST); instead, editing gestures directly change the AST, and the concrete syntax is rendered (“projected”) from the changing AST.2 This means that, in addition to text, languages can also use non-parsable notations such as mathematical symbols, tables and diagrams [52]. Since projectional editors never encounter grammar ambiguities, they can support language composition [50]. Traditionally, projectional editors were tedious to use and were hardly adopted in practice. With MPS, in contrast, editing textual syntax is quite close to “normal text editing”. It also supports diffmerge on the level of the projected concrete syntax. The study in [57] shows that users are willing and able to work with the editor after getting used to it.
2.3 C Extensions and mbeddr mbeddr [55] applies projectional editing to embedded software engineering. Built on MPS, it provides an extensible version of C plus a set of predefined extensions such as physical units, interfaces and components, state machines and unit testing. Since extensions are embedded in C programs, users can mix higher-level abstractions with low-level C code. Developers are not forced to use the extensions; they may use them only when they consider them appropriate. mbeddr also supports product line variability, requirements
1 2 Watch this video to gain a better understanding of projectional editing.

traces and documentation as well as formal verification [34]. mbeddr is open-source under the Eclipse Public License and is available from Several commercial systems have been developed with mbeddr. It forms the basis for a controls engineering tool by Siemens PLM Software.
Thanks to MPS, each mbeddr extension is modular: no invasive changes to C are required to add a new extension, and multiple extensions can be seamlessly combined in a particular program. Extensions provide concrete syntax, a type system, execution semantics and IDE support. AST transformations reduce extensions to C, possibly in multiple steps. Eventually, textual C code is generated which is compiled with existing (possibly platform-specific) compilers. Users are encouraged to use MPS’ facilities to define their own domain-specific C extensions. Details on building languages and language extensions are provided in [54] and [51].
3. Case Study Setup
The goal of this research is to find out the degree to which C language extensions (as implemented in mbeddr) are useful for developing embedded software. We adopt the case study method to investigate the use of mbeddr in an actual commercial project because we believe that the true risks and benefits of language extensions can be observed only in such projects. Focussing on a single case allows us to provide significant details about that case. To provide insight beyond this single case, we generalize analytically in section 7.4.
To structure the case study, we introduce four specific research question in Section 3.1. They are aligned with the general challenges for embedded software discussed in Section 2.1 as well as with the key non-functional requirements of the SMT case at hand. The data we collected to evaluate these research questions is introduced in Section 3.2.
The case study is not explicitly comparative. However, the implicit comparison is to the state of the practice in embedded systems development, which is the use of plain C (we briefly mention other, and in particular, model-based approaches in Section 8, Related Work). The comparison is not with an actually built alternative plain C implementation because it would be too expensive to build a production-quality second implementation. An example smart meter implementation that was available to the team was not productionready for the reasons discussed in Section 4.4, so it would not have been a useful and fair comparison. Instead, the comparison is analytical, based on the substantial experience of several of the authors in creating embedded systems in C.
Finally, this paper does not consider the development of mbeddr itself (as an example of language engineering). We refer the reader to Chapter 10 of [51]. 3.1 Research Questions C makes it hard to create abstractions for efficiently managing the complexities associated with embedded systems. However, to ensure quality, long-term maintainability and




Using C Language Extensions for Developing Embedded Software: A Case Study

the opportunity for reuse, such abstractions are necessary. The first research question is thus: RQ-Complexity: Are the abstractions provided by mbeddr beneficial for mastering the complexity encountered in a real-world embedded system? Which additional abstractions would be needed or useful? Testing embedded software is challenging because of hardware dependencies, restrictions on on-device debugging and subtle timing and resource constraints. Embedded software often has few or no automated unit tests, which is a problem for quality, productivity and evolvability. Additionally, because of hardware dependencies, some problems are found only during commissioning (the process of getting the code to run on the target device). Hence, our second question is: RQ-Testing: Can the mbeddr extensions help with testing the system? In particular, is hardware-independent testing possible to support automated, continuous integration and build? Is incremental integration and commissioning supported? Most embedded software is constrained regarding available memory, processor performance or as a consequence of external timing requirements. In a trade-off between efficiency and maintainability, efficiency usually wins because of unit price constraints. Abstractions thus must not come with too much overhead (the exact magnitude of too much depends on the context). We capture this in question three: RQ-Overhead: Is the low-level C code generated from the mbeddr extensions efficient enough for it to be deployable onto a real-world embedded device? Independent of how useful an approach is in terms of the first three research questions, it must not require significant additional effort in the various phases of development, or it will not be adopted. This leads to research question four: RQ-Effort: How much effort is required for developing embedded software with mbeddr?
3.2 Data Collected
Below we list the data collected to answer the research questions, taking into account that this is a real, revenuegenerating industry project, and some desired data may not be available (cf. Section 7.5 on Reliability). RQ-Complexity We look at the mbeddr extensions used in SMT as well as those developed specifically for the project and those identified as still missing. We qualitatively asses their impact on the complexity of the implementation. RQ-Testing We investigate test coverage of the SMT implementation and discuss the test-specific SMT code. We report on the experience with commissioning the system as well as the expected effort for certification by the customer. RQ-Overhead We measure the size of the system and compare it with the resources of the target hardware. We analyze the achieved performance. We also analyze the runtime overhead incurred by some of mbeddr’s generators.

RQ-Effort We measure and discuss the effort required for developing SMT, distinguishing implementation, testing and commissioning of the system as well as the development of custom language extensions.
4. Case Study Context
4.1 What is a Smart Meter? An electricity smart meter continuously senses the instantaneous voltage and current on a mains line using analog front ends and analog-to-digital converters. From the measured raw values, it computes various energy consumption data in physical quantities over time, most importantly RMS (root mean square) voltage and current, active, reactive, and apparent power, power factor, as well as active and reactive energy. The resulting data is displayed on an LCD display, recorded in histories, and analyzed and evaluated with regard to maximum loads, times of use, and billing periods. In addition, a smart meter communicates this data to the outside world over networks. It may also accept commands via the network. The primary success criterion for a smart meter is that it achieves a specified accuracy, verified through a certification process: a prerequisite for this is the real-time performance of the underlying computations (RQ-Overhead). In order to be a viable business, the smart meter has to be reliable, low cost, able to evolve (over time and across variants) and must be developed with an effort at or below industry average; this is reflected in RQ-Complexity, RQ-Testing and RQ-Effort. The specification for the particular smart meter developed in this project can be found in [44].
4.2 Hardware Architecture The SMT target hardware consists of two MSP430 processors3 clocked at 25 MHz. One variant of the system uses the MSP430F67791 with 256 KB Flash ROM and 32 KB RAM, the other variant uses the smaller MSP430F6736 with 128 KB Flash ROM and 8 KB RAM. One processor performs the real-time metrology, the other performs higherlevel application logic and communication; the separation ensures undisturbed execution of the real-time functionality. The two processors communicate using a lightweight implementation of MQTT4 over UARTs5. The application processor communicates with the outside world via RS485 and IrDA interfaces and an industry-specific communication protocol called DLMS/COSEM.6 The system has a 7segment LCD to show system status and measurements. The hardware was determined irrespective of the software development approach, so the implementation must cope with this hardware in terms of available resources (RQ-Overhead).
3 4 A lightweight communication protocol, see 5 Universal Asynchronous Receiver/Transmitter, used in serial comm. 6 Industry-specific data exchange messages, see



Using C Language Extensions for Developing Embedded Software: A Case Study


4.3 Software Architecture

The software functionality can be split into two major parts,

corresponding to the two processors: low-level measurement

and higher-level application functions. Figure 1 shows a

more detailed breakdown. Note that the SMT-specific func-

tionality of each of the boxes is not relevant for this paper.

No real-time operating system is used on the processors

and the system is fundamentally interrupt-driven: interrupt-

triggered background tasks preempt foreground tasks, which

are in turn activated cyclically from the main function. This

approach is known as one-threaded programming [40].

The interrupt-triggered tasks include reading the raw

measurements (triggered by the ADC interrupt7) as well han-

dling recalibration requests (triggered by UART-Receive).

Interrupt-triggered tasks preempt the foreground tasks and

always run to completion, which makes them time-sensitive.

This is why the measurement task only performs simple cal-

culations and then uses message passing to hand the data

off to a foreground task that performs more sophisticated

calculations involving expensive division and square roots.

Other foreground tasks include calibrations as well as the

UART-MQTT-based inter-processor communication.

In terms of performance, the challenge is to ensure that









1 4,096



second to maintain the required 4,096 Hz sample rate, and

to leave enough time for the foreground processes to finish

their tasks within their own time budgets (one second for the

calculations mentioned above).

4.4 Smart Meter Example Code The SMT development team had access to an existing example smart meter implementation for the MSP430, made available by the processor vendor.8 The purpose of this example smart meter code (ESC) is to serve as a realistic, but incomplete implementation of a smart meter on the MSP430. The ESC comprised only a subset of the functionality needed for SMT and required significant extension (for example, to run it on two processors and to support more flexible communication stacks; see Section 6.4), so a thorough understanding of the code was necessary. A document describing the high-level structure of the ESC was also available.
The SMT team decided that the code quality (understandability, modularity, maintainability, testability and test coverage) of the ESC was unacceptable for sustained SMT development; they decided to build a completely new implementation of SMT using mbeddr. Only the core algorithms were taken from the ESC; SMT is otherwise new software.

4.5 Development Timeline and Process Development started in July 2012. As of February 2015, most of the required functionality is implemented, but development and certification are ongoing. The project used

7 ADC is short for Analog-Digital Converter 8

Criterion # of Files

Common 134

Metro 101

App Total



Total LOC Code LOC Comment LOC Whitespace LOC

8,209 4,397
950 2,852

10,447 5,900 2,402 2,145

10,908 5,510 2,620 2,778

29,564 15,807
5,972 7,775

Table 1. Size of SMT. Common code runs on both processors, Metro runs on the metrology processor and App runs on the application/communication processor.

an iterative process based on a specification [44] that is updated approximately once per year. Integration with the target hardware started in February 2014 and was spread over 2 months. So far, 300 person days (PD) were spent, spread over 31 months, a 50% developer utilization. Fulltime work was not feasible because of constraints in project funding, decision gateways and requirements elicitation.

4.6 Tools In addition to mbeddr, SMT used gcc and gdb (for compiling and debugging on the PC) and the IAR Embedded Workbench9 and associated hardware-specific compilers.

5. The mbeddr SMT Implementation
5.1 Overall Structure Figure 1 shows the structure of SMT. Each of the small boxes represents one or more mbeddr components in the source code. SMT consists of a hardware-dependent Hardware Abstraction Layer (HAL) as well as hardware-independent communication stacks (COMM), utilities (UTIL) and functionalities for the actual measurement (METROLOGY) and higher level computations and external communication (APPLICATION). The separation into hardware-dependent and hardware-independent layers is a prerequisite for testing of components on the PC (RQ-Testing).
5.2 Size of the System The SMT implementation consists of code that is deployed onto the target as well as code that is used only for testing. Table 1 shows the size of the deployed code in terms of generated C; it is ca. 22,000 non-empty lines of code (LOC). The additional test code is roughly similar in size, resulting in 44,000 LOC in total. While this is small compared to automotive, aerospace or defense systems, its size is typical for software found in industrial sensors, AUTOSAR basic software or Internet-of-Things devices.
Table 2 shows the number of instances of important language concepts. Because projectional editing can use nontextual notations, counting lines is not easily possible and we use a conversion factor similar to the one in [53] to calculate the LOC for language constructs; this leads to ca. 42,000 non-empty LOC total, a size roughly similar to the generated





Using C Language Extensions for Developing Embedded Software: A Case Study

Figure 1. Layers, subsystems and components in SMT. The dashed-border components are optional. Only the HAL subsystem (below dotted line) is hardware-dependent.
C code. This demonstrates that mbeddr’s extensions do not lead to a significant reduction in code size: they trade boilerplate in some places for well-structuredness, readability, analyzability and maintainability in others.
5.3 Use of mbeddr’s Built-in Extensions Table 2 shows that SMT makes use of all major mbeddr C extensions, indicating their relevance for embedded software, as well as their composability. The rest of this subsection introduces mbeddr’s extension in some detail. The code examples in this subsection are kept simple for reasons of space and do not show all features of the respective extension; more details on and bigger examples of the extensions can be found in [55] and [51]. Figure 2 shows a screenshot of the mbeddr IDE with some of the languages and notations. Chunks mbeddr structures code into chunks; a chunk can be seen as (and is often generated into) a single file. There are chunks for units and conversion rules, chunks for requirements, chunks for feature models, and chunks for (extended) C code called implementation modules (generated to a .c and a .h file each). Chunks also act as namespaces and are the primary means for structuring mbeddr code. SMT has 382 implementation modules and 46 other chunks. C Constructs mbeddr supports almost all C language constructs (the few exceptions are discussed in [53]). 310 functions, 144 structs, 334 global variables and 8,500 constants





Implementation Modules


(≈ Files)

Other (Req, Units, etc.)


C Constructs

Functions Structs / Members Enums / Literals Global Variables Constants

310 144 / 270 150 / 1,211
334 8,500


Interfaces / Operations Atomic Components Ports / Runnables Parameters / Values Composite Components Component Config Code

80 / 197 140
630 / 640 84 / 324
27 1,222

State Machines

Machines States/Transitions/Actions

2 14 / 17 / 23

Physical Units

Unit Declarations Conversion Rules Types / Literals with Units

122 181 593 / 1,294

Product Line Variability

Feature Models / Features Configuration Models Presence Condition

4 / 18 10 117


Register Definition



Interrupt Definitions


Protocol Messages



Statements total Statements in components Statements in test cases Statements in functions

16,840 6,812 5,802 3,636


Test Cases / Suites Test-Specific Components Stub / Mock Components assert Statements

107 / 35 56
9/8 2,408

Table 2. Number of instances of language concepts in the mbeddr SMT sources (before generation to C text).

are used in SMT, the large number of constants being typical for embedded software. As discussed below, most of the SMT implementation is factored into components; however, 3,636 statements (ca. 22%) remain in functions. These are mostly mathematical utilities and filters, conversions, safe access to memory, and test helper functions. Components Components form the backbone of the SMT implementation (and most other mbeddr-based systems). Components are modularized units of behavior, specified via interfaces. Interfaces either define operations (callable through required ports and implemented via provided ports) or data items (received and sent through provided and required ports). Here is an interface that defines one operation:
// ADC is the analog-digital converter interface IADC {
int16 read(uint8 addr) }



Using C Language Extensions for Developing Embedded Software: A Case Study


Figure 2. The screenshot shows various parts of the SMT implementation: a part of the protocol parser state machine (top left), unit declarations (top right) and component wiring for a test case (bottom). It also illustrates how mbeddr provides IDE support for C and its extensions, including syntax highlighting, code completion, error markup, refactorings, quick fixes and tooltips. The screenshot also showcases the support for mixed notations (text, tables, diagrams).

Components provide and require ports. Each port is associated with an interface. Components implement the operations of the interfaces associated with provided ports in runnables, essentially C functions inside components. SMT has 80 interfaces, 167 components and 640 runnables. Here is a component ADCDriver that provides the IADC interface:

component ADCDriver { provides IADC adc int16 adc_read(uint8 int16 val = // low return val;

addr) level

<= op { code to read from


A client component can now declare a required port that uses the IADC interface. Implementation code in runnables can call operations on this required port:
component CurrentMeasurer { requires IADC currentADC internal void measureCurrent() { int16 current =; // do something with the measured current value

Components must be instantiated to be used, and their required ports connected to interface-compatible provided ports of other instances. This can be edited graphically (inline in a “C editor”), as shown in the bottom pane of Figure 2.
mbeddr also supports composite components, enabling hierarchical decomposition of systems (they contain their own set of instances). Of the 167 components, 27 are composite components. The code that instantiates, parametrizes and connects ports of components instances comprises 1,222 LOC (for deployment and multiple test setups).

The majority of SMT behavior resides in components: of the 16,840 total statements, 40% live in component runnables, 22% are in regular C functions (discussed above) and 34% are in test cases; the remaining 4% reside in state machines and a few other places. On average, each runnable consists of 11.5 LOC. The cyclomatic complexity of each runnable is low; the average is 1.98. State Machines State machines encode state-based behavior, and they live inside implementation modules, alongside C code or components. Textual, graphical and tabular syntax is available for any given state machine via multiple projections. SMT is not primarily a state-based system, so the use of state machines is limited to two examples. One implements the communication protocol and message parsing, a typical use case for state machines. The other one drives the display: since the display has limited real estate, its contents change based on various parameters, events and system states. The state machine tracks these changes and updates the display. Here is a very much simplified example of the state machine used for message parsing:
statemachine FrameParser initial = idle { var uint8 idx = 0 in event dataReceived(uint8 data) state idle { entry { idx = 0; } on dataReceived [data == LEADING_BYTE] -> wakeup } state wakeup { on dataReceived [data == START_FLAG] -> receivingFrame { idx++; } } state receivingFrame { .. }




Using C Language Extensions for Developing Embedded Software: A Case Study

State machines can be used as types in C. For example, the code below shows a local variable of type FrameParser. Built-in operators are available to interact with them:
// create and initialize state machine FrameParser parser; parser.init; // trigger dataReceived event for each byte for (int i=0; i parser.trigger(dataReceived|data[i]); }
Physical Units C types and literals can be annotated with physical units. New units can be declared based on existing units and conversion rules between different units can be defined. The type system then performs unit computations and checks. Figure 3 shows an example.
In SMT, which measures and samples real-world quantities and uses other quantities for calibration, units provide an additional level of checks that cannot be provided by just data types. Based on the 7 SI units available by default, SMT has 122 unit declarations and 181 conversion rules (units with different magnitudes such as km or mm count as different units in mbeddr). 593 types are annotated with a unit (in local or global variables, constants or arguments) and 1,294 numeric literals in the code have a unit associated with them. Testing mbeddr has first-class support for assertions, unit tests, and test suites. Below is an example that contains test cases for the FrameParser state machine plus a test expression (which represents test suites):
testcase testFrameParser1 { FrameParser p; assert(0) p.isInState(idle); // invalid byte; stay in idle parser.trigger(dataReceived|42); assert(0) p.isInState(idle); // LEADING_BYTE, go to awakening parser.trigger(dataReceived|LEADING_BYTE); assert(0) p.isInState(awakening);
} testcase testFrameParser2 { ... } testcase testFrameParser3 { ... } int32 main(int32 argc, char* argv) {
return test[testFrameParser1, testFrameParser2, testFrameParser3];

Figure 3. Example of physical units in SMT. Assigning a














error in the IDE. Note also the use of mathematical syntax.

mbeddr also supports constructs for efficiently writing tests for some of the other extensions. The most important one are mock components, which use special syntax for specifying expected behavior as part of a test case. The mocks can then be validated in a test case. Below is an example of a mock component for a protocol handler that specifies operation sequencing, assertions over parameters and also remembers the handle argument so it can be closed later:
mock component USCIReceiveHandlerMock { provides ISerialReceiveHandler handler Handle* hnd; sequence { step 0: { } do { hnd = handle; } step 0: handler.dataReceived { assert 0: parameter data: data == 1 } step 1: handler.dataReceived { assert 1: parameter data: data == 2 } step 2: handler.dataReceived { .. } step 3: handler.dataReceived { .. } step 4: handler.finsihed { } do { close(hnd); }

SMT has 107 test cases in 35 test suites, with over 2,400 assert statements. 56 of the 167 components are specific to tests. Of those, 8 are mocks and 9 are stubs. As discussed in Section 5.4, two of the three custom extensions were developed to simplify testing. Variability Feature models are an established formalism for expressing product line variability [3]. A feature model consists of a tree of features with constraints between them. Constraints include mandatory (feature must be in any valid system), optional (feature may not be in a system), or (one or more from a group of features may be in a system) and xor (exactly one of a group of features must be in a system). A feature may have attributes, and additional crosstree constraints may be specified. The code below is one of the feature models from SMT, expressed in mbeddr’s textual notation for feature models. It handles the variability associated with different LCD displays and configurations.

feature model SMTFeatures root opt Data_LEDs opt DataReadLED DataWriteLED [DigitalIOPortPin DISPLAY xor DISPLAY_V10 DISPLAY_V22 WRITABLE_FLASH_MEMORIES


The features (and hence, the variability expressed) in a feature model can be connected to implementation code through presence conditions. A presence condition is a Boolean condition over the features from a feature model attached to a part of a program; only if the condition evaluates to true for the selected product configuration will the corresponding code fragment be included in the program. Presence conditions are roughly similar to C’s #ifdef, but they are more structured, because they operate on MPS’ syntax tree: they cannot lead to syntactically invalid code. Figure 4 shows an example of a presence condition used on component ports.



Using C Language Extensions for Developing Embedded Software: A Case Study


In SMT, mbeddr’s variability support was used to implement 4 different feature models (metrology, platform, display variability, LED variability) with 18 features in total. 10 different configurations were defined for deployment and test setups. 117 presence conditions are used throughout the code. SMT also used the built-in consistency analysis which ensures that no variant contains dangling references: it checks that for every reference in the code (e.g., a reference to a variable), the referenced node (e.g., the variable) is part of (at least) all configurations that contain the reference.
Figure 4. A part of a composite component where two of its provided ports have presence conditions (the gray area marked with question marks). The ports are only part of the system if the respective features are selected. 5.4 Custom Extensions mbeddr encourages user-defined, project-specific extensions to grow the language towards a domain [46]. For SMT, three extensions have been developed; below we introduce the extensions and the specific rationales for developing them. Registers The MSP430 processor has special-purpose registers: when a value is written to such a register, a hardwareimplemented computation is automatically triggered based on the value supplied by the programmer. The result of the computation is then stored in the register. The reason for developing a custom extension is testability. In particular, running code that works with these registers on the PC for testing purposes leads to two problems: first, the header files that define the addresses of the registers are not valid for the PC’s processor. Second, there are no special-purpose registers on the PC, so no automatic computations are triggered. SMT solves this problem with a language extension that supports the definition of registers as first-class entities and allows read/write access from C code (see code below). The extension also supports specifying an expression that performs the computation. When the code is translated for the real device, the real registers are accessed based on the addresses defined in the processor header files. In the emulated case used in testing, generated structs are used to hold the register data; the expressions are inserted into the code that updates the struct, simulating the hardware-based computation.
exported register8 ADC10CTL0 compute as val * 1000 void calculateAndStore( int8 value ) {
int8 result = // some calculation with value ADC10CTL0 = result; // stores result * 1000 in reg. }
Interrupts As explained in Section 4.3, SMT is driven by interrupts. To integrate the component-based architec-

ture used in SMT with interrupts, it is necessary to be able to trigger component runnables via an interrupt. Similar to registers, the primary driver was testability: interrupts must be emulated for testing on the PC. A language extension allows the declaration of interrupts. In addition, the extension provides runnable triggers that connect the execution of the runnable to the occurence of an interrupt. The example below declares two interrupts, and the runnable interruptHandler is marked as triggered by an interrupt:
module USCIProcessor { exported interrupt USCI_A1 exported interrupt RTC exported component RTCImpl { void interruptHandler() <- interrupt { hw->pRTCPS1CTL &= ~RT1PSIFG;
Note that this code does not specify which interrupt triggers the runnable, because, for reasons of deployment flexibility, this is done as part of component instantiation, as shown below. Instantiation also checks that each interrupt-triggered runnable is bound to at least one interrupt. In addition, for testing purposes on the PC, there are language constructs that simulate the occurrence of an interrupt: test cases then simulate triggering of interrupts based on a test-specified schedule, and assert that the system reacts correctly.
instances usciSubsystem { instance RTCImpl rtc; bind RTC -> rtc.interruptHandler connect ... // ports
Messages External communication of the SMT device takes place via DLMS/COSEM messages. The low level protocol definition involves arrays pointing into other arrays, linked lists, multi-byte identifiers, fields that contain the size or number of other fields as well as other fine-grained, low-level details. Below is an example (DLMS/COSEM is even more complex, but the example below illustrates the challenges). SMT contains hundreds of message definitions.
// a field representing a timestamp for 10:20:00 uint8[6] f_time = {0x00A, // field type identifier
UNIT_TIME24, // unit used: time 3, // 3 payload bytes follow 10, 20, 00 // the time itself }; // a field representing a measured value uint8[4] f_value = {0x04D, // field type identifier
UNIT_QDOT, // unit used: mass flow 1, // 1 payload byte follows &dataField // addr of variable }; // a message that uses the two fields uint8[5] message = {0xAEE, // message type idenfier ID, // unique running message ID 2, // two fields folllowing f_time, // embed the time field f_value// embed the value field };
It is tedious and error prone to set up these structures manually, so the primary driver for this extension is robust-



Preparing to load PDF file. please wait...

0 of 0
Using C Language Extensions for Developing Embedded Software