Chex Details

This document covers details of the chex (cheops execute) utility used for executing Cheops programs. It provides an overview of how chex downloads a program and then communicates with it using Magic Seven's interprocess communications facilities to provide myriad services on the host platform. All services that chex provides to the client program are then reviewed including: standard UNIX system calls and file I/O, blocked and non-blocked massive data transfers, communications and control, redirection of standard I/O, conventional datfile library support, and Cheops specific extensions to the datfile library.

For a basic understanding of how to use chex, see Chex Usage.

How Chex Works

Chex uses the basic client-server model (see Magic7 IPC System) to handle all the communication between the application program and itself. Chex plays the part of the host server, handling requests given to it by the local client application program being executed on Cheops. When chex is called from the host machine to execute a Cheops program, it creates a service port that allows message passing between it and its client, allocates space in Cheops local memory for the programs text, data and stack, fixes the program's relative addresses to the given, loads the program's sections into the appropriate addresses, and informs the Magic7 (M7) operating system to start a new process with given path name, command line parameters, and address space.

Having started execution of the local client program, chex then waits in a tight infinite loop, expecting to service messages that come from it.When the local client makes a system call, the call is trapped by the M7 operating system. M7 prepares a request message which it sends to the service port used to communicate to the chex server. Chex notices the request, takes the message off the message queue, interprets the request and services requested UNIX system call. Upon completion, the system call's return value is put into into a response message and is sent back to the client via the service port. M7 immediately recognizes the response, takes it off the message queue, parses off the return value, and returns it to the calling application.

UNIX File System Calls

Cheops does not have a file system, but a Cheops client program can request that chex do file system calls in its behalf, and then return the results. The following standard UNIX file system calls are currently supported by Magic7 and chex if your local program links the Cheops library (denoted as -L$CHEOPS_LOCAL_LIB -lcheops in your Makefile).

Since the functions are standard, you need not include any special files to get their prototypes -- just the standard UNIX include files like stdlib.h (found by the compiler at -I${G960INC} ) to get prototypes for these functions:
open(), close(), read(), write(), create(), lseek(), unlink(), access(), system(), umask(), exit()

Massive Data Transfers

System call handling normally requires very little message passing overhead since parameters and return values are only a few bytes. System calls that require massive data transfers, such as read() and write(), cannot be handled by sending messages to the service port, but rather are handled as a separate high speed host/Cheops data transfer. When the transfer has completed, the return value of the system call is then returned in the normal message passed manner.

Semaphored Massive Data Transfers

All standard system calls are supported in the typical remote procedure call fashion. That is, the caller blocks until the remote handler completes the request. This blocking approach to handling massive data transfers is clearly non-optimal when we consider that we have two different processes (the host server and the local client) which are running on different processors (the host platform and Cheops). To circumvent this shortcoming, Chex supports semaphored data transfer operations for its local client:
future_read()
future_write()

These functions take the same parameters as read and write, with an additional semaphore (refer to Magic Seven IPC System) that should be set when the call is made. No response from chex is required, so these functions return to the calling local client immediately, so it can go on to other tasks. Meanwhile, chex has begun servicing the massive data transfer via high speed SCSI transfer. Because data transfers from/to host memory to/from Cheops memory via SCSI is handled by a separate DMA controller, the clients program execution is not blocked and we can achieve true multiprocessing. When chex has completed the request, it clears the given semaphore, which acts as a signal to the calling client.

Other standard UNIX calls

Most other UNIX calls depend only upon the standard set listed above. Thus, all other UNIX calls can be implemented completely on the Cheops side. These functions are prototyped in $G960INC and are linked in by the -L$G960LIB -lcgca library.

Chex Control Issues

This section reviews various commands available to affect chex server / local client communications.

Running Programs without Host Support

If chex is invoked with the -ser option, the service port is not created and chex terminates immediately after the local client begins execution. This disables client/server communications, and system calls made by the local client that are normally serviced by chex will return with a negative value indicating failure.

Even without chex, however, standard I/O functions like fprintf and fscanf will still be supported thru M7's serial I/O driver.

Terminating Chex but not the Cheops Program

If you should ever need to terminate the chex host server, but wish to let the local client application remain active, this may be done with the function chex_close_connection(). This function redirects all standard I/O functionality to be handled by the serial I/O device rather than chex, and then terminates the chex program. The locally running application will continue running, but any subsequent host file system calls will return a negative value indicating failure since chex is no longer available to service those requests.

Terminating Chex and the Local Program

The system call exit()will terminate both the host server as well as the local client.

Use (control-c) to terminate the client before normal program termination. Chex handles this signal by telling the Magic Seven operating system to kill the local process, before chex itself is terminated.

WARNING! : Don't (control-a) in your chterm monitor console window while your local client program is being serviced by chex. Control-a is the Magic Seven soft reboot key which will kill all local processes. Chances are, a process will be in the middle of a SCSI transaction with the chex host server when the reboot occurs. The host's SCSI device driver will then either wait indefinitely for Cheops to complete its side of the transaction (and it can't since it's state has been wiped) or it will simply terminate that connection. Either way, this connection can only be reestablished during the host's reboot cycle, so you want to avoid arbitrarily terminating a program from the Cheops side.

Flushing stdio at program exit

Whereever you call exit() in your local program you may want to call _ioflush() in order to insure that all output reaches their destination. If you do not call exit(), the flush is done automatically at the end of your program.

I/O Redirection

In a Cheops program, printf commands (more specifically putchar commands) are directed to its console device. The console can be one of several devices:

SER_0, SER_1
One of the serial ports on Cheops. To open a serial window on the host to view anything sent to a serial port, use the command alias cheops (see Chterm Usage).
ALPHA_NUMERIC
The alphanumeric LED's on the side of Cheops.
SERVICE_PORT
The chex service port. If this is the selected console device, all file system calls will be redirected to the chex host server.

To get and set the console use the functions:
proc_get_console(long proc_no,long *dev,long *service_port)
proc_set_console(long proc_no,long dev,long service_port)

NOTE: All I/O requests are handled by that process' selected console device. Remote file system calls made when the console device is not selected to be SERVICE_PORT will return a negative value indicating failure since they can only be handled by chex via its service_port.

Output redirection can also be done directly to one of the above devices by using the function:
dev_printf( long dev, char *format,... )

Rate of Service Queries

When a program has begun execution, it has been mentioned that chex enters an endless service loop. Chex achieves this by periodically interrupting M7 (via a hardware SCSI interrupt) to see if any messages have arrived for it. This interruption will introduce an effect on the run-time speed of local applications.

The delay time in milliseconds between these service queries that chex makes may be set from the local client with the function chex_set_polling_rate(). The default time between queries is 50 milliseconds.

Increasing this delay value will result in increasing the average time it takes for each remote service request to be handled. However, for those sections of an application that are time critical, significant processing speed up can be achieved by decreasing the number of requests that chex can service and increasing the delay between service queries.

If you reach a point in your application where chex is no longer needed to service host file system calls, use the function chex_close_connection() described above.

Datfile Support

Chex also provides full support for standard Media Lab datfile library functions. For an introduction to datfiles, refer to garden man pages man dat, man datio and man datsubs. Prototypes for these funtions may be found in $CHEOPS_LOCAL_INC/cheops_dat.h. Stub functions that make datlib requests to chex are not provided in the standard -lcheops library. To use these functions, the library -lcheops_dat needs to be included in the library list before the -lcheops entry.

Standard Datlib Functions

These are the standard datlib functions. They should be identical to the function prototypes for host platforms found at /usr/local/include/dat.h.

DATFILE *dxopen (const char *name, const char *mode);
DATFILE *d_open (const char *name, const char *mode);
long d_flush (DATFILE *df);
long d_close (DATFILE *df);
long d_gettype (const DATFILE *df, long *nbps);
int d_settype (DATFILE *df, int type);
char *d_getname(const DATFILE *df);
long d_getdim (const DATFILE *df,long *recs,long *chan,long dim[],long maxdim);
long d_setdim (DATFILE *df,long dset,long chan,long dim[10],long ndim);
long d_copyhdr (DATFILE *to, const DATFILE *from);
long d_read(DATFILE *df, char *buf, long n);
long d_readc(DATFILE *df, char *buf, long n, long buftype);
long d_write(DATFILE *df, char *buf, long n);
long d_writec(DATFILE *df, char *buf, long n, long buftype);
long d_seek(DATFILE *df, struct d_addr *where, long whence);
long d_getsubdim(DATFILE *df,struct d_addr *size);
const char *d_getkey (const DATFILE *df, const char *key);
int d_setkey (DATFILE *df, const char *key, const char *val);
int d_appkey (DATFILE *df, const char *key, const char *val);
int d_delkey (DATFILE *df, const char *key);
long d_submatrix(DATFILE *df, struct d_addr *start,
struct d_addr *stop, struct d_addr *step);
long d_getsubmatrix(DATFILE *df, struct d_addr *start,
struct d_addr *stop, struct d_addr *step);

Cheops Datlib Extensions

Since Cheops differs from general purpose platforms in terms of multiprocessing capabilities and memory management we have extended the datfile library to take maximum advantage of these peculiarities.

Multiprocessed Data Transfers via Semaphores

The utility of providing multiprocessing capabilities by using semaphored communications has been discussed above for the future_ versions of the standard read() and write() system calls. The same idea has been extended for datfiles as well.

long d_future_readc(DATFILE *df, char *buf, long len, long type, long sema)

Just as future_read() differs from the standard system call read() as explained above, the d_future_readc() differs from d_readc() in only that it requires an extra semaphore that should be SET (refer to sema_acquire() and sema_allocate() in Magic7 IPC System and prototyped in $CHEOPS_LOCAL_INC/cheops.h). When invoked by the local client, the chex server receives this call, but no response is required (the return long only signals if an error was encountered), so the client is allowed to continue execution even though the massive data transfer is not complete. When the data transfer has completed, chex signals the client by remotely CLEARing the given semaphore using sema_release(). The client process should check the semaphore's status using sema_poll() to determine when the transfer has completed.

long d_future_N( long N, DATFILE *dat[],
                 struct d_addr *where[], long whence[],
                 char *buf[], long len[], long type[], long sema );

The d_future_N() function is an extention of d_future_readc(). It takes an N-lengthed array of datfiles, wheres & whences (see d_seek()), bufs, lens, and types (see d_readc()). This allows the client to make multiple complex transfer requests at one time. As with d_future_readc() this function must be invoked with a SET semaphore. The return value indicates success of the request but not completion of the transfer. Completion of all N seeks and transfers is indicated by a CLEARed semaphore.

Two Dimensional Image Transfers

Typical datlib transfer functions, like d_readc() and d_writec() operate on 1-D streams of data. Cheops stream processors operate on 16-bit samples arranged within 1024 long "strides" of samples. This two dimensional format allows row order processing of image data (refer to Cheops Memory and Image Management). Chex supports data transfers of any datfile type into or out 2-D short samples with the family of d_2d extentions to the datfile library on Cheops.

NOTE: In order for these functions to work correctly, the horizontal image dimension (xdim) must be a multiple of 16.

long d_2d_read_byte_to_lo_short( DATFILE *df,
            short *short_addr, long short_stride,
            long data_xdim,long data_ydim );

long d_2d_read_byte_to_hi_short( DATFILE *df,
            short *short_addr, long short_stride,
            long data_xdim, long data_ydim );

long d_2d_write_lo_short_to_byte( DATFILE *df,
            short *short_addr, long short_stride,
            long data_xdim, long data_ydim );

long d_2d_write_hi_short_to_byte( DATFILE *df,
            short *short_addr, long short_stride,
            long data_xdim, long data_ydim );

The above functions are written OPTIMALLY: each image is read as a contiguous scsi transfer into a temporarily allocated buffer, and then quad word converted from byte to short format. They are, however, not very general in that they strictly impose the limitation that the destination buffer be of type short and that the source data be first converted to bytes.

To sidestep these limitations the more general, but less efficient function, d_2d_readc() is provided. It allows the local client program to read data from a datfile into any type of 2-D image with the given stride in bytes.

long d_2d_readc( DATFILE *df,
            char *byte_addr, long byte_stride,
            long data_xdim, long data_ydim, long data_type );

The d_2d_future_readc() function does the equivelent of d_2d_readc() but in nonblocking semaphore mode (see d_future_readc):

long d_2d_future_readc(DATFILE *df,char *byte_addr,long byte_stride,
            long data_xdim,long data_ydim,
            long data_type,long sema);

NOTE: The d_2d_readc functions are very NON-OPTIMAL as each scan-line is written seperately over scsi.


Jump to Software Tools
Jump to the Cheops Homepage
This document originally written by Shawn Becker (sbeck@media.mit.edu).
cheops-web@media.mit.edu