Autosar Os MCU 多核 启动
1.1 core0 main 之前MCU 干了什么
1.1.2 链接文件指定入口函数
mcu 启动的地址有很多方式,这里介绍链接文件指定启动位置。使用ENTRY 指定一个symbol (不知道咋翻译)
ENTRY 是 编译器给的link文件命令。
这里面我们就可以在代码里定义一个函数。函数名字叫做
这里面 函数的链接位置,也是通过链接文件来指定。这里不赘述,一般都是”从头开始“。
这里我们要注意一点。cstart 每个core 都可以来调用。我们来看一下cstart 的实现。
1.1.3 __StartUpSoftware
由前面可以知道cstart 是上电后,MCU 指定的第一个函数入口。
第一句有个指令 __MFCR
就是说把core 寄存器 0xFE1C 的数 拿给coreID这个 变量。
这个寄存器是什么呢?
所以当不同的core 来执行 该指令的时候,返回值就是该core的coreID.
到了这里我们开始启动core0。
首先这里对A1 寄存器进行了初始化。
实际上就是把SDATA2(0) 数值 set 给 a1 寄存器。那么 SDATA2(0) 是什么呢?
那么问题来了 __A1_MEM 又是哪里来的呢?
这个又涉及到了链接文件。
这个是什么意思呢?
所以也就是看有没有内容链接到 这个sec,如果没有,那么就只需要知道 CORE_SEC 的地址就可以了。
这里 default_rom 是 0x8000xxxx, 所以最终计算下来是
也就是说A1 寄存器现在的数值是0x80008000。
下面看一下这些寄存器分别的介绍。
设置完A1 寄存器的global register 地址之后。set 另外一个寄存器。
这里设置固定值
看一下具体含义。
- Call depth counting is enabled
- Write permission to global registers A[0], A[1], A[8], A[9] is enabled.
- Supervisor ModeEnables access to all peripheral devices. It enables read/write access to coreregisters and protected peripheral devices. Tasks at this level may disableinterrupts.
什么意思呢?简言之,开启栈深度counter, 开启牛逼权限,开启一些寄存器的读写权限。
有了这些权限之后。判断一下,本次启动是因为软件重启还是硬件复位。不同的reset 方式 对应的启动流程是不一样的。
这里是通过寄存器来判断,具体不同bit 含义如下。不做赘述。
我们这里说 硬件从零上电的过程。
总结一下:在phase0 主要是初始化core 寄存器,以及赋予一些操作权限。
1.1.4 __StartUpSoftware_Phase2
这里主要对mcu 电源,内部内存进行一些自检。还是要看用户有没有进行配置,进行写自检代码,否则没有实际代码执行。
总结一下:mcu 自检操作。
1.1.5 __StartUpSoftware_Phase3PowerOnResetPath
这里主要是进行上下文初始化。包含 stack 与 CSA。也就是 上下文切换,占空间的 ram 地址。
我们先来分析一下链接文件对stack,CSA 的指定地址。
看这个之前先了解一下PROVIDE 是干什么的。简单来说就是通过链接文件来定义了一下symbol,可以给程序使用。程序如果没有定义,就用这里面的定义。具体如下(翻译麻烦,不翻译了。)。
在set好A10 寄存器 (也就是SP 指针)后,这里加了一行汇编。
什么意思呢:就是保证所有的数据都搞定了,在开始访问下一个数据,也就是说保证做CSA前A10搞好了。
随之而来初始化CSA。
具体CSA 是什么,这里直接copy一下之前写过的文章。
上下文查看
上下文保存了一些数据寄存器,地址寄存器以及程序状态字和链接字。上下文分为高级context和低级context, 高级context自动保存,低级context需要用户手动保存。这里以高级context为例。看一下怎么查看context使用情况。
连接好调试器,打开CPU 寄存器,这里会有高级context, 低级context以及一起其他的系统寄存器。
从芯片系统架构手册可知上下文的数据结构和内容如下图。
我们在调试器打开CPU 寄存器能找到PCXI,这个是链接字。这里以链接字为0x00370B70 为例。
通过取中间可用的信息来计算链接字对应RAM 空间位置。具体计算方式来自英飞凌系统架构手册,比较详细的介绍。实际上就是数据段加上偏移量。填零。
微软自带计算器 计算出 ram位置这里计算出来的是0x7002DC00 是上文我们提到的core0 的 ram空间。文档搬运工。。。
这时候可以使用调试器我这里使用的是Lauterbach可以查看内存信息。dump... 工具搬运工。。。
通过英飞凌系统架构手册里面对context的解析,可以看到上下文使用情况,以及具体数据。需要对每一个寄存器进行调查 可以查看手册。
上下文在任务切换过程中总是保存或者读取。新任务抢占老任务,老任务的上下文会被抢占,新任务结束,继续执行老任务,老任务的上下文会被重读恢复读取使用。后进先出的过程。那么下面看一下在哪些情况下上下文会被操作。
和上面说的一样,高级context保存是系统自动保存。在中断发生,进入trap,函数调用,换句话说 当系统发生需要使用另一个context的时候。高级context就会被自动保存。然而lower context则需要相应的指令去保存。这里提一下,lower 和 upper 对应的地址,对应的链接字,大小格式,都是一样的。比如配置了64个上下文位置,那么这是lower + upper 一共可以用的空间。所以可以通过程序状态字去查看这个context具体指的是lower or upper.
FCX, PCX 这两个分别的 free context List 和 Previous context List 对应的就是用过了的和可用的链接字的位置。那么问题来了,会不会存在用超了的现象,用超了会有措施吗。这里就到提到LCX 这个寄存器,指向的是最后的某个context 所以 当系统认为的 free context 和 LCX 相等时 就需要注意了。Context 马上就要超了。
举个梨子
当前在用的context时CSA2, 那么FCX 指向下一个可用的context就是CSA3, 在CSA3 里面指向的链接字就是CSA4,以此类推 直到LCX和最后一个。手册搬运工。。。
往前的话,上一个使用了的context的链接字就是CSA1.
新建一个context后,前一个用过了的 和 下一个可用的 context都会被更新,但是下一个可用后面的链表还是原来的样子。当CSA3被new出来之后就变成了这样。
自此 SP 指针 与 CSA 都已经被初始化完毕。
执行一次
isync 来确保 指令的操作完整性。
1.1.5 __StartUpSoftware_Phase4
开启内部看门狗,没什么好说的。
1.1.6 __StartUpSoftware_Phase5
如果有配置smu, 这里就可以开启SMU 模块,来监控芯片状态。
1.1.7 __StartUpSoftware_Phase6
这里是和用户main 最接近的一次。
这里面执行两个事情。
- 启动下一个core
- 运行至本core的main 函数
所以这里之后真正的多核开始了并行。一步一步来。
1.2 多核启动
1.2.1 core0 start
- 重新加载关闭看门狗
- 通过写寄存器来决定是否开启P/D cache
然后对其他几个通用寄存器进行初始化。初始化方式和上面类似。这里不一一赘述。
随之而来的就是 初始化中断向量表。注意这里中断向量表可以说是每个core都可以存在一个。互不干扰。
这个中断向量表是配置在os 里面的中断。里面包含中断服务函数,与优先级。在os 的实现代码里面是汇编。部分如下。详细可查看文件:Os_vectors.c
初始化了中断向量表,后面就需要立即给中断进行栈分配。
注意这里中断有自己独立的栈空间。和前面任务栈一个意思。
由于看门狗是自动起的。下面需要进行ram 初始化,这里先关闭看门狗,如下操作。
前面已经对中断,对栈空间,对通用寄存器进行了初始化与配置。
下面开始对ram 进行初始化。ram初始化主要有两个方面。一个是从对应的falsh 拿数值放到ram 里面。
一个是初始化直接是0. 那么怎么,哪里,如何从flash 里拿到对应的ram呢?CPU 怎么知道的。
前面有个文章已经进行说明。也是通过链接文件的方式。copytabble 方式。
这里给出接口函数。
ram已经初始化完毕。可以开启看门狗。
终于到了core0 的main 函数。
好这里我们等一下,前面说到,这个函数做了两个事情,一个是运行至core0的main. 还有一个是 __Core1_start。
对了现在两个核独立的跑了,我们需要同步分析一下另外一个core.
1.2.2 __Core1_start
首先我们看一下start core 的接口函数什么样的。
其实就是把对应的core的 寄存器 置为,进而让核运行起来。然后第二个参数,就是即将运行的函数。
分析一下
这里与前面分析的Core0 一致。只是不需要对全局ram进行初始化。因为前面已经初始化了。
还有不同的点就是这里可能会拉起后面的core. 如core2.
然后运行至自己的main函数。即:
所以从上面也能看出来,多核的启动是一个拉着一个的,
不是说core0 直接把所有都拉起来的。
后面的每一个core 怎么拉起来, 这里就不介绍了,和core1被拉起来的方式一致。
那么也就是说,我们说到了 两个main 函数。
core0 和 core1 都已经运行到了自己的main 函数。这里就涉及到同步的概念了,先期的core 要等一会 暂时还没有启动的core. 是什么意思呢?我们继续说。
1.3 各自main函数
1.3.1 core0 的main 函数
不难看出基操后面跟了个main() 然后就是while 死循环了。由此猜想。走完这个main. 可能就被os 接管了。换句话说,分析到这里,基本和autosar 没有一毛钱关系,除了,前面的中断向量表。不过那个中断其实mcal也可以实现。只是为了配合os 所以才有的。好 现在我们来分析这个
这实际是个宏展开。我来给大家展开一下就一目了然了。
这里也看出在EcuM_Init() 的时候,才真正运行到Autosar 协议栈。
好,那我们看看前面是什么。
有一个函数:
这是在干啥呢。哦。这里貌似在同步每一个core. 好不着急,我们继续往下看。
暂不介绍Dem 模块,这里进行了EcuM_Init().
当然这里会对mcu外设,等寄存器进行初始化。这里我们先不说,后面有专门的文章来介绍EcuM的相关说明。我们只说EcuM 是怎么拉起来Os的。
在EcuM 初始化list0,list1 之后,会调用
也就是说会对后面的每一个core进行
这里就是Autosar Os对应的start core.
这里可以发现,在起core的时候有callback函数。那这个对应的是什么呢。
这里是不是豁然开朗,其实core本身已经启动了。这里os 同步一下。给每一个Os_StartBlock[] 进行赋值。
也就是说当core0 走了EcuM_Init 到这里之后,才可以说 来识别每一个core 同步。core0 来给每一个core进行赋值。
这样各自core 就不会卡在 下面函数里了。
当每一个core同步之后。除了core0 其他的core都跑到各自的
因为那些core暂时没有配置EcuM 所以就直接到了while(1)
也就是说,等着os 来进行调度。
core0 这时候也进入了
即:
到这里为止 多核系统启动完毕。从裸机让Autosar Os 进行接管。
文章转载自公众号:汽车与基础软件
文档能发一下吗