Highly DEcoupled Operating System - HiDEOS

IMPORTANT NOTE: This documentation is complete but not completely proof read.

Contents

Introduction - What is HiDEOS

HiDEOS is a software package designed to be used for development of communicating tasks in an embedded systems application environment. The initial target for this development is the Motorola MVME162 board. The reason for this choice is because of the Industry Pack Bus available on the MVME162. The package contains a preemptive, multitasking kernel and an object oriented task model with message passing support. The board support is also object oriented with classes to easily manage the Industry Pack bus. HiDEOS comes with several drivers for serial, adc, and gpib Industry Packs. A VME backplane driver is included for communications with other processors along with an interface library for other software packages to communicate with HiDEOS. An important attribute of this package is the ability to run the MVME162 without an additional operating system, and communicate with other boards in a VME crate.

The goal of HiDEOS is to make it simple to create communicating processes which run on a stand alone processor. The processes can be strictly algorithmic, such as running a high level communications protocol, or device drivers, where they communicate with a specific piece or hardware. In order to accomplish this goal, all HiDEOS tasks are created by deriving from a class which knows all about interprocess communications and operating system resources. The primary interprocess communication method is message passing; HiDEOS contains tools for defining messages to the system and base class methods for sending them from process to process. The HiDEOS build tree contains an easy to understand makefile structure for making HiDEOS executables. The tree imposes structure on the creation of messages and tasks, making it simple for the user to incorporate new programs into the system.

Conceptual Overview

In order to create processes effectively in HiDEOS, it is important to understand the structure and methodology of it's components. The purpose of this section is to give a description of the major components of HiDEOS. The major components of HiDEOS are name service, message management, task management, dispatching, and resource management.

HiDEOS is a multitasking operating system, but differs radically from other systems such as Unix. Processes operate within their own context similar to Unix, except they all share the same address space. HiDEOS uses an object oriented approach to process creation. Special classes exist in the system for context/task control. By deriving from one of the special classes, the user is automatically implying that upon construction of an instance of the derived class, a new process will be created. To reiterate, any user class derived from the special task classes will create a process when an instance of the user class is constructed.

Processes within HiDEOS are identified by name. The name is an arbitrary character string assigned by the user at the time of construction. A name registry object exists in the system to maintain the list of available processes in the system. Since all processes created in the system are actually objects, a simple handle (or pointer) to that object can be used to access the public methods of the object and control the major facilities of the process. Only one instance of the name registry is present in a running HiDEOS. The name registry is just a mapping between character string name and process object pointer. The registry provides methods for querying names. The name registry is a globally accessible object, every process in the system has access to it. The main purpose of this facility is to allow other processes, either local or on another cpu, to locate available services by name and communicate with them.

The main interprocess communication facility for HiDEOS is message passing. A HiDEOS message is a user defined class derived from the message base class. All message types in HiDEOS must be derived from this message base class. Each message type in HiDEOS is assigned a unique integer ID for easy identification. The unique ID is automatically assigned by a utility at HiDEOS build time. The ID must be used to identify messages when they are received by user processes. A special message buffer management class exists for allocating and freeing all message buffers. The user requests message buffers from the manager by message type ID. The message buffer is freed using by passing the pointer to the previously allocated message to the message buffer manager. The message buffer manager maintains free lists of all message types in the system to prevent memory fragmentation and facilitate user requests for buffers.

In a similar fashion to task creation, user classes inherit the ability to send and receive messages by deriving off a message passing task class. The message passing task object incorporates an event driven model. All message passing task objects contain an inbound message queue and methods of the user's derived object are automatically invoked in the user's context by the presence of messages in the queue. When a message is received and the process awakes, the user must decode the message type and perform the actions associated with that message. The rule of message passing in HiDEOS is that the receiver of the message owns the buffer and is responsible for freeing the buffer.

Task dispatching in HiDEOS is currently strictly round-robin with all processes running at the same priority. The default scheduling clock frequency is 60Hz. The time slice for processes is 166ms. They values are simple to adjust to suite various applications. Any process running in the system can choose to disable the dispatches, this feature allow the user to take over the entire CPU and be assured that the running process will not be preempted. The dispatcher provides for watchdogs. Users can request watchdog services from the dispatcher.

All board support is accomplished in HiDEOS using classes. All the chips available on the board have a class associated with them for control. The class allows the user to access and control chips with methods such as TurnOnInterrupts() and GetRamSize() instead of using macros, bit masks, and unfriendly structures. A resource manager exists for locating the chip classes. Services which are available across many chips are controlled through special classes. An example of a services such as this are tick timers. Tick timers are available from the VMECHIP2 and the MCCHIP of the MVME162. At initialization time, the VMECHIP2 constructor and MCCHIP constructor register their tick timers with the tick timer control class. User can ask the tick timer control class for a general tick timer, not caring or knowing that chip is came. The general tick timer contains methods common to all tick timers such as SetInterruptHandler() and ClearCounter().

A running HiDEOS system contains a set of classes managing all aspects of operating system resources. There is one instance of a dispatcher class, one name registry, one message buffer manager or message pool, and one instance of a chip class for each of the supported chips such as the IPIC, VMECHIP2, and MCCHIP of the MVME162 board. There will be an instance of the task class for each of the running processes in the system.

Programming Model

Developing programs under HiDEOS basically requires three steps: identify the data structure interface for the service that is to be provided, identify the sources of data for the service, and coding the solution to the problem. The purpose of this of this section is to understand the coding and principles of HiDEOS processes and messages. The next section will show where to put files and run various HiDEOS utilities in the development tree.

Data Structure Interface

The main purpose of HiDEOS processes will usually be to communicate control information to users. HiDEOS processes communicate by sending and receiving messages which are user defined classes derived from the Message base class. The messages exist in the system as standard header files. An example message to communicate two longs could look like this:
#include "msg/message.h"

class LongMsg : public Message
{
public:
	long x;
	long y;
};
All messages header files are found by a set of HiDEOS utilities (explained in the next section) at build time. The utilities generate several files to help HiDEOS work with messages efficiently. All messages are assigned an integer tag for identification, all messages are sent and received using this tag. The utilities generate a simple enumerator for each of the tags (messages) found. The enumerator is always the name of the class follows by the word "Type". In the above example, the tag is some unknown integer and the message is identified by the enumerator LongMsgType. If the user process was to receive a LongMsg, the only way to know this is to check to see if the type is LongMsgType.

HiDEOS manages messages, they are not managed by the malloc/free mechanism. The integer tag is used to manage message free lists for each message type. The user must request and release message buffers using the HiDEOS message pool management system. The message pool system prevents memory fragmentation and facilitates quick message allocation and release.

There are several considerations which must be made before a new message is introduced to the system. Understanding the device for which the message is to be defined is very important. If there is already a device similar to the one being added such as an ADC, an interface to ADCs may already be established and no new messages are needed. Adding a second message interface for the new ADC could cause headaches for users, instead of all ADCs looking similar, the new one looks different from the user standpoint. The first rule is to to check the existing messages and see it one of them matches the needed interface. The second important consideration is methods within messages. The message should be very much like data structures. Try to keep methods to simple inline routines. Prevent actual functions to be generated to run the methods of the message class. The reason for this is simple, messages cross CPU boundaries, the implementations of facilities such as malloc/free and even bit order may be different. If the message contains a complex method to manipulate a chuck of data in the message, the originating machine's code for the method may be quite different then the destination machine's code. An example of this is a method when vxWorks is used as one CPU and the MVME162 CPU is used as a second CPU in stand alone mode. If a method exists which allocates a buffer and another which frees it, the vxWorks CPU could allocate it off it's private heap, and the MVME162 CPU could free it into it's own heap, this will probably could the system to crash at some point.

To summarize, messages are user defined structures derived from the message class. Message are searched for in header files during the build process and assigned tags and enumerator for the HiDEOS message pool system. The user processes use the enumerator to identify message and the allocate and free message from the message pool. Keep messages simple and short, and see if a message exists in the system before generating a new one.

The Process

HiDEOS processes are defined by deriving a class from the Task base class. The derived class (the user process class) will inherit the ability to work with messages, find other HiDEOS processes running in the system, and use watchdogs to name a few. The user gains control of the process by defining the Receive(Message*) method. The Receive(Message*) method is a virtual function, when the base class invokes it, the user defined method will be called instead of the one in the base class, thus giving the user control. The task class has an inbound message queue, when a message appears in the input queue, the process is automatically scheduled and the Receive(Message*) method is invoked. If the user has defined this method, the user will get control and have the ability to decode the message, process it, and response. It is important to note that all messages sent to HiDEOS processes are queued. The task class pulls messages one at a time off the queue and calls the Receive(Message*) method with the message it pulled off the queue. The task class also handles registering a name or the process with the name registry.

Normally a subclass of the Task class will minimally define a constructor and the Receive(Message*) method discussed above along with private data that defines the users state. The subclass will utilize methods of the base to manipulate operating system resources.

Defining a Constructor

The purpose of the constructor is to initialize the user's subclass data. If the process controls a device, the device will probably be initialized here. The subclass has a choice of constructors to create. The task base class defines constructors for making a task object with a name and without a name. Normally a subclass will choose to override the constructor with a name. This constructor requires the user to name the process, and the base class will automatically registry the name in the system so it can be used by other processes. The constructor with no name is meant to be used in advanced applications and will be discussed later in this document.

Defining Receive(Message*)

The purpose of the Receive(Message*) method, as discussed above, is to process incoming messages. Most Receive(Message*) methods will look similar, they will check the message type and process it if it is valid (understood by this process), pass it to the base class Receive(Message*) method if not valid for this process. Processing usually involves pulling data out of the message, acting on the data, and responding to the source of the message with some results.

Using System Resources

Most of the HiDEOS system resources can be manipulated with methods from the Task task base class, consult the reference manual for details of the available services. Some of the facilities which will commonly be used are Bind(), Send(), GetMessageBuffer(), FreeMessageBuffer(), and WatchDog(). GetMessageBuffer() and FreeMessageBuffer() provide a simple way to get and free message buffers using the message type enumerator. The Bind() will locate other processes in the system which have been registered. Send() will send a message to another process in the system. The purpose of WatchDog() is to create a watchdog.

Example Tasks

An simple example of a HiDEOS process which uses the above defined LongMsg might look like the following:
#include "task.h"
#include "long_msg.h"

class AdderTask : public Task
{
public:
	AdderTask(const char* process_name);
	void Receive(Message* msg);
private:
	int counter;
};

AdderTask::AdderTask(const char* process_name) { counter=0; }

void AdderTask::Receive(Message* msg)
{
	switch(msg->Type())
	{
	case LongMsgType:
		LongMsg* lm = (LongMsg*)msg; // cast to known type
		lm->x=counter++; // set the return value
		Send(lm->from,msg); // reply to source
		break;
	default: Task::Receive(msg); break;
	}
}
This process will block until messages appear at the input queue. The message will be processed by the user process if the type is LongMsgType. The only actions performed here are to assign the counter value to the x variable of the message, increment the counter, and response to the sender of the message.

This second task will connect to the above AdderTask and perform a transformation on the value returned from the AdderTask as its function. This task assumes that an instance of AdderTask is running in the HiDEOS system and has been registered with the name "adder_task".

#include "task.h"
#include "long_msg.h"

class TransformTask : public Task
{
public:
	TransformTask(const char* process_name);
	void Receive(Message* msg);
private:
	Task* connected_task;
};

TransformTask::TransformTask(const char* process_name)
{
	if(Bind(connected_task,"adder_task")<0)
	{
		printf("adder_task not found!\n");
		connected_task=NULL;
	}
}

void TransformTask::Receive(Message* msg)
{
	switch(msg->Type())
	{
	case LongMsgType:
		LongMsg* lm = (LongMsg*)msg; // cast to known type

		// get a message buffer to send to the connected adder task
		LongMsg* to_adder_msg = (LongMsg*)GetMessageBuffer(LongMsgType);

		// send the message to adder task
		if(connected_task) Send(connected_task,to_adder_msg);

		// block until message from adder task arrives
		to_adder_msg=WaitForMessage(connected_task,LongMsgType);

		lm->x=to_adder_msg->x * 5; // perform a transformation

		// free the message buffer received from the adder task
		FreeMessageBuffer(to_adder_msg);

		Send(lm->from,msg); // reply to source
		break;
	default: Task::Receive(msg); break;
	}
}
This example illustrates many of the important features of the task base class. Bind() is used to get a handle to a specific adder task. The TransformTask sends a message to the adder task each time it is asked to process a message. Remember that it is the responsibility of the receiver of a message to free the buffer if it is not reused, which is the case with the to_adder_msg receiver from the adder task.

Task Groups

A HiDEOS task group is an array of tasks (processes) with the same name. Each process in HiDEOS has a name and an instance number associated with it. It is possible in HiDEOS to generate an array of processes and register them under one name. The individual processes of the group are identified by their instance numbers (the instance numbers are assigned automatically by the registry system when the name is registered). There exists binding methods in the task class for binding to the entire task group, or binding to a specific instance of the task group. In the latter case, a name and instance number must be supplied on the bind call.

Certain types of devices fit the task group model very well. An Industry Pack such an the Octal Serial contains eight serial ports, each which required a unique thread of control (a process). A special Octal Serial initialization function creates eight OctalSerialTasks (a subclass of task) in the form of an array of pointers and registers the array with the name "OctalSerial". Now users can reference the ports using notation such as "OctalSerial[0]" for port 0, and "OctalSerial[4]" for port 4. When using task groups, the task class constructor with no name will probably be used to delay the names from being registered.

The task group is considered an advanced feature and will be explained in an Advanced Features Document.

Configuring HiDEOS

Up to this point, the discussion has been primarily about defining task classes and message classes. A third element is required, this element being the creation of task class instances or the actual creation of the processes. This section describes how to create and configure HiDEOS processes. The operating system contains a system initialization manager. The initialization manager uses globally constructed objects called Entities to locate and run user initialization functions. Users register initialization function with the operating system by globally constructing objects called Entities At compile time, the user has an opportunity to register a function to be called when the operating system is initializing. An explanation of the classes involved in system initialization will provide a good means of understanding it.

Registering a Initialization Function

Two classes exist to manager user initialization functions, the Entity class and the Entitymanager. The EntityManager has a static data member which is an Entity linked list head pointer (initialized to NULL). When the EntityManager instance is created, it runs through the linked list of entities, running each user function which is registered. Each instance of the Entity class adds itself to the EntityManager's linked list when it is constructed, this is possible because the linked list is a static data member of class EntityManager. Obviously the Entity class instances must be constructed before the EntityManager instance for this to work correctly. This is simple if the user creates the Entity class instance globally (outside the scope of a function). Globally created class instances are created before main() is called, so the globally created Entity instances will exist before the operating system initializes with main().

The Initialization Function

The initialization function gives the user an opportunity to start processes by creating instances of task classes, configure the instances, and send initial messages to processes. This function is called before the dispatcher starts, so configuration can take place without interfering with processing. Messages will not start being processing until the dispatcher starts, so any messages sent here will queued up and processed later by the tasks. The easiest way to understand this function is by example. This function will initialize the system to run the sample HiDEOS tasks described above.
// globally constructed entity instance for my initialization function
static Entity my_test("user_init",MyInitialization,NULL);

static void MyInitialization()
{
	AdderTask* adder = new AdderTask("adder_task");
	TransformTask* trans = new TransformTask("trans_task");
}
The task constructor with name is used here to automatically register the task name with the system task registry. Users can bind to the "trans_task" or "adder_task" for communications. In all the examples provided in the distribution, there is one Entity or initialization function per HiDEOS executable.

Development Environment

HiDEOS contains a development structure for creating the messages classes, task classes, and initialization functions. It uses a single makefile system to easily add new pieces to the system and contains the HiDEOS kernel and board support. The tree can be viewed as having five major components: the system area, the build configuration area, the host tools area, the user applications area, and the installation area.

hideos/
config/
The main makefile include files which influence the entire build process in every directory exist here. The file HIDEOS_VARS contains all the build variables, this file should be checked to insure that flags and locations of tools are correct for a site. The HIDEOS_VARS_bare contains additions variable declarations specific to building the stand alone version of HiDEOS, the HIDEOS_VARS_vx contains variables specific to building HiDEOS to run using the vxWorks kernel. To run HiDEOS using EPICS, both the vxWorks version and the stand alone version must be build, see the quick installation guide for details.

hideos_sys/
This is the directory where the kernel, malloc, and the board support exist and are built. All the base class header files come from here and are installed in the install directory install/include. Three important libraries are produced here, libhideos-sys.a (the kernel) and libhideos-arch.a (the architecture depend code). The crt0.o (main boot code) and GNU loader script are also produced here. The libraries are also installed in the install directory install/lib. The casual user will probably never need to enter this directory.

tools/
These are tools that run on the host processor (the processor where builds take place). The tools are installed into install/bin. Currently none of the tools are seem directly by the user, they are all run by the build system. A special install program is built here, along with a several tools to parse header files to find messages and generate files for the message pool and message enumerators.

apps/
All user development takes place here. Users define messages, tasks, and initialization functions here. This is an important structure and is defined more thoroughly in the next section. User applications produced under this directory are installed in the install/usrapps directory.

doc/
Source for this and the rest of the documents. Use Netscape or Mosaic and open file:intro.html to view them.

install/
Everything explained above is installed in this directory for easy access.
The build generally occurs in three phases, each phase depending on the latter: build the tools directory and install it, build the hideos_sys directory and install it, and build the apps directory and install it. Each directory (apps, hideos_sys, tools) are considered independent of each other and references to files produced by each is done through the install directory.

The apps Directory

apps/ip_servers/
msg/
User messages are defined here. The makefile installs the header files defined here in install/include/msg. Create messages in the msg directory. There are plenty of examples. The makefile must be edited to add the new message type so that the header file will be installed during the build.

task/
Task subclasses are defined here along with code that implements them. The makefile generates a library called libhideos-usr.a which contains all the code built in this directory. Tasks are created in the task directory. Typically two files are generated, a header file describing the class and a ".cc" file containing the implementation code. The makefile must be editted and the new files added to the appropriate make variables OBJS and SRCS.

cfg/
The user initialization function exist here. Each of the c++ files in this directory will produce one fully resolved HiDEOS executable. Each c++ file contains one user initialization function. The HiDEOS executables are installed into install/usrapps.

gen/
This directory is used by the system to generate files for the messaging system. The makefile searches the msg directory for new message types and generates the message enumerators. The user never needs to enter this directory.

Building Executables in cfg

Each configuration of HiDEOS is built into a fully resolved executable. Only one of these executables can be loaded onto a CPU board at a time. The initialization function files define what services will be available on the CPU board under HiDEOS. An example in the love.cc file. This file contains an initialization function that starts the backplane interface, the Industry Pack manager, and a love protocol task. In order to use the love protocol, a user must download the love executable or another executable that contains the code which initializes the love task. The makefile in the cfg directory contains one rule for each of the executables built. To restate, each executable file produced in this directory provides for specific functionality on the board it is downloaded to. Each executable starts the tasks specified in the registered user initialization functions when run.

Programming using Industry Packs

A special class is provided for manipulating the industry pack controller and automatically starting drivers for supported industry packs. The facility requires the industry pack drivers to be registered in a database so that when an industry pack is found, the make and model can be read and the database queried for the proper driver to start.

Industry Pack Background Information

The industry pack bus is basically a 16 bit bus. Each industry pack module contains three address spaces: the I/O space, the ID space, and the optional memory space. The I/O and ID space are both fixes in size to 64 bytes, the memory space is a window that can be programmed to appear anywhere in the processor's address space. The memory space window size can be allocated in 64KB chunks. Both the I/O space and the ID appear in CPU address space at a fixed location.

The I/O space is generally registers that control the device. They are always device specific; the device manufacture is free to allocate the space as they wish. For many industry packs, all the registers needed to operate the device are located here.

The ID space is a PROM. The first 22 bytes are defined by the industry pack specification, the remaining 42 bytes are deviced by the industry pack module designer. The fixed portion of the PROM contains information such as the make and model of the industry pack, and revision numbers. The device specific portion usually contains information such as calibration values or error values. The ADC is an example of where the error values are stored in the ID space immediately following the fixed portion.

Each industry pack slot can generate interrupts on two interrupt request lines: IRQ0 and IRQ1. Vectored interrupts are fully supported. Normally the interrupt vector is programmed into the industry pack I/O or memory space if vectored interrupts are supported.

The Industry Pack Controller

A special class is available for manipulating the industry pack controller for an industry pack. Each industry pack module is installed in one slot (a, b, c, or d) and each module is essentially independent of the rest. The industry pack controller has a set of registers for each of the slots. A class called IpModule. exists for manipulating an industry pack slot. When the system is initialized, four instances of the IpModule class are created, one for each slot. The description of all the facilities available in this class is in the reference manual. Some important methods in the IpModule class manipulate the interrupts and get pointers to the memory space of the industry pack module.

Writing a Driver

All industry pack drivers should be written to be started automatically by the industry pack manager. An industry pack task is the same an any HiDEOS task except that the task must accept the IpModule instance (or industry pack slot controller) on the constructor. An additional requirement is needed if the industry pack has multiple ports or channels managed by several tasks, this requirement being a static member function designed to perform global initialization of the multi-channel board and start all the tasks that will manage each of the channels.

A very simple industry pack driver task for a ficticous ADC will be shown below as an example. The ADC has two independent channels implemented with three 16 bit registers defined in the I/O space. The first register is the current value at the ADC channel 1, the second register is the current value at ADC channel 2. The third register is a global reset, which resets the industry pack.

class FakeAdcIP : public Task
{
public:
	FakeAdcIP(const char* name, IpModule* slot, int channel);
	void Receive(Message*);
	static Task* InitFunc(const char* name, IpModule* slot);
private:
	unsigned short* value_reg;
	IpModule* ip;
};

FakeAdcIP:FakeAdcIP(const char* name,IpModule* slot,int channel) : Task(name)
{
	unsigned short* regs = slot->GetIoSpace();

	printf("Fake ADC %s found in slot %c\n",slot->GetSlot());
	ip=slot;
	value_reg=&(regs[channel]);
}

void FakeAdcIP::Receive(Message* msg)
{
	switch(msg->Type())
	{
	case LongMsgType: // only interpret long messages
		LongMsg* lm = (LongMsg*)msg;
		lm->value=*value_reg;
		Send(msg->from,msg);
		break;
	default: Task::Receive(msg); break;
	}
}

Task* FakeAdcIP::InitFunc(const char* name, IpModule* slot)
{
	unsigned short* regs = slot->GetIoSpace();
	char* name0 = new char[strlen(name)+5];
	char* name1 = new char[strlen(name)+5];

	// The name parameter is the manager assign name.
	// Build unique names for each channel, an alternate is to use
	// task groups.  See the serial drivers for examples of using task groups
	strcpy(name0,name); strcat(name0,"-one");
	strcpy(name1,name); strcat(name1,"-two");

	regs[0]=1; // reset the industry pack

	Task* t0 = new FakeAdcIP(name0,slot,0); // task for channel 0
	Task* t1 = new FakeAdcIP(name1,slot,1); // task for channel 1

	return t0;
}
As can be seen, the InitFunc has the job of creating a task for each channel of the industry pack. This example illustrates that the argument list to the task constructor is designed to fit the application. In this case, the constructor need the name, the IP controller, and the channel number. As mentioned in the comments, a better way to manage the registration of the tasks is to use the task groups. By doing this, only one name, name will be registed with two instances or tasks. One advantage of using the task group is that uses can use the notion such as "a-fakeADC[0]" and "a-fakeADC[1]" instead of "a-fakeADC-one" and "a-fakeADC-two".

Registering a Driver

The above driver task must be registered with the industry pack management class. To do this, the files task/ip_modules.h and task/ip_modules.cc must be editted.

The ip_modules.h file contains defines for the make/model and name of the industry pack being added to the system. The make/model codes can be found in the book received with the industry pack. The name is assigned by the user. When this industry pack is found in a slot, the name will be appended to the name assigned by the manager. The manager naming convention is to prepend the slot identifier to the user assigned name. If the name of the fake device above was "FakeADC" and a fake ADC was found in slot 'd', then the manager assigned name would be "d-FakeADC".

The ip_modules.cc file contains the database of all the recognized industry pack (ones that have drivers) and simple functions for creating the drivers. Adding a driver requires a new entry in the database and a new function be added to this file.

Defining the above fake ADC example to the system in ip_modules.h (the assumption is that Green Springs made the industry pack):

#define GSIP_FAKE_ADC	0x45		// this is the model definition
#define GSIP_FAKE_ADC_N	"FakeADC"	// the user assigned name

Defining the above fake ADC example to the system in ip_modules.cc:

static Task* IpFakeADCMake(char* name, IpModule* ip)
	{ return new FakeAdcIP::InitFunc(name,ip); }

IP_TASK_MAP IpTaskMap[] = {
	{ ... }
	{ ... }
	{ GREEN_STRING_ID,GSIP_FAKE_ADC,GSIP_FAKE_ADC_N,IpFakeADCMake,0 }
	{ NULL }
};
See ip_task_map for the definition of the IpTaskMap. Here the new entry describing the make and model of the new industry pack is added to the data structure. The manager will call IpFakeADCMake when the make/model is found in an industry pack slot and append GSIP_FAKE_ADC_N to the name it assigns. The function IpFakeADCMake knows how to call the InitFunc() defined as a static member function of the example driver.

Available Drivers

This section is a brief discussion of the backplane message routing task. All messages coming into a HiDEOS system will be routed through the backplane task. All messages sent back as responses to incoming message will be router through the backplane task. In the standard configuration of one master CPU (typically a MVME167) running vxWorks and one MVME162 running only HiDEOS, the backplane driver will run on both CPUs to relay message back and forth to tasks. The backplane task is simple, it manipulates the message header so that tasks believe the message came from another task on the same CPU.

The backplane task uses mailbox interrupts to inform other CPUs of pending messages. The task maintains a pool of fixed length buffers which it copies message data to and from. The special buffer pool uses semaphores which are backplane safe. The current implementation does not fragment messages, so a transfer will fail if the message does not fit into one of these fixed size buffers. Developers are encouraged to enhance this interface.

External System Interface

All external processes communicate with HiDEOS using the BPD interface. The BPD is a facility within HiDEOS for passing messages to local or remote HiDEOS tasks. Local tasks being on the same CPU, remote tasks being on a different CPU (on the VME backplane).

The BPD is a class that can send and receive HiDEOS messages. The BPD can also get task descriptors, which are used in a similar fashion to Unix file descriptors. Each task descriptor is bound to a particular HiDEOS task and is used in calls to send messages for identifying the destination of the message.

The BPD can be used is a synchronous mode or an asynchronous mode. The synchronous mode is the default mode, the assumption here is that the task will block when the BPD is asked to wait for a message to appear at the BPD. The asynchronous mode utilizes a user registered message receive function. In this mode, the user function gets invoked each time a message is destine for the BPD, it is up to the user function to process the message (queue it?) and inform the task owning the BPD that a message has arrived.


Argonne National Laboratory Copyright Information
Jim Kowalkowski (jbk@aps.anl.gov)
updated 4/12/95