Sei sulla pagina 1di 11

i

e , " :
ELS EVI ER Microprocessors and Microsystems 20 (1996) 67-77
MI CI I 0Pl K) CES. ~I 3RS AND
MICROSYSTEMS
Languages for the programming of real-time embedded systems
A survey and comparison
J.E. Cooling
Dept. of Electronic and Electrical Engineering, Loughborough University, Loughborough, Leics LEl l 3TU, UK
Received April 1995; accepted October 1995
Abstract
The choice of programming language for use in a real-time embedded systems project can be a crucial one. It is clear, however, that
decisions are frequently made purely on commercial and/or 'fashion' grounds rather than technical aspects. While commercial factors
are often the more important, software developers should be aware of the associated technical implicatlo~ns. This paper details the
technical requirements of real-time embedded systems programming: then evaluates a variety of modem languages in the light of such
requirements. A general overview of 13 languages is given, followed by a detailed evaluation of Ada, C, C++, Modula-2/Pascal and
Oberon-2.
Keywords: Programming languages; Embedded systems; Survey
1. I nt r oduc t i on
When we come to eval uat e and select a language
for use in real-time systems, two questions are
fundament al :
(a) What languages are available?
(b) What do we require from these languages?
Many languages have been developed over the last 40
years for use in real-time comput i ng applications [1].
However, few have been widely applied - on an
i nt ernat i onal scale - in i ndust ry and academia. The
discussion here restricts itself (mainly) to languages
belonging to t hat category. Furt her, issues of suitability,
availability, current usage and fut ure devel opment s also
have a bearing on l anguage selection.
It is also i mpor t ant t o mat ch languages t o our
requirements. Thus it is essential to clearly, and
explicitly, define such requirements. To put this in simple
terms, answers depend on questions. This is well
illustrated in the papers by Cul l yer et al. [2] and Hal ang
and St oyenko [3]. In this paper the pr i mar y con-
sideration is the devel opment of soft ware for embedded
systems. Moreover, the title has been carefully chosen
to avoi d disputes over what are ' real-time' languages
[3].
0141-9331/96/$15.00 1996 Elsevier Science B.V. All fights reserved
SSDI 0141- 9331( 95) 01067- X
2. Ma t c h i n g requi rement s t o l anguage s
2.1. General
In smaller embedded applications, the comput er is
merely one component within the complete system. Its
pri mary role is to perform some system function, e.g. a
missile aut opi l ot controller. The fact t hat it is a digital
comput er is al most incidental to the role. That is, it is not
a comput i ng machi ne per se. In larger embedded systems
- air traffic control, for instance - the si t uat i on is not so
dear-cut . Such systems consist of many computers,
arranged in bot h mul t i processor and mul t i comput er con-
figurations. Some of these are applied in a true embedded
mode. Some may be used purel y as i nf or mat i on pro-
cessing machines. Yet others may be empl oyed t o
provide complex man- machi ne interfaces (graphical
user interfaces - GUIs). Such combi nat i ons are here
defined as hybri d systems.
In hybri d systems the requirements are not homo-
geneous. There are, for instance, significant differences
between GUI design and safety-critical software. This is
leading t o more use of mul t i -l anguage worki ng within
the same project (discussed l at e0. However, in this
paper emphasi s is placed on the pr ogr ammi ng of ' t rue'
embedded systems. Language features required to
68
support such programming have been grouped into four
categories:
Essential.
Primary.
Secondary.
Performance.
It is taken for granted that, wherever possible, pro-
gramming should always be performed in a high-level
language.
2.2. Categorisation of language features
.I.E. Cooling/Microprocessors and Microsystems 20 (1996) 67-77
Multitasking - language and run-time support.
Interfacing to ' foreign' high level languages.
A subset for safety-critical applications.
A comprehensive standard library.
Object oriented programming (OOP) constructs.
Finally there are performance features, which include:
Setting of task deadlines.
Simple evaluation of code execution times.
Generation of deterministic code.
Selection of scheduling strategies.
Incorporation of timing analysis mechanisms.
These, though they are desirable, are not generally
available at the present time.
(a) Essential features
Essential features of a language fall into two groups.
There are those which must be provided to enable the
computer to be programmed. Then there are the ones
necessary for the computer to be programmed directly
in the native language. The following items have been
defined to be essential:
Assembly language and machine code interfacing.
Absolute addressing.
Access to and control of hardware.
Bit manipulation.
Interrupt handling.
Pointers (for use with dynamic dat a structures).
Specification of time delays via source code statements.
Processor (or environment) specific extensions to the
core language.
A less stringent category is ' primary' . Primary features
are the underpinnings for the development of correct,
reliable and safe programs. These may be regarded as
essential for the production of high quality software,
and include:
Well defined language standard.
Well defined behaviour
Strong dat a typing.
Rigorously defined control structures.
Modul ar structure and separate compilation facilities.
Exception handling constructs.
Facilities to develop task structures.
Facilities to interface to real-time executives (RTEXs)
or real-time operating systems (RTOSs).
Memory exhaustion checks.
Defined maths facilities.
This leads on to the secondary fe.atures, those which
make a significant impact on productivity, portability,
maintainability, flexibility and efficiency (please note
that portability and efficiency may be contradictory
attributes). They may also enhance correctness,
reliability and safety aspects, though that is not their
basic intention. Secondary features include:
Good syntax and l ayout features.
2.3. Candidate languages
The following languages are either used in, or are
potential candidates for, real-time applications.
Ada
BASIC
. C
C++
Coral66
Fort h
FORTRAN
Modula-2
Modula-3
Oberon/Oberon-2
Pascal
Pearl
Occam
Embedded systems development has also been done in
RTL-2, Parallel C, Concurrent Pascal, Smalltalk and
Conic and many other specialised languages. However,
as these account only for a small proport i on of
embedded software, they will not be discussed here.
Looking to the future, several languages can be
removed from further consideration. Coral66, for
instance, is gradually being superseded by other
languages, notably Ada. FORTRAN, for embedded
work, appears to be succumbing to C and C++. Fort h
occupies a specialised niche in the embedded world, and
is likely to continue in that role 0nly. Pearl is little used
outside Germany. Occam is highly specialised (Trans-
puter platforms), and is also giving way to C. A brief
overview of those in common use is given below; later
sections give a detailed review and comparison.
(a) Ada
In the 1970s, the US Department of Defense (DoD) set
out to bring order to their (at that time) chaotic software.
Over 300 languages, including many specialised ones,
were in use. The cost of supporting these, in particular
J.E. Cooling~Microprocessors and Microsystems 20 (1996) 67-77 69
the maintenance aspects, was extremely high. Four
separate design studies were commissioned to develop a
new language, taking into account the following
attributes (in priority order):
Cost
Responsiveness
Timeliness
Flexibility
Reliability
Maintainability
Training
Transportability
Readability
Writeability
Efficiency
Acceptability
The winning study [4] was taken forward and
developed into what is now known as Ada 83 (here, for
brevity, Ada). This was formalised as ANSI / MI L- STD
1815. The language supports modularisation, separate
compilation, concurrency (tasking), exception handling,
reliability and modifiability. Readability is good, pro-
grammer productivity can be high, and significant levels
of portability can be achieved.
Ada was trademarked by the US DoD to control its
future development. It was decreed that no subsets or
supersets would be allowed. This was to be a ' do-all'
language. Unfortunately, this resulted in a large,
complex language, posing many problems for compiler
writers. One consequence is that, for UK safety-critical
applications, a ' sub-set' - SPARK Ada [5] - has been
created. Further, experience showed fundamental weak-
nesses with the tasking model. Consequently, many (if
not most) fast embedded systems do not use the Ada
standard run-time support system. Instead tasks are
executed under the control of separate RTEXs or
RTOSs.
Ada is now in a mature state, and is being widely used
in defence, aerospace, and similar systems. However,
there has been much pressure to updat e the language.
This has come about mainly for two reasons. First,
practical experience highlighted the shortcomings of
the original design. Second, the language lacks the
(claimed) advantages of object-oriented programming.
As a result, a new standard was developed, Ada 95.
This is built on Ada 83, and may be regarded as a super-
set of the original language. Some incompatibilities exist
between the two, but these are few in number. It is
claimed that the majority of existing Ada 83 programs
can be handled by Ada 95 compilers. The new compilers
are now becoming available, but none have yet
implemented the full set of Ada95 features.
(b) BASI C
BASIC (Beginnners All-purpose Symbolic Instruction
Code) was designed by Kemeny and Kertz in 1965 at
Dart mout h College, USA. It was intended to be a simple,
easy-to-learn language, geared mainly to input and out-
put via keyboard and screen. Because of its simplicity it
quickly became popular and thus widely used. Although
it was never intended for real-time use, versions suitable
for embedded programming became available in the
1970s. Its original features have been extended to provide
compilable (not interpretive) code, structured features,
and low-level and multitasking facilities. These have
been reinforced with the production of an American
National Standards Institute (ANSI) standard for
BASIC.
For those without a formal computing background it
has been highly popular. This is especially true in the
fields of mechanical, civil and process control engineer-
ing. At the present time there is a great deal of embedded
software in use which has been written in BASIC. How-
ever, the de facto situation is that numerous dialects of
the language exist; thus it is difficult to compare it with
other languages (the ANSI standard could, of course, be
used as a reference point). At the present times there are
strong indications that, for embedded working, C/ C++
is replacing BASIC. Thus there is little point in discussing
it further in the context of embedded systems. However,
there has been significant use of BASIC for GUI develop-
ment, using visual BASIC. There is no doubt that for
hybrid systems, the language has many attractive
features. An assessment of visual Basic is beyond the
scope of this paper; it is left to the reader to follow this up.
( c) c
C came from AT&T Bell research labs in 1972, having
been designed by Dennis Richie [6]. Up to that time most
systems programming had been done using assembly
language; C was meant as a replacement for such tech-
niques. Its intention was to provide the flexibility of
assembly level programming together with the power
and productivity of high-level language working. It was
also designed to be highly efficient. One early major
application was in the writing of the UNI X operating
system. Only much later, circa the mid 1980s, did it
make inroads into real-time embedded applications.
Since then there has been a major uptake of the language,
assisted by the production of the ANSI ' C' standard. At
the present time it is probabl y the most widely used real-
time language, for a number of reasons, including the
fact that:
It is a relatively easy language to learn and use.
It is significantly more powerful than assembly
language.
Compilers are available for most microprocessors.
Many vendors offer powerful, graphics-based develop-
ment environments. Most support host/target systems,
the majority being PC and workstation based.
70
Many support tools (such as run-time code analysis)
are integrated into the programming environment.
However, for many applications it is being replaced by
C++.
J.E. Cooling/Microprocessors and Microsystems 20 (1996) 67-77
(d) C++
C++ is also a product of Bell labs, first appearing as
' C with clauses' in 1980. The designer is Bjarne
Stroustrup [7]. In 1985 the first specification proper
appeared, version V1.0. A standard has been issued
(ISO/IEC 9899), but this is constantly being updated.
C++ was based on, and was made compatible with, C
(thus C can be viewed as a subset of C++) . The reasons
for these decisions were very practical ones:
Within Bell labs and AT&T as a whole, C was well
established as a systems programming language.
Moreover, it was in widespread use. These factors
would minimise acceptance and integration problems.
C had reached a mature state, many proven compilers
being available.
An extensive set of library functions had been
developed.
There was wide availability of development tools such
as editors, debuggers, etc.
However, the main reason for developing C+ + was
the desire of Stroustrup to overcome the deficiencies of C
itself. The primary objectives were to provide:
Support for modern software engineering and
program design methods.
Improved program structures.
Implementation of object oriented programming
(OOP) features.
Good type checking.
Portability and efficiency.
Straightforward ' foreign' language interfacing.
In early days compilation was a two-stage process,
using C compilers. However this is no longer the case;
all modern C+ + compilers are ' native' . There are exten-
sive and comprehensive sets of C+ + libraries (although
vendor incompatibility can be a maj or problem).
( e ) M o d u l a - 2
Modula-2 has its roots in Pascal and Modula. It was
developed by Nicklaus Wirth, the designer of Pascal,
during the period 1977 to 1980. In the main its objective
was to improve on Pascal in the areas of:
Modul ar construction.
Systems programming.
Concurrency.
To achieve these ends it based its program structure
on sets of separately compilable modules, having
extremely strong type checking. Low-level facilities
were incorporated making device handling a relatively
simple task. Further, a standard interrupt handling
mechanism was specified as part of the language. The
result is that many (if not most) real-time programs
may be written entirely in Modula-2; there is no need
to resort to assembly language.
Modula-2 has replaced Pascal in many areas (although
the issue is clouded by the fact that many modern
' Pascals' are essentially Modula-2). At the present time
most compilers are based on the language definition in
Programming in MODULA- 2 [8]. This has given rise to a
de-facto standard, leading to good language portability.
Unfortunately, there are significant differences in the
libraries provided by the compiler vendors. The language
is also being standardised by ISO (document D106/
N336). However, few compilers are yet available which
comply with this (draft) standard.
Please note that Pascal is not evaluated because it is
highly implementation dependent. Further, comments
relating to PI M Modula-2 also apply to many of the
extended Pascals.
( f ) M o d u l a - 3
Modula-3 [9] originated from the language Modula-
2+, which had been developed by Digital Equipment
Corporat i on at its Palo Alto Systems Research Centre
(SRC). Proposals to revise and standardise Modul a-2+
were produced at the end of 1986. The result was a
collaborative project between SRC and the Olivetti
Research Centre to develop the new language Modula-
3. Design t ook place in the late 80's, the language
definition appearing in 1988. The primary design aim
was to produce a powerful, but simple, modern general
purpose programming language. Moreover, it aimed to
continue the philosophy of Modula-2 by aiming for
simplicity and safety, based on proven features. It
corrected some of the less secure features of Modula-2
and Pascal, at the same time introducing new facilities.
These include support for:
The threads model of concurrency.
Object oriented programming.
Exception handling.
Automatic garbage collection.
Interfacing to C and UNI X functions.
Modula-3 has many excellent features. It is as power-
ful as Ada 95, and almost as powerful as C+ + (but is
significantly clearer, simpler and safer). Many compilers
have been produced for the language, mainly for main-
frame computers and workstations. Unfortunately, few
PC compilers are available, and the range of micro-
processors supported is limited. Taking this into
account, Modula-3 is unlikely to be used in embedded
systems in any significant way. Therefore a detailed
assessment of the language will not be given here.
J.E. Cooling~Microprocessors and Microsystems 20 (1996) 67-77
However, developers of hybrid systems may well find the 3.2. Primary features
language attractive as an alternative to C++.
(g) Oberon~Oberon-2
The Oberon project [10] was launched in 1985 by
Nicklaus Wirth and Juerg Guttknecht. Strictly speaking,
Oberon embraces three items:
It is the name of the project.
It is the name of a programming language, Oberon.
It is an operating system for personal workstations.
In Oberon, the language is enmeshed with the operat-
ing system. To fully use the language features, support of
the operating system is essential. Mossenbock extended
and refined Oberon to produce Oberon-2 [11], this being
a language in its own right. Oberon was based on
Modula-2. Thus it is no surprise to find that Oberon-2
is closely related to that language. What it does is build
on the concepts of Modula-2 and introduce some new
features. The most important ones support object
oriented programming concepts. However, these differ
considerably from the features of many other OOP lan-
guages, being considerably simpler (some would say
minimalistic). An informal standardisation process is
being pursued. At the present time this has generated a
set of guidelines for compiler developers, the Oakwood
guidelines [I 2]. Many compilers are now available, based
on bot h PC and UNI X platforms. Compilers for various
microcontrollers are also under development. All later
comments refer to Oberon-2 as specified in the Oakwood
document.
3. La ng ua g e c o mpa r i s o n
3.1. General comment
Experienced developers know that it isn' t sufficient to
evaluate languages in the abstract; implementation
factors significantly affect the results obtained. In a
paper such as this, however, it isn' t possible to take
compilers and environments into account. This is left
to the reader.
The rest of this section is given over to a comparison of
the following languages:
Ada
C
C + +
Modula-2 (includes Pascal)
Oberon-2
This assessment is presented in three categories: essential,
primary and secondary. Performance issues are not
addressed.
71
Assembly language and machine code interfacing
(a) Ada. In Ada it is necessary to use subprograms to
access machine code constructs. Within these only
low-level statements may be used; Ada source code
is not permitted. A standard library package
MACHI NE_CODE is required to support such
operations. Beware, implementations are not required
to provide such a package.
Assembly language operations must also be housed
within a subprogram. Operations defined within this
are inserted into the Ada source code using a pragma
called I NTERFACE. This area is very machine and
compiler dependent; it need not be provided by an imple-
mentation. Thus the best sources of information are the
compiler manuals.
(b) C. Assembly language may be embedded within C
source code using a block denoted by asm. They may
also be written as separate external routines, these com-
municating with the C program via function calls and
return parameters. Macros may also be used to provide
assembly language operations.
(c) C++. This is similar to C.
(d) Modula-2. There are no defined standard facilities
for dealing with assembly language or code inserts.
While this is a noticeable omission, in reality it isn't
a problem. Compilers which generate ROMabl e code
also provide such features as an extension to the basic
language.
(e) Oberon-2. The Oakwood document specifies that this
is a highly system dependent feature. As such it must be
encapsulated within a clearly identified code procedure.
The body of the procedure contains hex bytes or
assembler instructions. Such procedures are to be
regarded as being inherently unsafe because they are
unlikely to be portable [12].
Absolute addressing
(a) Ada. Ada contains a type ADDRESS supplied
within a standard package called SYSTEM. Variables
of this type may be used to specify the absolute ad-
dresses of:
Program items.
The start point of a subprogram, package task or task
entry.
Interrupt vectors.
Addressing operations are carried out using
representation clauses.
72
Not e that SYSTEM is a predefined library package
which includes configuration-dependent items.
J.E. Cooling~Microprocessors and Microsystems 20 (1996) 67-77
(b) C. In C, absolute addressing is normally done
through the use of pointers. Precise details are
implementation dependent.
(c) C++. Absolute addressing can, as in C, be done by
using pointers. Many C+ + compilers have library
functions which include some form of peek and poke
operation for accessing memory addresses.
(d) Modula-2. Like Ada, Modula-2 uses an implementa-
tion dependent library, the SYSTEM module, to support
absolute addressing operations.
(e) Oberon-2. Oberon-2, like Modula-2, uses a SYSTEM
module to provide low-level operations specific to a
particular processor and/ or operating system.
Access to and control of hardware
(a) Ada. Ada uses representation clauses to map Ada
code (abstract) ont o particular machines (concrete).
This requires facilities supplied as part of the SYSTEM
package.
(b) C. Hardware operations are normally carried out
using pointers. Such pointers are associated with specific
(absolute) device addresses.
(c) C++. Like C, pointers can be used to perform hard-
ware operations. In many cases machine-specific opera-
tions are defined in compiler-specific library classes.
(d) Modula-2. The SYSTEM module ensures that dat a
types used in conjunction with hardware (e.g. BYTE) are
made available. Devices are identified as items located at
absolute addresses. Device access is performed using
straightforward assignment operations t o/ from such
items.
(e) Oberon-2. Oberon-2, like Modula-2, assumes that
such features are implementation dependent, being sup-
plied in the SYSTEM library. Device access is performed
using straightforward assignment operations t o/ from
such items.
Bit manipulation
(a) Ada. The bit patterns of individual words can be
specified using enumeration representation clauses. It is
also possible to form a word as an array of Boolean
elements, each element corresponding to a single bit.
The standard logical operations may be applied to such
structures.
(b) C. C is particularly strong in this area. Logical AND,
OR, NOT and XOR can be carried out in bitwise
fashion. Moreover, bit shifting, bot h left and right, can
be performed.
(c) C++. In C++ the same facilities are provided as in
C. It also, however, supports a different approach using
structures declared as ' bit fields'.
(d) Modula-2. In Modula-2 the set structure is used for
bit handling. For embedded applications the most useful
construct is BITSET. This, in PIM, is defined as a set of
enumerated cardinal values. All standard set operators
can be used with BITSET. As a result bit handling is easy
to perform, except for bit shifting. The new standard
remedies this, and also provides a ROTATE function.
(e) Oberon-2. Oberon-2 considers such operations to be
low-level ones, and hence implementation dependent.
Support for these is provided in module SYSTEM,
which has predefined procedures for logical shift and
rotate.
Interrupt handling
(a) Ada. Here the standard met hod of dealing with inter-
rupts is to create an interrupt handler task. An interrupt
vector is associated with the task by locating the task
entry at the vector address.
(b) C. There are no standard constructs in C for dealing
with interrupts. Users frequently devise their own
solutions incorporating assembly language instructions.
(c) C++. C++ does not define a standard method of
interrupt handling. However, compiler-specific exten-
sions are often provided for the writing of interrupt
handlers. Programmers may use these or else provide
their own handler.
(d) Modula-2. Interrupts are handled in Modula-2 using
the standard procedure I OTRANSFER, provided by
module SYSTEM. Alternatively, programmers may
write their own handlers.
(e) Oberon-2. Interrupt operations are encapsulated
within a clearly identified interrupt procedure. This is
then associated with the appropriate interrupt vector
using an installation mechanism such as:
Install (InterruptProcedure, InterruptVectorNumber);
Such procedures are to be regarded as being inherently
unsafe owing to issues of portability.
Pointers
All the languages provide pointer operations.
J.E. Cooling~Microprocessors
Specification of time delays
This is actually a much more complex issue than it
appears to be. How delays are implemented is closely
linked with the overall task structuring of the software.
When the software is constructed as a multitasking
system, the solution is relatively simple. Responsibility
for generating the necessary time delays is given to the
executive/operating system. This is the approach used in
Ada, using the delay statement. Otherwise it is left to
the designer to implement time delays in the most
appropriate manner. Normally this involves the use
of hardware timers and interrupts. Thus, provided
hardware access and interrupt control features are
supplied, high-level language time delay constructs
can be devised.
and Microsystems 20 (1996) 67-77 73
(b) C: To quote from the ANSI rationale document [15]
' many operations are defined to be how the target
machine' s hardware does it rather than by an abstract
rule'. Thus it is not possible to define, in an abstract way,
what should happen when a C program executes. Imple-
mentation details must be taken into account. As a
result, a C program which works perfectly well on one
machine may fail when ported to a different one. Similar
problems may arise i f different compilers are used. This is
not surprising as the ANSI standard lists one page of
unspecified behaviour, seven pages of undefined
behaviour, and one page of locale-specific behaviour.
Moreover, the language structures provide many ways
of generating side-effects. A comprehensive assessment
of the behaviour of C programs is given by Hat t on [16].
Processor (or environment) specific extensions to the core
language
It can be seen from the preceding text that the core
language must be extended to tailor features to specific
implementations. This can be done in two ways. First, as
with Ada, Modula-2 and Oberon-2, an implementation-
specific SYSTEM module can be provided. Second,
extensions can be provided using language libraries.
Ideally, all libraries should be defined within the
language standard. Unfortunately the reality, at the
present time, is far from ideal. Variation between
compiler libraries is a major source of portability
problems.
(c) C++: ISO/IEC 9899 lists one page of unspecified
behaviour and more than four pages of undefined
behaviour. This is an unsatisfactory situation. More
telling is the tutorial by Cargill [14] which identifies 50
specific points wort hy of consideration.
( d) Modula-2: There are few undefined aspects or
insecurities in the PIM version of Modula-2. The new
standard is even more rigorous.
(e) Oberon-2:Oberon-2 is still evolving, but does not
include unspecified or undefined behaviour. It has
removed some of the insecurities of PIM Modula-2.
3.3. Primary features Strong data typing
Well defined language standard
(a) Ada: Yes.
(b) C." Yes. But be aware of the difference between
conforming and strictly conforming compilers.
(a) Ada." Ada has very strong data typing rules. Type
mixing is forbidden; type conversion must be performed
first. All conversion operations are fully defined to
permit full compiler checking. However, to deal with
low-level operations, unchecked type conversion is
permitted. This should be used with great care.
(c) C++: Yes, but still evolving.
(d) Modula-2: Yes, but few compilers available which
meet the ISO standard.
(e) Oberon-2: Yes, but informal. Still evolving (Oakwood
standard).
Well defined behaviour
All the languages can produce unexpected (and
unpredicted) behaviour when using pointer operations.
In practice this implies that C and C+ + programs are
likely to produce the greatest number of problems
[13,14].
(a) Ada: There are relatively few implementation
dependent operations in Ada.
(b) C: The data typing in C is extremely weak as it can be
broken in many ways. Particular problems arise with
implicit type conversion, unexpected compiler-generated
conversion, and type-breaching through the use of
pointers [2].
(c) C++. This has tighter type checking than C, in that it
provides checking types of function parameters (but
there are undefined aspects to the handling of such
errors). C++ also has implicit type conversion, and
pointers can be used to breach almost any type rule [14].
( d ) Modula-2." Modula-2 is, like Ada, a strongly typed
language. No implicit type-changing is allowed; explicit
type conversion and type transfer mechanisms are
provided. Typing rules can be breached when using
records; care is needed here.
74 J.E. Cooling/Microprocessors and Microsystems 20 (1996) 67-77
(e) Oberon-2:Oberon-2 is also strongly typed. However,
the object oriented features introduce new issues which
must be considered.
Rigorously defined control structures
(a) Ada: Ada provides a full set of rigorous control
structures supporting selection and repetition opera-
tions. It also allows unconditional transfer of control
using the GOTO < label > (although the label must be
in scope). Unfortunately programmers can breach the
controls in all cases by using the raise-exception
construct.
(b) C: Selection and iteration constructs are provided in
C. Care must be taken in using the switch statement to
prevent 'fall-through' to the next case. The for loop must
be used most carefully. It is highly flexible, allowing the
programmer to specify loop initial, continuation and
terminating conditions. The constructs are highly abus-
able and thus quite insecure [17,18]. Unconditional
transfer of control can be made to a label within the
same function.
(c) C++: This is similar to C.
(d) Modula-2: Both selection and iteration are supported
in a fairly rigorous manner. Care must be taken with the
CASE statement to include an ELSE clause. Uncondi-
tional transfer of control can be made in two ways. EXIT
and RETURN. EXIT is used to jump out of a loop;
RETURN can be used to terminate a procedure. Use
these carefully.
(e) Oberon-2: This is similar to Modula-2.
Modular structuring and separate compilation features
(c) C++: A C++ program consists of a collection of
compilation files. Such files may be header files that
declare interfaces. Otherwise they are program files
that define class, function and global variable imple-
mentations. Like C, every program must have a function
main. Individual files can be separately compiled.
Modularity is not explicitly supported, but it can be
provided by encapsulation in classes. These are units
which encapsulate type representation and operations
to act on objects of that type.
(d) Modula-2: Here programs are structured as sets of
program and library modules. The program module can
be considered to be the highest level program unit. This
calls on facilities provided by library modules. Each
library module actually consists of two individual -
but related - modules: definition and implementation.
Modules are separately compilable units. Type checking
and name resolution is strongly enforced.
(e) Oberon-2:Oberon-2 has, strictly speaking, only one
module type, the implementation. There is no concept of
a program module. The interface of any module can be
listed in a definition file. However, this is not an Oberon-
2 module, merely a list of all exported items.
Exception handling constructs
(a) Ada: This provides, as standard, a comprehensive
error handling mechanism. It deals with both language-
defined and user-defined error conditions.
(b) C: There aren't standard exception handling facilities
in C. These must be implemented by the programmers
using some form of uncontrolled jump: e.g. goto, return,
break or continue.
(a) Ada: The major program units of Ada are the sub-
program, package, task and generic. However, the basic
units of compilation are the subprogram and package.
All such units can be compiled separately, and there is
strong type-checking across units. There are no global
data areas. Scoping rules are rigorous and well defined.
(b) C: The C equivalent to an Ada subprogram is the
function. Here the function main is the highest-level
one, normally calling on the use of other functions. Iden-
tifier scope is related to storage class. External variables
are global. Those within a function are local to the func-
tion, and by default are dynamic.
Local variables can be made static by defining them
using the class specifier static. Variables declared at the
top of a file (a global) can be kept local to the file by using
the static specifier. Note, however, that such rules can be
overcome by using pointers.
(c) C++: Version 3.0 (and the ANSI draft standard) now
supports exception handling. Its operation is based on
an expression that throws an exception to be caught
by a handler. The basic approach is to support
termination rather than resumption semantics.
(d) Modula-2: There are no standard exception handling
mechanisms defined in PIM Modula-2. Many compilers
provide some run-time error detection and handling
mechanisms (e.g. overflow, array violations, etc.).
Programmer-defined mechanisms can be supplied using
coroutines for handling exception responses. The new
standard will define an exception handling mechanism.
(e) Oberon-2: There are no standard error handling
features in the language. These could be implemented
using the library module 'Coroutines'.
J.E. Cooling~Microprocessors and Microsystems 20 (1996) 67-77 75
Facilities to develop task structures
(a) Ada: One of the standard building blocks of Ada is
the task. These form an important and integral part of
the language.
(b) C: There isn' t an identifiable task structure as such,
but these can be built using functions. With this
approach, task access and manipulation may be carried
by pointers.
(c) C++: Tasks can be implemented using classes and
class instances.
(d) Modula-2: Here tasks are constructed using the
predefined type PROC.
(e) Oberon-2." Tasks can be built using classes and class
instances.
RT EX/ R TO S interfacing
It should be possible to interface programs developed
in all these languages to standard RTEXs/ RTOSs.
Specific interfacing methods vary, but typically involve
the use of absolute addressing techniques. However, this
topic is highly implementation-dependent. Also, the
reality of current practice is that most embedded systems
designers use vendor-specific - and not standardised -
products.
Memory exhaustion checks
There are two aspects to consider here: static and
dynamic allocation of memory.
Static (fixed size) allocation is mainly used for stack
and task work spaces. None of the languages provide, as
standard, methods to test for static memory exhaustion.
However, these are frequently included as compiler
supplied run-time checks.
Ada is the only language which checks for
dynamic memory exhaustion using the exception
STORAGE ERROR. In all other cases, allocation and
deallocation of dynamic memory is the responsibility of
the programmer. Reclamation of unused but not deal-
located store (garbage collection) is normally left to the
program execution environment (e.g. the Oberon
environment).
Defined maths facilities
(a) Ada: Ada has a good maths model. The behaviour of
programs, when running on any particular processor,
can (in general) be checked against the model.
(b) C: The language supports integer and floating point
operations. However, the maths model is insecure as
there are a number of significant aspects which lead to
undefined behaviour. For instance, to quote the ANSI
standard: ' The behaviour is undefined in the following
circumstances: an arithmetic operation is invalid (such as
division or modulus by O) or produces a result that
cannot be represented in the space provided (for
example, underflow or overflow)'.
(c) C++: Similar comments apply to C++, although
generally it is superior to C.
(d) Modula-2: As specified in PIM, the maths model is
implementation dependent. Care must be taken to check
such a model, and to consider portability aspects. The
new standard is quite a comprehensive and rigorous
one.
(e) Oberon-2: At the present time the maths model is
defined mainly in the Oakwood implementation recom-
mendations. These represent the basis for a good maths
model. However, the current situation is that maths
behaviour is implementation dependent.
3.4. Secondary features
Synt ax and layout
(a) Ada: Good readability was a basic requirement of
the language. Thus its syntax is based on English-like
constructs, avoiding cryptic notation. It is well defined,
using a combination of plain language and Backus-Naur
Form (BNF) of notation. The language is not case
sensitive.
(b) C: The syntax of C is fully specified in the ANSI
standard. However, the operator symbols are uncon-
ventional, operations have complex precedence rules,
and programs are often written in a terse (some would
say cryptic) style [19]. In practice, C programs are
frequently difficult to read (it has been described as a
' write-only' language). The language is case sensitive.
(c) C++: If anything this language is more unreadable
than C. C++ syntax has, in essence, extended that of C
with some complex OOP constructs. The language is case
sensitive.
(d) Modul a-2:Modul a-2 was designed to be highly read-
able, and is well defined. PIM uses extended BNF
(EBNF) notation. In the new standard it is defined
using the formal specification language VDM (Vienna
Development Method). It is case sensitive.
(e) Ober on- 2: Ober on- 2 is very readable, though
perhaps not quite so clear as Modula-2. Its syntax is
defined using EBNF and, like Modula-2, is case
sensitive.
76 J.E. Cooling~Microprocessors and Microsystems 20 (1996) 67-77
Multitasking - language and run-time support
(a) Ada: These aspects are an integral part of the Ada
language. However, it is not a requirement that programs
use the predefined run-time support mechanism.
(e) Oberon-2: At the present time Oberon-2 has a
relatively small set of standard libraries. It is expected
that this will be extended in due course.
Object oriented programming constructs
(b) C: No dedicated multitasking facilities or support
mechanisms exist within the language.
(c) C++: A deliberate decision was made to omit
multitasking features from the language. These may be
provided within a library or as a compiler extension.
(d) Modula-2: PIM Modula-2 implements coroutines. It
also provides sets of signals which may be used to
construct more powerful multitasking features. The
new standard is much more comprehensive.
(e) Oberon-2: The Oakwood guidelines recommend a
library for the support of coroutines. This is intended
to be an optional feature.
Interfacing to foreign high level languages
All the languages here can be interfaced to other
languages. However, even where such mechanisms
are defined within the language, they tend to be
implementation dependent.
A subset f or safety-critical applications
(a) Ada: Yes, primarily through SPARK Ada [5].
(b) C: Seems highly unlikely [2], but has its supporters [16].
(c) C++: Doubtful, but it is being worked on.
(d) Modula-2: Not devised, but could be readily
achieved.
(a) Ada: Ada 83 is considered to be an object-based lan-
guage. It enables the programmer to devise abstract data
types (ADTs) using packages (for encapsulation), private
types and subprograms to operate on such types. Ada 95
[20] adds:
Type extension - for inheritance.
Late (dynamic) binding - for selection of operations at
run time.
Polymorphism - for selection of types at run time.
(b) C++: This has an extensive range of OOP features,
including classes, objects, methods, messages, inheritance
and dynamic binding (in some C++ texts, late binding
and polymorphism are taken to be synonymous).
(c) Oberon-2: This also has a good range of OOP features:
classes, objects, methods, messages, inheritance and
dynamic binding. However, the class attributes are some-
what different to those of C++, being much simpler.
4. Assembly language programming
It is strongly recommended that, wherever possible,
assembly language programming should be eliminated.
There are many reasons for this, including:
Source code documentation - quantity and
complexity.
Weak data typing, poor control of scope, limited flow
control mechanisms.
Maintenance and portability problems.
(e) Oberon-2: As for Modula-2.
Comprehensive standard library
(a) Ada: A good basic library.
(b) C: Very comprehensive library, but beware of
incompatible implementations (those conforming but
not strictly conforming to the standard).
(c) C++: Again, very comprehensive, but major
concerns relating to compatibility.
(d) Modula-2: Very comprehensive library range
available, but again real problems of compatibility in
current implementations. This is rectified by the new
standard.
Some of these problems can be overcome (or
minimised) by using a structured assembler.
However, there will always be a need to use assembly
language. The most obvious case is where a high level
language isn't available. Even when an HLL is provided,
there are situations where assembly language may still be
preferred, viz.:
Testing of hardware.
Interrupt and device/subsystem handling.
Efficiency of operation - speed and code size.
5. Conclusion
What of the future? Where do we go from here? Is any
one language going to totally dominate the real-time
scene? These are difficult questions to answer, but
J.E. Cooling~Microprocessors and Microsystems 20 (1996) 67-77
based on current trends the following out come is a References
reasonable prediction.
(a) Ada: In defence, aerospace, avionics and similar
applications, Ada will be dominant. The language
seemed to lose ground (in Europe) in the early 90's, but
now appears to be having somewhat of a renaissance
(based on personal discussions with tool vendors). It
will also find increased use in safety-critical systems.
(b) C: This will continue to be the dominant language in
embedded applications. For PC and workstation appli-
cations it will remain extremely important, although here
it is likely to lose ground to C++.
(c) C++: This will be the most important language for
workstation applications. It will, to a somewhat lesser
extent, be a major language for the development of
PC-based systems. Visual C+ + will be extremely widely
used for the building of GUIs. However, our own
experience [21] has indicated that C+ + object code can
easily become large and complex: such concerns may
limit its penetration into the embedded field. One
interesting possibility is the development of an effective,
efficient subset of the language.
(d) Modula-2: The language (including its Pascal off-
shoots) is on a plateau of use (although there appears
to have been a major uptake in eastern Europe in the last
two years). The new standard may lead to new compilers
being produced; one significant application area is that
of safety-critical systems.
(e) Oberon-2:Oberon-2 has a very bright future in the
research and education fields [22]. Furthermore, pro-
vided a reasonable range of compilers become available,
it should carve itself a niche market in industrial and
commercial systems (many developers would like a
good alternative to C+ + or Ada). It also has great
potential for use in safety-critical applications.
What we are also beginning to see are projects where
software has been developed in a number of languages.
One (not so new) application is multiple-redundant
processor systems, as in the A320 Airbus [23]. A more
recent one is the development of the control system for
the new London Underground Jubilee line. Here the
critical software is being written in Ada, C+ + being
used elsewhere.
Acknowledgements
This paper was first presented as part of the TEMPUS
project ' Programska Oprema za Vodenje Procesov' , held
at the University of Ljubljana, Slovenia, Feb. 1995. It is
reproduced with permission.
77
[1] A.D. Stoyenko, The evolution and state-of-the art of real-time
languages, J. Syst. Softw. 18 (1992) 61-84.
[2] W.J. Cullyer, S.J. Goodenough and B. A Wichman, The choice of
computer languages in safety-critical systems. Softw. Eng. J.
March 1991, 51-58.
[3] W.A. Halang and A.D. Stoyenko, Comparative evaluation of
high-level real-time programming languages, Real-Time Syst. 2
(1990) 365-382.
[4] J.D. Ichbiah, J.G.P. Barnes, J.C. Heliard, B. Krieg-Brueckner,
O. Roubine and B.A. Wichmann, Rationale for the design of the
ADA programming language, Proc. ACM, 14 (June 1979).
[5] SPARK - The SPADE Ada Kernel, Program Validation Ltd.,
Southampton, UK.
[6] B.W. Kernighan and D.M. Richie, The C Programming Language,
Prentice-Hall, Englewood Cliffs, NJ, 2nd edn., 1988.
[7] B. Stroustrup, A History of C++: 1979-1991, ACM SI GPLAN
Not. 28 (March 1993) 271-297.
[8] W. Wirth, Programming in MODULA-2, Springer-Verlag, Berlin,
4th edn., 1988.
[9] G. Nelson (ed.), Systems Programming with Modula-3, Prentice-
Hall, Englewood Cliffs, NJ, 1991.
[10] M. Reisser, THE OBERON S YS TEM - User Guide and Program-
mer's Manual, Addison-Wesley, Reading, MA, 1991.
[11] H. Mossenbock, Object-Oriented Programming in Oberon-2,
Springer-Verlag, Berlin, 1993.
[12] B. Kirk (ed.), The Oakwood Guidelines f or Oberon-2 Compiler
Developers, First Issue (September 1994)
[13] A. Koenig, C Traps and Pitfalls, AT&T Bell Laboratories
Computer Science Technical Report No. 123, New Jersey, July
1986.
[14] T. Cargill, C++ Gotchas, C4- World, Dallas, October 1993,
2-78.
[15] Rationale for Draft Proposed American National Standard for
Information Systems - Programming Language C, ANSI,
1988.
[16] L. Hatton, SAFER C: Developing Software f or High-integrity and
Safety-critical Systems, McGraw-Hill, New York, 1994.
[17] A.D. Hill, The choice of programming languages for highly
reliable software - a comparison of C and Ada, Ada User, 12
(March 1991) 11-31, 92-103.
[18] P.H. Welch, GOI NG TO Ceed?, Internal report, Computing
Laboratory, University of Kent at Canterbury, January 1989.
[19] I. Pohl and D. Edelson, A TO Z: C language shortcomings,
Comput. Lang. 13 (February 1988) 51-64.
[20] J. Barnes, Object-oriented programming: Ada 9X offers
flexibility and reliability, Alsys Worm Dial 7 (Spring 1993)
4-5.
[21] K. Wardley, Embedded systems programming with C++, M.Sc.
project report, Dept. of Electronic and Electrical Engineering,
Loughborough University, September 1993.
[22] Proc. Advances in Modular Languages, University of Ulm,
Germany, September 1994.
[23] J.P. Potocke de Montalk, Computer software in civil aircraft,
Microprocessors Microsyst. 17 (January/February 1993) 17-24.
Jim Cooling specialises in the area of real-time
embedded computer systems, including hard-
ware, software and systems aspects of the
topic. He has published extensively on the sub-
]ect, being the author of a number of textbooks
relating to embedded systems. Currently he is a
senior lecturer with the Department of Electro-
nic and Electrical Engineering, Loughborough
University of Technology.