AURIX Tricore TC3XX启动代码流程分析

目录

AURIX™ BootROM

Startup Software(SSW)

_START()

 代码解析

__StartUpSoftware()

 代码解析:

__StartUpSoftware_Phase2()

代码解析:

 __StartUpSoftware_Phase3PowerOnResetPath()/ __StartUpSoftware_Phase3ApplicationResetPath

代码解析:

__StartUpSoftware_Phase4

代码解析:

 __StartUpSoftware_Phase5

__StartUpSoftware_Phase6

代码解析:

__Corex_start

补充知识

TriCore架构寄存器


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);
}

根据用户配置启动不同的内核。

IFX_CFG_SSW_ENABLE_TRICORE11: 启动core1;0: 不启动core1
IFX_CFG_SSW_ENABLE_TRICORE21: 启动core2;0: 不启动core2
IFX_CFG_SSW_ENABLE_TRICORE31: 启动core3;0: 不启动core3
IFX_CFG_SSW_ENABLE_TRICORE41: 启动core4;0: 不启动core4
IFX_CFG_SSW_ENABLE_TRICORE51: 启动core5;0: 不启动core5

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问题的排查。

  • 4
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱思考的发菜_汽车网络信息安全

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值