Object-Oriented Analysis and Design for Real-Time Systems
David M. Kline
The primary focus of this paper is the application of Object-Oriented Analysis and Design, using the Booch method, to Real-Time systems. Specifically, for the Controls Universal Bridge (CUB), a system primarily designed to support the transportation of device data retrieved by the Epicure data acquisition system  to various beamlines and experiments, and to perform gateway functions between various communication protocols and devices. The paper is divided into two parts. The first part contains an annotated description of the hardware platform of the CUB system, discusses some concepts of object orientation and the models which are used, and includes other methodologies in the discussions which were researched for their application to Real-Time systems. The second part describes the development system, discusses issues related to Real-Time systems, and provides the analysis and design of the CUB system. Furthermore, since the Booch method was chosen, the discussions will gravitate towards his definitions and vocabulary.
From the information provided by , the CUB system is implemented as a standard CAMAC module that is physically separated into a main and an IO board. The main board consists of an Intel 80960 (i960) microprocessor, memory systems, and standard communication devices which interface to the Epicure control system, such as serial and ARCnet ports, and the CAMAC interface. The IO board consists of the specialized hardware and software used to interface with devices external to the control system. Furthermore, the IO board can support other intelligent devices, such as microprocessors and controllers, for loosely coupled distributed and multiprocessor applications.
The discussion above implies that particular hardware pieces can be grouped, or chunked, into logical abstractions. Therefore, the abstractions which can be identified thus far, representing the CUB system consist of the Central Processing Unit (CPU), Standard IO (StIO), and Specialized IO (SpIO). Each directly representing the hardware components described above. The CPU and StIO abstractions represent the main board components, such as the microprocessor and memory systems; whereas, the SpIO represents the specialized IO board components for a particular client. A later section discusses additional details and decomposition of the abstractions identified to this point. The following sections describe the semantics of classes and objects, the Object-Model, and Object-Oriented Analysis and Design and its application to the CUB system.
Booch classifies objects as either active or passive [p. 91]. Yourdon defines active objects as ones which control particular aspects of a system . Booch describes them as autonomous independent machines encompassing it own thread of control. Thus changing state independent of its communication with other objects. Because of this behavior, Booch characterizes them as tiny finite state machines. Additionally, Yourdon adds the characteristic that active objects are ones associated with high speed external sources of data and that exhibit time-dependent behavior, such as Real-Time systems. Booch suggests that active objects can play a role as an actor or agent. An actor object is one that can operate upon other objects but is never operated on. A agent object can operate on objects, and be operated on by other objects. Passive objects are ones which change state, or respond, when stimulated by one or more active objects. Booch identifies these as systems where sequential system architectures exist, such as transaction processing. Furthermore, passive objects play a role of a server, where objects operate only on it.
``A class is a set of objects that share a common structure and common behavior. The terms class and type are usually (but not always) interchangeable; a class is a slightly different concept than a type, in that it emphasizes the classification of structure and behavior.''
A class is a template, or abstraction, for any number of objects. Booch comments that a class serves as a binding contract between the abstraction (see subsequent discussions on abstraction) and its clients or users. Objects are instantiated from a particular class. They contain the properties of state, behavior, and identity as discussed in the previous section. Pragmatically, the terms instance and object are interchangeable . Selic comments that, however, there exists ambiguity with the meaning of instance and suggests using the term ``incarnation'' instead . Classes define an interface, with a variety of visibility levels, for particular kinds of clients. Generally, the class interface provides mechanisms to pass messages between objects and clients. In C++, the implementation language of the CUB system, a class interface is divided into public, protected, and private domains. Each representing specific interface for particular clients. The public interface allows clients to access members providing a message passing mechanism, a software bus, between objects. The protected domain provides accessibility to the object itself and its subclasses. The private domain allows access by only the object.
Wirfs-Brock and Booch distinguish classes as either being abstract or concrete. Abstract classes, also referred to as superclasses, are ones which are not intended to be instantiated and not to create objects. They define a base behavior and attributes which are either inherited by a subclass or overridden by other abstract classes or concrete classes. Wirfs-Brock points out that an abstract class can provide full or partial implementation of its behavior serving as a template for other classes. Concrete classes, also referred to as subclasses, are intended to be instantiated and to create objects. They inherit the behavior and attributes of a superclass either by using the default implementation, or redefining and extending its behavior and attributes. Booch defines other types of classes which are mixin and metaclass . Mixin classes are ones where a subclass inherits two or more superclasses, also referred to as multiple inheritance, to form another behavior different than the classes of which were inherited. Metaclasses are ones where an instance of a class is a class that can be manipulated similarly as an object. Thus allowing certain behavior independent of instance behavior. C++ does not support metaclasses directly but through static members and methods, an implementation detail that will be addressed in Part II.
``An abstraction denotes the essential characteristics of an object that distinguish it from other kinds of objects and thus provide crisply defined conceptual boundaries, relative to the perspective of the viewer.''
``The process of compartmentalizing the elements of an abstraction that constitute its structure and behavior; encapsulation serves to separate the contractual interface of an abstraction and its implementation.''
``Modularity is the property of a system that has been decomposed into a set of cohesive and loosely coupled modules.''
``Persistence is the property of an object which its existence transcends time (i.e. the object continues to exist after its creator ceases to exist) and/or space (i.e. the object's location moves from the address space in which it was created).''
The above discussed both the major and minor principles of the Object-Model. It has been referred to earlier that a model provides a conceptual framework or mindset, and a particular point of view for Object-Oriented Analysis and Design. However, Booch has pointed out that it is impossible to capture all the details in one view . Therefore, he proposes that a multimodeled approach be used to acquire different perspectives of the same system. Booch suggests a two dimensional approach where one axis describes the logical and physical aspects of a system, and the other axis describes its static and dynamic behaviors. In addition, a number of diagrams are used to capture each dimension denoting the models. Below discusses the models of each of the dimensions and the diagrams used in the Booch methodology which describe them.
The physical model describes the composition of the software and hardware aspects of a system in terms of physical files and devices. Module diagrams are used to identify the placement of class and object declarations. Consider the example given above, the classes and objects used to represent the operating system processes are logically grouped into software modules, consisting of various files. Process diagrams are used to express the hardware aspects of the system and allocation of processes to a particular processor or processors. For example, considering the abstract discussion about the CUB system, the hardware can be described using a process diagram.
The basic components of the hardware consists of a Personnel Computer (PC), rack, and a CAMAC crate mounted within the rack. The PC contains an ARCnet/CAMAC interface for access to EADnet, which supports communication between beamline instrumentation and Epicure, and standalone CAMAC, for diagnostic capabilities. An interface to ethernet is supported for access to the Epicure development system, through PATHWORKS, which is a server for PC development and software products (see below). The CAMAC crate, within the rack, contains a CAMAC crate controller that is connected to the PC through a parallel link to the ARCnet/CAMAC interface. A prototype CUB module resides in the CAMAC crate and represents the target system. The CUB module is connected to the PC through two ARCnet ports and a serial port.
The PC runs under Windows 3.1 and includes the standard packages for standalone ARCnet and CAMAC communications. The development software includes Rational Rose / C++, Microtec C / C++ i960 cross-compiler, and the Microtec i960 simulator. Rational Rose / C++ is a graphical Computer Aided Software Engineering (CASE) tool that supports Object-Oriented analysis, design, and implementation. It automates the development process by supporting the Booch notation and generating C++ code from the analysis and design models as discussed in part I. It additionally provides reverse engineering of existing C++ code into object models. The Microtec C / C++ cross compiler works in conjunction with the Microtec i960 simulator, it will use the C++ code derived from the models created in Rational Rose / C++ to generate an executable for model simulation and verification. This process supports rapid prototype development, an iterative and incremental scheme, as suggested by the Booch methodology.
Booch divides the development into the macro and micro processes. The macro process is similar to the traditional ``waterfall'' and ``structured'' approaches but serves as an appropriate controlling framework for the micro process. The macro process focuses on the strategic aspects which includes risk assessment and the overall architecture of the system. Its basic objective is incremental development and repeats itself after a major product release. The activities of the macro process include analysis, design, evolution, and maintenance.
The micro process is applied during the evolution activity of the macro process. It focuses on the tactical aspects of the development process, such as considering various alternatives to the implementation of a class. The activities of the micro process include the identification of the classes and objects that constitute a given level of abstraction, identifying the semantics and relationship between classes and objects, and specifying an interface and implementing the classes and objects . The description above of the activities provide a heuristic for the development of Real-Time systems, in particular the CUB system. However, an elaborate discussion of these activities is beyond the scope of this paper but can be acquired from [Chapter 6-7].
Because of the scope of the CUB system, the development process is divided in two phases. The first phase includes the development of a Real-Time operating system consisting a micro-kernel that is responsible for managing processes within the CUB system, IO and memory management, resource allocation and deallocation, and network services, in particular ARCnet, serial communications, and a CAMAC interface. The second phase is the development of the CUB system. It is based from the Real-Time Operating System (RTOS) developed in the first phase and consists of the particulars related to the CUB system, such as the number of ARCnet, serial communication ports, and specifics related to the devices which exist on the IO board. Furthermore, since the RTOS essentially represents the CUB system, the analysis and design will focus primarily on the classes and objects of the RTOS.
Issues related to Real-Time systems can be grouped into three areas, these are Object-Oriented development, the implementation language, and Real-Time mechanisms. Ones related to Object-Oriented development include the adaptation of a new methodology, and the learning curve associated with various development tools and training. Since algorithmic decomposition is the primary methodology for implementing Real-Time systems, it takes time to acquire a new mental framework for object decomposition. Furthermore, it takes time to integrate, learn and train with the development tools. For example, consider the tools which need to be integrated and learned for the CUB system. These include the Rose / C++ modeling application, and the Microtec C / C++ cross-compiler and i960 simulator. Another issue that is quite problematic and merits discussion deals with testing and simulating a model with external devices. For example, consider testing the communication between the CUB system and external hardware devices using the i960 simulator, such as ARCnet message passing. How are the external objects represented? At what level is the simulation consider reasonable? The solution is to create a surrogate object which models the functionality of the external device. The level of functionality to which is implemented is dependent on the level of testing driven by the scope of the model given that particular increment in the development process .
The C++ programming language has been chosen for implementing the CUB system. Primarily because C is the implementation language for many of the embedded systems and applications of the Epicure control system, thus the time required to overcome the learning curve and training is significantly reduced. Furthermore, C++ is one of the prominent implementation languages and has adequate support and tools available. Below discusses the issues pertinent with the implementation language and its relationship to Object-Orientation and Real-Time systems.
It has been shown that message passing between objects, bound dynamically in particular, is a definite performance issue. Booch indicates that a dynamic function call, through the use of inheritance, might take 1.75 to 2.5 times longer to call an object's method than a traditional function call; however, Booch points out that, on average, dynamic invocations constitute only 20 percent of all invocations, and therefore may not have as significant of an impact. Coleman shows a 2.9 increase in overhead due to dynamic binding; however, the functionality dynamic binding replaces, such as union and switch statements in C, can improve performance significantly . Both  and  suggest that dynamic binding be used only when ``...it is natural to regard a subclass instance as a superclass instance...'' . In the context of the CUB system, this can be applied to the various communication devices and types of processes that will exist. Another issue is related to the number of abstraction layers. Booch comments that calling a method from a high abstraction layer could call numerous other methods of lower abstraction layers. For some Real-time systems this might be unacceptable; however, it might be necessary from comprehension of a complex system. Another issue is that deep inheritance lattices can cause large object code. However, Booch comments that object code size can be reduced by using a compiler and linker that eliminates duplicate and unused code. Consequently, the Microtec C / C++ cross-compiler is optimized to perform these duties.
Booch therefore suggests implementing for functionality first, then determine which methods represent the bottlenecks and declare those as inline. Others alternatives are to bind the calls statically, collapse the hierarchy in the inheritance lattice, or declare some class attributes (data members) as static. Coleman concurs with  in terms of declaring methods as inline, albeit use them judicious. He suggests, as Booch has, that inlining can improve performance by declaring the methods which are most frequently called. However, he does caution that using too many inline methods can lead to ``code bloat'', which can cause paging problems in virtual machines  and a larger executable that may not fit into limited memory resources. Furthermore, he comments that compiling and linking time will increase with many inline methods. Booch also observes that paging can be a bottleneck because of where the compiler places the objects. Ones placed in segments that reference others in different segments can cause paging and destroy microprocessor caches, thus hindering performance . In terms of the CUB system, suggests from both Booch and Coleman will be used. Specifically, Boochs suggestion of acquiring metrics and identifying the bottlenecks, and Colemans suggestion of using inline method judiciously by declaring the ones that are called most frequently. Another issue related to performance is the creation and destruction of objects, and the mechanism used to pass messages containing objects. Booch identifies that the creation of objects could serve as a bottleneck in Real-time systems. He suggests that objects which are created frequently be statically allocated, thus eliminating repeated calls to the objects constructor and deconstructor. He makes another suggestion by overloading the global memory allocator ``new'' to perform operations specific to the memory system. In respect to the passing mechanisms,  argues that passing objects by reference, although introducing another level of indirection, is advantages because it reduces the objects time and space requirement. He poses that whether an object is passed by reference or value is predicated by the objects identity and the overhead associated with its constructor and deconstructor. Furthermore, Coleman contends that storing an object in another object by value has performance advantages. He supports this by stating that accesses are performed directly rather than through a reference, thus eliminating indirection costs which can be quite heavy under certain circumstances relating to paging and caching. Although, if the reference is qualified by ``register'', accesses can be as quick as direct. However, it has been pointed out that the use of the ``register'' storage class specifier is a request to the compiler which may or may not be satisfied . Additionally, since the object is static, memory allocation and deallocation is not necessary, thus reducing overhead incurred by the allocator. The loss of objects and possibility of memory leaks is eliminated because synchronization between the allocation and deallocation of contained objects is guaranteed. Because the objects are static, virtual methods are bound at compile time, thus eliminating the overhead associated with dynamic binding. Most of the objects used by the CUB system will be created when the system boots, such as processes performing network and remote terminal functions. Furthermore, objects will be passed by and returned by reference whenever possible so that the objects copy constructor will not be called.
The last discussion addresses issues directly related to Real-Time operating systems and their measurements. Microtec Research contends that context switch and interrupt latency times are inadequate measurements of an RTOS performance. Context switch times measures the time for an RTOS to save the context of one process and restore the context of another. It provides little information about the overhead associated with a system call and is not a noticeable operation within the RTOS. Interrupt latency is defined as the time measurement from the assertion of an interrupt to the execution of the first instruction in the Interrupt Service Routine (ISR). Microtec suggests that interrupt latency measurements does not identify the impact of an RTOS on the behavior of the system. The impact of interrupt latency of a single interrupt is not as significant as if interrupts have been disabled by an application, or if multiple interrupts occur simultaneously. For both cases, Microtec suggests that interrupts remain enabled, the ISR perform the minimal operations, use systems calls with published execution times and include process rescheduling times, and to defer the processing to a lower interrupt level. Another issue with an RTOS is priority inversion. An effect where a lower priority process holds a resource while a higher priority process contends for the same resource  and is exaggerated when the lower priority process is preempted further delaying access to the resource. Microtec suggests evading priority inversion by implementing a ``priority inheritance'' scheme where a lower priority process holding a particular resource inherits the priority of the higher priority process contending for the same resource. Labrosse supports this suggestion by commenting that by assigning a lower priority process a higher priority, this effect will be avoided, and therefore kernels should allow processes to have floating priorities. Microtec further suggests that rescheduling latency can be minimized by the priority inheritance scheme by implementing higher level preemptable system services with lower level non-preemptable services. Since the lower priority services are not subject to preemption, priority inversion is not an issue and thus reduces the impact of rescheduling latency .
The Epicure Real-Time Operating System
(RTOS) is a portable, preemptive, multiprocessing, soft real time operating system intended for embedded systems using commercially available microprocessors. The operating system is portable across various microprocessors which include x86, HP PA-RISC, PowerPC, Alpha AXP, and others which support a priority based interrupt structure. Ones which do not implement a priority based interrupt structure can be used, however, require the necessary support hardware. As referred to earlier, the RTOS is initially implemented for the CUB system based on the i960 microprocessor. Future releases of the RTOS will be targeted for other CAMAC modules, but primarily for the Cyclone PCI 80960 board that will be used to implement the DAE Timer, CAMAC, and Listener functions in an AXP based workstation of a prototype for the next generation data acquisition front end.
From the above discussion, one can intuitively identify the existence of several classes and objects representing both logical entities, such as virtual classes and processes, and physical entities, such as serial ports, ARCnet ports, and clocks, which directly model the hardware abstractions. Below briefly discusses the behavior and implementation of these abstractions and their semantics within the context of the CUB system and its RTOS.
The Microprocessor abstraction is a class that represents, in the context of the RTOS, the i960 microprocessor. However, since the intention of the RTOS is to be portable across various hardware platforms and requires a priority based interrupt structure, another abstraction can be identified as the Interrupt Controller (InterruptController). The InterruptController is a virtual class that allows one to manipulate the hardware for any type of interrupt structure that the hardware designer selects. It makes the appropriate associations between an interrupt priority level and an Interrupt Service Routine (ISR), and other tables and data structure required which implement the priority structure. The Microprocessor class primarily serves as a logical entity for purposes of documentation in class and object diagrams, and serves as a container for Symmetric Multiprocessing (SMP) support. In addition, the class can be implemented as a metaclass  which instantiates classes representing other types of microprocessors, such as RISC for the AXP, i960, PowerPC, & HP PA-RISC, and CISC for the Pentium, x86, 8051, and Z80.
The Memory abstraction is responsible for accessing memory components and storing client applications (programs) and other information pertinent to the RTOS. The abstraction can be further decomposed into the other types of memory devices available in the CUB system. Because of the required versatility, the memory abstraction will be implemented as a virtual class. The further decomposition includes the Electrically Erasable (EPROM), Static (SRAM), and Non-volatile (NvRAM) abstractions, which inherit the characteristics of the virtual class. The EPROM class is responsible for storing the RTOS, system programs, and client applications, or other data which needs to be held for long periods of time and for read only purposes. Additionally, the EPROM class supports methods to program (write) the memories. The SRAM is responsible for storing the dynamic aspects of the RTOS. For example, consider when processes are created or when memory pools require expansion. The NvRAM is responsible for storing information for when the power is either available or unavailable to the CUB system. It contains pertinent information for system startup, parameters, or exception information. For example, the number of processes and virtual circuits active, or a list of exceptions which last occurred before power failure. These classes are not necessarily used to instantiate objects but are primarily utility classes . Ones which contain methods that are used by other objects to support their responsibilities. For example, a memory manager would used these classes to access the various types of memories available.
The Date abstraction is responsible for keeping track of the current time in hours, minutes, and seconds, as well as the current month, day, and year. The device used to implement this functionality is the Dallas DS1643 . It contains registers that allow one to set the current date and time, and are battery backed up. Therefore, independent of the existence of power, the date and time is updated continuously. The registers are set when the RTOS boots by requesting the current date and time information from the VMS to ARCnet gateway process residing on the Epicure front end. The data is received in the clinks format, converted appropriately and verified, then written to the registers. In addition, the clinks are updated every twenty four hours by establishing a timer entry and generates and interrupt when it expires. The clinks information is then acquired and updated simular as when the Date object is instantiated. Since other hardware exists supporting this functionality, the Date class is considered virtual. The methods (member functions) used to write and read the date/time information are overridden by the methods of the derived class which instantiates the object. The object is defined as an agent  since it can stimulate and be stimulated by other objects.
The FrontPanel abstraction is a virtual class for the various front panel configurations of CAMAC modules, as well as VMEbus modules. It is responsible for writing to the various types of displays, such as LEDs and segmented displays, and reading from various inputs, such as DIP switches and push buttons. It is also considered an agent process because of its interaction with other objects.
Before discussing the remaining abstractions, there exists common functionality between the classes and objects that will be identified below. Most of the abstractions are implemented as virtual classes in order to exploit reuse and plug compatibility among the numerous hardware devices that can be used to implement the circuitry of the CUB system, such as the CAMAC decoding circuitry. The methods implemented in the virtual classes are overridden by their derived classes in order to meet the specific requirements of the hardware used. Furthermore, the virtual classes implement common methods which support logical links and register manipulation. The connect method establishes a logical link between two objects which binds them together to support resource allocation and deallocation, message passing, and event notification. An object can support many links, or virtual circuits, between other objects in order to share resources, such as a single ARCnet port. The link is destroyed either by explicitly calling an objects disconnect method, or by abnormal termination through an exception. The read and write methods are used to manipulate control, data, and interrupt vector registers which are supported by the hardware. Below continues the discussion and identification of the abstractions, classes, and objects.
The Serial abstraction is a virtual class supporting plug compatibility among the various serial communication devices which reside on the CUB system main board, or others which might exist in the SpIO abstraction. The derived class implementing the particulars related to the
Philips/Signetics 2692 DUART , instantiates an object that implements the common methods which were referred to above. The client object calls the connect method to establish a virtual connection (logical link) for communication to the serial port and notification of incoming messages. Typically, the serial port will be responsible for local communications with a VT-100 compatible terminal; therefore, one virtual circuit is available. However, if the serial port has been configured as part of a serial network (TiWay), multiple virtual circuits can be enabled as part of the RTOS boot in addition to any protocol requirements. When a message appears in the serial port, the object is responsible for communicating the event to the attached object.
The TevCLK abstraction is a virtual class which supports plug compatibility between the numerous methods of designing the Tevatron decoding circuitry. The derived class supports the common methods to manipulate the Cypress CY7C167 and CY7C408 , and communicate Tevatron events to multiple objects which have established a virtual circuit. The initialization of the object is performed either when the RTOS boots, or through protected system management services. The parameters indicating the maximum number virtual circuits, and other particulars related to resources, are stored and read from the NvRAM. When a Tevatron events occurs, the object notifies every connected object by using semaphores, messages queues, or mailboxes.
The CAMAC abstraction models the CAMAC function and request decoding circuitry, as defined in the EPLD . It is implemented as a virtual class in order to remain plug compatible with other CAMAC decoding circuits. The derived class supports the common methods to manipulate the EPLD and notify clients (objects) of CAMAC events. Typically, one virtual circuit is available, however, multiple virtual circuits can be configured as part of the RTOS startup. The number of virtual circuits available is stored in the NvRAM. When a message appears in the CAMAC interface, an interrupt is generated and activates the CAMAC object which collects the information from the CAMAC registers, constructs a message, and passes the message to every connected object. Write messages primarily constitute responses from the Epicure front end for data acquisition consisting of internal data related to the CUB system, such as performance monitoring, or for transporting data collected from the SpIO abstraction.
The ARCnet abstraction is a virtual class for plug compatibility among the various protocols used to access an ARCnet interface, such as EADnet and the instrumentation network (i.e. InNet ). The derived class uses the common class methods to manipulate the SMC COM20020 hardware device . The object supports multiple virtual circuits for the various protocols available and the number of clients possible. The object is initialized when the RTOS boots and configures the control registers and others related to ARCnet communication, such as the node id. The information is stored in the NvRAM and can be accessed through the common methods for system management duties or system monitoring. When an interrupt occurs, the object reads the information from the COM 20020, and processes it according to the particular protocol. The message is then sent to the appropriate process or processes. The response messages, if required, is passed to the common methods in which the appropriate protocol is applied and sent to the destination node.
The Ticker abstraction is a virtual class and is responsible for generating an interrupt at a particular interval. It is implemented as virtual since there exists many devices that can implement the timer circuitry. The derived class uses the common methods to access and manipulate the hardware registers of the Philips/Signetics 2692 DUART hardware device . The common methods are used to configure the time interval at the start of the RTOS, and to establish logical links between objects. When an interrupt occurs, every connected object is notified of the event. Typically, it is used to decide when data acquisition will occur, or to begin process scheduling.
The RTOS is comprised of basic abstractions representing other abstractions which support the lower and higher level functions of the operating system. The basic abstractions are tiered, or layered, starting at the most primitive layer to the most sophisticated, representing the interface of the RTOS. Each layer supports its primitive operations which act as an interface to the next layer representing a particular form of functionality. At the most primitive level, resides the Physical Abstraction. It is responsible for providing the basic interface for the kernel (see below) and other abstractions of the RTOS to the particular hardware components, and protects them from the dependencies imposed by peculiarities inherent in the hardware. The micro kernel (k) abstraction resides on top of the PA and is responsible for processing interrupts and exceptions, scheduling processes, and synchronizing activities related to processes and ones related to memory, file, and IO resource management. The Management abstraction layer includes the abstractions which implement the logical aspects of the RTOS and is the interface to client processes and applications to establish virtual circuits, allocate and release memory blocks, read, write, and manipulate files, and enable and disable the reception of events from the particular hardware components of the CUB system. The Services abstraction encapsulates the Management abstraction and serves as a barrier between the client and the RTOS protecting the objects from inadvertent accesses. It consists of the various routines which allow interaction between applications and the RTOS. The routines use the underlying objects to execute their defined responsibilities. Thus, the barrier can be perceived as the RTOS Application Programming Interface (API). Furthermore, the combination of the abstractions identified above are collectively known as the RTOS executive.
Before proceeding with discussing the particulars related to the RTOS abstractions, a brief discussion including the tactical decisions of the RTOS will provided a frame of reference for the remainder of this section. One of the primary tactical decisions was that the RTOS be designed using a preemptive kernel because of the real time functionality required by the CUB system, and the benefits outlined in . Some of these requirements include the coordination of the activities to transport messages between the existing Epicure data acquisition system and experiments and beamlines, and to support remote and local clients from the different types of communication devices, such as CAMAC, ARCnet, and the serial port, and the flexibility to accommodate different IO boards supporting a diverse scope of communication devices and microprocessors. Real time systems, such as the CUB RTOS, are categorized as being either hard or soft. Hard real time systems are ones that must adhere to critical time deadlines, where responses to interrupts and exceptions must be deterministic, otherwise catastrophic results might occur, such as in defense and medical systems. Soft real time systems are ones which response times to external and internal events are not as deterministic as hard real time systems. They typically do not lead to catastrophic results, such as with cellular phones and washing machine systems. The CUB system employs both philosophies and is based from discussions in , , and . This will become apparent when discussing the process scheduling algorithm that was selected for the RTOS.
The RTOS executes either in user or kernel (supervisor) mode. The RTOS API, referred to earlier, executes in kernel mode so that the objects, protected by the kernel, can be accessed on behalf of the client. This implies that all the underlying abstractions, such as ones residing in Services, Management, k, and PA, execute in kernel mode. This is necessary so that the RTOS can perform process scheduling, exception handling, and API activation, in an elevated mode preempting user mode activities. User mode typically pertains to client activities which call the kernel mode activities (API) to perform their responsibilities. Such activities include remote logins, through ARCnet or the serial port, and performing system management duties and system monitoring. Furthermore, the RTOS is designed such that other Management abstractions, possibly representing other protocols and services, such as POSIX, DECnet, TCP/IP, or others extending the capabilities of the RTOS API, could be absorbed quite easily. Below discusses the particulars relating to the definition and responsibilities of the aforementioned abstractions.
The Physical Abstraction (PA) implements the primitive operations of the RTOS in terms of accesses to hardware devices residing in the CUB system. It acts as a protective barrier for the kernel and management abstractions encapsulating the particulars and hides the peculiarities inherent with the microprocessor, interrupt controller, and communication devices used to implement the hardware systems. The PA contains objects (also referred to as PA objects) which include the ones identified in the section ``Hardware Abstractions, Classes, and Objects'' which are, namely, the Microprocessor, Memory, Date, FrontPanel, Serial, TevCLK, CAMAC, ARCnet, and Ticker objects.
The kernel abstraction is responsible for responding to interrupts from the hardware and dispatching them to the appropriate client, handling exceptions from API activation, and performing process scheduling and context switching when a higher priority process or thread is ready for execution. The kernel accesses the Microprocessor, InterruptController, and Ticker objects located in the PA abstraction to perform these duties. The kernel uses the Microprocessor object to perform activities specific to the ones supported by the microprocessor employed, and to synchronize the scheduling of multiple microprocessors (SMP), if required. The InterruptController object provides compatibility with the implemented interrupt structure, and transparency among the various interrupt controllers available, thus allowing the kernel to process interrupts in a consistent manner regards of the hardware implementation. The Ticker object drives the process scheduling and context switching functions of the kernel. When an interrupt occurs from the Ticker, the kernel executes its scheduling algorithm to determine which process or thread will gain control of the microprocessor, and whether a process or thread quantum (execution time) has expired, causing it to be rescheduled for later execution. Thread scheduling is the fundamental service provided by the kernel and is implemented by the Scheduler. The Scheduler applies policies which determine which thread will gain control of the microprocessor. After that determination has been made, the Scheduler resumes control of the current thread or performs a context switch. A priority scheme is used to determine which thread gains control of the microprocessor. If a thread is present in the ready queue (see below) that has a higher priority than the currently running thread, a context switch is performed. Context switches also occur when a thread time quantum expires. In this case, the thread is inserted into the ready queue at its priority level. The Scheduler then gives control of the microprocessor to the highest priority thread.
Interrupts and exceptions are events which occur and are processed by the kernel. Interrupts are caused by external hardware devices and are considered asynchronous events. Exceptions are synchronous events that are caused by the execution of a particular instruction or the activation of an API routine, also referred to as a soft interrupt. In either case, the interrupt or exception is prioritized by mapping it to a particular Interrupt Priority Level (IPL).
The kernel implements a priority based interrupt structure that assigns a particular level to an interrupt. The level indicates the importance of the interrupt. There are thirty-one (31) priority levels (IPLs) available where the higher the value, the more important the interrupt. IPLs with values of 16-31 are reserved for time critical events such as interrupts from hardware or a real time clock (Ticker). IPLs with values of 1-15 are reserved for exceptions used by the Scheduler, forking threads (see below), and API routine activation. IPL zero (0) is reserved for User & System thread execution.
The Management abstraction includes the objects which implement the functions necessary to access, acquire, and manipulate resources supported by the RTOS. It contains the system objects namely, the ObjectRegistry, ConnectionManager, QueueManager, MemoryManager, ProcessManager, FileManager, and TimeManager. System objects use each others methods and the methods supported by PA objects (hardware components) to pass messages and service requests from clients and applications, which implement the responsibilities of the RTOS. The ObjectRegistry is a directory of the known objects in the RTOS. Before objects can communicate between one another, a virtual circuit must be established and its accessibility must be verified. The connect API routine, xxxx_open_circuit() , is called to establish the connection. It queries the ObjectRegistry for the existence of the destination object and whether the caller has the appropriate access codes. If it exists and the verification process was successful, the ConnectionManager is called to perform the logical association. If the process failed, the routine returns an error response for subsequent exception handling.
The ConnectionManager is responsible for establishing a virtual circuit between objects. The number of virtual circuits allowed for a particular object is dependent on the parameter settings when the RTOS booted. After the information about an object is returned to the connect API routine, it is passed to the ConnectionManager for final processing. The information is reviewed by the ConnectionManager and the necessary resources are allocated for the object communication. A handle is returned by the connect API routine to the calling object, and is a reference to the destination object, thus allowing it to be accessed in the standard calling mechanism (i.e. object.method()). Another responsibility of the ConnectionManager is to establish links with other ConnectionManagers located on remote CUB systems, allowing limited access to objects on the remote machine, implementing a Remote Procedure Call (RPC) mechanism. For example, to request a download of the buffered exception messages, or queries related to IOBoard object configurations.
The MemoryManager object is responsible for initializing and organizing the memory blocks available to the system. These include all types of memory objects, such as ones derived from the EPROM, SRAM, and NvRAM classes. For each type, the memory is divided into fixed length blocks. The clients, such as processes or other system objects, call the methods provided to enqueue and dequeue a particular size memory block from the selected queue and memory type. The primary customer of the MemoryManager is the QueueManager; however, other objects can access the memory blocks after establishing a virtual circuit with the object by calling the connect API routine.
The QueueManager object is responsible for managing memory blocks supported by the MemoryManager object. The client calls the allocate API routine, xxxx_alloc_block(), to acquire the appropriate size memory block. The QueueManager supports various fixed length blocks and can support variable length blocks if the request block does not fit reasonably into a fixed length block . The returned block represents the ``best fit'' given the size requirements passed with the message. The complimentary deallocate API routine, xxxx_dealloc_block(), inserts the memory block back into the appropriate queue. Processes and threads use the queue blocks to support message passing, such as interprocess communication using semaphores, mailboxes, and message queues. Other objects use the memory blocks to store application programming and event information. For example, the IOBoard object (see below) uses the memory blocks to store the programming information supported by the IO board, and the TimeManager (see below) uses the blocks to track and signal events.
The ProcessManager object is responsible for allocating and creating a process or thread within the RTOS or thread, respectively. The API routines, xxxx_create_process() and
xxxx_create_thread(), call the ProcessManager object to instantiate a process, allocate any necessary resources, and request that it be entered into the ObjectRegistry, thus allowing other objects to open virtual circuits to it.
Processes and threads are the primary objects which consume the resources provided by the RTOS. Booch defines processes either as heavyweight or as lightweight. A heavyweight process is managed independently by the RTOS and encompasses its own address space, also referred to as a process. Lightweight processes are referred to as threads and exist with either one or more other lightweight threads sharing the same address space as a heavyweight process . In terms of the CUB system RTOS, a process can contains one or more single threads of execution. For convenience, both terms are used interchangeably throughout this paper. Threads can exist in different flavors within the RTOS. They can be User, System, or Kernel threads. User threads refer to applications and programs written to perform a specific function, such as database servers or remote terminal logins that perform debugging or system administration duties. System threads are provided by the RTOS such as processor and thread performance monitoring, network support, and remote terminal access (server). Kernel threads execute as part of the kernel and are used to defer the processing of interrupts and exceptions, also referred to as forking threads. User and System threads are considered loosely coupled from the RTOS and are detached from the interworkings of the Executive, thus executing under their own context. Kernel threads, however, execute within the context of the Executive and are directly related to hardware interrupt and exception processing. Furthermore, because the requirement exists to provide various types of processes, the definition of a process is derived from a virtual class. The virtual class provides plug compatibility among the various process types, allowing the particulars to be implemented by the specific derived class methods and attributes.
Regardless of the type of thread, they are scheduled for execution by the kernel given its state and priority. Threads exist in 1 of 3 states at any given moment. They include the ACTIVE (ACT), READY (RDY), or WAIT (WAIT) states. Every state, except for ACTIVE, is implemented as a doubly linked queue for every priority (see discussion on priorities) and is referred to as the RSQ (Read State Queue) and WSQ (Waiting State Queue). The ACT state refers to the thread that has control of the microprocessor currently; whereas, RSQ represents threads which are ready to be scheduled for execution, and WSQ are threads waiting for events to complete.
The priority structure which exists for threads consists of 64 priorities where zero (0) is the lowest and sixty-three (63) is the highest. They are divided into segments where priorities (0-15) are reserved for user threads and are subject to a quantum; whereas, ones with priorities (16-63), such as system threads, are considered real time and are not subject to a quantum. Real time threads run to completion or are preempted by other ones with a higher priority. User threads run for a predetermined amount of time (quantum), then are placed in the RSQ. Threads which reside at the same normal priority are scheduled in a round robin manner. Threads with real time priorities can not exist at the same priority level. Since real time threads run to completion, there is no guarantee that the thread currently running will relinquish the microprocessor to another thread at the same priority level. Therefore, real time threads are allocated a unique priority level. Several user threads can exist at the same priority level. A user thread can experience a priority boost when waiting for an event to occur. The purpose of the priority boost is to ensure that threads waiting for events will be serviced before ones at lower priorities in the RSQ and avoid priority inversion. This implements the solution as discussed in a previous section. Furthermore, real time threads do not receive any priority boost and user threads are never boosted into a real time priority levels. Primarily, user threads are used for either local or remote interactive sessions; whereas, real time threads process interrupts from external or internal hardware resources and are reserved for CUB system RTOS responsibilities.
Threads that exist at the kernel level act as an intermediate step between the hardware Interrupt Service Routine (ISR) and the thread requesting the notification of an event. Kernel threads are considered device drivers. To illustrate, a hardware interrupt occurs from an external device and activates an ISR. Since the ISR should be as short as possible , its only responsibility is to queue a notification message to the device driver. This is also referred to as fork processing . More specifically, the ISR queues the packet and causes an exception to occur at a lower Interrupt Priority Level (IPL) (See Interrupt Structure section) than the hardware IPL. That activates the service routine to process the message. Since the IPL is greater than any user or real time threads, the forking thread gains control of the microprocessor and will continue to execute unless an interrupt occurs with a higher IPL. Forking threads are used by the kernel to defer the processing of hardware interrupts. Furthermore, since various IPL levels are supported, forking threads can preempt other forking threads with lower IPLs.
The FileManager object is responsible for opening files for clients to store applications and other information. The object overloads the standard file API routines to allow the appearance of an underlying file subsystem, however, the information and applications are stored in EPROM memory blocks that were requested from the MemoryManager object. It is possible, however, that multiple blocks are allocated for files, in which case a file descriptor is allocated to maintain a list of pointers to the blocks storing the information. Furthermore, the FileManager maintains a directory of all the files which have been stored. During operation of the RTOS, the directory is maintained in a SRAM block, serving as a cache function for quick lookups and as a RAM disk for accessing file information. A mirror image of the directory is located in an EPROM block. Subsequent modifications of the directory are made to both EPROM and SRAM locations. Since the information is stored in a non-volatile memory, the RTOS is not required to reconstruct the directory every time it boots. It copies the information from the EPROM to the SRAM block. When a client saves an application, the FileManager calls the appropriate API routine to store it in an EPROM block. The API routine requests a memory block from the MemoryManager and copies the program. The access information is stored in the directory so that clients can access the application at a later time.
The TimeManager object is responsible for signalling when particular time synchronous events take place. The object uses the TevCLK and Ticker objects to signal processes and threads when Tevatron events have occurred and when timers expire. A client uses the API routines,
xxxx_set_event() and xxxx_set_timer(), to establish event and timer queue entries, respectively, and to be signalled when a Tevatron event has occurred or a timer has expired. The TimeManager enters the request into its internal queue for processing. When the event occurs, the object sends a message to the object. The resources used to signal the event are then returned to the QueueManager, unless, they were tagged as recyclable. In this case, the event is reentered into the processing queue where the sequence repeats. The complimentary API routines,
xxxx_cancel_event() and xxxx_cancel_timer(), are called to remove the timer queue entries from the TimeManager internal queuing mechanism. An event to be cancelled can be identified by the return handles from the set API routines; otherwise, all events and timers are canceled for that particular object (process). Furthermore, the allocated resources for events and timers are automatically released when the object is destroyed.
The Services abstraction is a container object which includes all of the objects discussed above. It envelopes the objects with a set of API routines which access the appropriate object on behalf of the client and consists of methods by which applications implement process communication, synchronization, and request and release resources. Furthermore, it serves as an abstraction layer that logically separates applications from the system objects and kernel mechanisms.
API routines are available which control access to shared resources and process synchronization through the use of binary and counting semaphores. Typically, binary semaphores are used when a single instance of a resource is available such as a serial IO port; whereas, counting semaphores are used when multiple instances of a resource are available, such as a buffer pool or many network ports. Resources do not have to necessarily exist as physical entities. Logical resources can exist such as network Service Access Points (SAPs), virtual circuits, or devices representing many entry points for a single IO port (hardware). Threads request notification when events occur on these resources through the use of semaphores. If the event hasn't occurred, the process will be placed in a wait state (if so specified) or continue execution. When the event arrives, the thread can be notified (signalled) by the scheduler placing the thread in the RSQ or executing an IO completion routine on behalf of the thread. The latter case allows a process to be notified of multiple events. Message queues are another resource that provides process synchronization and communication. When a process needs to communicate to another process, a queuing mechanism is used to pass the data. A virtual circuit is established between the two processes by the ConnectionManager. The sending process passes the message to the destination process using the standard calling mechanism object.method(data). The message queue is implemented as part of the destination process and is transparent to the sender, thus enforcing the abstraction and encapsulation attributes of the Object-Model.
The services are divided into Core and User services. Core services are called by System and Kernel threads; whereas, User services are called by User threads. Some User services may require calling Core services which entails elevating the thread mode to kernel. An exception mechanism, typically supported by the Microprocessor and InterruptController objects, are used to perform the change mode operation. This allows User threads to access (read only) Executive objects and other components which are hidden by the abstraction layers. Some System and Kernel threads can call System services without incurring the overhead associated with the activation. This requires the thread to be executing in kernel mode and occurs at ISR activation within forking threads. Kernel and user system services are available to applications. Both services are activated through an exception. The exception causes a software interrupt and the operating system to change from user to kernel mode. This allows other privileged services and instructions to be executed, and the operating system to catch failure traps, exceptions and verify whether the application followed prescribed activation policies and protocols. After the exception occurs, the software interrupt causes the operating system to lookup the service routine in an interrupt vector table. The placement of the vector determines the IPL. Typically, system services reside at IPL 1. Consequently, other exceptions which are assigned higher IPLs, such as scheduling processes (IPL 7) and hardware interrupts (IPL 15-31), preempt API routine activation.
The RTOS automatically creates a collection of process objects which implement the additional responsibilities of the CUB system. Ones which are visible to user objects. Most of these objects connect to PA objects to support their functionality and others exist to implement the responsibilities and services supported by the CUB system. These represent system threads which include EADnet, CAMAC, SerialCOM, ErrorLogger, DataLogger, SystemMonitor, and the IOBoard. The EADNet process is responsible for servicing unsolicited messages from the ARCnet object, creating detached processes, establishing virtual circuits with remote clients, and dispatching messages through an existing circuit or creating a new one. To illustrate, an interrupt is generated when a message is received in the ARCnet object. A message is sent to the EADnet process for servicing. The process determines whether the message is destined for an existing process, or a request for a remote login. In the former case, the message is simply passed to the appropriate process. In the later case, EADnet creates a detached process and establishes a virtual circuit between the remote client and the newly created process. The process then sends a login message to the client querying for a user name and password. The client sends a response message to the query and EADnet verifies the information. If the login is successful, EADnet acts as a message router between the two objects. If the login verification failed or a timeout occurred because of no response, the client is sent another message indicating the circumstance, then the link is automatically terminated.
The CAMAC process is responsible for servicing requests received by the CAMAC PA object. Typically, requests received by the CAMAC object are from the Epicure control system and intended for reading IO board data which has been buffered, therefore, no virtual circuit establishment and activity is necessary. The request is serviced from within the process and returned to Epicure. However, CAMAC may need to establish connections to objects with the CUB system to retrieve the information.
The SerialCOM process is responsible for serving the CUB system serial port. The processing is simular to the EADnet process to create a remote login, however, not as sophisticated since the serial port is not typically setup for a network. Therefore, a detached process is created for a single client. The same sequence of operations is performed as with EADnet in terms of login and verification. After login verification, the SerialCOM process serves as a router for the remote login.
The ErrorLogger is responsible for buffering and interpreting exceptions which have occurred at all levels within the RTOS. When an object encounters an event that it can not interpret, or when an error status is returned by an API routine, an exception message is sent to the ErrorLogger where it is buffered. The information can be readout through CAMAC, SerialCOM, or the EADnet processes. When the ErrorLoggers internal queuing mechanism determines that the resources are exhausting, the exceptions are automatically packetized and sent to the EADnet process destined for the VMS/ARCnet gateway process located on an Epicure front end. The exceptions are routed to the Epicure ALARM_SERVER process where they are communicated to operators, experimenters, and system programmers.
The DataLogger process is responsible for buffering the messages received by the IOBoard object. The information contains particular data about the status of the devices located on the IOboard. Simular to the ErrorLogger, the information can be readout through the CAMAC, SerialCOM, or EADnet processes.
The SystemMonitor process is responsible for collecting data from system and client processes, and buffering the information until later retrieved by user and system clients. The information is used by clients and system objects for determining the availability of system resources and statistic reports, and capacity planning, respectively.
The IOBoard object is responsible for processing the specific requirements of the optional IO board. When the process initializes, it scans for the existence of an IO board. If one exists, its identification is read and the process downloads the information containing its device drivers and other objects required to process its information. This might include creating other detached processes. The objects it creates are entered into the ObjectRegistry for access from other objects within the CUB system, and makes them eligible for scheduling and communication with objects existing either locally or remotely.
The last discussion relating to the CUB system RTOS pertains to its initialization. The initialization procedure executes as a consequence from either depressing the reset push button, located on the modules front panel, or from a power up condition. The former denotes a warm startup or restart condition, where some of the startup procedures are not executed, such as memory system verification and hardware tests. The later condition causes all startup procedures to execute and denotes a cold startup. The following discussion focuses on the execution of the boot procedures of the RTOS and identifies the order in which objects are loaded.
A cold startup of the RTOS entails the initial branching to the boot block located at the microprocessors reset/power up address. The boot block procedure is responsible for loading the data areas and tables, which are particular to the microprocessor, which indicate the location of the hardware components and where the executable code resides. The boot procedure includes basic testing and verification of the communication interfaces and memory systems, respectively. If however, a warm startup is in progress, as indicated by a flag residing in the NvRAM, the testing and verification phases are not performed. The initialization continues by loading the objects for each of the abstraction layers beginning with the most primitive, particularly the physical, to the highest layer, which include the system objects and API routines. The boot procedure calls the executive loader to begin downloading the RTOS from the EPROM to the SRAM memories. The physical objects are loaded initially, thus instantiating the objects and calling their constructors for local initialization. The kernel is then downloaded into SRAM and executive loader exists. Before existing however, the loader branches to the entry point of the kernel. At this point, the basic objects of the RTOS are loaded and are available to client objects. As part of the initialization of the kernel, a process is created that loads the management abstraction. It creates processes, representing the system objects of the system. Their constructors are called which establish connections with other objects implementing their defined responsibilities. The API routines are loaded into SRAM next, and a connection to the remote CUBServer process is established to initialize the network database . Clients, such as remote logins and others requiring the services of the RTOS, are now able to access the system objects and perform their duties.
This concludes the discussion on the analysis and design of the hardware and software abstractions, classes, and objects. The next section summarizes the details that were presented in the individual sections of this paper.
As referred to earlier, the predominant methodologies for the analysis, design, and implementation of Real-Time operating systems are algorithmic decomposition or structured design techniques. This paper focused on a specific Object-Oriented Analysis and Design technique and its application to the CUB system and its Real-Time Operating System (RTOS). A survey was conducted of the various Object-Oriented methodologies which resulted in the selection of the one suggested by Booch . This methodology is appropriate for RTOS because of its flexible notation, reputation, and availability of software tools. Below summarizes the highlights from each of the discussions presented in this paper.
Keywords: EPICURE, CUBS