单核系统启动流程
本节使用 mini2440 的 SylixOS BSP 来介绍 SylixOS 单核启动流程。其主要流程如下图 单核启动流程图:
前面提到过 SylixOS 启动时,会先从 startup.S 文件中的 reset 汇编函数开始执行,reset 函数最后会调用 bspInit.c 文件中的 C 入口函数 bspInit 函数。
bspInit 函数包含了整个 BSP 单核启动的所有流程,其内容可以总结为以下部分:
- 初始化硬件,包括调试串口。
- 设置操作系统启动参数。
- 启动内核。
目前,因为 mini2440 大多使用 u-boot 启动,u-boot 本身会对板子上基础硬件做初始化操作,其中包括调试串口,所以第一步可以不必做过多操作。
第二步启动参数设置,在上节已经做过介绍,需将启动参数传入 API_KernelStartParam 函数执行即可。
bspInit 函数最主要的是第三步启动内核。启动内核调用的是 API_KernelStart 。 API_KernelStart 是一个宏,内容如下:
#Include <SylixOS.h>
#define API_KernelStart API_KernelPrimaryStart
它对应内核里的 API_KernelPrimaryStart 函数。此函数原型如下:
#Include <SylixOS.h>
VOID API_KernelPrimaryStart (PKERNEL_START_ROUTINE pfuncStartHook,
PVOID pvKernelHeapMem,
size_t stKernelHeapSize,
PVOID pvSystemHeapMem,
size_t stSystemHeapSize);
函数 API_KernelPrimaryStart 原型分析:
- 此函数没有返回值。
- 参数 pfuncStartHook 是系统启动中的用户回调。
- 参数 pvKernelHeapMem 是内核堆内存首地址。
- 参数 stKernelHeapSize 是内核堆大小。
- 参数 pvSystemHeapMem 是系统堆内存首地址。
- 参数 stSystemHeapSize 是系统堆大小。
API_KernelPrimaryStart 函数是系统内核的入口,只允许系统逻辑主核调用。因为 mini2440 是单核,其唯一的一个核就是逻辑主核,因此可以使用这个函数。
API_KernelPrimaryStart 函数会先对内核底层做初始化操作,这里的底层指的是系统堆和内核堆,以及一些消息队列,内存管理等内容。然后会初始化中断系统,接着会对内核高层和CPP运行库做对应的初始化操作。
完成上述这些步骤后,会执行用户的回调函数,即执行 pfuncStartHook 。mini2440 的 BSP 里实现的这个回调参数为 usrStartup。其调用如下:
API_KernelStart(usrStartup,
(PVOID)&__heap_start,
(size_t)&__heap_end - (size_t)&__heap_start,
LW_NULL, 0);
usrStartup 函数会初始化应用相关的组件,并且创建操作系统的第一个任务。在初始化相关组件的时候,用户需要注意代码编写的顺序,必须先初始化 vmm,才能正确初始化 cache。网络因为需要其他资源,所以需要最后初始化。如下:
static VOID usrStartup (VOID)
{
LW_CLASS_THREADATTR threakattr;
/*
* 注意, 不要修改该初始化顺序 (必须先初始化 vmm 才能正确的初始化 cache, 网络需要其他资源必须最后初始化)
*/
halIdleInit();
#if LW_CFG_CPU_FPU2_EN > 0
halFpuInit();
#endif /* LW_CFG_CPU_FPU_EN > 0 */
#if LW_CFG_RTC_EN > 0
halTimeInit();
#endif /* LW_CFG_RTC_EN > 0 */
#if LW_CFG_VMM_EN > 0
halVmmInit();
#endif /* LW_CFG_VMM_EN > 0 */
#if LW_CFG_CACHE_EN > 0
halCacheInit();
#endif /* LW_CFG_CACHE_EN > 0 */
API_ThreadAttrBuild(&threakattr,
__LW_THREAD_BOOT_STK_SIZE,
LW_PRIO_CRITICAL,
LW_OPTION_THREAD_STK_CHK,
LW_NULL);
API_ThreadCreate("t_boot",
(PTHREAD_START_ROUTINE)halBootThread,
&threakattr,
LW_NULL); /* Create boot thread */
}
从上述代码中可以发现,在 usrStartup 最后会创建的操作系统的第一个任务,“t_boot”任务。其会初始化系统启动时要用的各项设备、驱动及 log 等内容。初始化完之后,会执行启动脚本并会创建“t_main”线程。
当“t_main”线程创建完成之后, pfuncStartHook 执行完成,此时代码继续从 API_KernelPrimaryStart 函数中往后执行,API_KernelPrimaryStart 函数最后通过调用 _KernelPrimaryEntry 函数启动内核。_KernelPrimaryEntry 函数原型如下:
#include <SylixOS.h>
static VOID _KernelPrimaryEntry (PLW_CLASS_CPU pcpuCur)
函数 _KernelPrimaryEntry 原型分析:
- 此函数没有返回值。
- 参数 pcpuCur 是当前CPU的信息。
这个函数是主核调用的启动函数。因为 mini2440 是单核,因此当前的CPU就是主核,从而可以调用此函数。_KernelPrimaryEntry 函数最终会调用 _KernelPrimaryCoreStartup 函数,其函数原型如下:
#include <SylixOS.h>
static VOID _KernelPrimaryCoreStartup (PLW_CLASS_CPU pcpuCur)
函数 _KernelPrimaryCoreStartup 原型分析:
- 此函数没有返回值。
- 参数 pcpuCur 是当前CPU的信息。
_KernelPrimaryCoreStartup 函数使得系统的主核 (负责初始化的核) 进入多任务状态,方法是调用 archTaskCtxStart,即进行一次任务切换。此时,SylixOS 单核启动的流程基本完成。