> 文档中心 > OpenHarmony(3) —— SMP调度理解

OpenHarmony(3) —— SMP调度理解


代码仓:https://codechina.csdn.net/fu851523125/rtos

现代调度执行都是基于时间片(Time Slice)概念的。

 启动一个定时器,一个时间片时隙就是定时器的计数周期。

在kernel\liteos_a\kernel\base\core\los_timeslice.c中VOID OsTimesliceCheck(VOID),一个时间片过去后进行检测是否改变当前调度Task

OsTimesliceCheck()

   <<=== OsTickHandler()

      <<=== OsTickEntry()        

VOID HalClockInit(VOID)
{
    UINT32 ret;

    g_sysClock = HalClockFreqRead();
    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);
    }
}

HalClockInit()

   <<=== OsTickInit()

      <<=== OsMain()

         <<=== main()

LITE_OS_SEC_TEXT_INIT VOID HalClockStart(VOID)
{
    HalIrqUnmask(OS_TICK_INT_NUM);

    /* triggle the first tick */
    TimerCtlWrite(0);
    TimerTvalWrite(OS_CYCLE_PER_TICK);
    TimerCtlWrite(1);
}

HalClockStart()

   <<=== OsTickStart()

      <<== OsStart()

         <<=== main()

         <<=== secondary_cpu_start()

这里HalClockInit()有三处,

1. platform\hw\hisoc\timer\timer.c (宏定义 STATIC_ASSERT(LOSCFG_KERNEL_SMP != YES, "hisoc timer does not suppot on SMP mode!");) 

   SMP是不使用这个

2. platform\hw\arm\timer\arm_private\arm_private_timer.c

3. platform\hw\arm\timer\arm_generic\arm_generic_timer.c

platform\bsp.mk下

ifeq ($(LOSCFG_PLATFORM_HI3516DV300), y)
    HWI_TYPE     := arm/interrupt/gic
    TIMER_TYPE   := arm/timer/arm_generic
    HRTIMER_TYPE := hisoc/hrtimer
    NET_TYPE     := hieth
    UART_TYPE    := amba_pl011
    USB_TYPE     := usb3.0_hi3516dv300
    LITEOS_CMACRO_TEST += -DTEST3516DV300

## HI3518EV300 Options
else ifeq ($(LOSCFG_PLATFORM_HI3518EV300), y)
    HWI_TYPE     := arm/interrupt/gic
    TIMER_TYPE   := hisoc/timer
    HRTIMER_TYPE := hisoc/hrtimer
    NET_TYPE     := hieth
    UART_TYPE    := amba_pl011
    USB_TYPE     := usb3.0_hi3518ev300
    LITEOS_CMACRO_TEST += -DTEST3518EV300

endif
    选择platform\hw\arm\timer\arm_generic\arm_generic_timer.c

 

  看到一次初始化,多次start。(是否可以改成一次start呢???多次start会启动多个timer吗???)

  在OsTickHandler()开始处,

    TICK_LOCK(intSave);
    g_tickCount[ArchCurrCpuid()]++;
    TICK_UNLOCK(intSave);

由此可以看到,哪个核的计数到了,对应的tick加1,所以是每个核对应一个定时器,那么一个硬件中断怎么对应多个核呢?

#define NUM_HAL_INTERRUPT_CNTPSIRQ      29
#define NUM_HAL_INTERRUPT_CNTPNSIRQ     30
#ifdef LOSCFG_TEE_ENABLE
#define OS_TICK_INT_NUM                 NUM_HAL_INTERRUPT_CNTPNSIRQ // use non-secure physical timer for now
#else
#define OS_TICK_INT_NUM                 NUM_HAL_INTERRUPT_CNTPSIRQ // use secure physical timer for now
#endif

在hi3516dv300手册上,中断号29属于保留的,但注释中看到secure physical timer,这个就只有海思自己知道了。

那么就要看HWI了,这个是个硬件中断的统一接口。

  中断处理函数OsInterrupt()

<<=== HalIrqHandler() (GICv2)

   <<=== ./arch/arm/arm/src/los_dispatch.S:238

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

    /*
     * 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
既然是IRQ了,那就是vectors中的IRQ中断了。

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
 

    .section ".vectors","ax"

    .global __exception_handlers
__exception_handlers:
    /*
    *Assumption:  ROM code has these vectors at the hardware reset address.
    *A simple jump removes any address-space dependencies [i.e. safer]
    */
    b   reset_vector
    b   _osExceptUndefInstrHdl
    b   _osExceptSwiHdl
    b   _osExceptPrefetchAbortHdl
    b   _osExceptDataAbortHdl
    b   _osExceptAddrAbortHdl
    b   OsIrqHandler
    b   _osExceptFiqHdl
 

还是没有接触之前的疑惑,中断22号是否与某个定时器中断号挂钩?

  所以,要从定时器方向开始找了。

LITE_OS_SEC_TEXT_INIT VOID HalClockInit(VOID)
{
    UINT32 ret;

    g_sysClock = HalClockFreqRead();
    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);
    }
}

UINT32 HalClockFreqRead(VOID)
{
    return READ_TIMER_REG32(TIMER_REG_CNTFRQ);
}

所以这里忽略了一点,由于SMP没有选择hisoc的timer,而是使用了arm_generic_timer,

#define OS_CYCLE_PER_TICK (g_sysClock / LOSCFG_BASE_CORE_TICK_PER_SECOND)

UINT32 HalClockFreqRead(VOID)
{
    return READ_TIMER_REG32(TIMER_REG_CNTFRQ);
}

VOID HalClockFreqWrite(UINT32 freq)
{
    WRITE_TIMER_REG32(TIMER_REG_CNTFRQ, freq);
}

这里选择了系统时钟来做tick处理

LOSCFG_BASE_CORE_TICK_PER_SECOND  = 100 设定每秒100个tick,每个tick 10ms(大家都是这么设定了,linux,RT-Thread)

每秒100个tick,系统时钟每秒多少个周期,

#define OS_CYCLE_PER_TICK (g_sysClock / LOSCFG_BASE_CORE_TICK_PER_SECOND)
每个tick需要多少个周期

设置

VOID HalClockFreqWrite(UINT32 freq)
{
    WRITE_TIMER_REG32(TIMER_REG_CNTFRQ, freq);
}

main()

{
    OsSetMainTask();
    OsCurrTaskSet(OsGetMainTask());

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

    。。。

}

 

===================================================

 

在platform/main.c

#define CLEAR_RESET_REG_STATUS(regval) (regval) &= ~(1U << 2)
LITE_OS_SEC_TEXT_INIT VOID release_secondary_cores(VOID)
{
    UINT32 regval;

    /* clear the slave cpu reset */
    READ_UINT32(regval, PERI_CRG30_BASE);
    CLEAR_RESET_REG_STATUS(regval);
    WRITE_UINT32(regval, PERI_CRG30_BASE);

    /* wait until all APs are ready */
    while (LOS_AtomicRead(&g_ncpu) < LOSCFG_KERNEL_CORE_NUM) {
        asm volatile("wfe");
    }
}

启动核使用wfe等待其他核启动

WFI(Wait for interrupt)和WFE(Wait for event)是两个让ARM核进入low-power standby模式的指令,由ARM architecture定义,由ARM core实现。

WFE可以被任何PE上执行的SEV指令唤醒。

所谓的SEV指令,就是一个用来改变Event Register的指令,有两个:SEV会修改所有PE上的寄存器;SEVL,只修改本PE的寄存器值。

在arch/arm/arm/src/los_hw.c中

VOID Sev(VOID)
{
    __asm__ __volatile__ ("sev" : : : "memory");
}
然而Sev()并没有被调用,而且

VOID secondary_cpu_start(VOID)

{

。。。

OsIdleTaskCreate();

。。。

}

所以这里的设计,主核只是启动并做一部分初始化,然后唤起其他核,由其他核执行系统,自身并不再参与。

这样,由原来的双核又变成了单核。

(PS: 随着后面的认知,这个想法是错误的,WFE可以由其他事件唤醒,比如ARM核内部的system counter)