Interrupt Handling
Interrupt handling in a MicroBlazei system, like most systems, is composed of four basic steps: two initialization steps and two processing steps. During initialization you first register and interrupt handler and then you enable interrupts on the system. During processing your must first handle the interrupt and then acknowledge that the interrupt has been handled. Note: in order to use the Xilinxi provided library functions described in this tutorial you must include the header file "mb_interface.h" in your source file.
Registering an Interrupt Handler
Registering an interrupt handler in a MicroBlazei system-on-chipi design is a relatively easy process because the libraries provided by the Xilinxi toolset take care of performing many of the low-level details on behalf of the user. All that needs to be done is to call the function microblaze_register_handler with the appropriate arguments. As an example, the following code shows how a interrupt handler can be registered:
microblaze_register_handler( intr_handler, NULL );
This example registers the function intr_handler as the interrupt handler for the system. The second parameter, NULL in the example, is an argument which is passed to the registered interrupt handler. Thus, the interrupt handler should be a function which takes a void* parameter and has no return value.
Enabling Interrupts
Enabling interrupts is a slightly more complicated procedure because interrupts must be enabled anywhere interrupts are either generated or processed. In a typical system this means interrupts must be enabled in three different places: at the device which generates the interrupt, at the PIC which processes the interrupt, and at the processor which handles the interrupt.
Enabling interrupts at the device level is device specific. However, most devices follow similar patterns and so this tutorial will describe how interrupts are enabled on a Xilinxi OPB GPIOi device. Reading the GPIO reference manual reveals that the OPB GPIOi uses two registers to control interrupt generation. The GPIOi master interrupt enable is located at offset +0x11C and the GPIOi interrupt enable is located at +0x128. Writing a '1' to bit 0 of the master interrupt enable register will cause the OPB GPIOi to generate interrupts for any channels enabled in the interrupt enable register. Write a '1' to bit 31 of the interrupt enable register will enable interrupts from channel 1 of the OPB GPIOi and writing a '1' to bit 30 of the interrupt enable register will enable interrupts from channel two. For example, to enable interrupts from channel 1 of an OPB GPIOi device you will have to write '1' to be 0 of the master interrupt enable register and '1' to bit 31 of the interrupt enable register.
Enabling interrupts at the PIC level, much like at the device level, is accomplished by writing to the correct bits in the registers exposed by the PIC. Reading the Interrupt Controller reference manual reveals that there are four registers of interest: the master interrupt enable register, the interrupt enable register, the set enable register, and the clear enable register. The master interrupt enable works much like the master interrupt enable in the GPIOi. The PIC's master interrupt enable register is located at offset +28 and writing a '1' to bits 30 and 31 will enable interrupts. The interrupt enable, set enable, and clear enable registers overlap in their purpose. Writing a '1' to any bit in the interrupt enable registers enables that interrupt while a '0' disables that interrupt. For instances writing a '1' to bit 31 enables interrupt 0 while writing a '1' to bit 30 enables interrupt 1. Each bit in the interrupt enable register corresponds to exactly one interrupt. The set and clear registers provide simple, atomic modification of the interrupt enable register. Writing a '1' to any bits in the set enable register will enable that interrupt and leave any other interrupts unchanged. Likewise writing a '1' to any bit in the clear enable register will disable that interrupt without affecting any other interrupts. For example, to enable interrupt 29 in the PIC you would first write '1' to bit 29 in the set enable register and then write '1' to bits 30 and 31 in the master enable register.
Enabling interrupts at the processor level is accomplished with a simple call to the Xilinxi provided library function microblaze_enable_interrupts. This function takes no parameters and produces no results. Calling it simply has the side effect of enabling interrupts at the processor level.
Handling Interrupts
When you have enabled interrupts correctly, your interrupt handler function will be invoked any time an enabled interrupt occurs. For instance, if you enable interrupts for the OPB GPIOi push buttons at the device, PIC, and processor levels then anytime a push button is either pressed or released your interrupt handler function will be invoked. Your interrupt handler needs to perform two major tasks. First, the interrupt handler must determine which interrupt in a multi-interrupt system occurred. For example, your system might include a serial port, two GPIOs, and an ethernet device which all generate interrupts. However, there is only one interrupt handler because the MicroBlazei soft-processori only responds to a single interrupt generated by the PIC.
To determine which interrupt occurred your interrupt handler must examine the registers in the PIC. In the case of the Xilinxi INTC PIC, there is one interesting registers for this purpose: the interrupt status register located at offset +0. Each bit in this register corresponds to a hardware interrupt. For instance, if you serial port is connected to interrupt 0 in the PIC then bit 0 of the interrupt status register will be '1' if the serial port generated an interrupt and '0' if it did not. Thus, when you interrupt handler is invoked the first thing that it should do is read the interrupt status register out of the PIC and determine which interrupts are active.
The second major task that your interrupt handler must do is process the interrupt. This is done is a device specific manner. For instance, when an OPB GPIOi device generated an interrupt you would probably read the GPIOi status register to determine what happened. In the case of a serial port device you might read a character out of the receive FIFO.
Acknowledging Interrupts
The last step required when handling an interrupt is acknowledgment. In this step, which keeps the hardware and software in sync with each other, the software acknowledges to the hardware that it has both received and processed the a specific interrupt. The hardware will wait for this acknowledgment before it generated another interrupt. Like interrupt enabling, interrupt acknowledgment must be done at both the device level and at the PIC level.
Acknowledging at the device level is obviously device specific. However, most devices operate in a similar manner. For instance acknowledging an interrupt for an OPB GPIOi device means writing a '1' to either bit 30 or 31 in the interrupt status register located at offset +0x120. Likewise, acknowledging an interrupt at the PIC involves writing a '1' to the appropriate bit of the interrupt acknowledge register located at offset +12. For instance, suppose an OPB GPIOi device connected to interrupt 1 of the PIC generates an interrupt. In order to acknowledge that the interrupt has been handled, the software must first write a '1' to bit 31 of the OPB GPIOi's interrupt status register and then write a '1' to bit 30 of the interrupt acknowledge register in the PIC.
