Interrupt Hwi: The secret weapon to improve the real-time performance and execution efficiency of the Hongmeng light core system

Interrupt Hwi: The secret weapon to improve the real-time performance and execution efficiency of the Hongmeng light core system

Abstract: 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.

This article is shared from the Huawei Cloud Community "Hongmeng Light Core M Core Source Code Analysis Series 5.Interrupt Hwi" , the original author: zhushy.

In this article, we talk about interrupts, and will 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 gitee.com/openharmony ...

1. Introduction to the interrupt concept

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: devices, interrupt controllers, and the 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, which enables the computer to determine which device is requesting the interrupt. 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 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 routine.

  • 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 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 the value will be decreased by 1 when the interrupt processing is completed and exit. 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); } Copy code

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 () used 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 Copy code

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 it is further called HalHwiInit() function defined in the kernel\arch\arm\cortex-m7\gcc\los_interrupt.c file to complete the interrupt vector initialization . We 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. From 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; } Copy code

2.3 Create interrupt UINT32 HalHwiCreate()

Developers can call the function UINT32HalHwiCreate() 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_ThwiPrio 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; } Copy code

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, and 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; } Copy code

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 get 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) {} } Copy code

Continue to look at the interrupt processing execution entry program HalInterrupt (), the source code is as follows.

(1) Add 1 to the number of interrupts being processed represented by the global variable g_intCount. After the interrupt is executed, reduce the number of interrupts being processed by 1 at (6). (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.

LITE_OS_SEC_TEXT VOID HalInterrupt(VOID) { UINT32 hwiIndex; UINT32 intSave; #if (LOSCFG_KERNEL_RUNSTOP == 1) SCB->SCR &= (UINT32) ~((UINT32)SCB_SCR_SLEEPDEEP_Msk); #endif intSave = LOS_IntLock(); g_intCount++; LOS_IntRestore(intSave); hwiIndex = HalIntNumGet(); OsHookCall(LOS_HOOK_TYPE_ISR_ENTER, hwiIndex); HalPreInterruptHandler(hwiIndex); #if (OS_HWI_WITH_ARG == 1) if (g_hwiHandlerForm[hwiIndex].pfnHandler != 0) { g_hwiHandlerForm[hwiIndex].pfnHandler((VOID *)g_hwiHandlerForm[hwiIndex].pParm); } #else if (g_hwiHandlerForm[hwiIndex] != 0) { g_hwiHandlerForm[hwiIndex](); } #endif HalAftInterruptHandler(hwiIndex); OsHookCall(LOS_HOOK_TYPE_ISR_EXIT, hwiIndex); intSave = LOS_IntLock(); g_intCount--; LOS_IntRestore(intSave); } Copy code

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 UINT32LOS_IntLock(VOID) function closed, and the return value of UINT32 LOS_IntLock(VOID) is used as the parameter of VOIDLOS_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 Copy code

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 will be 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 CPSIEI will set PRIMASK=0 to turn on the interrupt.

(1) The function HalIntLock writes the value of register PRIMASK into register R0 and returns, and executes CPSIDI to turn off the interrupt. (2) The function HalIntUnLock writes the value of the register PRIMASK into the register R0 and returns, and executes the instruction CPSIEI 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 Copy code

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. You are also welcome to share your experience of learning and using the
Hongmeng light kernel. If you have any questions or suggestions, you can leave a message to us: gitee.com/openharmony .... In order to find the Hongmeng light kernel code warehouse easier, it is recommended to visit gitee.com/openharmony... , follow Watch, like Star, and Fork to your account, thank you.

Click to follow and learn about Huawei Cloud's fresh technology for the first time~