1. Concept of Interrupts
In operating systems, interrupts are a crucial component. When certain unexpected situations require host intervention, the machine can automatically stop the currently running program and switch to a program that handles the new situation. After processing, it returns to the original paused program to continue running.
With an interrupt system, polling for events can be avoided, which improves system efficiency.
Typically, in a system, interrupt control is divided into three parts: modules, interrupt controllers, and processors.
The module usually controls whether to enable interrupts and the conditions for triggering interrupts through registers; the interrupt controller can manage interrupt priorities, while the processor uses registers to respond to interrupts.
2. GIC
The Generic Interrupt Controller (GIC) is the universal interrupt controller in ARM systems, currently with four versions, V1 to V4 (V2 supports up to 8 ARM cores, while V3/V4 supports more ARM cores, primarily used in ARM64 architecture).
Note: For some older ARM processors, such as ARM11 and Cortex-A8, the interrupt controller is generally a VIC (Vector Interrupt Controller).
1. GIC-400
Taking GIC-400 as an example, it is better suited for embedded systems and complies with the v2 version of the GIC architecture specification. GIC-400 connects to one or more ARM processors via the AMBA (Advanced Microcontroller Bus Architecture) on-chip bus.
As shown in the figure above, the GIC is a bridge connecting peripheral interrupts and the CPU, and it is also a channel for interrupt interconnection between CPUs (which also has management functions). It is responsible for detecting, managing, and distributing interrupts, with capabilities to:
-
Enable or disable interrupts; -
Group interrupts into Group0 or Group1 (Group0 is used for secure mode connected to FIQ, Group1 is used for non-secure mode connected to IRQ); -
Distribute interrupts in multi-core systems to different processors; -
Set level-triggered or edge-triggered modes (not equal to the triggering modes of peripherals); -
Virtualization extensions.
ARM CPUs only have two external interrupts: IRQ and FIQ, corresponding to the general interrupt (IRQ) handling mode and the fast interrupt (FIQ) handling mode. Thus, the GIC ultimately consolidates interrupts into two lines to interface with the CPU.
The distributor: responsible for enabling various sub-interrupts, setting trigger modes, prioritizing, and distributing to specific CPUs; the interface: responsible for enabling overall interrupts and maintaining statuses.
2. Distributor Functions
The primary function of the distributor is to detect the status of each interrupt source, control the behavior of each interrupt source, and distribute interrupt events generated by each interrupt source to one or more CPU interfaces. Although the distributor can manage multiple interrupt sources, it always sends the highest priority interrupt request to the CPU interface. The distributor’s control over interrupts includes:
-
(a) Control of enabling or disabling interrupts. The distributor’s control over interrupts is divided into two levels: one is the control of global interrupts (GIC_DIST_CTRL). Once global interrupts are disabled, any interrupt events generated by any interrupt source will not be passed to the CPU interface; the other level is control over individual interrupt sources (GIC_DIST_ENABLE_CLEAR). Disabling a specific interrupt source will prevent that interrupt event from being distributed to the CPU interface, but it does not affect the distribution of interrupt events from other interrupt sources. -
(b) Control to distribute the currently highest priority interrupt event to one or a group of CPU interfaces. -
(c) Priority control. -
(d) Setting interrupt attributes, such as whether it is level-triggered or edge-triggered. -
(e) Setting interrupts.
The distributor can manage several interrupt sources, which are identified by IDs, referred to as interrupt IDs.
3. CPU Interface Functions
The CPU interface is mainly used for interfacing with the CPU.
Main functions include:
-
(a) Enable or disable the CPU interface from submitting interrupt events to the connected CPU. For ARM, the interrupt signal lines between the CPU interface and the CPU are nIRQCPU and nFIQCPU. If disabled, even if the distributor distributes an interrupt event to the CPU interface, it will not submit the specified nIRQ or nFIQ to notify the CPU. -
(b) Acknowledging interrupts. The CPU will respond to the CPU interface regarding the interrupt. Once the interrupt is acknowledged, the distributor will change the state of the interrupt from pending to active. If there are no subsequent pending interrupts, the CPU interface will deassert the nIRQCPU and nFIQCPU signal lines. If a new interrupt occurs during this process, the distributor will change the state of that interrupt from pending to pending and active. At this time, the CPU interface will still maintain the asserted state of the nIRQ or nFIQ signals, notifying the CPU of the next interrupt. -
(c) Notification that interrupt processing is complete. When the interrupt processor has handled an interrupt, it will write to the CPU interface register to notify the GIC that the interrupt has been processed. This action not only notifies the distributor to change the interrupt state to deactive but also allows other pending interrupts to submit to the CPU interface. -
(d) Setting priority masks. Through priority masks, some lower-priority interrupts can be masked, and these interrupts will not notify the CPU. -
(e) Setting interrupt preemption strategies. -
(f) When multiple interrupt events arrive simultaneously, select the one with the highest priority to notify the CPU.
In the above figure, the path for the interrupt signal generated by the key to reach the CPU is shown.
-
There are many peripheral interrupt sources; chip manufacturers usually design several first-level interrupt controllers for initial processing. The key connects to the GPX1 interrupt controller, and the register EXT_INT41_MASK is used to enable that interrupt; -
The GIC mainly includes the distributor and CPU interface; -
ICDISER is used to enable the distributor, and ICDIPTR is used to distribute interrupt signals to the corresponding CPU interface; -
ICCICR is used to enable the CPU interface; -
There are two pins on the CPU for irq and fiq; the gic will ultimately connect to the CPU’s irq. Once all registers are configured, when the key is pressed, an interrupt signal will be sent to the CPU’s irq, which will then execute the “4 big steps and 3 small steps” to enter the interrupt exception handling process.
3. Classification of Interrupts
1. Sources of Interrupts
Hardware Interrupts
-
Maskable Interrupts. A type of hardware interrupt that can be disabled by setting a bit mask in the interrupt mask register. -
Non-maskable Interrupts (NMI). A type of hardware interrupt that cannot be disabled by setting a bit mask in the interrupt mask register. A typical example is a clock interrupt (an interrupt generated by a hardware clock at a constant frequency, such as 50Hz). -
Interprocessor Interrupts. A special type of hardware interrupt issued by one processor and received by other processors. It is only seen in multiprocessor systems for interprocessor communication or synchronization. -
Spurious Interrupts. A type of hardware interrupt that is undesirable. There are many reasons for occurrence, such as electrical signal anomalies on the interrupt line or problems with the interrupt request device itself.
Software Interrupts
Software Interrupt (SWI) is a CPU instruction used to generate an interrupt. Since the soft interrupt instruction usually runs a subroutine that switches the CPU to kernel mode, it is often used to implement system calls.
External Interrupts
-
I/O Devices: such as displays, keyboards, printers, A/D converters, etc. -
Data Channels: floppy disks, hard disks, CDs, etc. Data channel interrupts are also known as direct memory access (DMA) operation interrupts, such as interrupts required for disks, tape drives, or CRTs that directly exchange data with memory. -
Real-time Clocks: such as external timing circuits. In control, when timing detection and control is encountered, an external clock circuit (programmable) is often used to control its time interval. When timing is needed, the CPU issues a command to start the clock circuit. Once the specified time is reached, the clock circuit issues an interrupt request, and the CPU turns to complete detection and control work. -
User Fault Sources: such as power outages, parity errors, external device failures, etc.
Internal Interrupt Sources from the CPU
-
Generated by the CPU’s execution results: such as division by zero, overflow, breakpoint interrupts, single-step interrupts, memory read errors, etc. -
Execution of the interrupt instruction SWI. -
Illegal operations or instructions causing exceptions.
2. Types of Interrupts
There are three types of GIC interrupts: SGI (Software-generated Interrupt), PPI (Private Peripheral Interrupt), and SPI (Shared Peripheral Interrupt).
-
SGI: SGI is an interrupt that can be triggered by software, uniformly numbered 0~15 (ID0-ID7 are non-secure interrupts, ID8-ID15 are secure interrupts), used for communication between cores. This type of interrupt is identified by the associated interrupt number and the CPUID of the processor that generated the interrupt. Typically, it is edge-triggered.
-
PPI: PPI is a private peripheral interrupt for each core, uniformly numbered 16-31. For example, the local timer of each CPU, namely the Arch Timer, generates interrupts sent to the CPU via PPI (secure is 29, non-secure is 30).
Typically, they are edge-triggered and level-triggered.
-
SPI: SPI is an interrupt generated by system peripherals, which are shared interrupts for each core, uniformly numbered 32~1019, such as interrupts generated by global timers, UARTs, and GPIOs. Typically, they are edge-triggered and level-triggered.
Note: Level-triggered interrupts are triggered while the high or low level is maintained, while edge-triggered interrupts are triggered at the moment of transition from high to low or from low to high; in the GIC, PPI and SGI types of interrupts can have the same interrupt ID.
3. Interrupt Dispatch Modes
-
1-N mode (SPIs using the GIC 1-N model) indicates that an interrupt can be sent to all CPUs, but only one CPU can handle the interrupt; in other words, this type of interrupt has N target CPUs, but only one can handle it; when a specific processor acknowledges the interrupt, it will clear the pending state of that interrupt on all target processors.
-
N-N mode (PPIs and SGIs using the GIC N-N model) indicates that interrupts can be sent to all CPUs, and each CPU can handle that interrupt simultaneously. When the interrupt is acknowledged by one processor, it does not affect the status of that interrupt on other CPU interfaces.
Two examples to illustrate:
1) When a UART receives a packet of data, it generates an interrupt to the GIC, which can assign that interrupt to any of CPU0-7 for processing; assuming that the interrupt is assigned to CPU0 for processing, the interrupt handling function will read the received data from the UART FIFO. Imagine if CPU0 is reading data while another CPU is also handling that interrupt and happens to read data at the same time, the data read by CPU0 will be incomplete. This is the 1-N model interrupt or SPI interrupt.
2) For example, CPU0 sends an interrupt to CPUs 1-7 to inform them that it is processing a certain process A. In this scenario, CPUs 1-7 all receive the interrupt and enter the interrupt handling function. After obtaining the information from CPU0, during process scheduling, they can bypass process A and schedule other processes.
Note: This example only illustrates the N-N model; in reality, process scheduling is not always like this.
4. General Interrupt Handling
When the GIC receives an interrupt request, it sets its status to Pending. Re-generating a pending interrupt does not affect the status of that interrupt.
The order of interrupt handling:
① The GIC determines whether the interrupt is enabled; if it is not enabled, it has no effect on the GIC;
② For each Pending interrupt, the GIC determines the target processor;
③ For each processor, the Distributor decides the highest priority pending interrupt based on its interrupt priority information and passes that interrupt to the target CPU Interface;
④ After the GIC Distributor passes an interrupt to the CPU Interface, that CPU Interface decides whether that interrupt has a sufficient priority to send the interrupt request to the CPU;
⑤ When the CPU begins to handle that exception interrupt, it reads GICC_IAR to acknowledge the interrupt. The read GICC_IAR obtains the interrupt ID, and for SGIs, the source processor ID. The interrupt ID is used to find the correct interrupt handler.
After the GIC identifies the read process, it changes the status of that interrupt:
a) When the interrupt status changes to active, if the pending status of that interrupt continues to exist or the interrupt occurs again, the interrupt status will change from Pending to pending & active.
b) Otherwise, the interrupt status will change from pending to active.
⑥ When the interrupt processing is completed, it needs to notify the GIC that the processing is finished. This process is called priority drop and interrupt deactivation:
a) It is always necessary to write a valid value to the EOIR register (end of interrupt register).
b) It is also necessary to write a value to the GICC_DIR (deactivate interrupt register).
5. Interrupt Priorities
Software can configure interrupt priorities by assigning priority values to each interrupt source. The priority value is an 8-bit unsigned binary number, and the GIC supports a minimum of 16 and a maximum of 256 priority levels.
If the priority implemented by the GIC is less than 256, then the lower-order bits of the priority field are RAZ/WI. This means that the range of the number of implemented priority fields is 4 to 8, as shown in the figure below:
Note:
1) How to determine the supported priority bits of the priority field? By writing 0XFF to the writable GICD_IPRIORITYn priority field and then reading back the value of that field, you can determine the supported priority bits of the priority field (because some bits are not implemented as RAZ/WI).
2) ARM recommends that before checking the interrupt priority range, the software first disables that interrupt for peripheral interrupts and checks that the SGI interrupt is determined to be inactive.
6. Interrupt Preemption
Before an active interrupt is completed, the CPU interface supports sending a higher-priority pending interrupt to the target processor. The necessary conditions for this situation are:
-
The priority of that interrupt is higher than the currently masked priority of the CPU interface. -
The group priority of that interrupt is higher than the priority of the currently handled interrupt in the CPU interface.
7. Interrupt Masking
The GICC_PMR register of the CPU interface defines the priority threshold of the target processor. The GIC only reports pending interrupts that are above the threshold to the target processor. The initial value of the register is 0, masking all interrupts.
4. FS4412 Interrupt Peripheral – Key
Next, we will analyze the first interrupt device key on the FS4412 development board.
1. Circuit Diagram
From this circuit diagram, we can deduce:
-
The key k2 is connected to pin GPX1_1.
-
Control logic: k2 pressed —- K2 closed —- GPX1_1 low voltage; k2 normal state —- K2 open —- GPX1_1 high voltage.
Below is the connection of key2 to the SoC.
It can be seen that key2 reuses the GPX1_1 pin, and this pin can also be used as an interrupt [XEINT9].
Let’s take a look at the configuration of the GPXCON register.
As shown in the figure above:
-
GPX1CON address is 0x1100C20; -
To use key2 as an input device, simply set GPX1CON[7:4] to 0x0; -
To use key2 as an interrupt signal, simply set GPX1CON[7:4] to 0xf.
2. Key Interrupt Handling
Interrupt Configuration
The relationship diagram of the key and SoC is shown in the following figure:
As shown in the figure above:
-
The key is directly connected to the GPIO controller. -
EXT_INT_CON is used to set the trigger mode of the key interrupt, which is triggered on the falling edge. -
The GPX1CON register is used to set the GPIO bit as an interrupt signal input. -
EXT_INT_MASK is used to enable that interrupt. -
ICDISER is used to enable the corresponding interrupt to the distributor. -
ICDDCR is the distributor switch. -
ICDIPTR selects the CPU interface. -
ICCPMR sets the interrupt masking priority. -
ICCICR opens the CPU switch to allow interrupts to be sent to the corresponding CPU.
Clearing Interrupts
After the CPU processes the interrupt, it needs to clear the interrupt. For the key, there are three registers that need to be operated:
As shown in the figure above:
-
EXT_INT41_PEND clears the corresponding interrupt source. -
ICDICPR clears the corresponding interrupt flag after the interrupt ends; this flag is set by hardware. -
ICCEOIR clears the corresponding interrupt number in the CPU after the interrupt execution is completed, filled by hardware.
3. Register Summary
Previously analyzed that the key is connected to GPX1_1. Now let’s see how to configure the corresponding registers.
[1] GPIO Controller
-
GPX1PUD
Disable pull-up and pull-down for GPX1_1 pin.
GPX1PUD[3:2]= 0b00;
-
GPX1CON
Set the function of GPX1_1 pin to interrupt function.
GPX1CON[7:4] = 0xf;
-
EXT_INT41CON
Configure for falling edge trigger:
EXT_INT41CON[6:4] = 0x2;
-
EXT_INT41_MASK
Interrupt enable register
EXT_INT41_MASK[1] = 0b0;
-
EXT_INT41_PEND Interrupt Status Register
When the GPX1_1 pin receives an interrupt signal, the interrupt occurs, and the corresponding bit in the interrupt status register EXT_INT41_PEND will automatically be set to 1. Note: When the interrupt processing is completed, the corresponding status bit needs to be cleared. Set 1 to clear 0.
EXT_INT41_PEND[1] =0b1;
[2] GIC
Check the HW ID maintained by the GIC for the peripheral interrupt named EINT9. [All interrupt sources are assigned a unique ID during chip design, and the GIC drives the interrupt source through that ID.]
Check the chip manual (datasheet – Table 9.2)
From the [9.2 Interrupt Source Table], find the corresponding interrupt control interrupt identifier (GPIO has 32 wake-up registers) for the peripheral interrupt indicator, which is EINT[9], interrupt ID 57. This is very important and plays a significant role in the subsequent register settings.
1) ICDISER enables the corresponding interrupt to the distributor.
ICDISER is used to enable the corresponding interrupt to the distributor; one bit controls one interrupt source, and one ICDISER can control 32 interrupt sources. Here, INT[9] corresponds to interrupt ID 57, so it is set in ICDISER1, 57/32 = 1 remainder 25, so set bit 25 in ICDISER1.
ICDISER.ICDISER1 |= (0x1 << 25); //57/32 =1...25取整数(那个寄存器)和余数(哪位)
-
ICDIPTR selects the CPU interface.
ICDIPTR register controls one interrupt source for every 8 bits, and CPU0 can handle 160 interrupt sources, so 40 registers are needed. To select CPU0, the first bit must be 1.
Set SPI[25]/ID[57] to be processed by CPU0, 57/4=16 remainder 1, so set the second byte [15:8] of register ICDIPTR14.
//SPI 25 interrupts are sent to processor 0
//57/4 = 14..1 14号寄存器的[15:8]
ICDIPTR.ICDIPTR14 |= 0x01<<8;
-
ICDDCR enables the distributor.
The register is used to enable the distributor.
ICDDCR =1;
-
ICCPMR Priority Mask Register, set CPU0 to handle all interrupts. For example, setting the interrupt masking priority to 255 means the lowest priority, and all interrupts can respond.
CPU0.ICCPMR = 0xFF;//设置cpu0中断屏蔽优先级为255,最低,所有中断都能响应)
-
ICCICR Globally enables CPU0 interrupt handling.
EXYNOS 4412 has a total of 4 CPUs, controlled by 4 registers, with each register’s bit[0] used for global control of the corresponding CPU. We choose CPU0 to handle interrupts by setting bit[0] to 1.
CPU0.ICCICR |= 0x1;
使能中断到CPU。
-
ICCIAR
When an interrupt occurs, the HW ID value of the interrupt will be written by hardware into the register ICCIAR[9:0]; for SGIs in a multiprocessor environment, the CPU interface value will be written to [12:10].
Read HW ID:
int irq_num;
irq_num = CPU0.ICCIAR&0x3ff; //获取中断号
5. Code Implementation
To handle interrupt exceptions, the exception vector table must be installed. The handling process of exceptions can refer to the previous article “6. Learning ARM from Scratch – Exceptions, Exception Vector Table, SWI”.
1. Base Address of Exception Vector Table
The address of the exception vector table can be modified. For example, when U-Boot starts, it will move code from flash to RAM, and the addresses of the exception vector table in flash and RAM are definitely different. Therefore, after moving the code, the corresponding address of the exception vector table must be modified.
Modifying the address of the exception vector table requires the use of coprocessor instructions mcr:
ldr r0,=0x40008000
mcr p15,0,r0,c12,c0,0 @ Vector Base Address Register
The above command sets the address 0x40008000 as the address of the exception vector table. Regarding the mcr instruction, we do not need to delve into it; just knowing is sufficient.
The address of the exception vector table in RAM is chosen as 0x40008000. Below is the address space distribution of Exynos4412.
2. Installation of Exception Vector Table
.text
.global _start
_start:
b reset
ldr pc,_undefined_instruction
ldr pc,_software_interrupt
ldr pc,_prefetch_abort
ldr pc,_data_abort
ldr pc,_not_used
ldr pc,=irq_handler
ldr pc,_fiq
reset:
ldr r0,=0x40008000
mcr p15,0,r0,c12,c0,0 @ Vector Base Address Register
init_stack:
//初始化栈
……
b main //跳转至c的main函数
irq_handler: //中断入口函数
sub lr,lr,#4
stmfd sp!,{r0-r12,lr}
.weak do_irq
bl do_irq
ldmfd sp!,{r0-r12,pc}^
stacktop: .word stack+4*512//栈顶
.data
stack: .space 4*512 //栈空间
Interrupt entry function do_irq()
void do_irq(void)
{
static int a = 1;
int irq_num;
irq_num = CPU0.ICCIAR&0x3ff; //获取中断号
switch(irq_num)
{
case 57:
printf("in the irq_handler\n");
//清GPIO中断标志位
EXT_INT41_PEND = EXT_INT41_PEND |((0x1 << 1));
//清GIC中断标志位
ICDICPR.ICDICPR1 = ICDICPR.ICDICPR1 | (0x1 << 25);
break;
}
//清cpu中断标志
CPU0.ICCEOIR = CPU0.ICCEOIR&(~(0x3ff))|irq_num;位
}
Implement the key interrupt initialization function key_init():
void key_init()
{
GPX1.CON =GPX1.CON & (~(0xf << 4)) |(0xf << 4); //配置引脚功能为外部中断
GPX1.PUD = GPX1.PUD & (~(0x3 << 2)); //关闭上下拉电阻
EXT_INT41_CON = EXT_INT41_CON & (~(0xf << 4))|(0x2 << 4); //外部中断触发方式
EXT_INT41_MASK = EXT_INT41_MASK & (~(0x1 << 1)); //使能中断
ICDDCR = 1; //使能分配器
ICDISER.ICDISER1 = ICDISER.ICDISER1 | (0x1 << 25); //使能相应中断到分配器
ICDIPTR.ICDIPTR14 = ICDIPTR.ICDIPTR14 & (~(0xff << 8))|(0x1 << 8); //选择CPU接口
CPU0.ICCPMR = 255; //中断屏蔽优先级
CPU0.ICCICR = 1; //使能中断到CPU
return ;
}
6. Polling Method
In addition to the interrupt method, we can also read the key information through polling. The principle is as follows:
Loop to detect the input level of GPX1_1 pin; when low voltage, the key is pressed; when high voltage, the key is released.
-
Configure the GPX1_1 pin function as input, and set internal pull-up and pull-down to disabled.
GPX1.CON = GPX1.CON & (~(0xf << 4));
GPX1.PUD = GPX1.PUD & ~(0x3 << 2);
-
Key Debouncing: After pressing the key, due to mechanical characteristics, there will be a brief moment when the level fluctuates between 0 and 1, so after detecting that the key is pressed, we need to provide a delay and then check if the key is still pressed.
-
Code Implementation
int main (void)
{
led_init();
pwm_init();
GPX1.CON = GPX1.CON & (~(0xf << 4))|0x0<<4;
while(1)
{
if(!(GPX1.DAT & (0x1<<1))) // Returns true, key pressed
{
delay_ms(10);
if(!(GPX1.DAT & (0x1<<1))) // Second detection, debounce
{
GPX2.DAT |= 0x1 << 7; //Turn on LED2
delay_ms(500);
beep_on();
GPX2.DAT &= ~(0x1<<7); //Turn off LED2
delay_ms(500);
while(!(GPX1.DAT & (0x1<<1)));
beep_off();
}
}
}
return 0;
}
Recommended Reading
To join the group, please add my personal WeChat, and I will guide you through embedded systems from beginner to advanced.
Leave a Comment
Your email address will not be published. Required fields are marked *