Hongmeng light core M core source code analysis: interrupt Hwi

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~


开发者之家
生于云,长于云,让开发者成为决定性力量

生于云,长于云,让开发者成为决定性力量

1.4k 声望
1.7k 粉丝
0 条评论
推荐阅读
【贺】来自开发者的点赞,华为云开发者联盟入选 2022 中国技术品牌影响力企业榜
2023 年 1 月 4 日,中国技术先锋年度评选 | 2022 中国技术品牌影响力企业榜单正式发布。作为中国领先的新一代开发者社区,SegmentFault 思否依托数百万开发者用户数据分析,各科技企业在国内技术领域的行为及影...

华为云开发者联盟阅读 628

浅析eTS的起源和演进
引言 Mozilla创造了JS,Microsoft创建了TS,Huawei进一步推出了eTS。从最初的基础的逻辑交互能力,到具备类型系统的高效工程开发能力,再到融合声明式UI、多维状态管理等丰富的应用开发能力,共同组成了相关的演...

HarmonyOS开发者2阅读 1.5k评论 1

手把手教你使用HarmonyOS本地模拟器
2021年的华为开发者大会(HDC2021)上,我们随DevEco Studio 3.0 Beta1版本发布首次推出了本地模拟器(也称为Local Emulator),支持模拟手机品类。我们通过下面的动图来回顾下手机本地模拟器的使用效果。本期,...

HarmonyOS开发者1阅读 901

【直播回顾】OpenHarmony知识赋能五期第三课——多媒体整体介绍
5月5日晚上19点,知识赋能第五期第三节课《OpenHarmony标准系统多媒体子系统整体介绍》,在OpenHarmony开发者成长计划社群内成功举行。

OpenHarmony开发者3阅读 736评论 1

Sample上新,从API 8开始支持!速来拿走
搭载API 8的新SDK已经发布。围绕着新SDK,官方贴心地输出了一波Sample,供各位开发者参考。本期我们将介绍6个基于eTS实现的Sample,开发者可以从中掌握基于TS扩展的声明式开发范式的核心机制和功能,同时还能从中...

HarmonyOS开发者2阅读 727评论 1

巴延兴:从主导多个SIG组到OpenHarmony “代码贡献之星”,我是如何做到的?
编者按:在 OpenHarmony 生态发展过程中,涌现了大批优秀的代码贡献者,本专题旨在表彰贡献、分享经验,文中内容来自嘉宾访谈,不代表 OpenHarmony 工作委员会观点。

OpenHarmony开发者3阅读 724评论 2

OpenHarmony技术日全面解读3.1 Release版本 系统基础能力再升级
4月25日,OpenAtom OpenHarmony(以下简称“OpenHarmony”)技术日在深圳举办,对 OpenHarmony 3.1 Release 版本核心技术进行了深入解读,同时分享了生态的最新进展。全新的 OpenHarmony 3.1 Release 版本,大幅优...

OpenHarmony开发者2阅读 720评论 1

生于云,长于云,让开发者成为决定性力量

1.4k 声望
1.7k 粉丝
宣传栏