打印
[AURIX™]

TC3XX启动代码流程分析

[复制链接]
199|1
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
tpgf|  楼主 | 2024-7-9 11:26 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
AURIX™ BootROM
Tricore有一段自带的BROM:固化的启动代码,这段代码是英飞凌出厂就烧进去的,且无法覆盖,包括:

• 启动软件(SSW)。

• 实现附加功能的软件模块(Bootstrap loader)。

• 测试固件



Startup Software(SSW)
TC3xx系列MCU复位后会从Ifx_Ssw_Tc0.c文件的_START()函数开始启动,启动的时候都会从CPU0开始运行,直到在__StartUpSoftware_Phase6()函数中才会根据用户配置依次启动其他核。





_START()
代码解析
void _START(void)
{
    Ifx_Ssw_jumpToFunction(__StartUpSoftware);
}
//直接跳转到__StartUpSoftware()
_START()的地址由link文件中RESET决定,以Tasking为例. 在Link文件中:



这个地址也必须和BMHD中的应用程序起始地址定义一致。



__StartUpSoftware()
代码解析:
static void __StartUpSoftware(void)
{
    /* A1指向small data区域 */
    Ifx_Ssw_setAddressReg(a1, __SDATA2(0));

    /* 设置CPU0的PSW寄存器 */
    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);
    }
}
__StartUpSoftware()主要用来设置A1寄存器,设置PSW寄存器,以及判断是否是Application Reset, 从而进行不同的跳转。

在Tricore中A0,A1,A8,A9是全局地址寄存器,此处的A1所指向区域用来存放small RODATA, 所谓small data区域就是可以直接寻址。

PSW的默认值设的是0x980, 即Supervise mode, Global Address Register Write Permission Enable, Call Depth Count Enable, User Stack



__StartUpSoftware_Phase2()
代码解析:
static void __StartUpSoftware_Phase2(void)
{
    /* Power and EVRC configurations */
    IFX_CFG_SSW_CALLOUT_PMS_INIT();   //执行EVC初始化流程

    /* LBIST Tests and evaluation */
    IFX_CFG_SSW_CALLOUT_LBIST();      //执行LBIST测试

    /* MONBIST Tests and evaluation */
    IFX_CFG_SSW_CALLOUT_MONBIST();      //执行MONBIST测试

    Ifx_Ssw_jumpToFunction(__StartUpSoftware_Phase3PowerOnResetPath);
}
在这个API里面,会根据用户的配置,执行EVRC的初始化配置,LBIST测试,MONBIST测试。



__StartUpSoftware_Phase3PowerOnResetPath()/ __StartUpSoftware_Phase3ApplicationResetPath
代码解析:
static void __StartUpSoftware_Phase3ApplicationResetPath(void)
{
    IFX_SSW_INIT_CONTEXT();  //初始化上下文

    Ifx_Ssw_jumpToFunction(__StartUpSoftware_Phase5);
}

static void __StartUpSoftware_Phase3ApplicationResetPath(void)
{
    IFX_SSW_INIT_CONTEXT();

    Ifx_Ssw_jumpToFunction(__StartUpSoftware_Phase5);
}

#define IFX_SSW_INIT_CONTEXT()                                                   \
    {                                                                            \
        /* Load user stack pointer */                                            \
        Ifx_Ssw_setAddressReg(a10, __USTACK(0));                                 \
        Ifx_Ssw_DSYNC();                                                         \
                                                                                 \
        /*Initialize the context save area for CPU0. Function Calls Possible */  \
        /* Setup the context save area linked list */                            \
        Ifx_Ssw_initCSA((unsigned int *)__CSA(0), (unsigned int *)__CSA_END(0)); \
        /* Clears any instruction buffer */                                      \
        Ifx_Ssw_ISYNC();                                                         \
    }
在这个API里面, 会初始化上下文,所谓的上下文就是SP指针和CSA。

在Tricore中,SP是A10寄存器,在这里会把栈顶地址赋给A10.

CSA是存放上下文的区域,在Tricore中,每当调用子函数或者进中断时,都会把一些通用寄存器自动存放在CSA区域 (Push),然后等到子函数返回或中断返回时,再把这些寄存器恢复(Pop)

__StartUpSoftware_Phase3ApplicationResetPath 和__StartUpSoftware_Phase3PowerOnResetPath()一样。

__StartUpSoftware_Phase4
代码解析:
static void __StartUpSoftware_Phase4(void)
{
    /* This is for ADAS chip, where clock is provided by MMIC chip. This has to be
     * implemented according the board.
     */
    IFX_CFG_SSW_CALLOUT_MMIC_CHECK();

    {
        /* Update safety and cpu watchdog reload value*/
        unsigned short cpuWdtPassword    = Ifx_Ssw_getCpuWatchdogPasswordInline(&MODULE_SCU.WDTCPU[0]);
        unsigned short safetyWdtPassword = Ifx_Ssw_getSafetyWatchdogPasswordInline();

        /* servicing watchdog timers */
        Ifx_Ssw_serviceCpuWatchdog(&MODULE_SCU.WDTCPU[0], cpuWdtPassword);
        Ifx_Ssw_serviceSafetyWatchdog(safetyWdtPassword);
    }

    /* Initialize the clock system */
    IFX_CFG_SSW_CALLOUT_PLL_INIT();

    /* MBIST Tests and evaluation */
    IFX_CFG_SSW_CALLOUT_MBIST();

    Ifx_Ssw_jumpToFunction(__StartUpSoftware_Phase5);
}
在这个API里面,会首先进行对CPU0 watchdog和safety watchdog进行一次喂狗(注意:复位后CPU0 watchdog和safety watchdog是使能的),然后根据用户配置,完成时钟初始化以及MBIST测试。



__StartUpSoftware_Phase5
在这个API里面, 只有SMU配置的一个API,但是这个API只是个空函数,所以其实在这里什么都没有做。

__StartUpSoftware_Phase6
代码解析:
static void __StartUpSoftware_Phase6(void)
{
    /* Start remaining cores as a daisy-chain */
#if (IFX_CFG_SSW_ENABLE_TRICORE1 != 0)
    Ifx_Ssw_startCore(&MODULE_CPU1, (unsigned int)__START(1));           //启动core1
#endif /* #if (IFX_CFG_CPU_CSTART_ENABLE_TRICORE1 != 0)*/

#if (IFX_CFG_SSW_ENABLE_TRICORE1 == 0)
#if (IFX_CFG_SSW_ENABLE_TRICORE2 != 0)
    Ifx_Ssw_startCore(&MODULE_CPU2, (unsigned int)__START(2));           //启动core2
#endif
#if (IFX_CFG_SSW_ENABLE_TRICORE2 == 0)
#if (IFX_CFG_SSW_ENABLE_TRICORE3 != 0)
    Ifx_Ssw_startCore(&MODULE_CPU3, (unsigned int)__START(3));           //启动core3
#endif
#if (IFX_CFG_SSW_ENABLE_TRICORE3 == 0)
#if (IFX_CFG_SSW_ENABLE_TRICORE4 != 0)
    Ifx_Ssw_startCore(&MODULE_CPU4, (unsigned int)__START(4));           //启动core4
#endif
#if (IFX_CFG_SSW_ENABLE_TRICORE4 == 0)
#if (IFX_CFG_SSW_ENABLE_TRICORE5 != 0)
    Ifx_Ssw_startCore(&MODULE_CPU5, (unsigned int)__START(5));           //启动core5
#endif
#endif /* #if (IFX_CFG_SSW_ENABLE_TRICORE4 == 0) */
#endif /* #if (IFX_CFG_SSW_ENABLE_TRICORE3 == 0) */
#endif /* #if (IFX_CFG_SSW_ENABLE_TRICORE2 == 0) */
#endif /* #if (IFX_CFG_SSW_ENABLE_TRICORE1 == 0) */

    Ifx_Ssw_jumpToFunction(__Core0_start);
}
根据用户配置启动不同的内核。



SYSCON寄存器的BHALT位用来设置Core是否启动,PC用来设置程序指针,Ifx_Ssw_startCore()中设置这两个寄存器,来启动其他内核



Ifx_Ssw_startCore()

void Ifx_Ssw_startCore(Ifx_CPU *cpu, unsigned int programCounter)
{
    /* 设置 PC */
    cpu->PC.B.PC = (unsigned int)programCounter >> 1U;
    /* release boot halt mode if required */
    Ifx_CPU_SYSCON syscon;
    syscon = cpu->SYSCON;

    if (syscon.B.BHALT)
    {
        syscon.B.BHALT = 0U;        //启动内核
        cpu->SYSCON    = syscon;
    }

}
以TC38x为例,如果所有4个内核都启动,那么__StartUpSoftware_Phase6()中会启动CORE1。这个时候,__Core0_Start()和__Core1_Start()是同时运行的,在__Core1_Start()的最后会启动Core2,然后跳转到__Core2_Start(),在_Core2_Start() 最后会启动Core3, 然后跳转到__Core3_Start().

__Corex_start
以__Core0_Start()为例,在__Core0_Start()中,会根据配置使能PCACHE和DCACHE,设置A0, A8, A9地址,配置Trap和中断向量表起始地址,配置中断栈指针,初始化全局变量。

CACHE配置:

    /*是否使能CACHE*/
    {
        Ifx_CPU_PCON0 pcon0;
        pcon0.U       = 0;
        pcon0.B.PCBYP = IFX_CFG_SSW_ENABLE_TRICORE0_PCACHE ? 0 : 1; /* depending on the enable bypass bit is reset/set */
        Ifx_Ssw_MTCR(CPU_PCON0, pcon0.U);
        Ifx_Ssw_ISYNC();
    }

    {
        Ifx_CPU_DCON0 dcon0;
        dcon0.U       = 0;
        dcon0.B.DCBYP = IFX_CFG_SSW_ENABLE_TRICORE0_DCACHE ? 0 : 1; /* depending on the enable bypass bit is reset/set */
        Ifx_Ssw_MTCR(CPU_DCON0, dcon0.U);
        Ifx_Ssw_ISYNC();
    }
是否使能CACHE,通过配置下面两个宏:



通过配置PCON0的PCBYP位来使能PCACHE:




通过配置DCON0的PCBYP来使能DCACHE:



Trap和中断向量配置:

BTV寄存器用来设置Trap向量表的首地址,在工程中Trap向量表的首地址在link文件中设置,通过下面语句会把link文件中Trap向量表地址赋给BTV.

Ifx_Ssw_MTCR(CPU_BTV, (unsigned int)__TRAPTAB(0));
BIV寄存器用来设置中断向量表的首地址,在工程中中断向量表的首地址在link文件中设置,通过下面语句会把link文件的中断向量表地址赋给BIV. 注意在Tricore中,默认状态下,中断向量是32字节对齐的,中断向量的位置不是固定的,而是由中断优先级决定的。

Ifx_Ssw_MTCR(CPU_BIV, (unsigned int)__INTTAB(0));
配置ISTACK:

在Tricore中,栈分为USTACK和ISTACK, 他们的使用由PSW.IS位来决定,默认配置PSW.IS=0,即在线程中的函数调用时,会用USTACK(A11)部分作为CPU的栈,而在中断函数中会用ISTACK(ISP)作为CPU的栈。USTACK和ISTACK大小和位置是在Link文件中配置的。下面语句设置ISP:

Ifx_Ssw_MTCR(CPU_ISP, (unsigned int)__ISTACK(0));
对于CPU0,在__StartUpSoftware_Phase3ApplicationResetPath()已经把USTACK的栈顶指针赋给了A10. 对于其他CPU,在各自的__Corex_Start()把USTACK的栈顶指针赋给了各自CPU的A10。

初始化全局变量:

下面函数用来初始化全局变量 (这个函数是编译器来实现的),全局变量分为两部分:Data和BSS。

Data: 存放有初始化全局变量,有初始化的全局变量的初始值最初是放在Flash中,在这个函数里面,会把初始值从Flash赋给全局变量。

BSS: BSS段用于存放没有初始化值的全局变量,在这个函数里面会把没有初始化值的全局变量清零。

    /* Initialization of C runtime variables */
    Ifx_Ssw_C_Init();
对于其它__Corex_Start()而言,和__Core0_Start()的流程基本一样,另外增加了把USTACK栈顶指针赋给A10的操作和PSW初始化操作。

在各个CPU的__Corex_Start()最后会跳转到各自的corex_main()中运行。

补充知识
TriCore架构寄存器
TriCore架构寄存器包括:16个通用的地址寄存器A[0]~A[15],16个通用的数据寄存器D[0]~D[15],1个PC(Program Counter)寄存器,1个程序状态字寄存器PSW(Program Status Word),1个上下文信息寄存器PCXI( Previous Context Information register)。除了这些,还有Core Special Function Registers (CSFRs),这里不一一展开。上述寄存器均是32 Bit寄存器,TriCore架构寄存器如下所示:





通用寄存器中,编号0~7对应的[地址寄存器|数据寄存器]称为低位寄存器(Lower Registers),编号8~15对应的[地址寄存器|数据寄存器]称为高位寄存器(Upper Registers)。

PSW、PCXI不仅记录着程序运行的状态信息,而且与PC寄存器一样,是程序切换过程中,存储(Save)和恢复(Restore)上下文的重要组成部分。

A[10]、A[11]、A[15]、D[15]这4个通用寄存器还有特殊功能:

A[10]:栈指针寄存器Stack Pointer (SP) register
A[11]:返回地址寄存器Return Address (RA) register
A[15]:隐式基地址寄存器Implicit Address register
D[15]:隐式数据寄存器Implicit Data register
A[0]、A[1]、A[8]、A[9]是系统全局寄存器,也就是说在函数调用、中断发生过程中,上下文的Save和Restore不会存储这4个寄存器中的信息。

这里需要着重关注一下A[11]和D[15]寄存器。这两个寄存器对于Bug问题的排查非常有用。

当程序异常时,

D[15]寄存器存储记录TIN(Trap Identification Number),

A[11]记录Trap的入口地址。

如果能获取这两个信息,很多问题可被快速定位。注意,如果Bug问题是异步的(Asynchronous),A[11]寄存器最近捕获的地址并不一定是问题发生点的入口地址。

D[15]寄存器很有用,在Tricore架构中,Trap分为8个等级:MMU (Memory Management Unit)、 Internal Protection、 Instruction Error、Context Management、System Bus and Peripherals、Assertion Trap、System Call、Non-Maskable Interrupt (NMI)。通过D[15]记录的TIN号即可确认Trap的Class,因此,可以在Trap程序中,将D[15]和A[11]寄存器的信息存储到NVM中,以便于Bug问题的排查。

文章知识点与官方知识档案匹配,可进一步学习相关知识
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/2301_76563067/article/details/131324733

使用特权

评论回复
沙发
weifeng90| | 2024-7-12 08:14 | 只看该作者
这个是多核MCU?

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

1648

主题

14853

帖子

10

粉丝