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:
- To demonstrate VxWorks' implementation of interrupt service routines.
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.
Last Updated: 11 April 1997
Created by: Dan Eyassu
eyassud@db.erau.edu