车载软件架构 --- Autosar OS MCU多核启动
我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。
老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师:
周末洗了一个澡,换了一身衣服,出了门却不知道去哪儿,不知道去找谁,漫无目的走着,大概这就是成年人最深的孤独吧!
旧人不知我近况,新人不知我过往,近况不该旧人知,过往不与新人讲。纵你阅人何其多,再无一人恰似我。
时间不知不觉中,来到新的一年。2025开始新的忙碌。成年人的我也不知道去哪里渡自己的灵魂,独自敲击一些文字算是对这段时间做一个记录。
一、MCU启动
Core0 main 函数执行之前 MCU 的启动准备工作
1、 链接文件指定入口函数
MCU 的启动地址可以通过多种方式来确定,这里我们重点介绍通过链接文件指定启动位置的方法。在链接文件中,我们可以使用 ENTRY 指令来指定一个符号(symbol)作为程序的入口点。
assembly
ENTRY(cstart)
ENTRY 是编译器提供的链接文件命令。
程序中的第一条执行指令被称为入口点。我们可以使用 ENTRY 链接脚本命令来设置这个入口点。其参数是一个符号名称,具体格式如下:
assembly
ENTRY(<symbol>)
设置入口点有多种方式,链接器会按照以下顺序依次尝试,并在成功设置后停止:
命令行选项:使用 -e 命令行选项指定入口点。
链接脚本命令:在链接脚本中使用 ENTRY() 命令指定。
符号 start:如果定义了符号 start,则使用其值作为入口点。
.text 段的起始地址:如果存在 .text 段,则使用其第一个字节的地址作为入口点。
地址 0:如果以上方法均未成功,则使用地址 0 作为入口点。
在实际应用中,我们可以在代码中定义一个函数,并将其名称指定为入口点。例如:
void cstart(void)
{// 初始化代码
}
该函数的链接位置同样是通过链接文件来指定的。通常情况下,我们会将其设置为从程序的起始地址开始执行,这里不再赘述具体的设置方法。
注意事项
需要特别注意的是,cstart 函数可以被每个核心(core)调用。接下来,我们将简要探讨一下 cstart 函数的实现要点。
深入解析
在多核系统中,每个核心在启动时都会执行 cstart 函数,但具体的执行流程和初始化内容可能会因核心的不同而有所差异。因此,在实现 cstart 函数时,需要考虑以下几点:
-> 核心标识:在 cstart 函数中,首先需要确定当前执行的核心标识,以便根据核心的不同执行相应的初始化操作。
-> 硬件初始化:根据核心的需求,对相应的硬件资源进行初始化,如时钟、外设等。
-> 内存初始化:对核心的私有内存或共享内存进行初始化,确保后续程序的正确执行。
-> 跳转到 main 函数:在完成所有初始化操作后,cstart 函数会跳转到 main 函数,开始执行用户程序。
通过合理设计 cstart 函数,我们可以确保每个核心在启动时都能正确执行初始化操作,并为后续程序的执行奠定坚实的基础。
这里我们要注意一点。cstart 每个core 都可以来调用。我们来看一下cstart 的实现。
void cstart(void)
{unsigned int coreID;coreID = __MFCR(0xFE1C);if(coreID == 0u){Ifx_Ssw_jumpToFunction(__StartUpSoftware);}if(coreID == 1u){Ifx_Ssw_jumpToFunction(__Core1_start);}if(coreID == 2u){Ifx_Ssw_jumpToFunction(__Core2_start);}if(coreID == 3u){Ifx_Ssw_jumpToFunction(__Core3_start);}if(coreID == 4u){Ifx_Ssw_jumpToFunction(__Core4_start);}if(coreID == 5u){Ifx_Ssw_jumpToFunction(__Core5_start);}
}
2、 __StartUpSoftware
从前面的内容可以了解到,cstart 是 MCU 上电后指定的第一个函数入口。在 cstart 函数的执行过程中,存在一条关键指令 __MFCR,其含义为 “Move from core register”(从核心寄存器中移动数据)。具体来说,这条指令会将核心寄存器 0xFE1C 中的数值读取出来,并赋值给 coreID 变量。
那么,这个核心寄存器 0xFE1C 究竟是什么呢?
通过分析可知,当不同的核心执行这条 __MFCR 指令时,返回的值就是该核心对应的 coreID。这一特性使得我们可以根据 coreID 来区分不同的核心,并为后续针对不同核心的初始化操作提供依据。
在获取了 coreID 之后,我们正式开始启动 core0,进入 __StartUpSoftware 函数的执行流程。
通过分析可知,当不同的核心执行这条 __MFCR 指令时,返回的值就是该核心对应的 coreID。这一特性使得我们可以根据 coreID 来区分不同的核心,并为后续针对不同核心的初始化操作提供依据。
在获取了 coreID 之后,我们正式开始启动 core0,进入 __StartUpSoftware 函数的执行流程。
c
void __StartUpSoftware(void)
{// 函数具体实现
}
寄存器初始化
在 __StartUpSoftware 函数的起始部分,首先对 A1 寄存器进行了初始化操作:
c
Ifx_Ssw_setAddressReg(a1, __SDATA2(0));
这条语句的作用是将 SDATA2(0) 的数值设置给 a1 寄存器。那么,SDATA2(0) 究竟是什么呢?
通过查看相关定义可知:
c
#define __SDATA1(cpu) __A0_MEM
#define __SDATA2(cpu) __A1_MEM
#define __SDATA3(cpu) __A8_MEM
#define __SDATA4(cpu) __A9_MEM
### 由此可见,SDATA2(0) 实际上对应的是 __A1_MEM。
__A1_MEM 的来源
那么,__A1_MEM 又是从哪里来的呢?这就涉及到了链接文件的相关内容。
在链接文件中,会对各个段(section)进行布局和定义,其中就包括与 __A1_MEM 相关的内存区域定义。链接器会根据这些定义,将不同的代码和数据分配到相应的内存地址中。__A1_MEM 通常是一个宏定义,用于表示某个特定的内存地址或内存区域,该区域可能用于存储与 A1 寄存器相关的数据或配置信息。
通过深入分析链接文件,我们可以找到 __A1_MEM 的具体定义和分配情况,从而更好地理解 __StartUpSoftware 函数中对 A1 寄存器的初始化操作。
通过对 __StartUpSoftware 函数的逐步剖析,我们了解到在 MCU 上电启动过程中,cstart 函数作为入口点,通过 __MFCR 指令获取 coreID,进而区分不同的核心。随后,在 __StartUpSoftware 函数中,对 A1 寄存器进行初始化,其中涉及到的 SDATA2(0) 和 __A1_MEM 等概念与链接文件密切相关。深入理解这些概念和机制,有助于我们更好地掌握 MCU 的启动流程和初始化过程,为后续的软件开发和调试工作奠定坚实的基础。
搁笔分享完毕!
愿你我相信时间的力量
做一个长期主义者
PS:
Hello 同行:首先谢谢您的关注!
可提供如下服务:
1、车载诊断架构、协议、数据库编辑(CDD/ODX/DBC)培训;
2、AUTOSAR软件架构理论、实践培训;
3、电子电器架构车载诊断刷写方式、网关、企业标准培训;
4、测试项目(脚本培训);
5、Bootloader demo源码、刷写上位机…