第一次接触Tricore, 学习下从芯片上电到运行到main函数的过程,本文以TC36X为例,具体可参考英飞凌官方的Infineon-AURIX_TC3xx_Part1-UserManual-v02_00-EN 首先Tricore类似三星的MPU,有一段自带的BROM,这段FLASH中存储了一段固化的启动代码,称为Startup Sofrware(SSW), 这个意味着这段代码是英飞凌出厂就烧进去的,且无法覆盖。 然后SSW什么时候执行呢?当然是RESET的时候,英飞凌把RESET分为三种:
- Cold (initial) power-on reset 上电复位,一切都处于初始状态
- System reset 触发源为内部看门狗,软件主动请求,PORST输入脉冲复位等,复位后一切都处于初始状态
- Application reset 触发源和System reset相同,只是可以通过复位前软件配置,使得复位后时钟,RAM不会跟着复位
复位后如果一切正常就会自动执行SSW 先放张图 SSW会去读取DFLASH UCB区的BMHD(Boot Mode Header)内容。 一共四个配置项,从BMHD0开始检测是否有效,如果BMHD0有效则使用它,否则依次检测出第一个有效的BMHD,如果都无效则需要进入BootStrap Loader刷程序,芯片无法正常启动,跳转到用户写的代码去执行 这就是一个UCB-BMHD内容,需要用户自己填写(不确定出厂是否会有默认的内容),编译链接到.section .bmhd_0_orig 并下载到DFLASH.UCB.BMHD对应的地址上去,这样下次芯片上电执行SSW才能读到正确的内容。 找了一个例程,大家看下配置
#if defined(__HIGHTEC__)
#pragma section
#pragma section ".bmhd_0_orig" a
#endif
#if defined(__TASKING__)
#pragma section farrom "bmhd_0_orig"
#endif
#if defined(__DCC__)
#pragma section CONST ".bmhd_0_orig" far-absolute R
#endif
#if defined(__ghs__)
#pragma ghs section rodata= ".bmhd_0_orig"
#endif
const Ifx_Ssw_Bmhd bmhd_0_orig =
{
0x003E,
0xB359,
0xA0000000,
0xD86CBDAB,
0x27934254,
{
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000
},
{
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
},
{
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000
},
0x43211234
};
这里的BMI =0x3E =0b111110 ,大家可以具体看看这种配置是社么意思 比如PINDIS=0且 HWCFGbits [3:1] = 0b111 代表上电无需检测外部Pin14.2等引脚电平,直接start from Flash,那么从Flash哪个地址开始执行呢? BMHD.STAD: User Code start address 是可以配置的 这里配置为了0xA0000000 这是NON-CACHE CPU0的Program Flash起始地址
#if defined(__HIGHTEC__)
#pragma section
#pragma section ".start" x
#endif
void _START(void)
{
Ifx_Ssw_jumpToFunction(__StartUpSoftware);
}
#if defined(__HIGHTEC__)
#pragma section
#endif
通过宏将void _START(void)代码放到了section .start
链接脚本
#define LCF_STARTPTR_NC_CPU0 0xA0000000
group (ordered)
{
group reset (run_addr=RESET)
{
section "reset" ( size = 0x20, fill = 0x0800, attributes = r )
{
select ".text.start";
}
}
group interface_const (run_addr=mem:pfls0[0x0020])
{
select "*.interface_const";
}
"__IF_CONST" := addressof(group:interface_const);
"__START0" := LCF_STARTPTR_NC_CPU0;
"__START1" := LCF_STARTPTR_NC_CPU1;
}
总之就是将_START放到了CPU0的PFLASH0的起始地址,也就是BMHD中指定的STAD,而CPU1的_START1同样也放到了PFLASH1的起始地址,顺便提一下,上电的时候CPU0是默认开启的,且SSW也是由这个核执行的,其他所有CPU都处于HALT状态。 到这里SSW就结束了,后面就是执行用户写的代码,第一个指令就是_START(相当于CPU0的启动代码)位于Ifx_Ssw_Tc0.c
void _START(void)
{
Ifx_Ssw_jumpToFunction(__StartUpSoftware);
}
static void __StartUpSoftware(void)
{
Ifx_Ssw_setAddressReg(a1, __SDATA2(0));
Ifx_Ssw_MTCR(CPU_PSW, IFX_CFG_SSW_PSW_DEFAULT);
if (Ifx_Ssw_isApplicationReset() != 1)
{
Ifx_Ssw_jumpToFunction(__StartUpSoftware_Phase2);
}
else
{
Ifx_Ssw_jumpToFunction(__StartUpSoftware_Phase3ApplicationResetPath);
}
}
static void __StartUpSoftware_Phase3ApplicationResetPath(void)
{
IFX_SSW_INIT_CONTEXT();
Ifx_Ssw_jumpToFunction(__StartUpSoftware_Phase5);
}
#define IFX_SSW_INIT_CONTEXT() \
{ \
\
Ifx_Ssw_setAddressReg(a10, __USTACK(0)); \
Ifx_Ssw_DSYNC(); \
\
\
\
Ifx_Ssw_initCSA((unsigned int *)__CSA(0), (unsigned int *)__CSA_END(0)); \
\
Ifx_Ssw_ISYNC(); \
}
按道理说只有初始化的Stack才能执行C语言写的函数,
那么在这之前的那些步骤难道不算C语言函数?
static void __StartUpSoftware_Phase5(void)
{
IFX_CFG_SSW_CALLOUT_SMU();
Ifx_Ssw_jumpToFunction(__StartUpSoftware_Phase6);
}
static void __StartUpSoftware_Phase6(void)
{
#if (IFX_CFG_SSW_ENABLE_TRICORE1 != 0)
Ifx_Ssw_startCore(&MODULE_CPU1, (unsigned int)__START(1));
#endif
Ifx_Ssw_jumpToFunction(__Core0_start);
}
Ifx_Ssw_startCore(&MODULE_CPU1, (unsigned int)__START(1));
void Ifx_Ssw_startCore(Ifx_CPU *cpu, unsigned int programCounter)
{
cpu->PC.B.PC = (unsigned int)programCounter >> 1U;
Ifx_CPU_SYSCON syscon;
syscon = cpu->SYSCON;
if (syscon.B.BHALT)
{
syscon.B.BHALT = 0U;
cpu->SYSCON = syscon;
}
}
可以看到是直接将cpu1->PC寄存器指向_START1()函数,
并且通过设置某个特殊寄存器,解除CPU1的HALT状态,使其进入RUN,
那么CPU1就可以执行_START1()了
CPU0开启CPU1后,继续自己的初始化
Ifx_Ssw_jumpToFunction(__Core0_start);
static void __Core0_start(void)
{
... 很多代码大家可以自己去看, 这里就不分析了,
值得注意的是初始化看门狗, 初始化RAM,
建立异常和中断Vector,
中断栈, 使能或不使能Cache等等
Ifx_Ssw_jumpToFunction(core0_main);
}
最重要的是Ifx_Ssw_jumpToFunction(core0_main) 这样芯片从上电CPU0 RUN CPUX HALT ->读取UFLASH-UCB-BMHD->_START0->core_start(cpu1)->go to MAIN函数就完成了, 同样的CPU1执行_START1() 后也会继续执行自己的core1_main 现在多核CPU就可以各自执行自己的MAIN函数(它们在不同的flash地址), 各玩各的,但这样是显然不行的,涉及到共享资源访问,核间通信等问题以后再研究。
|