Abstract: This article leads everyone to analyze the source code of the interrupt module of the Hongmeng Light Core, and grasp the concepts related to interrupts, interrupt initialization operations, interrupt creation, deletion, switch interrupt operations, etc.
This article is shared from the Huawei Cloud Community " Light Kernel M Core Source Code Analysis Series Five Interrupt Hwi ", the original author: zhushy.
In this article, we will talk about interrupts and introduce the concept of interrupts to readers, the source code of the interrupt module of the Hongmeng light kernel. The source code involved in this article, taking the OpenHarmony LiteOS-M kernel as an example, can be obtained from the open source site https://gitee.com/openharmony/kernel_liteos_m .
1. Interruption concept introduction
Interruption refers to the process in which the CPU suspends the execution of the current program when necessary, and then executes a new program. When the peripheral needs the CPU, it will respond to the interrupt request by generating an interrupt signal to make the CPU immediately interrupt the current task. Before analyzing the interrupt source code, the following introduces some interrupt-related hardware and interrupt-related concepts.
1.1 Introduction to interrupt related hardware
Hardware related to interrupts can be divided into three categories: equipment, interrupt controller, and CPU itself.
- equipment
The source that initiates the interrupt. When the device needs to request the CPU, it generates an interrupt signal, which is connected to the interrupt controller.
- Interrupt controller
The interrupt controller is one of the many peripherals of the CPU. On the one hand, it receives input from other peripheral interrupt pins. On the other hand, it will send an interrupt signal to the CPU. The interrupt source can be turned on and off, and the priority and trigger mode of the interrupt source can be set by programming the interrupt controller.
- CPU
The CPU will respond to the request of the interrupt source, interrupt the task currently being executed, and execute the interrupt handler instead.
1.2 Interrupt related concepts
- Interrupt number
Each interrupt request signal will have a specific flag, so that the computer can determine which device made the interrupt request. This flag is the interrupt number.
- Interrupt priority
In order to enable the system to respond to and handle all interrupts in a timely manner, the system divides the interrupt sources into several levels according to the importance and urgency of the interrupt time, called interrupt priority.
- Interrupt handler
When the peripheral generates an interrupt request, the CPU suspends the current task, and then responds to the interrupt request, that is, executes the interrupt processing program. Each device that generates an interrupt has a corresponding interrupt handler.
- Interrupt vector
The entry address of the interrupt service routine.
- Interrupt vector table
The storage area for storing the interrupt vector, the interrupt vector corresponds to the interrupt number, and the interrupt vector is stored in the interrupt vector table in the order of the interrupt number.
- Interrupt sharing
When there are fewer peripherals, one peripheral can correspond to an interrupt number, but in order to support more hardware devices, multiple devices can share an interrupt number, and the interrupt handlers sharing the same interrupt number form a linked list. When an external device generates an interrupt request, the system will traverse the linked list of interrupt handlers corresponding to the interrupt number until it finds the interrupt handler of the corresponding device. In the traversal execution process, each interrupt handler can determine whether it is an interrupt generated by the device corresponding to this interrupt handler by detecting the device ID.
Next, let's take a look at the Hongmeng light kernel interrupt source code.
2. Hongmeng light kernel interrupt source code
2.1 Interrupt-related declarations and definitions
Some structures, global variables, and inline functions are defined in the file kernel\arch\arm\cortex-m7\gcc\los_interrupt.c. Before analyzing the source code, let's take a look at these definitions and declarations. All variables g_intCount represent the number of interrupts being processed. Each time the interrupt processing program is entered, the value of this variable will be increased by 1, and when the interrupt processing is completed, the value will be decreased by 1. The corresponding inline function HalIsIntActive() is used to obtain whether the interrupt is being processed, and the return value is greater than 0, indicating that the interrupt is being processed.
UINT32 g_intCount = 0;
inline UINT32 HalIsIntActive(VOID)
{
return (g_intCount > 0);
}
Let's look at the interrupt vector table definition again. ⑴ The code defines the array g_hwiForm[OS_VECTOR_CNT] for the interrupts supported by the system. For each interrupt number hwiNum, the corresponding array element g_hwiForm[hwiNum] represents the interrupt processing execution entry program corresponding to each interrupt. (2) The macro OS_HWI_WITH_ARG at ⑵ indicates whether the interrupt handler supports parameter input, which is closed by default. If parameter transfer is supported, define the structure HWI_HANDLER_FUNC at ⑶ to maintain the interrupt handling function and its parameters, and also need to define the g_hwiHandlerForm array at ⑷. If parameter passing is not supported, use the g_hwiHandlerForm array defined at ⑹. For each interrupt number hwiNum, the corresponding array element g_hwiHandlerForm[hwiNum] represents the interrupt handler corresponding to each interrupt. ⑸, ⑺ define a function OsSetVector() to set the interrupt processing execution entry program and interrupt processing program corresponding to the specified interrupt number. The relationship between the interrupt processing execution entry program and the interrupt processing program is that when an interrupt occurs, the interrupt processing execution entry program will be executed, and this function will further call the interrupt processing program.
⑴ STATIC HWI_PROC_FUNC __attribute__((aligned(0x100))) g_hwiForm[OS_VECTOR_CNT] = {0};
⑵ #if (OS_HWI_WITH_ARG == 1)
⑶ typedef struct {
HWI_PROC_FUNC pfnHandler;
VOID *pParm;
} HWI_HANDLER_FUNC;
⑷ STATIC HWI_HANDLER_FUNC g_hwiHandlerForm[OS_VECTOR_CNT] = {{ (HWI_PROC_FUNC)0, (HWI_ARG_T)0 }};
⑸ VOID OsSetVector(UINT32 num, HWI_PROC_FUNC vector, VOID *arg)
{
if ((num + OS_SYS_VECTOR_CNT) < OS_VECTOR_CNT) {
g_hwiForm[num + OS_SYS_VECTOR_CNT] = (HWI_PROC_FUNC)HalInterrupt;
g_hwiHandlerForm[num + OS_SYS_VECTOR_CNT].pfnHandler = vector;
g_hwiHandlerForm[num + OS_SYS_VECTOR_CNT].pParm = arg;
}
}
#else
⑹ STATIC HWI_PROC_FUNC g_hwiHandlerForm[OS_VECTOR_CNT] = {0};
⑺ VOID OsSetVector(UINT32 num, HWI_PROC_FUNC vector)
{
if ((num + OS_SYS_VECTOR_CNT) < OS_VECTOR_CNT) {
g_hwiForm[num + OS_SYS_VECTOR_CNT] = HalInterrupt;
g_hwiHandlerForm[num + OS_SYS_VECTOR_CNT] = vector;
}
}
#endif
2.2 Interrupt initialization HalHwiInit()
When the system starts, call HalArchInit() in kernel\src\los_init.c for interrupt initialization. This function is defined in kernel\arch\arm\cortex-m7\gcc\los_context.c, and then further calls the function HalHwiInit() defined in the kernel\arch\arm\cortex-m7\gcc\los_interrupt.c file to complete the interrupt vector initialization . Let's analyze the code.
The macro LOSCFG_USE_SYSTEM_DEFINED_INTERRUPT indicates whether to use the pre-defined vector base address and interrupt handler of the system, which is enabled by default. ⑴ At the beginning, the 0th interrupt of the interrupt vector table is set to be empty, and the 1st interrupt corresponds to the reset handler Reset_Handler. (2) Set the rest of the interrupts as the default interrupt processing execution entry program HalHwiDefaultHandler(). (3) Set the system interrupt (an exception is a kind of interrupt, and the system interrupt is also called an exception). The execution entry function of the system interrupt is defined in kernel\arch\arm\cortex-m7\gcc\los_exc.S, which is implemented in assembly language. Among the system interrupts, the 14th interrupt corresponds to the HalPendSV handler, which is used for task context switching, and the 15th interrupt is a tick interrupt.
Execute the code at ⑷ to assign the interrupt vector table to SCB->VTOR. For Cortex-M3 and above CPU cores, it is also necessary to execute (5) to set the priority group. ⑹ The code enables the specified exception.
LITE_OS_SEC_TEXT_INIT VOID HalHwiInit()
{
#if (LOSCFG_USE_SYSTEM_DEFINED_INTERRUPT == 1)
UINT32 index;
⑴ g_hwiForm[0] = 0; /* [0] Top of Stack */
g_hwiForm[1] = Reset_Handler; /* [1] reset */
⑵ for (index = 2; index < OS_VECTOR_CNT; index++) { /* 2: The starting position of the interrupt */
g_hwiForm[index] = (HWI_PROC_FUNC)HalHwiDefaultHandler;
}
/* Exception handler register */
⑶ g_hwiForm[NonMaskableInt_IRQn + OS_SYS_VECTOR_CNT] = HalExcNMI;
g_hwiForm[HARDFAULT_IRQN + OS_SYS_VECTOR_CNT] = HalExcHardFault;
g_hwiForm[MemoryManagement_IRQn + OS_SYS_VECTOR_CNT] = HalExcMemFault;
g_hwiForm[BusFault_IRQn + OS_SYS_VECTOR_CNT] = HalExcBusFault;
g_hwiForm[UsageFault_IRQn + OS_SYS_VECTOR_CNT] = HalExcUsageFault;
g_hwiForm[SVCall_IRQn + OS_SYS_VECTOR_CNT] = HalExcSvcCall;
g_hwiForm[PendSV_IRQn + OS_SYS_VECTOR_CNT] = HalPendSV;
g_hwiForm[SysTick_IRQn + OS_SYS_VECTOR_CNT] = SysTick_Handler;
/* Interrupt vector table location */
⑷ SCB->VTOR = (UINT32)(UINTPTR)g_hwiForm;
#endif
#if (__CORTEX_M >= 0x03U) /* only for Cortex-M3 and above */
⑸ NVIC_SetPriorityGrouping(OS_NVIC_AIRCR_PRIGROUP);
#endif
/* Enable USGFAULT, BUSFAULT, MEMFAULT */
⑹ *(volatile UINT32 *)OS_NVIC_SHCSR |= (USGFAULT | BUSFAULT | MEMFAULT);
/* Enable DIV 0 and unaligned exception */
*(volatile UINT32 *)OS_NVIC_CCR |= DIV0FAULT;
return;
}
2.3 Create interrupt UINT32 HalHwiCreate()
Developers can call the function UINT32 HalHwiCreate() to create interrupts and register interrupt handlers. Let's take a look at the parameters of this function first, HWI_HANDLE_T hwiNum is the hardware interrupt number, HWI_PRIOR_T hwiPrio interrupt priority, HWI_MODE_T mode interrupt mode, reserved temporarily unused. HWI_PROC_FUNC handler is an interrupt handler that needs to be registered. This function will be called after the interrupt is triggered. HWI_ARG_T arg is the parameter of the interrupt handler.
Analyze the source code of this function together. At the beginning of the code, check the input parameters. The interrupt handler cannot be empty, the interrupt number cannot be greater than the maximum supported interrupt number, and the interrupt priority cannot exceed the specified priority. If the interrupt execution entry program corresponding to the interrupt number to be created is not equal to HalHwiDefaultHandler, it means that it has been created and an error code is returned. Turn off the interrupt, and then execute the OsSetVector() function at ⑵ to set the interrupt handler of the specified interrupt number. (3) Call the CMSIS function to enable the interrupt, set the priority of the interrupt, open the interrupt, and complete the creation of the interrupt.
LITE_OS_SEC_TEXT_INIT UINT32 HalHwiCreate(HWI_HANDLE_T hwiNum,
HWI_PRIOR_T hwiPrio,
HWI_MODE_T mode,
HWI_PROC_FUNC handler,
HWI_ARG_T arg)
{
UINTPTR intSave;
⑴ if (handler == NULL) {
return OS_ERRNO_HWI_PROC_FUNC_NULL;
}
if (hwiNum >= OS_HWI_MAX_NUM) {
return OS_ERRNO_HWI_NUM_INVALID;
}
if (g_hwiForm[hwiNum + OS_SYS_VECTOR_CNT] != (HWI_PROC_FUNC)HalHwiDefaultHandler) {
return OS_ERRNO_HWI_ALREADY_CREATED;
}
if (hwiPrio > OS_HWI_PRIO_LOWEST) {
return OS_ERRNO_HWI_PRIO_INVALID;
}
intSave = LOS_IntLock();
#if (OS_HWI_WITH_ARG == 1)
OsSetVector(hwiNum, handler, arg);
#else
⑵ OsSetVector(hwiNum, handler);
#endif
⑶ NVIC_EnableIRQ((IRQn_Type)hwiNum);
NVIC_SetPriority((IRQn_Type)hwiNum, hwiPrio);
LOS_IntRestore(intSave);
return LOS_OK;
}
2.4 Delete interrupt UINT32 HalHwiDelete()
The interrupt delete operation is the reverse operation of the create operation, and it is also easier to understand. Developers can call the function UINT32 HalHwiDelete(HWI_HANDLE_T hwiNum) to delete the interrupt. The function needs to specify the interrupt number parameter HWI_HANDLE_T hwiNum. Analyze the source code of this function together, (1) The code verifies the input parameters and cannot be greater than the maximum interrupt number supported. (2) Call the CMSIS function to disable the interrupt, then lock the interrupt, execute (3) Set the interrupt execution entry program of the interrupt number specified by the interrupt vector table as the default program HalHwiDefaultHandler.
LITE_OS_SEC_TEXT_INIT UINT32 HalHwiDelete(HWI_HANDLE_T hwiNum)
{
UINT32 intSave;
⑴ if (hwiNum >= OS_HWI_MAX_NUM) {
return OS_ERRNO_HWI_NUM_INVALID;
}
⑵ NVIC_DisableIRQ((IRQn_Type)hwiNum);
intSave = LOS_IntLock();
⑶ g_hwiForm[hwiNum + OS_SYS_VECTOR_CNT] = (HWI_PROC_FUNC)HalHwiDefaultHandler;
LOS_IntRestore(intSave);
return LOS_OK;
}
2.5 Interrupt processing execution entry program
Let's take a look at the interrupt processing execution entry program. The default function HalHwiDefaultHandler() is as follows, call the function HalIntNumGet() to obtain the interrupt number, print out, and then perform an endless loop. The function HalIntNumGet() reads the register ipsr to obtain the interrupt number of the triggered interrupt.
LITE_OS_SEC_TEXT_MINOR VOID HalHwiDefaultHandler(VOID)
{
UINT32 irqNum = HalIntNumGet();
PRINT_ERR("%s irqNum:%d\n", __FUNCTION__, irqNum);
while (1) {}
}
Continue to look at the interrupt processing execution entry program HalInterrupt(), the source code is as follows.
At ⑴, the number of interrupts being processed represented by the global variable g_intCount is increased by 1, and after the interrupt is executed, the number of interrupts being processed is reduced by 1 at ⑹. (2) Call the function HalIntNumGet() to get the interrupt number, (3) and (5) call the function HalPreInterruptHandler(), HalAftInterruptHandler() can handle some other operations before and after the interrupt handler is executed. The current default is an empty function. ⑷According to the interrupt number, the interrupt handler is obtained from the interrupt handler array, and it is called and executed if it is not empty.
3. The switch is interrupted
Finally, share the relevant knowledge of opening and closing interrupts. The opening and closing interrupts refer to:
- Open interrupt
After executing the specific short program, open the interrupt, you can respond to the interrupt.
- Off interrupt
In order to protect the executing program from being interrupted, turn off the corresponding external interrupt.
The corresponding opening and closing interrupt functions are defined in the file kernel\arch\include\los_context.h, and the code is as follows. ⑴ UINT32 LOS_IntLock(VOID) at the place will close the interrupt, and suspend the response to the interrupt. ⑵The function VOID LOS_IntRestore(UINT32 intSave) can be used to restore the interrupt that the UINT32 LOS_IntLock(VOID) function closed. The return value of UINT32 LOS_IntLock(VOID) is used as the parameter of VOID LOS_IntRestore(UINT32 intSave) to restore the interrupt. The function UINT32 LOS_IntUnLock(VOID) at ⑶ enables the interrupt and can respond to the interrupt.
UINTPTR HalIntLock(VOID);
⑴ #define LOS_IntLock HalIntLock
VOID HalIntRestore(UINTPTR intSave);
⑵ #define LOS_IntRestore HalIntRestore
UINTPTR HalIntUnLock(VOID);
⑶ #define LOS_IntUnLock HalIntUnLock
It can be seen that LOS_IntLock, LOS_IntRestore and LOS_IntUnLock are defined macros. They correspond to the assembly functions defined in the file kernel\arch\arm\cortex-m7\gcc\los_dispatch.S. The source code is as follows. We analyze these assembly functions. The PRIMASK register is a single-bit register. After it is set to 1, all maskable exceptions are turned off, and only NMI and HardFault exceptions can be responded to. The default value is 0, which means that the interrupt is not turned off. The assembly instruction CPSID I will set PRIMASK=1 to turn off the interrupt, and the instruction CPSIE I will set PRIMASK=0 to turn on the interrupt.
(1) The HalIntLock function writes the value of the register PRIMASK into the register R0 and returns, and executes CPSID I to close the interrupt. (2) The HalIntUnLock function writes the value of the register PRIMASK into the register R0 and returns, and executes the instruction CPSIE I to turn on the interrupt. The return results of the two functions can be passed to the HalIntRestore function at ⑶, and the register state value is written into the register PRIMASK to restore the previous interrupt state. Either HalIntLock or HalIntUnLock can be paired with ArchIntRestore.
.type HalIntLock, %function
.global HalIntLock
HalIntLock:
.fnstart
.cantunwind
⑴ MRS R0, PRIMASK
CPSID I
BX LR
.fnend
.type HalIntUnLock, %function
.global HalIntUnLock
HalIntUnLock:
.fnstart
.cantunwind
⑵ MRS R0, PRIMASK
CPSIE I
BX LR
.fnend
.type HalIntRestore, %function
.global HalIntRestore
HalIntRestore:
.fnstart
.cantunwind
⑶ MSR PRIMASK, R0
BX LR
.fnend
summary
This article led everyone to analyze the source code of the interrupt module of the Hongmeng Light Core, and grasp the concepts related to interrupts, interrupt initialization operations, interrupt creation, deletion, switch interrupt operations, etc. More sharing articles will be released in the follow-up, so stay tuned, and you are welcome to share your experience of learning and using the light kernel. If you have any questions or suggestions, you can leave a message to us: 160c179301fefb https://gitee.com/openharmony /kernel_liteos_m/issues . light kernel code warehouse easier, it is recommended to visit 160c179301ff02 https://gitee.com/openharmony/kernel_liteos_m , follow Watch, like Star, and Fork to your account, thank you.
Click to follow, and get to know the fresh technology of Huawei Cloud for the first time~
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。