Abstract: This paper leads us to analyze the interrupt module source code of hongmeng light kernel, master the concept of interrupt, interrupt initialization operation, interrupt creation, delete, switch interrupt operation.

This article is shared from huawei cloud community “Hongmeng light kernel M core source code analysis series five interrupt Hwi”, original author: zhushy.

In this article, we talk about interrupt, will introduce to the reader the concept of interrupt, hongmeng light kernel interrupt module source code. The source code in this article, using the OpenHarmony LitEOS-M kernel as an example, is available on the open source site gitee.com/openharmony… To obtain.

1. Introduction to interrupt concept

Interrupt is the process by which the CPU pauses the execution of the current program and moves on to a new program when needed. When a peripheral needs a CPU, it responds to an interrupt request by generating an interrupt signal that causes the CPU to immediately interrupt the current task. Before dissecting interrupt source code, the following introduces some interrupt related hardware, interrupt related concepts.

1.1 Hardware description of interrupts

Interrupt-related hardware can be divided into three categories: devices, interrupt controllers, and cpus themselves.

  • equipment

An interrupt source that 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, it receives the input of interrupt pins of other peripherals on the one hand. On the other hand, it sends an interrupt signal to the CPU. The interrupt controller can be programmed to turn on and off the interrupt source, set the priority of the interrupt source, and trigger mode.

  • CPU

In response to the interrupt source’s request, the CPU interrupts the currently executing task and executes the interrupt handler instead.

1.2 Interrupt related concepts

  • Interrupt number

Each interrupt request signal has a specific flag that allows the computer to determine which device is making the interrupt request. This flag is the interrupt number.

  • Interrupt priority

To enable the system to respond to and handle all interrupts in a timely manner, the system classifies interrupt sources into several levels, called interrupt priorities, according to the importance and urgency of the interrupt time.

  • Interrupt handler

When the peripheral generates an interrupt request, the CPU pauses the current task and responds to the interrupt request, that is, performs an interrupt handler. Each device that generates an interrupt has its own interrupt handler.

  • The interrupt vector

The entry address of the interrupt service routine.

  • Interrupt direction meter

A storage area for interrupt vectors, which correspond to interrupt numbers and are stored in order of interrupt numbers in the interrupt vector table.

  • Interrupt sharing

When there are fewer peripherals, one peripherals can correspond to an interrupt number, but in order to support more hardware devices, multiple devices can share an interrupt number, and interrupt handlers sharing the same interrupt number form a linked list. When an external device generates an interrupt request, the system traverses the linked list of interrupt handlers corresponding to the interrupt number until it finds the interrupt handler corresponding to the device. In the traversal execution process, each interrupt handler can determine whether the interrupt is generated by the device corresponding to the interrupt handler by detecting the device ID.

Next, we look at the hongmeng light kernel interrupt source code.

2, Hongmeng light kernel interrupt source code

2.1 Interrupt related declaration and definition

The file kernel\arch\arm\cortex-m7\ GCC \los_interrupt.c defines some constructs, global variables, and inline functions. Let’s take a look at these definitions and declarations before we analyze the source code. The total variable g_intCount represents the number of interrupts being processed. Each time the interrupt handler is entered, the value of the variable is incremented by 1, and when the interrupt is finished and the interrupt exits, the value is decreased by 1. The corresponding inline function HalIsIntActive() is used to get whether an interrupt is being processed, with a return value greater than 0 indicating that the interrupt is being processed.

UINT32 g_intCount = 0;


inline UINT32 HalIsIntActive(VOID)
{
    return (g_intCount > 0);
}
Copy the code

Let’s look at the interrupt direction scale definition again. G_hwiForm [OS_VECTOR_CNT] for each interrupt number hwiNum, the corresponding array element g_hwiForm[hwiNum] represents the corresponding interrupt processing entry program for each interrupt. The macro OS_HWI_WITH_ARG at ⑵ indicates whether the interrupt handler supports parameter passing. The macro OS_HWI_WITH_ARG is disabled by default. If passing parameters is supported, define the HWI_HANDLER_FUNC structure at (3) to maintain the interrupt handler function and its parameters, as well as the g_hwiHandlerForm array at (4). If passing parameters is not supported, use the g_hwiHandlerForm array defined in ⑹. For each interrupt number hwiNum, the corresponding array element g_hwiHandlerForm[hwiNum] represents the corresponding interrupt handler for each interrupt. ⑸, ⑺ defines the OsSetVector() function to set the interrupt processing entry and interrupt handler corresponding to the specified interrupt number. The relationship between the interrupt handler execution entry and the interrupt handler is that when an interrupt occurs, the interrupt handler execution entry is executed, and this function further calls 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_PROC_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 HWI_PROC_FUNC g_hwiHandlerForm[OS_VECTOR_CNT] = {0}; Once 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; } } #endifCopy the code

2.2 Interrupt HalHwiInit()

At system startup, HalArchInit() is called in kernel\ SRC \los_init.c for interrupt initialization. This function is defined in kernel\arch\arm\cortex-m7\ GCC \los_context. The HalHwiInit() function defined in kernel\arch\arm\cortex-m7\ GCC \los_interrupt.c is then further called to initialize the interrupt vector. Let’s analyze the code.

The macro LOSCFG_USE_SYSTEM_DEFINED_INTERRUPT indicates whether to use system-defined vector base addresses and interrupt handlers. It is enabled by default. Starting from (1), interrupt no. 0 of the interrupt vector table is set to null, and interrupt No. 1 corresponds to reset handler Reset_Handler. (2) set the rest of the interrupts to the default interrupt handling entry HalHwiDefaultHandler(). ⑶ set system interrupt (exception is a kind of interrupt, system interrupt is also called exception), system interrupt execution entry function defined in kernel\arch\arm\cortex-m7\ GCC \ los_exc.s, using assembly language implementation. In system interrupts, Interrupt no. 14 corresponds to the HalPendSV handler for task context switch, and interrupt No. 15 is the TICK interrupt.

Execute the code at (4) to assign the interrupt vector scale to SCB->VTOR. For CPU cores cortex-M3 and above, perform ⑸ to set the priority group. ⑹ enable the specified exception code.

LITE_OS_SEC_TEXT_INIT VOID HalHwiInit() { #if (LOSCFG_USE_SYSTEM_DEFINED_INTERRUPT == 1) UINT32 index; (1) 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) #endif #if (__CORTEX_M >= 0x03U) /* Only for cortex-m3 and above */ NVIC_SetPriorityGrouping(OS_NVIC_AIRCR_PRIGROUP); #endif /* Enable USGFAULT, BUSFAULT, MEMFAULT * / [6] * (volatile UINT32 *) OS_NVIC_SHCSR | = (USGFAULT | BUSFAULT | MEMFAULT); /* Enable DIV 0 and unaligned exception */ *(volatile UINT32 *)OS_NVIC_CCR |= DIV0FAULT; return; }Copy the code

2.3 Creation interruption UINT32 HalHwiCreate()

Developers can create interrupts by calling the function UINT32HalHwiCreate() to register interrupt handlers. HWI_HANDLE_T hwiNum is the hardware interrupt number. HWI_PRIOR_ThwiPrio interrupt priority. HWI_MODE_T mode Interrupt mode. The HWI_PROC_FUNC handler is the interrupt handler that needs to be registered and is called when an interrupt is triggered. HWI_ARG_T arg is the argument to the interrupt handler.

The interrupt handler cannot be empty, the interrupt number cannot be greater than the maximum interrupt number supported, and the interrupt priority cannot exceed the size of the specified priority. If the interrupt execution entry program corresponding to the interrupt number to be created is not equal to HalHwiDefaultHandler, it has been created. The error code is displayed. Turn off the interrupt, and then execute the OsSetVector() function at ⑵ to set the interrupt handler for the specified interrupt number. ⑶ Call the CMSIS function to enable the interrupt, set the priority of the interrupt, open the interrupt, 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); # the else 2 OsSetVector (hwiNum, handler); # endif (3) NVIC_EnableIRQ (IRQn_Type hwiNum); NVIC_SetPriority((IRQn_Type)hwiNum, hwiPrio); LOS_IntRestore(intSave); return LOS_OK; }Copy the code

2.4 Delete Interrupt UINT32 HalHwiDelete()

It is also easier to understand that the interrupt delete operation is the reverse of the create operation. Developers can call the function UINT32 HalHwiDelete(HWI_HANDLE_T hwiNum) to remove interrupts. The function needs to specify the interrupt number parameter HWI_HANDLE_T hwiNum. Analyze the source code of this function, the code in the ⑴ to verify the input parameter, cannot be greater than the maximum interrupt number supported. (2) call CMSIS function to disable the interrupt, and then lock the interrupt. (3) Set the interrupt execution entry program specified by the interrupt number to the table as 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; } [2] 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 the code

2.5 Interrupt processing Executes the entry program

Let’s look at the interrupt handler execution entry. The default function HalHwiDefaultHandler() looks like this, calling HalIntNumGet() to get the interrupt number, printing it out, and then doing an infinite loop. Where HalIntNumGet() reads register IPSR to get 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 the code

Continue with the interrupt handler execution entry HalInterrupt(), the source code below.

(1) Increment the number of interrupts being processed represented by the global variable g_intCount by 1. After the interrupt execution, decrease the number of interrupts being processed by 1 at ⑹. (2) HalIntNumGet() is called to obtain the interrupt number. (3) HalPreInterruptHandler(), HalAftInterruptHandler(), can handle additional operations before and after the interrupt handler. (4) fetch an interrupt handler from the interrupt handler array based on the interrupt number.

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(); (1) g_intCount + +; LOS_IntRestore(intSave); 2 hwiIndex = HalIntNumGet (); OsHookCall(LOS_HOOK_TYPE_ISR_ENTER, hwiIndex); (3) 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 5] HalAftInterruptHandler (hwiIndex); OsHookCall(LOS_HOOK_TYPE_ISR_EXIT, hwiIndex); intSave = LOS_IntLock(); [6] g_intCount -; LOS_IntRestore(intSave); }Copy the code

3. Switch interruption

Finally, share the knowledge about on and off interrupts. On and off interrupts refer to:

  • Open the interrupt

After executing a specific transient program, turn on interrupts and respond to interrupts.

  • Close the interrupt

To protect executing programs from interruption, close the corresponding external interrupt.

The corresponding functions to open and close interrupts are defined in the file kernel\arch\include\los_context.h with the following codes. The UINT32 LOS_IntLock(VOID) in ⑴ closes the interrupt and suspends the interrupt response. VOID LOS_IntRestore(UINT32 intSave) can be used to restore the interrupt of UINT32LOS_IntLock(VOID) function closed. UINT32 LOS_IntLock(VOID) Returns the value used as VOIDLOS_IntRestore(UINT32 intSave). The function UINT32 LOS_IntUnLock(VOID) enables and responds to interrupts.

UINTPTR HalIntLock(VOID); ⑴ #define LOS_IntLock (UINTPTR intSave); ⑵ #define LOS_IntRestore HalIntRestore UINTPTR HalIntUnLock(VOID); (3) # define LOS_IntUnLock HalIntUnLockCopy the code

LOS_IntLock, LOS_IntRestore and LOS_IntUnLock are defined macros corresponding to the assembly functions defined in the file kernel\arch\arm\cortex-m7\ GCC \los_dispatch. Let’s look at these assembler functions. The PRIMASK register is a single bit register. When set to 1, all maskable exceptions are turned off, leaving only NMI and HardFault exceptions to respond to. The default value is 0, indicating that no interrupts are turned off. The assembly instruction CPSID I sets PRIMASK=1 to turn off interrupts, and the instruction CPSIEI sets PRIMASK=0 to turn on interrupts.

The HalIntLock function at ⑴ writes the value of register PRIMASK to register R0 and returns, and executes CPSIDI to close the interrupt. (2) HalIntUnLock function writes the value of register PRIMASK to register R0 and returns, and executes the instruction CPSIEI to open the interrupt. The results of the two functions can be passed to the HalIntRestore function at ⑶, which writes the value of the register state to register PRIMASK, which is used to restore the previous interrupted state. Both HalIntLock and 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 LRCopy the code

summary

This article leads us to analyze the interrupt module source code of hongmeng light kernel, master the concept of interrupt, interrupt initialization operation, interrupt creation, delete, switch interrupt operation, etc. The follow-up will also launch more articles to share, please look forward to, but also welcome to share learning, using hongmeng light kernel experience, any questions, suggestions, you can leave a message to us: gitee.com/openharmony… . To make it easier to find the light kernel repository, visit gitee.com/openharmony… “, follow Watch, like Star, and Fork to your account, thank you.

Click to follow, the first time to learn about Huawei cloud fresh technology ~