arXiv:1709.03404v1 [cs.DC] 11 Sep 2017

A Domain-specific Language for
High-reliability Software used in the JUICE
SWI Instrument – The hO Language Manual
Felix Winkelmann∗, Oskar Schirmer†
Max-Planck-Institut für Sonnensystemforschung
Göttingen, 2017-09-11

Abstract
hO is a custom restricted dialect of Oberon, developed at the Max-Planck
Institute for Solar System Research in Göttingen and used in the SWI flight
software for the JUICE mission. hO is applied to reduce the possibility of
syntactically valid but incorrect code, provide better means of statically analyzing source code, is more readable than C and gives syntactic support
for the software architecture used in the SWI instrument software. By using
a higher-level, application-specific notation a whole range of possible errors
is eliminated and source code size is reduced, while making the code itself
easier to understand, review and analyze.

∗
†

e-mail: winkelmann@mps.mpg.de
e-mail: oskar.schirmer@mps.mpg.de

1

Contents
1 Introduction

3

2 Architecture

4

3 Language

5

4 Runtime System

6

5 The Compiler

7

6 Properties of the generated C code

7

Appendix A
hO Language Reference

8

Appendix B
Compiler Usage

17

Appendix C
Dynamic runtime checks

18

Appendix D
Proposed coding style

18

2

1

Introduction

This document describes the hO language, a dialect of Oberon[26] , which is
used as a replacement for C[17] in the implementation of the SWI[14] flight
software for the JUICE[8] mission.
The SWI flight software was initially planned to be implemented in C which
is a de-facto standard for most software written for current and future ESA
missions, as C is widely available and relatively well known. C is also directly
supported by the main hardware platform[10] that is used throughout the
JUICE project in the form of the Gaisler BCC toolchain[11] and debugging
utilities.
For embedded systems in constrained environments, C is a common choice
as an implementation language, but carries with it a considerable class of
hazards regarding memory-safety and the handling of exceptional situations.
C’s history as a systems programming language puts an emphasis on full
control over hardware resources and maximum efficiency, but intentionally
removes run-time checks and provides means to circumvent the type-system
in various ways, for example by using type-casts and unions. In an environment where high-reliability and fault tolerance are at a premium, as is the
case in space projects, the flexibility provided by C can be a liability, because error-checking and fault recovery are entirely the responsibility of the
programmer. Moreover, only very low-level and general control structures
are available which, when used, need careful attention to avoid out-of-bound
errors, non-terminating iterations or unhandled cases when discriminating
among a set of values. Additionally, unsafe pointer-handling makes it hard
to ensure the absence of bugs and results in hard to read code, incurring
additional time for reviews and more extensive levels of debugging[23][2] .
Therefore we decided to use a custom, domain-specific language that is
easier to parse – both for humans and tools – and that provides direct syntactic support for the architecture that we use in the SWI flight software. We
briefly considered existing languages targeted for embedded high-reliability
use:
Most obviously, Ada[16] may be considered the prime choice for software
development in this area, but a lack of programming experience in this language and the existing implementations and runtime-systems made us look
for simpler and more lightweight solutions.
Another option would be Forth[21] , which is already widely used in space
applications, is very lightweight and extremely flexible. The problem here
was that this would not have permitted us to use the Gaisler C toolchain
and the greatly different philosophy of Forth would have required a complete
redesign of the already existing prototypical C codebase that we used for
3

evaluating the architecture.
Finally, there are more recent, mostly academic attempts at designing languages for high-reliability software like Ivory[4] , but a lack of good documentation, a somewhat unstable implementation, and the very obscure syntax
made us reject this option.
As both authors have experience in language design and implementation[22] [25] ,
we decided to create a notation and translator of our own, tailored to the
software architecture used in the SWI project.
We still use C as a target language, to avoid the intricacies of direct native code generation and take advantage of existing optimization technology.
Moreover, using the vendors toolchain takes advantage of workarounds for
hardware bugs in the board that is used for testing the DPU software[12] .

2

Architecture

As the architecture of the SWI flight software had considerable influence
on the language design of hO, a short description of the structure of the
application is given here.
The SWI application employs a cyclic executive[1][18] to schedule repeating and concurrent tasks. Functionality is separated into modules which are
executed one after the other, repeatedly. Modules can be separated into the
following groups:
a) Modules responsible for sub-units of the instrument (spectrometers, motors, oscillator).
b) Modules processing specific hardware (SpaceWire, SPI, GPIO, ADC/DAC).
c) Modules processing incoming telecommand- (TC) and outgoing telemetry
packets (TM).
d) Modules performing checks of the overall status of the system as a whole
and reporting the status to the on-board computer and ground (memory, safety checks, house keeping, watchdog).
e) Modules processing higher-level funtionality, that is, sequences of operations specific to the instrument (scripts).
Long-running tasks have to be split up manually to ensure a minimum
latency between module executions, but the mininum latency for a complete
execution cycle can be determined statically, and the system is fully deterministic: tasks are interrupted at well known and safe locations.
4

SWI does not use an operating system. The functional and performance
requirements for the software are moderate and most hardware interfaces are
SWI-specific. Therefore the additional complexity and size of an operating
system is not justified. Moreover, having complete control of the system and
being able to understand all parts of it in detail gives us the confidence that
the number of errors in the final product is minimized.
Modules communicate by zero-copy message passing: each module has a
public data area, accessible from other modules, and a private data area for
internal use. A special structure named port holds a single message during
the transmission from module A to module B. Once a message is processed,
the port is emptied, the memory occupying the message is returned to the
memory management subsystem and the port can receive a fresh message.
Message buffers have a fixed size (just enough to hold the maximum telecommand/telemetry message packet size). Since nearly all messages are TC/TM
packets or are derived from those, no copying is involved, as long as we ensure
messages conform to the TC/TM layout. Internal messages for programmatic
control of some parts of the system (scripting) use TC packets as messages,
for example, and thus do not need special handling of packets that originated
from ground control or from an internal source.
Memory from messages is dynamically allocated from a pool of fixed-size
message blocks, which makes the memory management very simple and transparent. Unused memory is reclaimed as early as possible and messages can
not be queued, avoiding the potential of ports filling up with unhandled
messages and subsequent exhaustion of the available memory pool.

3

Language

hO is based on Oberon[26] , a Pascal/Modula dialect developed by Niklaus
Wirth. Oberon has the advantage of being syntactically simple and easy to
understand, it has a simple runtime model and a minimal runtime system.
hO further simplifies Oberon by removing support for strings, real numbers,
procedure types, multi-dimensional arrays, derived record types, sets, WITH
and most loop types. Dynamic memory management has been removed, with
the exception of PORT types.
By choosing the simpler and stricter notation of Oberon instead of a C
based syntax, many of the classic pitfalls in C programming are avoided.
Specifically, of the 142 rules specified in the MISRA-C 2004 guidelines[20] , a
total of 116 rules are automatically enforced by the syntactic rules of the hO
language and properties of the generated code and runtime system.
Similarly, only one out of the ten rules proposed with “The Power of
5

Ten”[15] continues to play a role when switching from C to hO, while the
other nine rules are obsolete. Moreover, while translating a fully functional
prototype of the software from C to hO, the amount of source code did not
increase, instead decreasing slightly from 3336 to 2853 LOC.
Extensions to Oberon that have been added to reflect the SWI software architecture are: syntax for state machines, a PORT type and built-in procedures
for message passing, extensions for Design-by-Contract[19] and some low-level
utilities for direct byte-level memory access. The MODULE construct has been
modified to simplify the implementation of modules that are executed by the
cyclic executive, which itself is implemented in C.
See appendix A for the language reference.

4

Runtime System

The runtime system, including memory management, low-level message passing, the cyclic executive scheduler, and an UART-driver for debugging output
consists of roughly 400 lines of C. hO is translated to C and linked with the
runtime system into an executable for final delivery to the processing unit
of the instrument. Note that all memory accesses are indirect – exported
and internal data of modules is held in a module-specific static data block,
given to the module when it executes. That way module-data is position independent and may be moved in case memory should fail due to radiation
damage. Moreover, all procedures are inlined, and no global variables are
used. This ensures all code is agnostic of the physical location of procedures
and variables on memory and can be moved at will.
Dynamic checks may result in error conditions that write debugging output
to the serial interface at development time or result in error event telemetry
and reboot (in case of fatal errors), when delivered in the flight model. To
make sure that error event handling takes place, regardless of the state of the
system, this mechanism has to circumvent the normal event and telemetry
handling.
Message memory is managed in a simple list of free 4 kB blocks. There are
basic operations for allocating a block (NEW) and releasing them back to the
runtime system (DISPOSE). All blocks have the same size, and are passed from
module to module, without copying. So, for example, the SpaceWire[5][6]
module retrieves a packet from the hardware interface, allocates and fills a
block with the data and sends it to another module responsible for validating
the packet. This module in turn will send the block further for more specific
processing. Once the block/message/packet has been fully processed, it can
be released and given back to the runtime system.
6

Direct access to the message contents is primitively only available on the
byte-level. However, most telecommand/telemetry messages that SWI handles are structured records as defined by ESTEC[7] . A machine-generated
library of accessor functions is used to take apart telecommands and construct telemetry packets, generated from the TC/TM (MIB) database that
has to be provided as a component of the flight software.

5

The Compiler

The hO compiler is implemented in Scheme[24] and parses our Oberon dialect, performs type-checking and generates ANSI C99. The compiler is implemented in a straightforward manner, using the Silex[3] lexer generator
and a custom PEG[9] parser. The type-checker implements a straightforward static typing scheme and performs some additional checks that we considered worthwhile. For documentation purposes the compiler also provides
generating a dot diagram depicting message-flow between modules, using the
graphviz[13] tool, and generating support files for mapping runtime error information to source code locations.
To allow circular module references, the compiler can be run in a restricted
mode that just generates interface information for each module, instead of
fully validating inter-module data access.

6

Properties of the generated C code

The use of hO eliminates many sources of undefined behaviour in C, among
these are:
1. NULL-pointer references
2. Pointer-arithmetic
3. Numeric overflow of signed integers, provided the necessary compiler
flags are given when compiling the generated C code
4. Right-shifts of signed integers always produce an unsigned result
5. Division/modulo by zero is detected and caught at runtime
6. Uninitialized variables

7

The code generated by hoc uses GNU extensions, namely local (inline)
functions and statement expressions. It is recommended to compile the C
code with the -fno-strict-aliasing -fwrapv -std=gnu99 compiler options to ensure correct semantics at runtime.

Appendix A – hO Language Reference
If not indicated differently, the lexical syntax of hO is the same as in Oberon.
Comments are written as
(* ... *)
and can not be nested.
hO is case-sensitive, builtin keywords may alternatively be given in lowercase.

Toplevel forms
Modules
The application is structured into modules, where each module contains some
toplevel code, executed periodically, and a private and optionally public set
of global variables. Modules may be instantiated multiple times, for example,
to provide implementations for two redundant hardware interfaces or for multiple variants of the same functionality. In SWI we have a second redundant
SpaceWire interface and both the spectrometer and autocorrelator exist in 2
versions.
A module must return to its caller, or the system will grind to a halt,
but since there are no syntactic constructs to perform unbounded loops or
mutually recursive procedures, such a situation is not possible.
A module is defined using the syntax
MODULE name;
VAR exported*: u32, listener*: port;
secret, unknown: u32;
BEGIN
...
END name.
Here, exported and listener are externally visible, secret and unknown
are internal. The variables declared are visible in the scope of the module
8

and retain their contents over every cyclic execution. To access the exported
variables of another module the name.listener syntax can be used, provided
the module name was imported into the current scope using them IMPORT
form.
Module variables are always initialized to 0 (zero) or, in the case of “port”
objects, to empty ports.
Modules that exist in multiple intstances (currently at most 2) are defined
like this:
MODULE spw*;
...
The spw module above will exist in two variants (spw0 and spw1) in the
final image. Note that though they do share source code, at runtime they
share neither code nor data.
To access the public variables of another module, use the IMPORT statement:
IMPORT some_module;
If a module provides several instances, then access to the variables of a
particular instance, or of the instance corresponding to the instance of the
current module can be specified as follows:
IMPORT mod0 := mod[0];
IMPORT mod := mod[*];

(* only access instance 0 *)
(* import corresponding instance *)

mods variables can be accessed now using dot notation, as in
SEND(msg, mod.port);
Message passing
Modules communicate by sending messages, A message is a raw u8 array of
at most 4096 bytes. Any variable of type port can hold at most one message
or is empty. Sending a message from one port to another port moves the
data array: sending a message from an empty port or to a blocked port does
nothing, otherwise the target port holds the message and the source port is
empty.
Uninitialized ports are empty

9

Type-, constant and procedure definitions
Types and constants are defined as in Oberon:
TYPE point = RECORD x, y: s32 END;
CONST zero = 0;
Builtin types exist for basic numeric types: u8, u16, u32, s8, s16, s32 and
boolean.
Procedure and function definitions are also similar to Oberon, with the
exception of unspecified ARRAY arguments – in hO all arrays must have a
fixed, statically known length. Procedures are always inlined, so should be
kept at a reasonable size.
Procedures may be declared at the start of a module to define internal
procedures, not visible in other modules. These internal procedures have full
access to the public and private module variables.
Procedures may modify their argument variables for arguments declared
with VAR (as in Oberon.)
Contracts
hO supports a basic Design-by-Contract facility to run dynamic checks at
runtime. A contract is a boolean function, optionally with arguments. Contracts must be defined at toplevel and can not be local to a module.
CONTRACT ensure_positive(x: s32)
BEGIN
return x > 0
END;
Contracts may not use VAR arguments.
To use a contract, use the REQUIRE, PROVIDE and INVARIANT statements at
the start of a statement sequence, that is, at the start of a module-, procedure,
conditional or loop body:
LOCAL counter := 0;
REPEAT 10 TIMES
REQUIRE ensure_positive(counter), other_contract;
...
END;
Contracts used by REQUIRE are checked at the start of the respective statement sequence, contracts used by PROVIDE at the end and contracts used by
INVARIANT are checked both at the start and at the end of a sequence, respectively.
10

Include Files
The contents of external files can be included using the INCLUDE form:
INCLUDE "some_file.ho";
An include-file is included at most once. Including the same file subsequently has no effect.

Statements
The following statements are allowed:
Conditionals
IF expression THEN ... [ELSIF ...] [ELSE ...] END
CASE expression OF const1: ... | const2: ... [ELSE ...] END
IF is the usual conditional statement. CASE provides an n-way comparison
with optional default (ELSE) clause. Multiple constants and constant ranges
can be used in CASE clauses, as in Oberon. If no case applies, then execution
continues normally after the CASE construct. At the end of a clause, execution
continues after the CASE statement.
Loops
There is only a single loop statement available, a counted loop with an optional condition:
[WHILE expression] REPEAT constant TIMES ... END
The loop counter must be constant to ensure termination. The expression
is evaluated before each loop iteration. A dedicated loop variable is not provided, but can be defined and updated manually. Note that regardless of
the value of the guard expression, the loop will terminate after the indicated
number of iterations.
State machines
Builtin support for state machines is provided. You can declare states using
the STATE form:
STATE init, process, idle;
11

STATE defines each declared state as a constant of type s32, starting with
0 (so an uninitialized state variable implicitly holds the first state.)
The SELECT statement dispatches between states, and NEXT switches to
another state:
SELECT current OF state1: ... | state2: ... END
current must be an assignable value or type u32. NEXT assigns a new state
to the state variable given:
NEXT process;
There is no “fall-through” in state clauses: at the end of the statementsequence in a state, execution commences after the SELEC statement.
Note that NEXT does not perform a transfer of control to a different state
clause, but to the end of the select statement instead. On the next execution
of the select statement, the state variable given in the SELECT statement will
have the new value assigned to it in the previous iteration.
NEXT is not allowed to exit a looping construct.
If no clause of the SELECT form applies to the current state, execution
continues after the SELECT form as normal.
Other statements
hO supports the usual procedure calls, assignments and RETURN statements
as in Oberon. Local variables may be declared at any point using
LOCAL var := 0;
The scope of the variable extends to the current block, that is, to the next
outer END token. Local variables must be initialized and may optionally be
declared to have a particular type, if the type of the initialization expression
is insufficient:
LOCAL unsigned := 0: u32;
So called external variables allow accessing direct memory locations:
EXTERNAL uart_reg := 80000100h: VOLATILE POINTER TO u32
Should a locally defined variable shadow an existing variable in an outer
scope, then the compiler will issue a warning.
12

Expressions
The usual arithmetic and comparison operators of Oberon are provided in hO
as well. Since hO does not support floating-point arithmetic, / and DIV are
equivalent. Consult the EBNF grammar later in this section for information
about operator precedence.

Builtin types, constants, operators and procedures
Types
s8, s16, s32, u8, u16, u32
Numerical base types.
boolean
The boolean type.
port
The type of a structure that can hold messages.
[VOLATILE] POINTER TO type
A pointer type, optionally declared volatile, to disallow assumption
made by the compiler about the value of memory locations, when the
memory location might be updated asynchronously, for example.
Constants
MODULE ID: u32
A constant holding the numeric ID of the current module.
INSTANCE: u32
A constant holding the instance-ID of the current module, if this module has several instances (currently 0 or 1).
TRUE, FALSE: boolean
The canonical true and false values.
Operators
AND OR NOT
Boolean operators. Note that AND and OR are short-circuiting as in C.
/\ \/ >< ~
Binary logic operators (and, or, xor, not).
13

>> <<
Perform unsigned left or right shift of x by y bits.
+ - * / DIV MOD
Numerical operators. Arithmetic between arguments of differing numeric type are allowed. DIV is currently an alias for /.
= # > < >= <=
Comparison operators for arguments of numeric type.
SIZE(type)
Returns the size in bytes of the memory occupied by type.
Expressions of boolean or comparison operators that produce a constant
result will produce a warning as this may indicate a programming error.
Operator precedence is as follows, listed from highest precedence to lowest,
sequent binary operators are evaluated from left to right:
1. Selection operators (. [] ^)
2. Subexpression (())
3. Unary operators (unary + - NOT ~ and SIZE)
4. Multiplicative operators (* / DIV MOD AND /\)
5. Shift operators (>> <<)
6. Additive operators (binary + - and OR \/ ><)
7. Comparison operators (= # <= => < >)
Procedures
CLONE(oldport,newport)
Clones the message stored by oldport and puts a copy into newport,
both of which must be port objects. oldport may be empty.
DEC(var)
INC(var)
Increase or decrease the numeric value in location var.
DISPOSE(port)
Free the memory used by the message in port. If the port is empty, do
nothing.
14

EXTEND(port,n)
Increase or decreases the used size of the message in port by n. n may
be negative.
LOG(string)
LOG(string,x)
Writes debugging output to the serial interface, optionally with a numeric argument.
NEW(port,size)
Allocates a buffer of the given size for holding a message and stores it
in port. The message is uninitialized and contains random data.
SEND(fromport,toport)
Moves a message from port to port. If the receiver port is non-empty,
this operation is a no-op. If the receiver port is empty, the sender port
will be emptied.
Functions
ADR(var): POINTER TOtype
Returns a pointer to the location given by var.
COUNT(port): s32
Returns the size of the message stored in port, in bytes.
DATA(port): ARRAY blocksize OF u8
Returns the data array holding the data of any message in port.
MAX(x,y): s32
MIN(x,y): s32
The minimum and maximum functions.
PENDING(port): boolean
Returns true if port holds a message.
SEND(fromport,toport): boolean
As the SEND procedure, but returns a boolean, indicating whether the
message was actually sent.

15

EBNF Grammar
compilation-unit ::= { toplevel “;” } .
toplevel ::= typedef | module | f unction | contract | include | constdef .
include ::= “INCLUDE” string .
constdef ::= “CONST” { id “=” constant “;” } .
typedef ::= “TYPE” { id “=” TYPE “;” } .
f unction ::= “PROCEDURE” id “(” param { “,” param } “)” [ “:” type ]
“BEGIN” statements “END” .
contract ::= “CONTRACT” id [ “(” param { “,” param } “)” ]
“BEGIN” statements “END” .
param ::= [ “VAR” ] id { “,” id } “:” type .
statement ::= if | loop | return | pcall | variable | external |
statedef | import | select | log | next | case | assign | “;” .
statements ::= { check “;” } statement { “;” statements } .
log ::= “LOG” “(” string [ “,” expr ] “)” .
check ::= “REQUIRE” assert { “,” assert } |
“PROVIDE” assert { “,” assert } |
“INVARIANT” assert { “,” assert } .
assert ::= pcall | id .
variable ::= “LOCAL” id “:=” expr [ “:” type ]
{ “,” id “:=” expr [ “:” type ] } .
external ::= “EXTERNAL” id “:=” constant “:” type
{ “,” id “:=” constant “:” type } .
statedef ::= “STATE” id { “,” id } .
import ::= “IMPORT” importspec { “,” importspec } .
importspec ::= id [ “:=” id [ “[” ( “*” | constant ) “]” ] ] .
select ::= “SELECT” lvalue “OF” state { “|” state } “END” .
state ::= range “:” statements .
range ::= range-element { “,” range-element } .
range-element ::= constant [ “..” constant ] .
next ::= “NEXT” constant .
loop ::= [ “WHILE” expr ] “REPEAT” constant “TIMES” statements “END” .
case ::= “CASE” expr “OF” state { “|” state } [ “ELSE” statements ] “END” .
assign ::= designator “:=” expr .
return ::= “RETURN” [ expr ] .
if ::= “IF” expr “THEN” statements { “ELSIF” statements }
[ “ELSE” statements ] “END” .
pcall ::= id “(” expr { “,” expr } “)” .
expr ::= sum [ ( “=” | “#” | “<=” | “>=” | “<” | “>” ) sum ] .
sum ::= term { ( “+” | “-” | “OR” | “\/” | “><” ) term } .
16

term ::= product [ ( “<<” | “>>” ) product ] .
product ::= unary { ( “*” | “/” | “DIV” | “MOD” | “AND” | “/\” ) unary } .
unary ::= [ “+” | “-” | “~” | “NOT” ] f actor | “SIZE” “(” type “)” .
f actor ::= designator | number | “(” expr “)” | boolean .
boolean ::= “TRUE” | “FALSE” .
designator ::= ( id | pcall ) { “.” id | “[” expr “]” | “^” } .
module ::= “MODULE” id [ “*” ] “;” [ modulevars ] [ modulefunctions ]
“BEGIN” statements “END” id “.” .
modulevars ::= “VAR” vardef { “;” vardef } .
modulef unctions ::= f unction { “;” f unction } .
vardef ::= id [ “*” ] { “,” id [ “*” ] } “:” type .
type ::= simpletype | structtype | ptrtype | arraytype .
ptrtype ::= [ “VOLATILE” ] “POINTER” “TO” type .
structtype ::= “RECORD” f ield { “;” f ield } “END” .
f ield ::= id { “,” id } “:” type .
arraytype ::= “ARRAY” constant “OF” type .
simpletype ::= id .
constant ::= exprconst .
id ::= (letter|“ ”){letter|“ ”|digit}.
string ::= "{character}" .
number ::= digit{digit}[{hexdigit}“h”] | ’{character}’ .

Appendix B – Compiler Usage
If correctly installed, the hO→C translator can be invoked by entering the
hoc command, passing the name of a hO source file and zero or more of the
following command line options:
-h
Show a short message listing the available options
-I directory
Specifies an additional include directory to be added to the search path
for files accessed via the INCLUDE form.
-d
Generate dependency rules for make(1), writing them to the standard
output channel.

17

-f
Generate a message flow diagram suitable for the dot(1) tool, writing
it to the standard output channel.
-o filename
Specify an alternative output filename. The default is to use the name
of the source file, but with the ‘.c’ extension.
-g
Only generate an interface file, named after the source filename, but
with the ‘.hi’ extension.
-k
Keep intermediate files from the parsing, processing and type-checking
stages.

Appendix C – Dynamic runtime checks
The following conditions are dynamically checked at runtime, unless being explicitly disabled by compiling the C files generated by hoc with the -DNDEBUG
option:
Empty port access
Accessing the DATA of an empty port.
Division/modulo by zero
Using /, DIV or MOD with a second argument that is zeri.
Invalid shift
Using << or >> with a second argument that is negative or equal or
higher than the word size of the used processor architecture.
Array bounds
Accessing an array with an index that is negative or exceeds the size
of the array.

Appendix D – Proposed coding style
The following is a description of the coding style that we use in the JUICE SWI
project. We recommend to follow this style when using hO.

18

1. A line of code should contain at most a single statement. If the statement is a complete control structure like IF/THEN and the body contains a single statements that fits into the line, then the conditional
may occupy the complete line.
2. THEN and OF should terminate a line, possibly with a trailing semicolon
;).
3. ELSE and END should always be on a separate line.
4. Define local or external variables as near to their first use as possible.
5. Declare local variables to a specific type to avoid expression ambiguity.
6. Avoid complex expressions that exceed a single line.
7. Indent CASE and SELECT cases consistently.
8. Separate VAR blocks and module bodies by an empty line.
9. Surround binary expression- and assignment operators with a single
space on both sides.
10. Separate toplevel entities (function- and module definitions, also comment blocks) by at least one empty line. Otherwise avoid empty lines
in source code.
11. Avoid unnecessary comments unless they convey something important
or critical to the understanding of the code.
12. Don’t use source lines that exceed 100 columns.
13. Write keywords in lowercase.
14. Avoid TAB characters as there is no standard width of a TAB and
source code may look differently depending on client settings.
15. Whatever you do in following or deviating from this coding style, be
consistent.

19

References
[1] Baker, T. P. and Shaw, A.: “The cyclic executive model and Ada”, The
Journal of Real-Time Systems 1, 1989, pp. 7-25
[2] E. Brewer et al: “Thirty Years is Long Enough: Getting Beyond C.”,
Proceedings of the 10th conference on Hot Topics in Operating Systems
– Volume 10, USENIX Association, 2005
[3] Danny Dubé: “A Scheme Implementation of Lex”, 2001
[4] Elliot, Seidel, Launchbury et al: “Guilt Free Ivory”, Haskell Symposium
’15
[5] ECSS Secretariat: “Space engineering – SpaceWire protocol identification” (ECSS-E-ST-50-51C), ESA-ESTEC, Noordwijk, 5.2.2010
[6] ECSS Secretariat: “SpaceWire – Links, nodes, routers and networks”
(ECSS-E-ST-50-12C), ESA-ESTEC, Noordwijk, 31.7.2008
[7] ECSS Secretariat: “Space engineering – Telemetry and telecommand
packet utilization” (ECSS-E-ST-70-41C), ESA-ESTEC, Noordwijk,
15.4.2016
[8] “JUICE – Exploring the emergence of habitable worlds around gas
giants” (ESA/SRE(2011)18), December 2011, European Space Agency
[9] Bryan Ford: “Parsing expression grammars: a recognition-based syntactic foundation”, Proceedings of the 31st ACM SIGPLAN-SIGACT
symposium on Principles of programming languages, p.111-122, Venice,
2004
[10] Aeroflex Gaisler AB: “LEON3-FT SPARC V8 Processor – Data Sheet
and User’s Manual”, January 2013, Version 1.9
[11] Cobham Gaisler AB: “Bare-C Cross-Compiler – User’s Manual”, September 2016, Version 1.0.46
[12] Cobham Gaisler AB: “LEON3FT Stale Cache Entry After Store with
Data Tag Parity Error” (Technical note GRLIB-TN-0009), September
2016, Issue 1.0
[13] Emden R. Gansner, Stephen C. North: “An open graph visualization
system and its applications to software engineering”, 1999
[14] P. Hartogh et al: “The Submillimetre Wave Instrument on JUICE”,
European Planetary Science Congress 2013, Vol. 8, EPSC2013-710,
2013

20

[15] Gerard J. Holzmann: “The Power of Ten – Rules for Developing Safety
Critical Code”, 2006, NASA/JPL Laboratory for Reliable Software,
Pasadena, CA 91109
[16] “Ada Reference Manual”, ISO/IEC 8652:1995(E)
[17] Brian W. Kernighan, Dennis M. Ritchie: “The C Programming Language (1st ed.)”, February 1978, Englewood Cliffs, NJ: Prentice Hall,
ISBN 0-13-110163-3
[18] C. Douglass Locke: “Software Architecture for Hard Real-Time Applications: Cyclic Executive vs. Fixed Priority Executives”, The Journal
of Real-Time Systems 4, 1992, pp. 37-53
[19] Meyer, Bertrand: “Design by Contract”, Technical Report TR-EI12/CO, Interactive Software Engineering Inc., 1986
[20] “MISRA-C:2004 – Guidelines for the use of the C language in critical
systems”, MIRA Ltd., October 2004
[21] Moore, C.H.: “Programming a Problem-Oriented Language”, Amsterdam, NY: Mohasco Industries Inc. (internal pub.) 1970
[22] Oskar Schirmer: “GuStL – An Experimental Guarded States Language”, Göttingen, 2016
[23] Paul Soulier, Depeng Li: “Blueprint for an Embedded Systems Programming Language”
[24] Gerald Jay Sussman and Guy Lewis Steele, Jr.: “Scheme: An Interpreter for Extended Lambda Calculus”, MIT AI Lab., AI Lab Memo
AIM-349, December 1975
[25] “CHICKEN – A Practical and Portable Scheme System”,
http://www.call-cc.org
[26] Niklaus Wirth: “The Programming Language Oberon”,
1.10.2013 / 3.5.2016

21

Revision

