Embry-Riddle Real-Time Laboratory Experiment
Experiment #9
Interrupt Service Routines


Introduction

Interrupt handling is important in real-time operating systems. The system becomes aware of external events via the interrupt mechanism and the response of a real-time systems depends on the speed of the system's response to interrupts and the speed of processing interrupt handlers. To achieve the best response possible the application writer must be aware of how to take advantage of the utilities provided by VxWorks.


Objectives

The following are the primary objectives of this experiment:


Description

The user may write an interrupt service routine (ISR) and attach it to a particular interrupt using the intConnect routine provided by VxWorks. What basically happens when an interrupt to the system occurs, is at the first non-critical code after the interrupt occured, guaranteed to be within musec by WRS, the ISR is executed. This time span is generally knonw as interrupt latency. Because many interrupts may occur within a short time of each other and a higher interrupt will block lower priority interrupts, it is necessary to keep the ISR processing to a minimum. This is the responsibility of the application writer.

The header files which relate to VxWorks interrupt management are, intLib.h - this is the interrupt library header file; and ARCH/mc68k/ivMc68k.h. The ISR code does not run in the normal task context. It has no task control block and all ISR's share a single stack. Because of these differences there are restrictions to the type of routines that can be used in the ISR.

ISR's should not invoke functions which may cause ``blocking'' of the caller. For example, semTake. malloc and free cannot be used because they call functions which may cause blocking and thus all creation and deletion functions are forbidden since they use malloc and free. An ISR must not perform I/O through the VxWorks I/O system. A call to a device driver may block the system if the caller needs to wait for the device. However, the VxWorks pipe driver has been designed to permit writes by interrupt service code.

The best way to print out messages from an ISR is to use the function logMsg or other functions provided by the library logLib. ISRs should not use floating point instructions since these registers are not saved on entry to the ISR. If floating point instructions are to be used the registers must be saved using the functions in fppALib. However, floating point operations are time intensive and should be avoided in ISRs.

Ideally, an ISR only contains a semGive system call. That is to say, the function of a ISR is to cause the execution of a task to perform whatever processing is necessary. To improve cooperation between VxWorks' ISRs and tasks, the best mechanism is semaphores.

1. Example:

In the example below, the interruptGenerator task generates a hard interrupt, sysBusIntGen(INTERRUPT_NUM,INTERRUPT_LEVEL), which is caught by interruptCatcher.

The synatx for sysBusIntGen is:
SYNOPSIS 
       STATUS sysBusIntGen
           (
           int  intLevel,  /* bus interrupt level to generate          */
           int  vector     /* interrupt vector to generate (0-255)     */
           )

RETURNS 
 	OK, or ERROR if intLevel is out of range or the board cannot generate 
 	a bus interrupt. 
interruptCatcher is able to handle this hardware interrupt by installing an interrupt handler, interruptHandler.

interruptCatcher is "attaches" to the hardware interrupt using intConnect(INUM_TO_IVEC(INTERRUPT_LEVEL),(VOIDFUNCPTR)interruptHandler,i). The INUM_TO_IVEC(INTERRUPT_LEVEL) is a macro that converts a hardware interrupt number to a vector.

The synatx for sysBusIntGen is:

SYNOPSIS 
       STATUS intConnect
           (
           VOIDFUNCPTR *  vector,    /* interrupt vector to attach to     */
           VOIDFUNCPTR    routine,   /* routine to be called              */
           int            parameter  /* parameter to be passed to routine */
           )

DESCRIPTION 
       This routine connects a specified C routine to a specified interrupt vector. The 
       address of routine is stored at vector so that routine is called with parameter when the 
       interrupt occurs. The routine is invoked in supervisor mode at interrupt level. A proper 
       C environment is established, the necessary registers saved, and the stack set up.
       The routine can be any normal C code, except that it must not invoke certain 
       operating system functions that may block or perform I/O operations.
       This routine simply calls intHandlerCreate( ) and intVecSet( ). The address of the 
       handler returned by intHandlerCreate( ) is what actually goes in the interrupt vector. 

RETURNS 
       OK, or ERROR if the interrupt handler cannot be built. 

The run time scenario consists of interruptCatcher running and simulating normal processing until interruptGenerator generates a hardware interrupt. Upon the generation of the interrupt, interruptCatcher suspends its normal processing and branches to interruptHandler. Once the interrupt handling code has been executed, control is passed back to interruptCatcher. This activity is repeated multiple times.

------------------------------------------------------------------------------------
/* includes */
#include "vxWorks.h"
#include "intLib.h"
#include "taskLib.h"
#include "arch/mc68k/ivMc68k.h"
#include "logLib.h"

/* function prototypes */
void interruptHandler(int);
void interruptCatcher(void);
 
/* globals */
#define INTERRUPT_NUM 2
#define INTERRUPT_LEVEL 65
#define ITER1 40
#define LONG_TIME 1000000
#define PRIORITY 100
#define ONE_SECOND 100

void interruptGenerator(void) /* task to generate the SIGINT signal */
{
int i, j, taskId, priority;
STATUS taskAlive;

if((taskId = taskSpawn("interruptCatcher",PRIORITY,0x100,20000,(FUNCPTR)interruptCatcher,0,0,0,0,0,0,0,
	0,0,0)) == ERROR)
	logMsg("taskSpawn interruptCatcher failed\n",0,0,0,0,0,0);

for (i=0; i < ITER1; i++)
	{
	taskDelay(ONE_SECOND);/* suspend interruptGenerator for one second */
	/* check to see if interruptCatcher task is alive! */
	if ((taskAlive = taskIdVerify(taskId)) == OK)
    		{ 		
    		logMsg("++++++++++++++++++++++++++Interrupt generated\n",0,0,0,0,0,0);   	
    		/* generate hardware interrupt 2 */
    		if((sysBusIntGen(INTERRUPT_NUM,INTERRUPT_LEVEL)) == ERROR)	 
    			logMsg("Interrupt not generated\n",0,0,0,0,0,0);   			
    		}
    	else  /* interruptCatcher is dead */
    		break;
	}  
logMsg("\n***************interruptGenerator Exited***************\n\n\n\n",0,0,0,0,0,0);
}

void interruptCatcher(void) /* task to handle the interrupt */
{
int i, j;
STATUS connected;

/* connect the interrupt vector, INTERRUPT_LEVEL, to a specific interrupt 
handler routine ,interruptHandler,  and pass an argument, i */ 
if((connected = intConnect(INUM_TO_IVEC(INTERRUPT_LEVEL),(VOIDFUNCPTR)interruptHandler,i)) == ERROR)
	logMsg("intConnect failed\n",0,0,0,0,0,0);

for (i=0; i < ITER1; i++)
  	{
    	for (j=0; j < LONG_TIME; j++);
    	logMsg("Normal processing in interruptCatcher\n",0,0,0,0,0,0);
    	}	 	
logMsg("\n+++++++++++++++interruptCatcher Exited+++++++++++++++\n",0,0,0,0,0,0);
}

void interruptHandler(int arg)  /* signal handler code */
{
int i;

logMsg("-------------------------------interrupt caught\n",0,0,0,0,0,0);
for (i=0; i < 5; i++)
	logMsg("interrupt processing\n",0,0,0,0,0,0);
}


------------------------------------------------------------------------------------


Procedures

1. Copy the source code in the example and compile it.

2. Load the object file onto the target machine.

3. Execute the following command on the WindSh terminal: "logFdSet 1". This will direct the logMsg() output to the virtual console.

4. Run the examples by executing the main routine("interruptGenerator") of the example on WindSh terminal.

Note: Make sure you have redirected I/O, otherwise you won't see the results of the logMsg() commands.

Follow On Experiment

Experiment 1. Can the printf() statement be used instead of the logMsg()? Explain why?

Experiment 2. Modify the example program so that it can handle hardware interrupts 1,2, and 3. Generate these interrupts in ascending order in interruptGenerator. interruptCatcher will use three separate handlers for each hardware interrupt respectively.


Additional Information

What interrupt vectors are ok to use

The values 0 - 63 (decimal) are pre-defined by Motorola. Don't attach a handler to these values, though they are worth looking at to familiarize oneself with the default handlers. For 68K the file /VX_HSP_BASE/h/iv.h (which includes /VX_HSP_BASE/h/arch/mc68k/ivMc68k.h)

The vectors from 64 - 255 are available for use. Some are already used by the BSP code to connect to hardware dependent interrupts. In those cases we generally use the vectors recommended by the hardware manufacturer. The question arises; how to determine which vectors are available for customers applications to use. One method to determine this would be to manually check all the usages of intConnect(). VX_HSP_BASE/src/usr/usr*.c, VX_BSP_BASE/config/all/*, and VX_BSP_BASE/config/BSP/* are the locations to check. Admittantly, using the above methods to determine the used vectors seems unfriendly and tedious.

An alternative method is to use a C program to determine what vectors have been used. This can be done because VxWorks assigns a default handler to all "unassigned" vectors. ("Unitialized Vector...") We can scroll through the vectors and check for that handler, if it the stub does not exist, the vector is used. Here is some example code that will work, there is a much more elaborate & portable version on the vxWorks users group software archive that will perform this task (and more). There is also intVecGet().

/* vectors.c - show which mc68k user interrupt vectors are being used */ 

#include "vxWorks.h" 
#include "intLib.h" 
#include "stdio.h" 
#include "iv.h" 

extern excIntStub; /* Default handler. */ 

void vectorShow (void) 
{ 
int ix; 
int vecBase = (int) intVecBaseGet(); 

for (ix = (vecBase + 64); ix < (vecBase + 256); ix++) 
	{ 
	if ( *(int *) (UINT)(INUM_TO_IVEC(ix)) != (int) &excIntStub) 
		{ 
		printf ("User defined interrupt vector %d has been used.\n", ix); 
		} 
	} 
} 

Refer to VxWorks User's Manual and Reference Manual.


Return to Primary Table of Contents


Last Updated: 11 April 1997
Created by: Dan Eyassu
eyassud@db.erau.edu