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)