Abstract: This paper leads you to analyze the source code of the interrupt module of Hongmeng light kernel, master the concepts related to interrupt, interrupt initialization operation, interrupt creation, deletion, switch interrupt operation, etc.
This article is shared from Huawei cloud community< Hongmeng light kernel M core source code analysis Series 5 # interrupt Hwi >, original author: zhushy .
In this article, we will talk about interrupt and introduce the concept of interrupt and the source code of interrupt module of Hongmeng light kernel to readers. The source code involved in this article, taking OpenHarmony LiteOS-M kernel as an example, can be found on the open source site https://gitee.com/openharmony/kernel_liteos_m Get.
1. Interrupt concept introduction
Interrupt refers to the process in which the CPU suspends the execution of the current program and executes a new program when there is a need. When the peripheral needs 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 describes some interrupt related hardware and interrupt related concepts.
1.1 introduction to interrupt related hardware
Interrupt related hardware can be divided into three categories: device, interrupt controller and CPU itself.
- equipment
The source that initiates the interrupt generates an interrupt signal when the device needs to request the CPU, which is connected to the interrupt controller.
- Interrupt controller
Interrupt controller is one of many peripherals of CPU. On the one hand, it receives the input of interrupt pins of other peripherals. On the other hand, it sends an interrupt signal to the CPU. The interrupt controller can be programmed to open and close the interrupt source, set the priority and trigger mode of the interrupt source.
- CPU
The CPU will respond to the request of the interrupt source, interrupt the currently executing task 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 judge which device made the interrupt request. This flag is the interrupt number.
- Interrupt priority
In order to enable the system to respond and handle all interrupts in time, the system divides the interrupt sources into several levels, called interrupt priority, according to the importance and urgency of interrupt time.
- Interrupt handler
When the peripheral generates an interrupt request, the CPU suspends the current task and responds to the interrupt request, that is, executes the interrupt handler. Each device that generates an interrupt has a corresponding interrupt handler.
- Interrupt vector
The entry address of the interrupt service program.
- Interrupt vector table
The storage area for storing the interrupt vector. The interrupt vector corresponds to the interrupt number. The interrupt vector is stored in the interrupt vector table in the order of the interrupt number.
- Interrupt sharing
When there are few peripherals, one peripheral can correspond to one interrupt number. However, in order to support more hardware devices, multiple devices can share one interrupt number, and the interrupt handler sharing the same interrupt number forms a linked list. When an external device generates an interrupt application, the system will traverse the linked list of interrupt handlers corresponding to the interrupt number until the interrupt handler of the corresponding device is found. In the process of traversal execution, each interrupt handler can determine whether it is the interrupt generated by the device corresponding to the interrupt handler by detecting the device ID.
Next, let's take a look at the interrupt source code of Hongmeng light kernel.
2. Hongmeng light kernel interrupt source code
2.1. Statements and definitions related to interruption
In the file kernel\arch\arm\cortex-m7\gcc\los_interrupt.c defines some structures, global variables and inline functions. Before analyzing the source code, let's take a look at these definitions and declarations. All variables g_intCount indicates the number of interrupts being processed. Each time you enter the interrupt handler, the variable value will be increased by 1. When you finish the interrupt processing and exit, the value will be reduced by 1. The corresponding inline function HalIsIntActive() is used to obtain whether the interrupt is being processed. If the return value is greater than 0, it indicates that the interrupt is being processed.
UINT32 g_intCount = 0; inline UINT32 HalIsIntActive(VOID) { return (g_intCount > 0); }
Let's look at the definition of interrupt vector table again. (1) the code at defines the array g for interrupts supported by the system_ Hwiform [os_vector_cnt], for each interrupt number hwiNum, the corresponding array element g_hwiForm[hwiNum] indicates the interrupt processing execution entry program corresponding to each interrupt. (2) macro OS at_ HWI_ WITH_ Arg indicates whether the interrupt handler supports parameter input. It is closed by default. If parameter transfer is supported, define the structure HWI at (3)_ HANDLER_ Func is used to maintain the interrupt processing function and its parameters, and g at (4) needs to be defined_ Hwihandlerform array. If parameter transfer is not supported, use the G defined at (6)_ Hwihandlerform array. For each interrupt hwiNum, the corresponding array element g_hwiHandlerForm[hwiNum] indicates the interrupt handler corresponding to each interrupt. (5) and (7) 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 handler is that when an interrupt occurs, the interrupt processing execution entry program will be executed, and this function will further call the interrupt handler.
⑴ 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)
At system startup, in kernel \ SRC \ Los_ init. HalArchInit() is called in C to interrupt initialization. This function is defined in kernel\arch\arm\cortex-m7\gcc\los_context.c. Then further call the definition in kernel \ arch \ arm \ cortex-m7 \ GCC \ LOS_ interrupt. HalHwiInit() function in C file completes the initialization of interrupt vector. Let's analyze the code.
Macro LOSCFG_USE_SYSTEM_DEFINED_INTERRUPT indicates whether to use the vector base address and interrupt handler predefined by the system. It is enabled by default. (1) starting at, interrupt 0 in the interrupt vector table is set to null, and interrupt 1 corresponds to the reset handler Reset_Handler. (2) set the remaining interrupts as the default interrupt processing execution entry program HalHwiDefaultHandler(). (3) set the system interrupt (exception is a kind of interrupt, and system interrupt is also called exception). The execution entry function of system interrupt is defined in kernel\arch\arm\cortex-m7\gcc\los_exc.S, implemented in assembly language. Among system interrupts, interrupt 14 corresponds to HalPendSV handler, which is used for task context switching, and interrupt 15 is tick interrupt.
Execute the code at (4) to assign the interrupt vector table to SCB - > vtor. For the CPU cores of Cortex-M3 and above, it is also necessary to execute (5) to set the priority group. (6) exceptions specified by code enable.
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 an interrupt and register the interrupt handler. Let's first look at the parameters of this function, HWI_HANDLE_T hwiNum is the hardware interrupt number, HWI_PRIOR_T hwiPrio interrupt priority, HWI_MODE_T mode interrupt mode, reserved and 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 a parameter of the interrupt handler.
Analyze the source code of this function together. ⑴ start the code and 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 indicates that it has been created and an error code is returned. Turn off the interrupt, and then execute the OsSetVector() function at (2) 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, which is also easy to understand. Developers can call the function UINT32 HalHwiDelete(HWI_HANDLE_T hwiNum) to delete interrupts. The function needs to specify the interrupt number parameter HWI_ HANDLE_ T hwiNum. Analyze the source code of this function together, and check the input parameters in the code at (1), which cannot be greater than the maximum interrupt number supported. (2) call the CMSIS function to disable the interrupt, then lock the interrupt, and execute (3) set the interrupt execution entry program with the interrupt number specified in 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 look at the interrupt handling 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(), and the source code is as follows.
(1) put the global variable g_intCount indicates the number of interrupts being processed plus 1. After the execution of interrupts, reduce the number of interrupts being processed by 1 at (6). (2) call the function HalIntNumGet() at (3) to get the interrupt number. The function halpreinterupthandler() called at (3) and (5) can handle some other operations before and after executing the interrupt handler. Currently, it is empty by default. (4) obtain the interrupt handler from the interrupt handler array according to the interrupt number, and call and execute if it is not empty.
3. Switch interrupt
Finally, share the relevant knowledge of on and off interrupts. On and off interrupts refer to:
- Open interrupt
After executing a specific short program, open the interrupt and respond to the interrupt.
- Off interrupt
In order to protect the executed program from being interrupted, close the corresponding external interrupt.
The corresponding functions of on and off interrupts are defined in the file kernel\arch\include\los_context.h, the code is as follows. (1) uint32 Los at_ INTLOCK (void) turns off the interrupt and pauses the response to the interrupt. (2) function void Los at_ Intrestore (uint32 intsave) can be used to restore uint32 LOS_ Interrupt that the INTLOCK (void) function closes, uint32 Los_ The return value of INTLOCK (void) is used as void LOS_ The parameter of intrestore (uint32 intsave) interrupts the recovery. (3) function uint32 Los at_ Intunlock (void) 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
As can be seen, LOS_IntLock,LOS_IntRestore and LOS_IntUnLock is defined macro, which is defined in the file kernel\arch\arm\cortex-m7\gcc\los_dispatch.S assembly function, the source code is as follows. Let's analyze these assembly functions. Register PRIMASK is a single bit register. When it is set to 1, all maskable exceptions are turned off, leaving only NMI and hard fault HardFault exceptions to respond. The default value is 0, indicating that the interrupt is not turned off. The assembly instruction CPSID I will set PRIMASK=1 to close the interrupt, and the instruction CPSIE I will set PRIMASK=0 to open the interrupt.
(1) the HalIntLock function at position 1 writes the value of the register PRIMASK into the register R0, returns, and executes the CPSID I shutdown interrupt. (2) the HalIntUnLock function at position 2 writes the value of the register PRIMASK into the register R0 and returns, and executes the instruction CPSIE I to start the interrupt. The return results of the two functions can be passed to the HalIntRestore function at (3) to write the register state value into the register PRIMASK for restoring the previous interrupt state. 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 paper leads you to analyze the source code of the interrupt module of Hongmeng light kernel, master the concepts related to interrupt, interrupt initialization operation, interrupt creation, deletion, switch interrupt operation, etc. More sharing articles will be launched in the future. Please look forward to it. You are also welcome to share your experience in learning and using Hongmeng light kernel. If you have any questions or suggestions, you can leave a message to us: https://gitee.com/openharmony/kernel_liteos_m/issues . In order to find Hongmeng light kernel code warehouse more easily, it is recommended to visit https://gitee.com/openharmony/kernel_liteos_m , follow Watch, like Star, and Fork to your account. Thank you.
Click follow to learn about Huawei's new cloud technology for the first time~