SylixOS内核入口文件是startup.S,这个文件是用汇编写的,在这个文件中会做一些必要的初始化工作(主要包含了架构相关的一些设置,比如中断向量表设置、栈设置等等。由于这些设置都是架构相关的,所以使用汇编来实现),然后跳转到C语言编写的代码中继续执行。创建BSP工程时会自动生成startup.S文件。
在介绍这里之前先简单的介绍一点中断的知识。
———————————————分割线——————————————————————————
(一) 中断简介
中断是计算机系统中的一个十分重要的概念,现代计算机毫无例外地都采用中断机制。
在计算机执行程序的过程中,由于出现某个特殊情况(或称为“事件”),使得CPU 中
止现行程序,转去执行该事件的处理程序(俗称中断处理或中断服务程序),待中断服务程
序执行完毕,再返回断点继续执行原来的程序,这个过程称为中断。
(二)中断信号线
在数字逻辑层面,外部设备和处理器之间有一条专门的中断信号线,用于连接外设与
CPU 的中断引脚。当外部设备发生状态改变时,可以通过中断信号线向处理器发出中断请
求。
处理器一般只有两根中断线,一根是IRQ 线(中断请求线),一根是FIQ 线(快速中断
请求线)。而管理的外设却有很多,为了解决这个问题,外设的中断信号线并不与处理器直
接相连,而是与中断控制器相连接,后者才跟处理器的中断信号线连接。中断控制器一般通
过CPU 进行配置。
(三)中断的实现过程。
大致分为三个过程:
(1)设备在发生状态改变时将主动发送一个信号给中断控制器;
(2)中断控制器接收到通知信号,并经过中断优先级控制器的处理,然后由中断控制器
向处理器发送中断信号;
(3)最后处理器接收到该信号,并作出相应处理。
大体框架如下图所示

(四)中断处理过程
(1)中断响应前的准备:
一般系统将所有的中断信号统一进行了编号(例如256 个中断信号:0~255),这个号
称为中断向量。在中断响应前,中断向量与中断信号的对应关系已经定义好。
中断向量和中断服务程序的对应关系是由中断向量表描述,操作系统在中断向量表中设
置好不同中断向量对应的中断服务函数,待CPU 查询使用。
(2)CPU 检查是否有中断信号
CPU 在执行完每一条指令后,都会去确认在执行刚才的指令过程中中断控制器是否发
送中断请求。如果有中断请求,CPU 就会在相应的时钟脉冲到来时从总线上读取该中断请
求的中断向量。
(3)中断处理
当中断产生时,CPU 执行完当前指令后,PC 指针将跳转到异常向量表的相应地址去执
行。该地址处是一句跳转指令,PC 指针继续跳转到系统定义的总中断服务函数里面去执行,
然后系统进行任务上下文的保存、中断向量号的获得、具体中断服务函数的执行等,执行结
束后,恢复被中断任务的上下文,继续执行任务。中断处理流程如下图所示。

—————————————————分割线————————————————————————
回到正题。
中断向量表是给中断系统使用的,当CPU产生中断或者异常时会去中断向量表中相应位置取出处理函数地址然后跳转过去运行:

当系统上电或者复位时就会跳转到reset地址处执行:

一般在这里会有一些架构相关的初始化操作,比如关闭Cache、关闭MMU、禁止TLB等等操作,具体要执行哪些操作大家可以参考U-Boot或者Linux中相关的启动代码。
SylixOS的内核中已经提供了一些常用的启动过程中会用到的接口,我们只需要在startup.S中调用这些接口即可:

我们知道程序运行过程中需要使用到栈,所以启动的时候需要将arm处理器各模式下的栈地址设置好,这里使用的栈大小直接使用默认的即可:

这里的栈起始地址是通过BSP的连接脚本(SylixOSBSP.ld)来定义的

在ARM平台上,栈一般都是递减形式的,也就是压栈时数据元素是由高地址往低地址存放的,所以BSP启动时栈的起始地址就是链接脚本中定义的__stack_end 符号地址,这个符号的值会在BSP链接阶段确定下来,最后被startup.S中的栈初始化程序使用:

这里主要就是将内核的数据段从加载地址搬运到运行地址处。

我们都知道BSS段中存放的是程序中使用的数据,需要默认值为0,所以这里需要将这段空间进行清零操作。如果系统上电前不对BSS段进行初始化,可能会导致在程序开始运行时一些全局变量加载时值并不是0值,进而导致系统异常。

当上述初始化工作都做完之后,我们就可以跳转到C语言编写的代码继续执行了:

其中bspInit函数是在bspInit.c中实现的。我们接下来将进入bspInit.c。
SylixOS内核启动的时候需要传入一些参数以打开、关闭或者设置内核的一些功能组件,这是通过调用API_KernelStartParam 这个接口来实现的:

这个函数的定义在BASE工程中:路径 libsylixos/SylixOS/kernel/interface/KernelParam.c

这些参数我们根据实际的需要进行设置,这里只介绍几个比较重要的:
ncpus:设置系统使用的cpu的个数
kdlog:内核日志,内核在创建线程、信号量等等事件时会输出打印信息,这个参数就是控制是否开启这些打印信息。我们在开发最小系统时,需要将这个参数设置为yes,在开发完成后再改回no 关闭。
kderror:内核出错时是否输出相关的打印信息,一般都需要输出,使用默认的yes 即可。
kfpu:内核是否使用硬件浮点寄存器,SylixOS内核默认不使用硬件浮点寄存器。
heapchk:是否进行堆越界检测,一般都打开。
hz:系统tick的频率,默认100,一般设置为100或者1000,不易设置过高。
hhz:高度定时器频率,默认100,需要BSP的支持。
sldepcache:这个参数只在arm平台上使用,用于指明当前架构的自旋锁实现是否依赖于Cache。因为在系统启动过程中,可能会在Cache还未打开的情况下就使用自旋锁,通过这个参数告诉系统在启动过程中不要使用这些依赖Cache的指令。在我们这个T3平台,这个参数需要设置为yes 。
rfsmap:用于指明系统根文件系统映射关系。在实际情况下,根文件系统可以挂载在SD卡、eMMC、Nandflash、内存等各种存储介质上,这个参数就是用来选择具体怎么挂载的。在最小系统的开发阶段,由于我们还没有实现SD卡等存储介质驱动,所以都是挂载在内存文件系统上,也就是这个参数需要设置为rfsmap=/:/dev/ram 。
根据上述的讲解,我们将T3平台上的启动参数设置如下:
API_KernelStartParam("ncpus=1 kdlog=yes kderror=yes kfpu=no heapchk=yes "
"sldepcache=yes hz=1000 hhz=1000 "
"rfsmap=/:/dev/ram");
/* 操作系统启动参数设置 */