modula-2 for real-time systems

10
61 Modula-2 for Real-time Systems Peter OLSEN Brown Boveri & Cie, Ltd., Department ESV-I4, Ved Vesterport 6, DK-1612 Copenhagen, Denmark The paper mainly deals with the module concept of Mod- ula-2 and the facilities that are particularly useful for systems programming. Based on examples, it is shown how the very primitive concurrency constructs of Modnla-2 can be em- ployed to create higher-level synchronization and communica- tion primitives that are accessed through implementation hid- ding interfaces. The paper also gives an overview of the kind and sizes of software systems which we have implemented in Modula-2 and the problems encountered. Keywords: High Level Languages, Modula-2, Parallel Pro- gramming, Real-Time Systems, Synchronization, Distributed Systems, Embedded Systems, Host- Target Development. Dr. Oisen received the Master of Sci- ence degree at the Department of Computer Science, Technical Univer- sity of Denmark in 1978 and the Danish Ph. D. degree in Computer Science in 1983. Since 1981, he has been employed at the firm of BBC Brown Boveri & Cie, working mainly with design and implementation of operating systems and primitives for distributed fault tolerant computing. Dr. Olsen is cur- rently working on a development pro- ject under the ESPRIT programme of the Commission of the European Communities with the goal of introducing formal methods in industrial software development (project RAISE). North-Holland Computer Standards & Interfaces 6 (1987) 61-70 1. Introduction Brown Boveri & Cie (BBC) has a substantial experience in using Modula-2 for embedded appli- cations in connection with process control and communication and also in porting software writ- ten in Modula-2 between different computers and operating systems. We started to use Modula-2 in 1981 in the development of systems software for use in a distributed process control system. Since then Modula-2 has become increasingly more popular and the language is now available for the most common microprocessors and also on several su- per-minis. The language supports a set of primitives for handling quasi-parallel processes and interrupts from peripherals. BBC has developed a portable real-time kernel mainly intended for embedded systems hosted by different computer architec- tures all interconnected via a Local Area Net- work; hence the name of the kernel - Kraka - a woman in Nordic mythology, who dressed in a net. Although perfectly suited for embedded sys- tems, a subset of the kernel has also been imple- mented on top of commercial operating systems like the Digital systems VAX/VMS and RSX-11 with the obvious advantage of portability of com- mercial distributed software packages. The rest of the paper is structured as follows: Section 2 gives a brief overview of the more inter- esting Modula-2 constructs. Section 3 describes a set of synchronization primitives for use in real- time systems which we have implemented in Mod- ula-2 and Section 4 gives an overview of our use of Modula-2 in the implementation of a large dis- tributed system for process control. Finally, Sec- tion 5 discusses the standardization issue for Mod- ula-2 and Section 6 contains the conclusions. 2. The Modula-2 Language The Modula-2 language was developed by Pro- fessor Niklaus Wirth at the Technical University of Zurich [1]. The language was a result of the 0920-5489/87/$3.50 © 1987, Elsevier Science Publishers B.V. (North-Holland)

Upload: peter-olsen

Post on 28-Aug-2016

218 views

Category:

Documents


2 download

TRANSCRIPT

Page 1: Modula-2 for real-time systems

61

Modula-2 for Real-time Systems

Peter O L S E N

Brown Boveri & Cie, Ltd., Department ESV-I4, Ved Vesterport 6, DK-1612 Copenhagen, Denmark

The paper mainly deals with the module concept of Mod- ula-2 and the facilities that are particularly useful for systems programming. Based on examples, it is shown how the very primitive concurrency constructs of Modnla-2 can be em- ployed to create higher-level synchronization and communica- tion primitives that are accessed through implementation hid- ding interfaces.

The paper also gives an overview of the kind and sizes of software systems which we have implemented in Modula-2 and the problems encountered.

Keywords: High Level Languages, Modula-2, Parallel Pro- gramming, Real-Time Systems, Synchronization, Distributed Systems, Embedded Systems, Host- Target Development.

Dr. Oisen received the Master of Sci- ence degree at the Department of Computer Science, Technical Univer- sity of Denmark in 1978 and the Danish Ph. D. degree in Computer Science in 1983.

Since 1981, he has been employed at the firm of BBC Brown Boveri & Cie, working mainly with design and implementation of operating systems and primitives for distributed fault tolerant computing. Dr. Olsen is cur- rently working on a development pro-

ject under the ESPRIT programme of the Commission of the European Communities with the goal of introducing formal methods in industrial software development (project RAISE).

North-Holland Computer Standards & Interfaces 6 (1987) 61-70

1. Introduction

Brown Boveri & Cie (BBC) has a subs tant ia l exper ience in using Modu la -2 for e m b e d d e d appl i - ca t ions in connec t ion with process cont ro l and communica t i on and also in por t ing sof tware writ- ten in Modula -2 be tween dif ferent compute r s and

opera t ing systems. We s tar ted to use Modu la -2 in 1981 in the

deve lopmen t of systems sof tware for use in a d i s t r ibu ted process cont ro l system. Since then Modu la -2 has become increas ingly more popu l a r and the language is now avai lable for the most c o m m o n microprocessors and also on several su- per-minis .

The language suppor t s a set of pr imi t ives for handl ing quas i -para l le l processes and in te r rupts f rom per ipherals . BBC has deve loped a po r t ab l e rea l - t ime kernel ma in ly in tended for e m b e d d e d systems hos ted by dif ferent compu te r architec- tures all in te rconnec ted via a Local Area Ne t - work; hence the name of the kernel - K r a k a - a w o m a n in N o r d i c mythology, who dressed in a net.

A l though perfect ly sui ted for e m b e d d e d sys- tems, a subset of the kernel has also been imple- men ted on top of commerc ia l opera t ing systems l ike the Digi ta l systems V A X / V M S and RSX-11 with the obvious advan tage of po r t ab i l i t y of com- mercia l d i s t r ibu ted software packages.

The rest of the pape r is s t ruc tured as follows: Sect ion 2 gives a br ie f overview of the more inter- est ing Modu la -2 constructs . Sect ion 3 descr ibes a set of synchroniza t ion pr imi t ives for use in real- t ime systems which we have imp lemen ted in M o d - ula-2 and Sect ion 4 gives an overview of our use of Modu la -2 in the imp lemen ta t i on of a large dis- t r ibu ted system for process control . F inal ly , Sec- t ion 5 discusses the s t andard iza t ion issue for Mod- ula-2 and Sect ion 6 conta ins the conclusions.

2. The Modula-2 Language

The Modu la -2 language was deve loped by Pro- fessor Nik laus Wi r th at the Technica l Univers i ty of Zur ich [1]. The language was a resul t of the

0920-5489/87/$3.50 © 1987, Elsevier Science Publishers B.V. (North-Holland)

Page 2: Modula-2 for real-time systems

62 P. Olsen / Modula-2

previous experience with the languages Pascal and Modula (a small experimental language).

Being a direct descendant of Pascal, the lan- guage has inherited most of its semantics and some of its syntax from that language and is therefore quite easy to learn for someone who is already familiar with Pascal. Consequently, we will not present the language in complete detail here but explain its most interesting features by means of examples.

2.1. An Introductory Example

Figure 1 shows a small example of a complete Modula-2 program. We have used indentation to show the structure of the program. All reserved words and all standard identifiers are written in uppercase letters while all user-defined names are in mixed (and significant) case.

As can be seen, a Modula-2 program is a MODULE. It carries a name ("myprogram") , and starts with a so-called IMP OR T list which de- dares the set of separate modules needed inside the current module and also which EXPORTED names from the other modules that are used here.

After the IMPORT list follows a section of local declarations of types, constants, variables, procedures and even nested modules which are needed in the executable part of the module.

The executable part of the module is indicated by the reserved word BEGIN and extends to the END which closes the MODULE. The "mypro-

MODULE myprogram; FROM InOut IMPORT WriteString, ReadCardinal, Done; FROM ReallnOut IMPORT WriteReal, ReadReal; VAR x, y: REAL; i, k: CARDINAL; BEGIN

REPEAT WriteString ("Enter value for 'x ':"); ReadReal (x); IF Done THEN

WriteString ("Enter value for 'i ':"); ReadCardinal (i); IF Done THEN

Y:=I.0; FOR k , = l TO i DO y : = y * x END; WriteString ("(x**i .--"); WriteReal (y, 8);

END; END;

UNTIL NOT Done; END myprogram.

Figure 1.

gram" module is an example of a so-called main module. Once "myprogram" has been compiled, a linker (or a linking loader) may be invoked in order to create an executable program. The linker will collect object modules of all imported mod- ules and any modules imported by these (and so on) .

In the present example, the program is a very simple one: It prompts the user for the value of the variable "x ", then for the value of the variable " i " (a non-negative integer - type CARDINAL) and then computes the (approximated) value of x to the power of i. Note that the IF statements are closed by a matching END, no matter how many statements appear in the T H E N branch.

2.2. Library Modules

What is the difference between a main module and a library module, such as ' InOut" used in Figure 1? First of all, it is evident that a library module offers a set of named facilities that can be EXPORTed to other modules. A main module, however, can only IMPORT. Furthermore, the source text of a library module consists of two parts: The D E F I N I T I O N part which describes the interface offered to other modules, and an IM- P L E M E N T A T I O N part which contains the code (procedure bodies and variables) necessary to im- plement the offered interface. These parts are usually stored in separate files and are separately compiled.

Prior to the execution of the statements follow- ing BEGIN in the main module, all other modules which are part of the program (i.e. modules " In- Out" and "Real lnOut" and any modules that these imports (and so on)) will have their execu- table statement part executed. The reason for this is that variables declared in such modules have the same lifetime as the main module and are there- fore frequently used for storing information needed between successive calls of procedures in the module. For instance, the " InOut" module needs an initialized file descriptor variable for reading characters from the user terminal. This variable should be initialized by the " InOut" module itself. The linker will ensure that such library modules get a chance for initializing them- selves before the main program starts *. Due to its

* Cycles in the initialization graph are detected by the linker and reported as a warning.

Page 3: Modula-2 for real-time systems

P. Olsen / Modula-2 63

special role as initialization code, the statements between BEGIN and END of a MODULE is called the initialization part of the module.

From a software engineering point of view, a very important aspect of the Modula-2 language system is version control. This is based on the fact that a definition module is "compiled" into a so-called symbol file which not only contains the definition module in an internal and checked form, but also includes a unique compilation identifica- tion (e.g. time-stamp). When one module imports another, the compilation identifier of the imported module will be recorded in the object or symbol file generated for the importing module. If a defi- nition module is re-compiled later, a new compi- lation identification will result and this enables the compiler/linker system to detect any version conflicts between modules in a Modula-2 pro- gram. This feature has been of indispensable im- portance in several large projects within BBC.

2.3. Library Module Example

As an example of the structure of library mod- ules, we sketch the structure of the "InOut" mod- ule. The DEFINITION part is shown in Figure 2.

The DEFINITION MODULE defines several procedures which can be IMPORTed in other modules and a variable "Done", which can also be imported (cf. Figure 1). The IMPLEMENTA- TION part of the "InOut" module is sketched in Figure 3.

The variables "InStream" and "OutStream" (of type "File" from module "FileSystem") are not accessible outside the "InOut" module. However, they are static variables, i.e. are permanently alloc- ated in the program. The initialization part of the module initializes these private variables by cal- ling the "Open" procedure in the "FileSystem" module. If this fails, the module HALTS the pro- gram. (HALT is a built-in procedure for this pur- pose).

DEFINITION MODULE InOut; VAR Done: BOOLEAN; PROCEDURE ReadCardinal (VAR c: CARDINAL); PROCEDURE ReadReai (VAR r: REAL); ... (* Rest of module omitted *)

END InOut.

Figure 2.

IMPLEMENTATION MODULE InOut; FROM FileSystem IMPORT Open, Close, Read, Write, File;

VAR InStream, OutStream: File; (* "Hidden" variables *) PROCEDURE ReadCardinal (VAR c: CARDINAL); ... (* local declarations *) BEGIN

... (* Try to read a number *) Done := ... (* and indicate success or failure *)

END ReadCardinal;

BEGIN (* InOut initialization part *) Done :~ Open ("INPUT", InStream); IF NOT (Done AND Open ("OUTPUT", OutStream)) THEN HALT END;

END InOut.

Figure 3.

2.4. The Process Concept of Modula-2

The basic language constructs for handling pro- cess creation, process switching and interrupts are described.

A somewhat surprising aspect of Modula-2 is that there are no built-in linguistic constructs for handling concurrency. However, the language is defined in such a manner that concurrent execu- tion of outermost-level procedures is assumed pos- sible implying that variables declared at the static level (e.g. "x ", "y ", " i " and " k " of Figure 1) are potentially shared variables. Also, the built-in module SYSTEM exports a PROCESS type and a set of primitives for explicitly saving and restoring the central processor state, as well as primitives for establishing mutual exclusion and for dealing with the interrupt system of the target computer. For instance, the SYSTEM procedure call "TRANSFER (current, new)" saves the state of the executing processor in the PROCESS variable "current" and restores a processor state from the PROCESS variable "new".

Inherently, these primitives are very implemen- tation dependent and of a low-level nature, so the programmer's first task is to define a library mod- ule of process-oriented synchronization primitives offering a more general process synchronization environment [2]. Thus Modula-2 gives the system implementer great freedom (and also great re- sponsibility) in the choice of synchronization primitives.

Although the implementation part of such a module will be rather system-dependent, the inter-

Page 4: Modula-2 for real-time systems

64 P. Olsen / Modula-2

face provided can be quite portable. And since the implementation is written in Modula-2, it can usually quite easily be adapted to other architec- tures. This is in contrast to most other concurrent programming language systems which usually have a built-in concurrency concept which cannot be (or is not) implemented in the respective language itself. Such language systems are usually more difficult (i.e. more expensive) to adapt to other architectures.

2.5. Memory Management

Similarly to concurrency, there is no built-in memory management in Modula-2 (disregarding the activation frame management necessary in the implementation of the procedure construct). How- ever, through the SYSTEM type ADDRESS and the SYSTEM function ADR, any kind of memory management can be implemented (except auto- matic garbage collection). The type ADDRESS is compatible with all pointer types and it is further- more possible to do arithmetic operations on this type. The function ADR will give the ADDRESS of any variable, and the functions SIZE and TSIZE are available for obtaining the size (in storage units) required by a variable or (the representation of elements of) a type. The standard procedures NEW and DISPOSE will be translated by the compiler into calls of procedures ALLOCATE and DEALLOCATE which must be provided by the user (e.g. imported from the library module "Storage"). These procedures will be passed a compiler-generated object size parameter as well as the pointer variable in question.

2. 6. Type Coercion

Although strongly typed in general, the Mod- ula-2 language has escape mechanisms which al- low arbitrary types to be imposed on a particular value. For instance, if T is a type name, the expression T(e), where e is an (arbitrary) expres- sion, is a valid expression of type T. Also, formal parameters of type ARRAY of WORD will be compatible with any actual argument type (and the size of the actual argument will be available at run-time through the function SIZE). The WORD type itself is otherwise incompatible with anything else and has no defined operations.

2. 7. Exception Handling

Exceptions seem to be a controversial issue in programming language design. Wirth has de- liberately excluded exceptions as a concept in the Modula-2 language, yet many software engineers argue in favour of exceptions and certain popular languages actually have an exception concept. However, it is worth distinguishing between two different uses of exceptions: (1) Reporting and recovering from exceptional -

but anticipated - situations in a program (e.g. resource limitations or hardware failures).

(2) Reporting and recovering from unanticipated errors (i.e. programming errors), such as an array index out of range or division by zero.

Case (1) can only be handled in Modula-2 by .reporting the exception through normal result parameters, but most implementations support a somewhat system-dependent module which allows case (2) to be handled. This is necessary in order that most of the Modula-2 run-time system can itself be written in Modula-2.

After having used Modula-2 for about two years, we came to the conclusion that the lack of proper exception-handling facilities was a serious obstacle to the writing and using of environment- independent library modules. We therefore de- fined and implemented an exception concept which allows procedures to dynamically attach an excep- tion handler to their activation record. The excep- tion handler may either choose to "resignal" the exception, indicating that it cannot be handled or to unwind the stack of procedure activations to the level of the establisher. The exception handler must be a procedure declared at the outermost level, but it will be passed two parameters: one designating the exception code and related infor- mation and the other being defined by the proce- dure activation that established the handler. The latter parameter is used for transferring the error condition to the establishing activation, if the ex- ception handler chooses to unwind the stack.

As can be imagined, the implementation of the exception handling module depends heavily on knowledge about the machine and the code gener- ator. Nevertheless, we have managed to imple- ment the exception module on all three currently used machines and all four operating systems. However, we would have preferred to have an exception concept built into the language. For

Page 5: Modula-2 for real-time systems

P. Olsen / Modula-2 65

more information about this exception module, the reader is referred to [3].

3. A Portable Real-Time Process Scheduler

This section gives a (slightly simplified) presen- tation of the synchronization primitives chosen by BBC in our use of Modula-2.

A set of synchronization primitives for use in a mono- or multi-processor kernel has been imple- mented in both stand-alone environments and on top of commercial operating systems. These primi- tives were originally implemented for the Kraka kernel which is a stand-alone kernel intended for real-time applications. The Kraka kernel was ini- tially implemented on the PDP-11 computer series, but has later been moved to other computers (the Intel IAPX-86 familily (stand-alone) and Digital PDP-11/RSX and VAX/VMS (hosted by the re- spective operating systems).

Below, we shall describe the aspects of the "scheduler" module in more general terms, rather than focus on a single stand-alone implementa- tion.

3.1. Process Queues

Process queues provide the basic process syn- chronization tool. A process queue is represented by a data structure into which any number of waiting processes can be inserted. The process queue data type is inspired by the "condition" variable of a Hoare-monitor [4]; however, process queues have weaker proof rules, but are more versatile (we claim).

Various operations applies to process queues. The basic ones are:

PROCEDURE init_proc_queue (VAR pq: process-queue); PROCEDURE empty (q: process-queue): BOOLEAN; PROCEDURE wait (VAR q: process-queue); PROCEDURE signal (VAR q: process-queue);

The "init_ proc_ queue" routine initializes a pro- cess queue to its empty state (i.e. containing no processes). Process queues must always be initial- ized prior to use. The "empty" routine can be used to determine whether a process queue is empty or not.

The "wait" and "signal" routines are used for process synchronization. When a process calls the "wai t" routine, it is appended to the specified

queue, and another ready process is selected to run on the current processor. When the "signal" routine is called, the first process in the queue (if any) is removed and made ready to run. These operations are all implemented entirely in Mod- ula-2, using the built-in PROCESS type and the built-in operations for creating, saving and restor- ing processor states (NEWPROCESS, TRANS- F E R and IOTRANSFER, DISABLE, EN- ABLE) *

Signals have no memory, meaning that signal- ling an empty process queue has no effect. The calling process merely continues, and the signal is lost.

The "scheduler" module also defines oper- ations for going through a process queue and for signalling an individual process in a queue. These operations are used when implementing more complicated resource managers. However, it is beyond the scope of this paper to describe these operations further.

3.2. Critical Regions

Process queues represent powerful and versatile scheduling primitives. However, they only make sense when used inside some kind of critical re- gion or monitor which provides mutual exclusion between the calling processes, thus protecting the process queues (and other shared variables) from interference. Since no syntactic construct repre- sents such a feature, operations and data types for establishing mutual exclusion have to be defined by the programmer in a manner similar to process queues. We decided to define a concept of "re- gions" of mutual exclusion.

A critical region is characterized by being oc- cupied by at most one process at a time, thus providing exclusive access to shared resources.

Initially, a critical region is free. When a pro- cess enters the region it becomes occupied by the process. Other processes attempting to enter the region are forced to wait until the occupant leaves the region. (No processor is allocated to a waiting process).

* Although these built-in operations are somewhat specific to monoprocessors, the resulting interface applies equally well to multiprocessors with shared memory. But the built-in primitives would have to be enriched for use on a mul- tiprocessor architecture.

Page 6: Modula-2 for real-time systems

66 P. Olsen / Modula-2

The "scheduler" module defines the following basic operations on critical regions:

PROCEDURE init_region (VAR reg: region); PROCEDURE enter (VAR reg: region); PROCEDURE leave (VAR reg: region);

The "init_region" routine initializes a critical region, and must always be called prior to using a region. The "enter" routine is used when reserving a critical region, while the "leave" routine is for freeing the region, thus suggesting a structured use, as e.g. in:

enter (disc_driver_region); . . . Access shared variables and .. . process queues

leave (discdriver_region);

Multiple critical regions can result in deadlock if not entered in the same sequence everywhere. However, software engineers are aware of this simple kind of deadlock - we have never seen it occur in practice.

3.2.1. Signalling and Waiting in Regions The primitives for signalling and waiting must

interact with the primitives for mutual exclusion, otherwise it becomes extremely difficult to imple- ment resource schedulers in which calling processes must wait for resources. Hoare's monitor primi- tives are extremely powerful for this purpose, but they are also quite complex to implement. We have settled for a simpler semantics, which is still quite powerful. When a process signals a process queue while inside (i.e. while having reserved) a region, the signalled process (if any) is made ready to run, but the signalling process remains holder of the region. Similarly, when a process issues a wait operation on a queue while inside a region, the process remains holder of the region. How- ever, the "scheduler" module provides another primitive linking critical regions and process queues:

PROCEDURE delay(VAR r: region; VAR q: process_queue);

actually in the queue and the region is free again. When later released, the process must call "enter" if it is necessary to re-acquire the region (which in many cases it is not).

3.3. Priority Scheduling

Scheduling is based on process priorities in such a way that when a higher-prioriy process becomes ready it will preempt a running process with lower priority. There is no assumption about scheduling between processes with the same prior- ity. It can be first-come first-served or some kind of time-slicing technique. Hence programmers should make no assumptions about the relative speed of concurrent Modula-2 processes.

3.4. Implementation

The region and process queue objects are repre- sented by straightforward descriptors containing pointers to process descriptors. Somewhat sim- plified, a process descriptor is declared as follows:

TYPE process_id = POINTER TO process_desc; TYPE process_desc =

RECORD flink, blink: processid; cpu_state : PROCESS; priority : [min_prio. . . max_prio];

END A process descriptor contains link fields for

holding the descriptor in the ready-list or in a process queue. On monoprocessors, a single varia- ble "running" designates the currently running process. The code for selecting a new process to run essentially looks like this:

old := running; running ,= select_ from_ ready_ list; TRANSFER (old'.cpu_ state, running'.cpu_ state);

When the "old" process is resumed later, it will continue execution after the above TRANSFER- call. The above piece of code is usually executed with interrupts disabled, but different techniques can also be used.

When calling the "delay" procedure the calling process must occupy the specified critical region. In an indivisible operation, it frees the region and waits in the specified process queue. In this way, it is ensured that the condition which causes the process to delay still holds when the process is

3.5. A Synchronization Example

In this section we present an example of how to use process queues and critical regions. We will describe the implementation of the definition module shown in Figure 4.

Page 7: Modula-2 for real-time systems

P. OIsen / Modula-2 67

DEFINITION MODULE sere; TYPE semaphore =

RECORD pq: process-queue; val: CARDINAL;

END; PROCEDURE init (VAR sere: semaphore); PROCEDURE signal (VAR sem: semaphore); PROCEDURE wait (VAR sere: semaphore);

END sem.

Figure 4.

The module defines ordinary counting sema- phores, and provides a set of primitives for signal- ling and waiting on semaphores. The implementa- tion of the " sem" module is shown in Figure 5. (We are not suggesting semaphores as an ad- ditional primitive - it is just an example).

In this example, we have used a variant of the I M P O R T clause which requires the module name "scheduler" to qualify all references to names from the "scheduler" module, e.g. "scheduler.re- gion".

The module requires a critical region to enforce mutual exclusion to shared data structures (i.e. the semaphores passed as parameters to the "signal" and "wai t" routines).

The "ini t" routine initializes the semaphore value and the associated process queue.

Notice that in the "signal" routine, exclusive access to the semaphore is required in order to test the emptiness of the associated process queue, and performing the semaphore operation. This is ob- tained by the "enter" and "leave" procedure calls. Even if the running process is preempted im- mediately after testing whether the process queue is empty, other processes trying to operate on the semaphore will be blocked when entering the "mutex" region.

In the "wai t" routine, we again enter the "mutex" region in order to obtain exclusive access to the semaphore. If the semaphore is locked (the value is zero), we must wait in the associated process queue. However, a call to "scheduler.wait" is not possible, because we are still inside the critical region. The region is not released unless explicitly demanded by the running process; hence, a call to "scheduler.wait" will cause a deadlock situation. Instead, we must release the region and

IMPLEMENTATION MODULE sem; IMPORT scheduler;

VAR mutex: scheduler.region; PROCEDURE init (VAR sere: semaphore); BEGIN

scheduler.init-proc-queue (sem.pq); sem.val : = 0;

END init; PROCEDURE signal (VAR sere: semaphore); BEGIN scheduler.enter (mutex); IF scheduler.empty (sem.pq) THEN INC (sem^.val) ELSE scheduler.signal (sem.pq) END;

scheduler.leave (mutex); END signal; PROCEDURE wait (VAR sem: semaphore); BEGIN

scheduler.enter (mutex); IF sem ̂ .val = 0 THEN scheduler.delay (mutex, sem.pq); ELSE

DEC (sem^.val); scheduler.leave (mutex);

END; END wait;

BEGIN (* Initialization *) scheduler.init-region (mutex);

END sem.

Figure 5.

wait in the process queue simultaneously. This is exactly what is offered by the "scheduler.delay" procedure. When resumed, we do not automati- cally enter the region. This must be done explicitly if required (and this is what makes process queue proof rules weaker than Hoare 's monitor proof rules).

3.6. Timer Support

Hardware timers are present in most computer systems, but they are usually as diverse in func- tionality and access method as central processors are. Consequently, the code for handling the timer device will usually be quite system-specific. How- ever, a portable interface has been provided. This allows for the ubiquitous delaying of a calling process for a specified time period and obtaining the date and time, but it also has additional facili- ties that are useful in e.g. device drivers for doing time-out supervision of device operations with very little overhead. However, it is out of the scope of this paper to go into these details.

Page 8: Modula-2 for real-time systems

68 P. Olsen / Modula-2

3. 7. Stand-Alone Features

The Kraka kernel also defines operations for connecting a Modula-2 procedure to a so-called interrupt descriptor, thus allowing re-entrant in- terrupt routines to be implemented in Modula-2. However, these primitives usually only make sense when the Modula-2 program has complete control of the computer and are therefore not present in the implementations hosted by (other) operating systems.

The availability of such primitives means that device drivers can be written directly in Modula-2 and that a great deal of portability even for device drivers can be obtained. For instance, we have written drivers for (floppy-)disk controllers, for serial line multiplexers for our LAN controller and for various other devices. These drivers were all written in Modula-2 except that some interrupt routines where later re-coded in assembly lan- guage. The reason for this is that certain devices generate a lot of interrupts without actually hav- ing done very much (e.g. for each character output on a non-DMA serial line controller). In such cases, an assembly interrupt routine consisting of very few instructions (5-20) can execute with limited saving of the current process's state. When the interrupt routine is written in Modula-2 (or any other high-level language for that matter), there is inherently more overhead in the interrupt handling. However, it was felt that writing only such a small part of the driver in assembly lan- guage was not a serious disadvantage and it did not affect the overall structure of a driver which was already structured in a reasonable manner.

With the current trend in hardware sophistica- tion, we would expect the need for such optimized interrupt routines to be steadily decreasing.

4. Distributed Systems Implemented in Modula-2

BBC has implemented a number of systems in a distributed architecture. This section describes some of the systems and the principles used in their implementation.

The current developments by BBC based on Modula-2 concentrate on a fault-tolerant distrib- uted industrial control system based on an in- dustrial local area network - in this case the BBC Partnerbus. Control systems gain from the distrib-

uted architecture in two ways. The decreasing prices in the microprocessor area reduce the costs of systems, and loose coupling of components in distributed architectures leads to better availabil- ity of the systems, since only minor components of the systems will be affected by computer failures like memory errors or defects in peripheral de- vices.

While developing systems for industrial appli- cations with distributed architectures we have fol- lowed the standardization efforts for LANs with great interest. Unfortunately, much of the com- mercial developments and standardization have been concentrating on the administrative area, attending the problems of sharing printers at- tached to an Ethernet etc.

During the last 2-3 years activities have in- creased in the field of interconnecting automation systems in industrial plants and we are following these international standardization efforts with great interest, planning to use Modula-2 as a fast tool for moving distributed software systems to potentially new LAN standards with as little pro- gramming effort as possible.

4.1. A Distributed Fault-Tolerant Message Primitive

The previously described scheduler module al- lows several processes in a single Modula-2 pro- gram to synchronize and share variables.

In a distributed environment different primi- tives are usually preferred, since sharing variables in such an environment is extremely difficult and inefficient. Usually, some kind of message passing, or the increasingly more popular remote proce- dure call, is used.

However, implementing such a primitive is it- self a difficult task, and frequently requires more traditional concurrent programming techniques to be used in the implementation. For this reason, we have chosen to implement a message-passing primitive in Modula-2 using the "scheduler" mod- ule. The message-passing mechanism is itself a relatively large concurrent Modula-2 program (about 16000 lines of Modula-2, and a dynami- cally varying number of processes).

The interface to the message mechanism allows Modula-2 programs (i.e. processes in a Modula-2 program) to communicate and synchronize using reliable many-to-many communication [5]. The message mechanism has been ported to commer-

Page 9: Modula-2 for real-time systems

P. OIsen / Modula-2 69

cial operating systems, on which it - for technical reasons - is integrated in the host operating sys- tems as a privileged function. In this case, the message primitive is available to programs written in other languages as well.

4.1.1. Porting the Message Mechanism It is interesting to note the techniques and

problems we encountered in porting this message package between various computers and operating systems. First of all, we wanted to keep the major part of the software machine-independent so that central maintenance of this could be achieved; secondly we insisted on not using conditional compilation techniques (macro preprocessors). Therefore, we decided to keep machine depen- dences at the module level. This implied that several abstractions over machine and operating system idiosyncrasies had to been defined.

Two different kinds of modules were identified during this process: Machine-independent inter- faces (DEFINITION modules) whose implemen- tation part was machine-dependent, and machine- dependent interfaces which had to exist in a ver- sion for each virtual machine, but which did not introduce machine dependences in its using mod- ules. The latter kinds of modules are partly a result of the language, partly a design choice. As an example, the number of bytes in a virtual page is needed in the user interface. This could either be made available as a function with machine-de- pendent implementation or as a constant declared (and defined) in a machine-dependent definition module. Since the latter approach is more efficient and does not lead to more machine-dependent modules than the other, it would be chosen.

This strategy allowed us to maintain the purely portable modules (i.e. the vast majority) in a central place and then maintain a library of ma- chine-dependent modules for each target architec- ture. Combining the modules for a particular sys- tem is just a question of search-path at compila- tion and linking time.

4.2. A Distributed Fault-Tolerance Process Control System

Using the above message primitive, a distrib- uted process control system with support for rep- licated functions has been implemented in Mod- ula-2. This is by far the largest single system that

we have implemented in Modula-2. It consists of 10-20 (depending on configuration) self-con- tained Modula-2 programs which communicate with each other through the message primitive (and hence are not aware of each others placement in the distributed system). Inside each program multiple (quasi-)concurrent processes exists. These synchronize and communicate internally through shared variables.

The distributed process control system uses the Partnerbus LAN, and is usually configured with a combination of PDP-11 and VAX computers as well as our own IAPX-86 based computers. Also, in certain peripheral devices and in the remote telecontrol stations the IAPX-86 family is used.

Again, these are programmed in Modula-2 and use the Kraka kernel. These embedded systems are always cross-developed, using either IBM PCs or VAX computers. The programs are usually down-line loaded during the development and testing phase, but stored in a non-volatile tech- nique when put into mass production.

By using Modula-2 and our own Kraka kernel in all the different target systems, an almost uni- form cross-development environment has been ob- tained. For instance, on a VAX host computer we can develop, down-line load, and debug Modula-2 programs for the PDP-11, VAX, and three differ- ent IAPX-86 based BBC-computers. The success of this approach is based on the uniform environ- ment provided by the different Modula-2 com- piler/ t inker systems. Although Modula-2 is yet to be standardized, the original Modula-2 distribu- tion from ETH, on which most commercially available Modula-2 systems are based, provides a de-facto standard for Modula-2 implementations.

5. The Standardization of Modula-2

With 30 software developers all designing and writing software in Modula-2, we naturally follow the British Standards Institute attempt to produce a standard proposal for the language. As both user and implementer of Modula-2 the need for a standard definition is apparent: There are several weak points in the definition of the Modula-2 language and there is a need for a standard library definition, since the modules originally described by Wirth are generally recognized as being too simplistic for serious use. However, it is very

Page 10: Modula-2 for real-time systems

70 P. Olsen / Modula-2

difficult for a larger group of people to agree on a proposal and especially the library definition seems to be difficult, so we expect the standardization effort to take long time.

In the absence of precise definitions of stan- dard module libraries, all implementers of Mod- ula-2 have provided their own set of library mod- ules, in addition to those defined by Wirth. In many cases, these extra modules are quite system- dependent, giving no or only very little abstraction over the host operating system. This is acceptable, because such low-level modules must be present in any practical Modula-2 implementation; what is lacking is a set of machine and operating-system- independent modules for use by portable pro- grams. Since portability was an important issue in our developments, we have developed such a set of portable modules. Once a standard library has been defined, we intend to implement that as well. The current draft proposal for a library standard can be found in [6,7].

6. Conclusion

Modula-2 is a relatively small language which is widely available (and inexpensive). It is very sui- table for the implementation of embedded system, yet gives a large degree of portability.

Of particular importance is the ability to define the concurrency concepts in the most application- oriented way, and the ability to do safe separate

compilation and information hiding. Although it was originally available as a univer-

sity product with only little support, we have experienced large benefits in several difficult and large projects. The major drawback of Modula-2, is the lack of a formal language definition and especially the lacking standard library. However, a standardization effort is started, and we expect Modula-2 to become even more popular once this is complete.

References

[1] Niklaus Wirth: "Report on the Programming Language Modula-2", In: Niklaus Wirth: Programming in Modula-2, Springer-Verlag, Heidelberg, 1983. Second Edition.

[2] Niklaus Wirth: "Schemes for Multiprogramming and Their Implementation in Modula-2", Institut fiir Informatik, Ei- genoessische Hochschule ZUrich, Report no. 59, 1984.

[3] "Modula-2 Exception Handling", BBC Dept. ESV-I4, Doc. Id. ESL-D96, Copenhagen, April 1984.

[4] C.A.R. Hoare: "Monitors: An Operating System Structur- ing Concept, Communications of ACM 17, 10, Oct. 1974, pp. 549-557.

[5] Karsten Kynde: "Synchronizing Distributed Processes by Means of Reliable Multi-Destination Message Passing" Proceedings of the 6th European Conference on Electro- techniques, EUROCON 84, 26-28 Sept. 1984.

[6] Modula-2 news, issue no. 0, Oct. 1984. The quarterly newsletter of the Modula-2 Users Association, P.O.B. 289, CH-8025 Zurich, Switzerland.

[7] Modula-2 news, issue no. 1, Jan. 1985. [8] "Modula-2/RSX User's Guide", BBC Dept. ESV-I4, Doc.

Id. ESL-D30, Copenhagen, April 1985.