When we last looked at the Zynq-based MicroZed low-cost development board, we focused on a polled-I/O example that read the state of a push-button switch. In that previous post, I mentioned that it was possible to also use an interrupt-driven approach, which is the approach typically used in most embedded systems. An interrupt-driven approach is actually required in many systems that have a number of inputs keyboards, mice, push buttons, sensors etc. Inputs from these devices are normally asynchronous to the process or task currently executing and a polled-I/O approach is often too inefficient for systems with many input devices, depending upon the amount of processing required. You cannot always predict when the event will occur so you must check I/O status frequently using a polled-I/O approach.
The polling routine will usually obtain a null result and that’s inefficient. Using interrupts enables the processor to continue with other processing until an event occurs, thus greatly improving execution efficiency. When there is an I/O event, the resulting interrupt causes the processor to branch and address the I/O event.
At the highest level, interrupts can be split into two types: maskable and non-maskable. But things are no longer that simple. As processors have gotten more advanced, the number of interrupt sources has exploded. Consequently, the dual-core Xilinx Zynq All Programmable SoC uses an ARM Generic Interrupt Controller (GIC) to process interrupts, which can come from:
Software-Generated Interrupts – 16 for each processor. Software-generated interrupts can interrupt themselves and either or both processors.
Shared Peripheral Interrupts – 60 in total. These interrupts can come from the I/O Peripherals in the Zynq SoC’s Processor System (PS) or from the Programmable Logic (PL) side of the device. The two ARM Cortex-A9 MPCore CPU’s share these interrupts.
Private Peripheral Interrupts – 5 interrupts, which are private to each CPU (e.g. CPU timer, CPU watchdog timer, and dedicated PL-to-CPU interrupt).
Below is a block diagram of the Zynq SoC with the GIC circled in red. Note how the GIC is located immediately adjacent to the two ARM Cortex-A9 MPCore processors:
The Zynq SoC Generic Interrupt Controller (GIC) circled in red
The shared peripheral interrupts are very interesting because they are very flexible. You can route them to either of the ARM Cortex-A9 MPCore CPUs from the I/O peripherals (44 interrupts in total) or from the FPGA logic (16 interrupts in total). It is also possible to route interrupts from the I/O periphery to the PL section of the device, allowing the programmable logic in the Zynq SoC to offload the processors when the application requires it.
Interrupts from the PS IOP to the PL
Before I explain how to set up interrupts on the Zynq, I think it’s a good idea to recap how the ARM Cortex-A9 MPCore processor handles interrupts. When an interrupt occurs, the following events take place:
The interrupt is shown as pending.
The processor stops executing the current thread.
The processor saves the state of the current thread on the stack to allow the interrupted processing to continue once the interrupt has been handled.
The processor executes the interrupt service routine (ISR), which defines how the interrupt is to be handled.
The processor resumes operation of the interrupted thread after restoring it from the stack.
Because interrupts are asynchronous events, it is possible that multiple interrupts will occur at the same time. Consequently, interrupts are prioritized so that the processor can first service the highest-priority interrupt pending.
The next blog in this series will look at how to initialize and use the Zynq SoC’s GIC.
Note: Please see the previous entries in this MicroZed series by Adam Taylor: