Bootstrap

liteOS-A学习笔记-3.移植中断控制器GIC

中断控制器

1.

路径:\OpenharmonyFor6ull\kernel\liteos_a\platform\main.c

LITE_OS_SEC_TEXT_INIT INT32 main(VOID)
{
    UINT32 uwRet = LOS_OK;

    OsSetMainTask();
    OsCurrTaskSet(OsGetMainTask());

    /* set smp system counter freq */
#if (LOSCFG_KERNEL_SMP == YES)
#ifndef LOSCFG_TEE_ENABLE
    HalClockFreqWrite(OS_SYS_CLOCK);
#endif
#endif

    /* system and chip info */
    OsSystemInfo();//涉及中断控制器

    PRINT_RELEASE("\nmain core booting up...\n");

其中,进入'HalIrqVersion'

CHAR *HalIrqVersion(VOID)
{
    //#define GICD_PIDR2V2                    (GICD_OFFSET + 0xfe8)
    //#define GIC_REG_32(reg)                 (*(volatile UINT32 *)((UINTPTR)(GIC_BASE_ADDR + (reg))))
    //#define GIC_BASE_ADDR             IO_DEVICE_ADDR(0x10300000) 物理地址转换为虚拟地址,需要把这个物理地址与目前芯片进行对应
    //#define GICD_OFFSET               0x1000     /* interrupt distributor offset */
    UINT32 pidr = GIC_REG_32(GICD_PIDR2V2);
    CHAR *irqVerString = NULL;

    switch (pidr >> GIC_REV_OFFSET) {
        case GICV1:
            irqVerString = "GICv1";
            break;
        case GICV2:
            irqVerString = "GICv2";
            break;
        default:
            irqVerString = "unknown";
    }
    return irqVerString;
}

那么宏定义原型为:《OpenharmonyFor6ull\kernel\liteos_a\platform\hw\include\gic_common.h》

#define GIC_REG_32(reg)                 (*(volatile UINT32 *)((UINTPTR)(GIC_BASE_ADDR + (reg))))

这里GIC的基地址需要修改:《OpenharmonyFor6ull\vendor\nxp\imx6ull\board\include\asm\platform.h》

#define GIC_BASE_ADDR             IO_DEVICE_ADDR(0x00a00000) // IO_DEVICE_ADDR(0x10300000)

2.异常向量表和中断初始化/调用过程

如何指定中断处理函数?

《OpenharmonyFor6ull\drivers\hdf\lite\adapter\osal\src\osal_irq.c》

int32_t OsalRegisterIrq(uint32_t irqId, uint32_t config, OsalIRQHandle handle, const char *name, void *dev)

分析中断处理过程:以触摸屏为例,《OpenharmonyFor6ull\vendor\nxp\imx6ull\driver\touch\touch_gt9xx.c》

(1)中断初始化函数:注册中断号和处理函数

void gt9xx_irq_init(TouchCoreData *cd)
{
	...
	/* IrqHandle */
    //中断号:cd->intGpioNum
    //配置:0
    //中断处理函数:IrqHandle
    //中断名称:"gt9xx_irq"
    //驱动:NULL
	ret = OsalRegisterIrq(cd->intGpioNum, 0, IrqHandle, "gt9xx_irq", NULL);
	...
}

(2)中断号的确定:

//note:0#中断对应数字32
//      GPIO1的pin0~pin15即66#中断对应数字(66+32)
#define GT9XX_INT_NUM  (66 + 32)

static void TouchConfigInit(TouchCoreData *cd)
{
    /* init waitqueue for poll */
    __init_waitqueue_head(&cd->pollWait);

    /* config i2c and input */
    cd->i2cClient.i2cCfg.addr = DRIVER_CHIP_I2C_ADDR;
    cd->i2cClient.i2cCfg.busNum = I2C_BUS_NUM;
    cd->inputCfg.solutionX = TOUCH_SOLUTION_X;
    cd->inputCfg.solutionY = TOUCH_SOLUTION_Y;

    /* config device type and module info */
    cd->inputDevType = INDEV_TYPE_TOUCH;
    (void)strncpy_s(cd->chipInfo, CHIP_INFO_LEN, "HZ1145130", strlen("HZ1145130"));
    (void)strncpy_s(cd->vendorName, VENDOR_NAME_LEN, "ZG1695", strlen("TG1695"));
    (void)strncpy_s(cd->chipName, CHIP_NAME_LEN, "GT9xx", strlen("GT9xx"));

    /* pin num and irq trigger config info */
    //cd->rstGpioNum = RST_GPIO_GROUP * GPIO_GROUP_SIZE + RST_GPIO_OFFSET;
    //中断号的确定
    cd->intGpioNum = GT9XX_INT_NUM;
    cd->irqFlag = OSAL_IRQF_TRIGGER_FALLING;
    cd->shouldStop = false;
}

(3)中断处理函数的确定:

《OpenharmonyFor6ull\kernel\liteos_a\arch\arm\arm\src\los_hwi.c》

STATIC UINT32 OsHwiCreateShared(HWI_HANDLE_T hwiNum, HWI_MODE_T hwiMode,
                                HWI_PROC_FUNC hwiHandler, const HwiIrqParam *irqParam)
{
    UINT32 intSave;
    HwiHandleForm *hwiFormNode = NULL;
    HwiHandleForm *hwiForm = NULL;
    HwiIrqParam *hwiParam = NULL;
    HWI_MODE_T modeResult = hwiMode & IRQF_SHARED;

    
    //g_hwiForm数组中序号为hwiNum的变量取出来
    hwiForm = &g_hwiForm[hwiNum];
...

    hwiFormNode->pfnHook = hwiHandler;//把中断处理函数放入对应变量
    hwiFormNode->pstNext = (struct tagHwiHandleForm *)NULL;
    hwiForm->pstNext = hwiFormNode;

    if ((irqParam != NULL) && (irqParam->pName != NULL)) {
        g_hwiFormName[hwiNum] = (CHAR *)irqParam->pName;
    }

    g_hwiForm[hwiNum].uwParam = modeResult;

    HWI_UNLOCK(intSave);
    return LOS_OK;
}

其中,HwiHandleForm结构体原型如下:

typedef struct tagHwiHandleForm {
    HWI_PROC_FUNC pfnHook;//中断处理函数
    HWI_ARG_T uwParam;
    struct tagHwiHandleForm *pstNext;//下一个元素的位置
} HwiHandleForm;

(4)中断执行流程:

step1:中断向量表 

b   OsIrqHandler

step2:《OpenharmonyFor6ull\kernel\liteos_a\arch\arm\arm\src\los_dispatch.S》

OsIrqHandler:
    SUB     LR, LR, #4

    /* push r0-r3 to irq stack */
    STMFD   SP, {R0-R3}
    SUB     R0, SP, #(4 * 4)
    MRS     R1, SPSR
    MOV     R2, LR

    /* disable irq, switch to svc mode */
    CPSID   i, #0x13

    /* push spsr and pc in svc stack */
    STMFD   SP!, {R1, R2}
    STMFD   SP, {LR}

    AND     R3, R1, #CPSR_MASK_MODE
    CMP     R3, #CPSR_USER_MODE
    BNE     OsIrqFromKernel

    /* push user sp, lr in svc stack */
    STMFD   SP, {R13, R14}^
OsIrqFromKernel:
    /* from svc not need save sp and lr */
    SUB     SP, SP, #(2 * 4)

    /* pop r0-r3 form irq stack*/
    LDMFD   R0, {R0-R3}

    /* push caller saved regs as trashed regs in svc stack */
    STMFD   SP!, {R0-R3, R12}

    /* 8 bytes stack align */
    SUB     SP, SP, #4

    /* 保存FPU寄存器的值
     * save fpu regs in case in case those been
     * altered in interrupt handlers.
     */
    PUSH_FPU_REGS   R0
#ifdef LOSCFG_IRQ_USE_STANDALONE_STACK
    PUSH    {R4}
    MOV     R4, SP
    EXC_SP_SET __svc_stack_top, OS_EXC_SVC_STACK_SIZE, R1, R2
#endif
    //
    //调用函数
    //
    BLX     HalIrqHandler

step3:《OpenharmonyFor6ull\kernel\liteos_a\platform\hw\arm\interrupt\gic\gic_v2.c》

VOID HalIrqHandler(VOID)
{
    //读取GIC控制器寄存器,判断中断向量编号
    UINT32 iar = GIC_REG_32(GICC_IAR);
    UINT32 vector = iar & 0x3FFU;

    /*
     * invalid irq number, mainly the spurious interrupts 0x3ff,
     * gicv2 valid irq ranges from 0~1019, we use OS_HWI_MAX_NUM
     * to do the checking.
     */
    if (vector >= OS_HWI_MAX_NUM) {
        return;
    }
    g_curIrqNum = vector;
    
    //处理向量号对应的中断
    OsInterrupt(vector);

    /* use orignal iar to do the EOI */
    GIC_REG_32(GICC_EOIR) = iar;
}

《OpenharmonyFor6ull\kernel\liteos_a\arch\arm\arm\src\los_hwi.c》

VOID OsInterrupt(UINT32 intNum)
{
    HwiHandleForm *hwiForm = NULL;
    UINT32 *intCnt = NULL;

    ...
    hwiForm = (&g_hwiForm[intNum]);
    ...

            //通过调用对应的结构体函数指针执行中断服务函数
            HWI_PROC_FUNC2 func = (HWI_PROC_FUNC2)hwiForm->pfnHook;
    ...
}

这里就执行了中断处理函数

3.tick中断

CPU core内部自带了timer,可以用来实现tick中断(10ms周期)

(1)

《OpenharmonyFor6ull\kernel\liteos_a\platform\hw\arm\timer\arm_generic\arm_generic_timer.c》

LITE_OS_SEC_TEXT_INIT VOID HalClockInit(VOID)
{
    UINT32 ret;
    //读取时钟频率->
    //return READ_TIMER_REG32(TIMER_REG_CNTFRQ); ---------调用-------->
    //#define READ_TIMER_REG32(reg)       ARM_SYSREG_READ(reg) --调用->
    //__asm__ volatile("mrc " REG : "=r" (_val)); \直接读取协处理器的寄存器值即可得到
    g_sysClock = HalClockFreqRead();
    //OS的系统滴答的中断向量号:OS_TICK_INT_NUM
    //OS的系统滴答的中断优先级:MIN_INTERRUPT_PRIORITY
    //OS的系统滴答的中断处理函数:OsTickEntry
    ret = LOS_HwiCreate(OS_TICK_INT_NUM, MIN_INTERRUPT_PRIORITY, 0, OsTickEntry, 0);
    if (ret != LOS_OK) {
        PRINT_ERR("%s, %d create tick irq failed, ret:0x%x\n", __FUNCTION__, __LINE__, ret);
    }
}

(2)tick中断向量号OS_TICK_INT_NUM的值

《OpenharmonyFor6ull\vendor\nxp\imx6ull\board\include\asm\hal_platform_ints.h》

#define NUM_HAL_INTERRUPT_CNTPSIRQ      29
#define NUM_HAL_INTERRUPT_CNTPNSIRQ     30
#define OS_TICK_INT_NUM                 NUM_HAL_INTERRUPT_CNTPSIRQ // use secure physical timer for now

(3)滴答中断的处理函数

《OpenharmonyFor6ull\kernel\liteos_a\platform\hw\arm\timer\arm_generic\arm_generic_timer.c》

LITE_OS_SEC_TEXT VOID OsTickEntry(VOID)
{
    //timer暂停计数
    TimerCtlWrite(0);

    //中断处理函数
    OsTickHandler();

    /*
     * use last cval to generate the next tick's timing is
     * absolute and accurate. DO NOT use tval to drive the
     * generic time in which case tick will be slower.
     */
    TimerCvalWrite(TimerCvalRead() + OS_CYCLE_PER_TICK);

    //timer继续计数
    TimerCtlWrite(1);
}

(4)继续追踪中断函数

《OpenharmonyFor6ull\kernel\liteos_m\kernel\base\core\los_tick.c》

LITE_OS_SEC_TEXT VOID OsTickHandler(VOID)
{
...
    //操作系统任务扫描,检查当前进程的剩余时间是否用完,若未用完则继续,用完则切换出去
    OsTaskScan();  // task timeout scan
...
}

《OpenharmonyFor6ull\kernel\liteos_a\kernel\base\core\los_task.c》

LITE_OS_SEC_TEXT VOID OsTaskScan(VOID)
{
    SortLinkList *sortList = NULL;
    LosTaskCB *taskCB = NULL;
    BOOL needSchedule = FALSE;
    UINT16 tempStatus;
    LOS_DL_LIST *listObject = NULL;
    SortLinkAttribute *taskSortLink = NULL;

    taskSortLink = &OsPercpuGet()->taskSortLink;
    taskSortLink->cursor = (taskSortLink->cursor + 1) & OS_TSK_SORTLINK_MASK;
    listObject = taskSortLink->sortLink + taskSortLink->cursor;

    /* 任务超时之后,任务块....看不懂,韦老师也没讲...
     * When task is pended with timeout, the task block is on the timeout sortlink
     * (per cpu) and ipc(mutex,sem and etc.)'s block at the same time, it can be waken
     * up by either timeout or corresponding ipc it's waiting.
     *
     * Now synchronize sortlink preocedure is used, therefore the whole task scan needs
     * to be protected, preventing another core from doing sortlink deletion at same time.
     */
    LOS_SpinLock(&g_taskSpin);

    ...

 

;