You are on page 1of 48

Real-Time Systems Design and Analysis

Dr. Phillip A. Laplante, PE Associate Professor of Software Engineering

Language considerations
Introduction Assembly language Procedural languages Object-oriented languages Survey of languages Know the compiler and rules of thumb Coding standards

Why the language matters Language taxonomies Cardellis metrics Schedulability analysis

Why the language matters

A programming language represents the nexus of design and structure. Misuse of the programming language is often a source of performance deterioration and missed deadlines. Since software build depends on tools to compile, generate binary, link, and create binary objects, coding should take relatively little time if the design is solid. Programming is craft-like and as with any craft, the best practitioners are known for the quality of their tools and their skill with them. Real-time systems have been built with a wide range of programming languages.

Language taxonomies

There two different ways to partition the world of programming languages:

Imperative, functional, logic, other Procedural or declarative

Imperative languages involve assignment of variables (most widely used language are imperative, e.g. C, Java, Ada 95, Fortran, BASIC). Functional (applicative) languages employ repeated application of some function (e.g. LISP). Logic programming involves a formal statement of the problem without any specification of how it is solved (e.g. PROLOG). Procedural languages are defined by a series of operations (most languages you can think of). Procedural also describes a style of programming that is not object-oriented. Declarative (non-procedural) languages involve specification of rules defining the solution to a problem (PROLOG, Spreadsheets). Object-oriented describes a style of programming. Languages written to support this are called object-oriented.

Cardellis metrics
Economy of execution. How fast does a program run? Economy of compilation. How long does it take to go from sources to executables? Economy of small-scale development. How hard must an individual programmer work? Economy of large-scale development. How hard must a team of programmers work? Economy of language features. How hard is it to learn or use a programming language?
Cardelli, L. Bad Engineering Properties of Object-Oriented Languages, ACM Computing Surveys, Volume 28A, Number 4, 1996, pp. 150-158.

Schedulability analysis
The compile time prediction of execution time performance is known as a schedulability analysis. Can be achieved in three ways:
Eliminate or remove language constructs that destroy determinism such as unbounded recursion and unbounded while loops. Provide typesafe language mechanisms that provide for complete control of time. Build the programming language in conjunction with the operating system in such a way as to provide explicit interaction and control.

Most so-called "real-time languages" strive to achieve the first.

Language considerations
Introduction Assembly language Procedural languages Object-oriented languages Survey of languages Know the compiler and rules of thumb Coding standards

Assembly language
In terms of Cardellis criteria:
Excellent economy of execution. Excellent economy of compilation (vacuously). Poor economies of small- and large-scale development, language features.

Its use in embedded real-time systems is discouraged.

Lacks features of high-level languages Is unstructured and has limited abstraction properties. Is usually difficult to learn, tedious, and error prone. Resulting code is non-portable. It is rare that even the best programmers can beat a good optimizing compiler.

Assembly language
Used only in cases where the compiler does not support certain macroinstructions, or when the timing constraints are tight. Requires complex prologues and epilogues to prepare an assembly language program. Often a shell of the program is written in the high-order language and compiled to an assembly file, which is then massaged. Some languages such as Ada provide a pragma pseudo-op, which allows for assembly code to be placed in-line with the high-order language code.

Language considerations
Introduction Assembly language Procedural languages Object-oriented languages Survey of languages Know the compiler and rules of thumb Coding standards

Procedural languages
Overview Parameter passing techniques Global variables Recursion Dynamic memory allocation Typing Exception handling Modularity Cardellis metrics and languages that support the procedural style of programming

In the programming sense, those languages in which the program is organized into a sequence of steps. These languages are characterized by facilities that allow for instructions to be grouped together into sub-programs or procedures (modules). Appropriate structuring of the sub-programs allow for achievement of desirable properties of the software (e.g. modularity, reliability, reuse, etc.) Some languages, notably Ada 95 and C++ can be used as procedural and/or object-oriented languages.

Parameter passing techniques

In call-by-value parameter passing, the value of the actual parameter in the subroutine or function call is copied into the procedure's formal parameter. Since the procedure manipulates the formal parameter, the actual parameter is not altered. Useful when either a test is being performed or the output is a function of the input parameters.

Parameter passing techniques

In call-by-reference (call-by-address) the address of the parameter is passed by the calling routine to the called procedure so that it can be altered there. Execution of a procedure using call-by-reference can take longer than call-by-value since in call-by-reference indirect mode instructions are needed for any calculations involving the variables passed. In the case of passing large data structures such as buffers between procedures it is more desirable to use call-byreference.

Global variables
Variables that are within the scope of all code. References can be made in direct mode and thus are faster than references to variables passed via parameter lists. Global variables are dangerous because references to them may be made by unauthorized code, thus introducing subtle faults. Even in languages like Fortran, where blocks of global variables can be defined via named COMMON declarations, access is still not wellcontrolled. For this and other reasons, unwarranted use of global variables is to be avoided. Only recommended when timing warrants, or if the use of parameter passing leads to convoluted code. The use of global variables must be clearly documented.


When a procedure can either call itself or use itself in its construction. Is elegant and is often necessary, but has negative performance impact. Procedure calls require the allocation of storage on one or more stacks for the passing of parameters and for storage of local variables this uses large numbers of expensive memory and register indirect instructions. Precautions need to be taken to insure that the recursive routine will terminate otherwise the run-time stack will overflow. Use of recursion often makes it impossible to determine the size of run-time memory requirements. Iterative techniques such as while and for loops must be used where performance and determinism are crucial. Recursion theorem guarantees that any recursive code can be written iteratively and vice versa.

Dynamic memory allocation

Is important in the construction and maintenance of many data structures needed in real-time systems. Can be time consuming but is necessary, especially in the construction of interrupt handlers, memory managers. Linked lists, trees, heaps and other dynamic data structures can benefit from the clarity and economy introduced by dynamic allocation. In cases where just a pointer is used to pass a data structure, then the overhead for dynamic allocation can be quite reasonable. Care should be taken to ensure that the compiler will pass pointers to large data structures and not the data structure itself. Languages that do not allow dynamic allocation of memory require data structures of fixed size. While this may be faster, flexibility is sacrificed and memory requirements must be predetermined. Languages such as C, Pascal, Ada, and Modula-2 have dynamic allocation facilities while BASIC and old versions of Fortran do not.


Typed languages require that each variable and constant be of a specific type declared before use. Strongly typed languages prohibit the mixing of types, forcing the programmer to be precise. Strong typing can prevent corruption of data through unwanted or unnecessary type conversion. Compiler type-checking is an important way to find errors at compile time, rather than at run-time, when they are more costly to repair. Abstract data types may contribute an execution time penalty as complicated internal representations are often needed to support the abstraction. Some languages are typed, but do not prohibit mixing of types in arithmetic operations. This causes performance impacts due to hidden promotions and/or loss of accuracy.

Exception handling
Exceptions are errors or other anomalous conditions that arise during program execution. Includes the obvious, such as floating-point overflow, square root of a negative, divide-by-zero, and user defined ones. Exception handling in the high-level language aids in the incorporation of fault-tolerance. Poor handling of exceptions can degrade performance. For example, floating-point overflow errors can propagate bad data through an algorithm and instigate time-consuming error recovery routines. Languages such as C++ and Java provide excellent exception handling through the throw-try-catch mechanisms. C provides raise and signal combination.

Procedural-oriented languages that support the principle of Information Hiding tend to make it easy to construct high-integrity real-time systems. E.g. the Ada package consists of a specification and declarations that include its public or visible interface and its invisible or private parts. Packages are separately compliable entities, which further enhances their application as black boxes. In Fortran there is the notion of a SUBROUTINE and separate compilation of source files. The C language provides for separately compiled modules and other features that promote a good modular design. There is a price to pay for modularity in the overhead associated with procedure calls and parameter passing.

Cardellis metrics and languages and the procedural style

Economy of execution is high for procedural languages provided the compiler is efficient. E.g. Fortran focused on code optimization. Compilation of large systems is efficient modules can be compiled independently. Small-scale development is economical type checking can catch many coding errors. Data abstraction and modularization have methodological advantages for large-scale code development because the interfaces between modules can be clearly defined. Procedural languages are economical the learning curve for programmers is relatively low.

Language considerations
Introduction Assembly language Procedural languages Object-oriented languages Survey of languages Know the compiler and rules of thumb Coding standards

Object-oriented languages
Overview Synchronizing objects Garbage collection Cardellis metrics and object-oriented languages Object-oriented versus procedural languages

Languages that support a high degree of data abstraction, inheritance, polymorphism, and messaging. The benefits of OO languages include increasing programmer efficiency, reliability, and the potential for extension and reuse. Objects are an effective way to manage system complexity they provide a natural environment for information hiding, protected variation, and encapsulation.

Synchronizing objects
Synchronized objects: e.g., mutex is associated with an object that can be concurrently accessed by multiple threads.
Internal locking : each public method acquires a lock on the synchronization object upon method entry and releases the lock on exit. External locking : clients are responsible for acquiring a lock on the associated synchronization object before accessing the object and subsequently releasing the lock when finished.

Encapsulated objects: No synchronization needed the lock of the enclosing object also protects the encapsulated object. Thread-local objects: Objects that are only accessed by a single thread require no synchronization. Objects migrating between threads: Ownership of a migrating object is transferred between threads. Require no synchronization the transfer of ownership does require synchronization. Immutable objects: An immutable objects state can never be modified after it is instantiated. Immutable objects require no synchronization when accessed by multiple threads since all accesses are read-only. Unsynchronized objects: Objects in a single-threaded program require no synchronization.

Garbage collection
Garbage refers to allocated memory that is no longer being used but is not otherwise available. Garbage is generally associated with object-oriented languages like C++ and Java. Excessive garbage accumulation can be detrimental to real-time perforamence. Garbage collection algorithms generally have unpredictable performance and are detrimental in real-time. Garbage can be created in procedural languages. For example, in Pascal or C garbage can be created by improper deallocation of memory. Java standard environment incorporates GC, whereas C++ does not. In those languages that do not provide GC, it must be implemented.

Cardellis metrics and objectoriented languages

Less efficient in execution every routine is a method. Introduces additional indirections through method tables and prevents optimizations such as inlining. Economy of compilation is low often there is no distinction between the code and the interface of a class. Some OO languages are not sufficiently modular and require recompilation of superclasses when compiling subclasses. Superior with respect to economy of small-scale development. E.g. individual programmers can take good advantage of class libraries and frameworks. Economy of large-scale development is low sometimes these languages have poor modularity properties. E.g. it is easy to override a method that should not be overridden, or to re-implement a class in a way that causes problems in subclasses. OO languages have low economy of language features what starts as economical and uniform ("everything is an object") ends up as a menagerie of class varieties.

Object-oriented versus procedural languages

There is still no agreement on which is better for real-time systems. OO languages for real-time can lead to unpredictable and inefficient systems, and they are hard to optimize. The unpredictability argument is hard to defend with respect to C++, which does not use garbage collection. Likely that a predictable system can be just as easily built in C++ as C. Also, just as easy to build an unpredictable system in C as in C++. The case for more unpredictable systems hangs on arguing about garbage collecting languages like Java. Other inefficiency is based on the execution time penalty due to late binding (in all OO languages) this represents considerable and often non-deterministic delay.

Object-oriented versus procedural languages

Inheritance anomaly is serious problem in OO languages:
Arises when an attempt is made to use inheritance as a code reuse mechanism, which does not preserve substitutability (i.e., the subclass is not a subtype). If the substitutability were preserved then the anomaly would not occur. But the use of inheritance for reuse has been subsumed by composition so most inheritance anomaly rejections of OO are off-the-mark.

Most opponents of OO languages for real-time assert that concurrency and synchronization are poorly supported.

When built-in language support for concurrency does not exist it is standard practice to create "wrapper facade" classes to encapsulate system concurrency APIs (e.g., wrapper classes in C++ for POSIX threads). We already noted several concurrency patterns for OO real-time systems. While concurrency may be poorly supported at the language level in practice it is not an issue since developers use libraries instead.

Bottom line there are no clear guidelines for where OO approaches and languages should be used in real-time. Each situation needs to be considered separately.

Language considerations
Introduction Assembly language Procedural languages Object-oriented languages Survey of languages Know the compiler and rules of thumb Coding standards

Survey of languages
Ada 95 C C++ C# Fortran Java Real-time Java Occam 2 Special real-time languages

Ada 95
Ada (1983) was originally intended to be the mandatory language for all U.S. Department of Defense projects, which included many embedded real-time systems. Ada 83, had significant problems and Ada (1995) was introduced to deal with these problems. Ada 95 was the first internationally standardized object-oriented programming language. Ada 95 is both procedural and object-oriented depending on how the programmer uses it. Ada 95 includes extensions to help deal with scheduling, resource contention, and synchronization. Other language extensions made Ada 95 an OO language (tagged types, packages, and protected units). Ada has never lived up to its promise of universality because users have found it to be too bulky and inefficient.

Invented around 1971, is a good language for low-level programming. C provides special variable types such as register, volatile, static, and constant, which allows for control of code generation at the high-order language level. C supports call-by-value only call-by-reference can be implemented by passing a pointer to anything as a value. Variables declared as type volatile are not optimized by the compiler. This is useful in handling memory-mapped I/O and other instances where the code should not be optimized. Automatic coercion (implicit casting of data types) can lead to mysterious problems. C provides for exception handling through the use of signals and setjmp and longjmp, constructs to allow a procedure to return quickly from a deep level of nesting. Overall C is good for embedded programming it provides for structure and flexibility without complex language restrictions.

C++ is a hybrid OO language that was originally implemented as a macro-extension of C. Exhibits all characteristics of an OO language. C++ extends C functions with classes and class methods, which are functions that are connected to classes. C++ still allows for low-level control using inline methods rather than a runtime call. The C++ preprocessor can lead to unnecessary complexity. Preprocessors also do weak type checking and validation. Misuse of pointers causes many bugs in C/C++ programming standard libraries of dynamic data structures (e.g. the Standard Template Language) help minimize pointer problems.

Multiple inheritance allows a class to be derived from multiple parent classes. Multiple inheritance is powerful, but it is difficult to use correctly, is subsumed by composition, and is very complicated to implement. C++ does not provide automatic GC, which mean dynamic memory must be managed manually or GC must be implemented. Tendency to take existing C code and objectify it by wrapping the procedural code very bad because brings all the disadvantages of C++ and none of the benefits. Bad C++ programmers use a mixture of function and methods leading to confusing programs. C versus C++ is a tradeoff between a "lean and mean" C program and a slower and unpredictable C++ program that will be easier to maintain.

C# is a C++-like language that along with its operating environment, has similarities to the Java and JVM. C# is associated with Microsofts .NET Framework for scaled down operating systems like Windows CE 3.0. C# and the .NET platform may not be appropriate for hard real-time systems unbounded execution of its GC environment and lack of threading constructs to adequately support schedulability and determinism. C# and .NET might be appropriate for some soft and firm real-time systems.


Fortran is the oldest high-order language still used in modern real-time systems (developed circa 1955). Old versions of Fortran did not support re-entrant code. Fortran was developed in an era when efficient code was essential to optimizing performance in small, slow machines. Fortran is weakly typed, but it still can be used to design highly structured code. Fortran has no built-in exception handling or abstract data types. Fortran is still used in many legacy and even new real-time applications (A new version of Visual Fortran was recently released for .NET). Embedded systems written in Fortran typically include a large portion of assembly language code to handle interrupts and scheduling, and communication with external devices.

Java is an OO language that looks very much like C++. Java is interpreted the code compiles into machine-independent code which runs in a managed execution environment. This environment is the Java Virtual Machine, which executes object code instructions as a series of program directives. Java code can run on any device that implements the virtual machine write once, run anywhere. There are native-code Java compilers byte code executes directly on the bare metal. Like C, Java supports call-by-value, but the value is the reference to an object, which appears as call-by-reference for all objects. One of the best-known problems with Java involves its garbage collection utility.

Real-time Java

National Institute of Standards and Technology (NIST) task force was charged with developing a version of Java that was suitable for embedded real-time applications. The final report (1999), defines core requirements for the real-time specification for Java (RTSJ).
Any garbage collection that is provided shall have a bounded preemption latency. Defines the relationships among real-time Java threads. Include APIs to allow communication and synchronization between Java and nonJava tasks. Includes handling of both internal and external asynchronous events. Incorporates some form of asynchronous thread termination. Provides mechanisms for enforcing mutual exclusion without blocking. Provides a mechanism to allow code to query whether it is running under a realtime Java thread or a non-real-time Java thread. Defines the relationships that exist between real-time Java and non-real-time Java threads.

Occam 2
Occam 2 is based on the Communicating Sequential Processes (CSP) formalism. The basic entity in Occam 2 is the process there are four fundamental types, assignment, input, output, and wait. More complex processes are constructed from these by specifying sequential or parallel execution or by associating a process with an input from a channel. The Occam 2 language was designed to support concurrency on transputers but compilers are available for other architectures. Occam-2 has found some practical implementation in the U.K.

Special real-time languages

Several specialized languages for real-time have appeared and disappeared over the last 30 years. PEARL: Process and Experiment Automation real-time Language developed in the early 1970s. Has fairly wide application in Germany, especially in industrial controls settings. Real-time Euclid: An experimental language that is completely suited for schedulability analysis. This is achieved through language restriction. Real-time C: A generic name for any of a variety of C macroextension packages. These macroextensions typically provide timing and control constructs that are not found in standard C. Real-time C++: A generic name for one of several object class libraries specifically developed for C++. These libraries augment standard C++ to provide an increased level of timing and control. Others include MACH, Eiffel, MARUTI, ESTEREL. Most of these languages are used for highly specialized applications or in research only.

Language considerations
Introduction Assembly language Procedural languages Object-oriented languages Survey of languages Know the compiler and rules of thumb Coding standards

Know the compiler and rules of thumb

Is essential in generating code that is optimal in either execution time or memory requirements. The easiest and most reliable way to learn about any compiler is to run a series of tests on specific language constructs. For example,
case statement versus if-then while loops versus for loops or do-while loops when to unroll loops comparison of variable types and their uses use of in-line expansion of code via macros versus procedure calls

Language considerations
Introduction Assembly language Procedural languages Object-oriented languages Survey of languages Know the compiler and rules of thumb Coding standards

Coding standards
Is a set of stylistic conventions. Complying with coding standards will not foster portability, but rather in many cases, readability and maintainability. May even increase reliability. May also be used to foster improved performance by encouraging or mandating the use of language constructs that are known to generate more efficient code. Many agile methodologies embrace coding standards, for example, Extreme Programming.

Coding standards
Standard or boilerplate header format. Frequency, length, and style of comments. Naming of classes, methods, procedures, variable names, data, file names and so forth. Formatting of program source code including use of white space and indentation. Size limitations on code units including maximum and minimum lines of code, number of methods and so forth. Rules about the choice of language construct to be used; for example, when to use case statements instead of nested if-then-else statements (previously discussed). Example: Hungarian notation -- a set of rules about naming variables, it has been used with C++, Ada, Java and even C.

Coding standards
They can promote very mangled variable names. Their very strength can be its own undoing. E.g. what if the type information embedded in the object name is, in fact, wrong? There is no way for a compiler to check this. Adoption of coding standards is not recommended midproject. It is much easier to start conforming than to be required to change existing code to comply. A bad coding standard is better than none at all.