An Introduction To Interrupts P2

Describe the problems associated with the timing of interrupts with respect to.

  1. Software Events

    The problem with interrupts is that their occurrence cannot be predicted. They could occur at any time. Critical code portions should be protected by disabling
    interrupts for their duration. A typical example is accessing shared data.The use of interrupts can affect the timing of existing coded routines. This effect
    is unpredictable, due to the nature of interrupts. Code which is heavily timing dependent may not work when interrupts are enabled. These timing critical portions need to be protected also.

    The processing of interrupts should be kept as short as possible, with reguard to the duration of the software event in question.

  1. Multiple interrupts from the same source

    Because all devices use the same interrupt line, and can assert it at any time, it is possible for a device not to be recognized. Status ports, daisy chaining and the use of PIC chips overcome these problems. Interrupts can be missed, especially if they are edge triggered or non-latched. Level sensitive interrupts need to be kept low till acknowledged by the processor.The interrupts must be arranged in priority. This can be done using either software (polling order) or hardware (use a PIC). It might be that higher priority interrupts keep occurring, preventing a lower level interrupt from being serviced.

    Decisions need to be made whether interrupt routines can also be nested. This could mean servicing a high priority interrupt which allows a lower priority interrupt inside it.

  2. Other interrupts

    Most processors support two levels of interrupts, maskable and non-maskable. Inherently, the processor assigns each a priority. Separate input lines are used by
    the processor for each, eg, NMI and IRQ.The use of a daisy chain or PIC system ensures correct timing with respect to other interrupt sources. It overcomes the problems of multiple interrupts occurring and their associated priorities.

    The programmer must decide, for each interrupt in the system, which must be

    • maskable
    • its priority level
    • whether it can be nested
    • how many interrupts can be active

    Some processors with limited stack space could
    experience problems with a large number of nested
    interrupts.


State the problems of working with multiple interrupts with respect to.

  1. Timing as above

    The issues involved are,

    • response times
    • priorities
    • number and frequency

    The purpose of interrupt routines are to ensure adequate response times to requests for service. Problems may occur if a lower level interrupt routine will not allow a higher level interrupt to interrupt it. The designer works out worst case scenarios for all interrupts in the system, which will highlight any potential problems. Ensuring that higher priority interrupts can interrupt a lower level routine is important in situations like power-down.

    To keep the interrupt routines short, a buffered or message approach might be adopted. This ensures the device is serviced quickly, but that interrupt routines do not consume excessive amounts of processor time.

  2. Priority of interrupts

    Where interrupts such as NMI are used for watchdog and powerdown cases, they must have absolute priority.Interrupt priority is determined by

    • the task to be done
    • the data transfer rate
    • expected response times
    • hardware requirements

    This might mean that an interrupt driven printer spooler would have lower priority than an interrupt routine which services a floppy disk unit. Conversely, the highest priority in the system could be refreshing the Dynamic RAM.

  3. Identification of interrupting device and its appropriate service routine

    Where more than one interrupt device is connected to the same interrupt line, some method of device determination must be employed.The three main methods are,

    • status ports
    • hardware vector
    • PIC

    Status ports are suitable for systems which are not very critical and the priority may need to be changed dynamically. Hardware vector systems are very fast at servicing interrupts, thus are used where response times are critical.

    PIC systems combine the features of both the status and hardware vector systems, and are used in complicated systems with a large number of interrupt sources, some of which may need to be periodically disabled/enabled.


TIMING DEVICES

State the different timing devices available, and give examples where each is most appropriately used, eg,

Timing devices and methods are used in computer systems for

  • timing
  • event logging
  • file stamping
  • task synchronization
  • implementing controlled delays
  • measurements

Software methods

These involve the calculation of the time period using software.
The advantage is no/little hardware and easy programming, at the expense of accuracy.

Hardware methods

These involve the use of support chips like a Real Time Clock (RTC), timer/counter chips and interrupts. They have greater accuracy and resolution at the expense of interrupt routines and extra hardware.


Software Loops

These are implemented by loading a register with a value, then decrementing and looping till the register value reaches zero, eg.


	 DELAY:  	Mov CX, FFFFh
	 DELAY1:  	Dec CX
			Jne DELAY1
			Ret

It is generally used to implement small delays, examples being

  • 20ms for keyboard debouncing
  • head settle times for floppy disk controllers
  • serial communications, eg. RTS to CTS delays

and where variances in iteration speed are not critical (accuracy is not important).

Longer delays are achieved by using another register to provide multiple calls, eg.


		Mov BX, 5
	Lp1:	CALL DELAY
		Dec BX
		Jne Lp1

The processor is tied up for the duration of the software loop.

Modern processors using high clock rates can buzz through a 16bit timing loop very quickly.


	8086 Code Example
		mov cx, 0 	;4 clocks
	delay:	loop delay 	;17 clocks
				;5 non-branch

	10mHz ->		; 100ns per clock cycle
	loop repeats ffffh * 17 clocks
	= 1114112 clocks
	= 111,411,200 nanoseconds
	= 111.4 milliseconds

though it will actually perform faster due to the instruction queue cache.

Advantages

  • no additional hardware required
  • simple to implement
  • takes up little memory space

Disadvantages

  • inaccurate
  • suitable for small delays only
  • ties up the processor

RTC in hardware

A peripheral chip, driven by the system clock, is used for timing purposes. It has registers which are programmed with specific time intervals. Each system clock decrements this value at a pre-determined rate (divide by 1,2,4,8,16 etc). When the register count reaches zero, the RTC chip generates an interrupt to the processor.

Most chips provide several channels which can either be used separately, or cascaded together for longer time intervals.

Other chips available are DATE/TIME calendar chips, which are used to implement time of day features for the computer operating system.

Real-time hardware implementations are suitable for

  • time-slicing in multi-user systems
  • counting pulses
  • pulse width or frequency determination
  • time intervals for A/D and D/A conversions (sample and hold)
  • accurate delays
  • watchdog timers

Advantages

  • the processor can perform other tasks
  • greater accuracy
  • several counters available
  • greater flexibility
  • wider range available
  • better resolution

Disadvantages

  • extra hardware required
  • software overheads required
  • requires initialization code

RTC in software

In this system, a periodic interrupt is used to implement timing features, either loop counts or time of day calendar functions.

The frequency of the interrupt determines the resolution and accuracy of the clock. On each interrupt the service routine might be,

		 ; assume interrupt occurs at mains frequency, every 20ms
		 ; could be mains derived
		 extern int ticks, seconds, minutes, hours;
		 void interrupt clock()
		 {
			  ++ticks;
			  if( ticks >= 50 ) {
				   ticks = 0; seconds++;
					   if( seconds >= 60 ) {
						    seconds = 0; minutes++;
						    if( minutes >= 60 ) {
							     minutes = 0; hours++;
							     if( hours >= 24 ) hours = 0;
						    }
					   }
			  }
		 }

This implementation requires the variables to be held in RAM space, thus the clock only works when power is on and the system has been initialised.

Advantages

  • easy to implement
  • reasonably accurate for calendar

Disadvantages

  • inaccurate if tied to mains frequency
  • inaccurate if doing a lot of higher priority interrupts like disk access
  • consumes cpu time (higher interrupt rate gives greater accuracy but increases service overhead, lower rate decreases service overhead at expense of accuracy)
  • initialization code required
  • lost on power down
  • requires 1 hardware interrupt

4 RTE controlled delay

In a multi-user system, the operating system controls delays which may be imposed upon tasks for any number of reasons.

  • task suspension
  • block/wakeup protocol
  • task synchronization

Tasks may also voluntarily request suspension. Each suspended task is inserted into a sleep queue, which is driven by a real-time clock. The entry for each task corresponds to the number of timer ticks before the task is to be rewaken.

If the timer tick is every 100microseconds, and taskA requests a sleep period of 400milliseconds, then the queue looks like,


	 taskA = 4000

If another task, taskB then requests a sleep period of 800 milliseconds, the queue looks like,


	 taskA = 4000
	 taskB = 4000

Once the time has expired, the operating system removes the task from the sleep queue.

The following code example illustrates a simple implementation for tasks to sleep under the control of the RTE.

 void far wakeup( int task_number )        /* unblock a task       */
 {
	disable();                         /* turn off interrupts  */
	PTN(process_state) = READY;        /* change task state    */
	enable();                          /* re-enable interrupts */
 }

 void far sleep(unsigned int clockticks)  /* voluntary task sleep  */
 {
	  disable();                      /* turn off interrupts   */
	  PTN(process_state) = SLEEPING;  /* change task state to SLEEPING  */
	  PTN(sleep_rate) = clockticks;  /* fill in duration to sleep */
	  enable();                      /* re-enable interrupts      */
	  asm hlt;                       /* interrupt call to transfer();     */
 }

 void far interrupt transfer( void )
 {
	  PTN(SS) = (unsigned int) _SS;  /* save current running task Stack 
					pointers*/
	  PTN(SP) = (unsigned int) _SP;
	  if( PTN(process_state) == RUNNING) /* if a running task, change to 
					ready*/
		   PTN(process_state) = READY;
		  task_number++;   /* select next task to run           */
		  if( task_number > (NUM_TASKS-1) )  task_number = 0;
		  /* skip tasks which are blocked and sleeping. for sleeping tasks
		    decrement their sleep_rate count, and if zero, alter their 
		    task state to READY.

		    For blocked tasks, see if they have recieved a message, and if 
		    they have, change their task state to READY  
		  */

		  while((PTN(process_state) == BLOCKED)
			||(PTN(process_state) == SLEEPING)) {
			   if( PTN(process_state) == SLEEPING) {
				    PTN(sleep_rate--);
				    if( PTN(sleep_rate)==0) wakeup(task_number);
			   }
			   if((PTN(process_state) == BLOCKED) 
				&& (PTN(msg) == TRUE))  {
				    PTN(process_state) = READY;
				    PTN(msg) = FALSE;
			   }
			   task_number++;
			   if( task_number > (NUM_TASKS-1) ) task_number = 0;
		  }
		  /* swap to stack of newly selected task      */
		  ................;
		  ................;
		  outportb( PIC, EOI );      /* signal EOI to 8239 PIC       */
 }

Software Polling using RS232 on IBM-PC

This technique involves using the CPU to detect when the device is ready to present data to it. In our case, it involves continually reading the RBF flag of the Line Status Register for the serial port.

When RBF is asserted, the CPU can then read the character from the receive buffer register of the serial card. The following program shows a C program which performs software polling of the serial port to receive characters from a transmitter station.


/* ser1.c, Software poll of receive side of serial port */
#define TRUE 1
#define FALSE 0
#define RBR 0x3f8
#define THR 0x3f8
#define LSR 0x3fd
#define IER 0x3f9
#define BYTE unsigned char
#define DATAREADY 1
#define OVERFLOW 2
#define OVEREADY 3
#include <conio.h>
#include <stdio.h>
#include <dos.h>

static int EXIT = FALSE;
union REGS rgs;
static BYTE attribute = 0x0f;

void rs232_init(void) {
	 outportb( IER, 0x00 );
	 outportb( 0x3fb,0x80);   /* access baud rate generator */
	 outportb( 0x3f8,0x0c);  /* 9600 baud   */
	 outportb( 0x3f9,0x00);
	 outportb( 0x3fb,0x03);  /* 8 bits, no parity, one stop*/
}

void send( BYTE ch) {
	 while( (inportb(LSR) & 0x20)  != 0x20) ;
	 outportb( THR, ch );
}

BYTE recieve( void ) {
	 return( inportb( RBR ) );
}

void scrollup( tlc, tlr, brc, brr ) 
unsigned int tlc, tlr, brc, brr;
{
	 rgs.h.al = 1;  rgs.h.ah = 6;
	 rgs.h.ch = tlr;  rgs.h.cl = tlc;
	 rgs.h.dh = brr; rgs.h.dl = brc;
	 rgs.h.bh = 7;  int86( 0x10, &rgs, &rgs );
}

void writechar( BYTE ch, BYTE attribute ) {
	 rgs.h.ah = 15; int86( 0x10, &rgs, &rgs );
	 rgs.h.ah = 9;  rgs.x.cx = 1;
	 rgs.h.al = ch;  rgs.h.bl = attribute;
	 int86( 0x10, &rgs, &rgs );
}

main() {
	 BYTE recch, LSRSTATE;
	 unsigned int key;
	 static int recx = 35, recy = 3, transx = 2, transy = 3;

	 recch = inportb( RBR ); /* dummy read of RBR to clear */
	 clrscr();
	 printf("+---------------------+  +---------------------+\n");
	 printf("|Transmit Window:     |  |Recieve Window:      |\n");
	 printf("|                     |  |                     |\n");
	 printf("|                     |  |                     |\n");
	 printf("|                     |  |                     |\n");
	 printf("|                     |  |                     |\n");
	 printf("|                     |  |                     |\n");
	 printf("|                     |  |                     |\n");
	 printf("|                     |  |                     |\n");
	 printf("|                     |  |                     |\n");
	 printf("|                     |  |                     |\n");
	 printf("|                     |  |                     |\n");
	 printf("|                     |  |                     |\n");
	 printf("|                     |  |                     |\n");
	 printf("|                     |  |                     |\n");
	 printf("|                     |  |                     |\n");
	 printf("+---------------------+  +---------------------+\n");
	 printf("\n Type keys to transmit to other PC.\n");
	 printf(" Incoming data displayed in recieve window.\n");
	 printf(" Press F10 to STOP.\n");
	 while( EXIT == FALSE ) {
		  if( kbhit() != 0) {
			   gotoxy( transx, transy );
			   key = getch();
			   if( key == 0x00 )  EXIT = TRUE; /* F10 */
			   else {
				    send( (BYTE) (key & 0xff) );
				    putchar( key & 0xff );
				    transx = transx + 1;
				    if( transx > 29 ) {
					     transx = 2;
					     transy++;
					     if( transy > 16 ) {
						      scrollup(1,2,28,15);
						      transy = 16;
					     }
				    }
			   }
			  }
		  else  {
			   LSRSTATE = inportb( LSR ) & 3;
			   if( (LSRSTATE > 0) && (LSRSTATE < 4) )  {
				    switch( LSRSTATE )  {
					     case OVERFLOW :
					     case OVEREADY :
						      attribute |= 0x80;
						      putchar( 0x07 );
						      break;
					     case DATAREADY :
						      attribute = 0x0f;
						      break;
				    }
				    recch = recieve();
				    gotoxy( recx, recy );
				    writechar( recch, attribute );
				    recx = recx + 1;
				    if( recx > 71 ) {
					     recx = 35;
					     recy++;
					     if( recy > 16 ) {
						      scrollup(34,2,71,15);
						      recy = 16;
					     }
				    }
			   }
		  }
	 }
}


Interrupt Driven RS232 on IBM-PC

This technique involves using the I/O device to tell the CPU when it is ready to present data. This frees the CPU for other tasks during this time, and also guarantees adequate response times. In our case, it involves enabling interrupt driven operation for the receive section of the serial port.

When a character arrives, the serial port will issue an interrupt request to the computers Priority Interrupt Controller chip (PIC). The PIC will interrupt the CPU and place the vector of the receive routine onto the data bus. The CPU uses this vector as an entry into a lookup table which then generates the address of the routine servicing that interrupt.

The receive interrupt routine reads the character stored in the RBR and deposits it into a circular queue buffer. Once the interrupt is serviced, the PIC must be reset to enable further interrupts to take place.

The 8259 Priority Interrupt Controller

This chip is reponsible for prioritizing interrupts from various I/O devices and passing them onto the CPU for servicing. When a device requests service, it asserts its associated interrupt line (IRQ0 to IRQ7), signalling the PIC. The PIC asserts IRQ to the processor which responds with INTA (Interrupt Acknowledge). Upon receiving this, the PIC presents a vector byte on the data bus which the CPU uses to determine the address of the interrupt service routine.

In the IBM-PC, the association of IRQ’s to I/O devices is,


	Interrupt Device		Vector byte
	 NMI   Parity and 8087 device 
	 IRQ0  Timer/Clock output  	08
	 IRQ1  Keyboard   		09
	 IRQ2  EGA/PC network  		0A
	 IRQ3  COM2   			0B
	 IRQ4  COM1   			0C
	 IRQ5  Fixed disk controller XT	0D
	 IRQ6  Floppy disk controller	0E
	 IRQ7  Lpt1   			0F

	PIC port addresses are 0x20 and 0x21

At port 0x21 is the PIC MASK REGISTER. Each bit in the mask register corresponds to an IRQ line. IRQ0 is associated with bit 0 and so on. Writing a ZERO to a selected bit enables the interrupt line for that particular bit.

When the PC is reset or powered on, all interrupt devices are disabled by writing 0x0f to the PIC mask register. The ROM BIOS/DOS routines enable IRQ0, IRQ1, IRQ6 and IRQ7 as part of the system initialisation.

The serial card on the PC uses IRQ4. Enabling bit 4 of the PIC mask register will thus also enable the reception of interrupts from the serial card.

To prevent destroying the current state of the mask register,
it is first read then bit 4 is enabled. The new mask setting is
then written back to the mask register.

Enabling of interrupt operation on the serial card

The Interrupt Enable register (IER) must be programmed for
receive interrupt generation. This is achieved by the statement


	  outportb( IER, 1 );

The PC serial card also has a little bit which needs to be set
in order to allow interrupts to be passed onto the PIC. This bit
must be set, and is done by the statement


  outportb( MCR, 0x0b );
  /* bit 0 = DTR, bit 1 = RTS, bit 3 = out2 */

Setting the interrupt vector to the receive interrupt
routine

This must be done before enabling the PIC controller, otherwise
it might cause the system to accept interrupts and branch to a
non-existent service routine.

The code which installs the interrupt service routine is,


	  setvect( 0x0c, recieve );

This places the segment:offset address of the service routine
into the interrupt vector for interrupt 0x0c. This is the vector
which the PIC chip presents when a device asserts IRQ4.

Enabling of the PIC to recognize the serial card

The PIC controller can provide masking of the various IRQ lines
coming from I/O cards. The PC serial card normally uses IRQ4 for
COM1. This interrupt must be programmed for acceptance by the
PIC.

The statements that achieve this are,


	  mask = inportb( PIC_MASK );
	  mask = mask & 0xef;  /* enable IRQ4 */
	  outportb( PIC_MASK, mask );

Also, after servicing the interrupt, the PIC must be reset to
allow further interrupts, by the command,


	  outportb( PIC, 0x20 );

The following program shows a C program which performs
interrupt driven operation of the serial port to receive
characters from a TRANSMITTER station.


/* ser2.c, Recieve interrupt driven serial port */
#define TRUE 1
#define FALSE 0
#define RBR 0x3f8
#define THR 0x3f8
#define LSR 0x3fd
#define IER 0x3f9
#define MCR 0x3fc
#define BYTE unsigned char
#define PIC 0x20
#define PIC_MSK 0x21
#define maxsize 4096

#include <conio.h>
#include <stdio.h>
#include <dos.h>

static int EXIT = FALSE;
union REGS rgs;
static BYTE attribute = 0x07;
static BYTE mask;
static void interrupt (*oldint)();
static BYTE buffer[maxsize];
static BYTE *inptr, *outptr;
static int char_ready = FALSE;
static int overrun = FALSE;
void interrupt recieve( void );

void rs232_install(void) {
	 outportb( 0x3fb,0x80);   /* access baud rate generator */
	 outportb( 0x3f8,0x0c);  /* 9600 baud   */
	 outportb( 0x3f9,0x00);
	 outportb( 0x3fb,0x03);  /* 8 bits, no parity, one stop*/
	 oldint = getvect( 0x0c );
	 setvect( 0x0c, recieve );  /* install com1 routine */
	 outportb( MCR, 0x0b );   /* enable DTR and OUT2 */
	 outportb( IER, 1 );
	 mask = inportb( PIC_MSK ); /* enable com1 on pic */
	 mask = mask & 0xef;
	 outportb( PIC_MSK, mask );
}

void rs232_deinstall( void ) {
	 mask = inportb( PIC_MSK );
	 mask = mask | 0x10;
	 outportb( PIC_MSK, mask );
	 outportb( IER, 0 );
	 outportb( MCR, 0 );
	 setvect( 0x0c, oldint );
}

void send( BYTE ch) {
	 while( (inportb(LSR) & 0x20)  != 0x20) ;
	 outportb( THR, ch );
}

void interrupt recieve( void ) {
	 disable();
	 *inptr = inportb( RBR );
	 ++inptr;
	 if( inptr > buffer + maxsize )  inptr = buffer;
	 char_ready = TRUE;
	 if( (inportb( LSR ) & 0x02) == 0x02)  overrun = TRUE;
	 else  overrun = FALSE;
	 enable();
	 outportb( PIC, 0x20);  /* signal EOI to 8239 PIC chip */
	 /* there is an IRET here */
}

void scrollup( tlc, tlr, brc, brr ) 
unsigned int tlc, tlr, brc, brr;
{
	 rgs.h.al = 1;  rgs.h.ah = 6;
	 rgs.h.ch = tlr;  rgs.h.cl = tlc;
	 rgs.h.dh = brr; rgs.h.dl = brc;
	 rgs.h.bh = 7;  int86( 0x10, &rgs, &rgs );
}

void writechar( BYTE ch, BYTE attribute ) {
	 rgs.h.ah = 9;  rgs.h.bh = 0;
	 rgs.x.cx = 1;  rgs.h.al = ch;
	 rgs.h.bl = attribute; int86( 0x10, &rgs, &rgs );
}

main() {
	 BYTE recch;
	 unsigned int key;
	 static int recx = 35, recy = 3, transx = 2, transy = 3;
	 inptr = buffer;
	 outptr = buffer;
	 rs232_install();
	 recch = inportb( RBR ); /* dummy read of RBR to clear */
	 clrscr();
	 printf("+---------------------+  +---------------------+\n");
	 printf("|Transmit Window:     |  |Recieve Window:      |\n");
	 printf("|                     |  |                     |\n");
	 printf("|                     |  |                     |\n");
	 printf("|                     |  |                     |\n");
	 printf("|                     |  |                     |\n");
	 printf("|                     |  |                     |\n");
	 printf("|                     |  |                     |\n");
	 printf("|                     |  |                     |\n");
	 printf("|                     |  |                     |\n");
	 printf("|                     |  |                     |\n");
	 printf("|                     |  |                     |\n");
	 printf("|                     |  |                     |\n");
	 printf("|                     |  |                     |\n");
	 printf("|                     |  |                     |\n");
	 printf("|                     |  |                     |\n");
	 printf("+---------------------+  +---------------------+\n");
	 printf("\n Type keys to transmit to other PC.\n");
	 printf(" Incoming data displayed in recieve window.\n");
	 printf(" Press F10 to STOP.\n");
	 while( EXIT == FALSE ) {
		  if( kbhit() != 0)  {
			   gotoxy( transx, transy );
			   key = getch();
			   if( key == 0x00 )  EXIT = TRUE;
			   else  {
				    send( (BYTE) (key & 0xff) );
				    putchar( key & 0xff );
				    transx = transx + 1;
				    if( transx > 29 )  {
					     transx = 2;
					     transy++;
					     if( transy > 16 )  {
						      scrollup(1,2,28,15);
						      transy = 16;
					     }
				    }
			   }
		  }
		  else  {
			   while( char_ready == TRUE ) {
				    recch = *outptr;
				    gotoxy( recx, recy );
				    if( overrun ) {
					     attribute |= 0x80;
					     putchar( 0x07 );
				    }
				    else attribute = 0x07;
				    writechar( recch, attribute );
				    ++outptr;
				    if( outptr == inptr ) 
					     char_ready = FALSE;
				    if( outptr > buffer + maxsize )
					     outptr = buffer;
				    recx = recx + 1;
				    if( recx > 71 ) {
					     recx = 35;
					     recy++;
					     if( recy > 16 ) {
						      scrollup(34,2,71,15);
						      recy = 16;
					     }
				    }
			   }
		  }
	 }
	 rs232_deinstall();
}

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.