汽车嵌入式开发中,经常听到一个名词:CSA(Context Save Area),上下文存储区。简单说,就是上下文信息保存的内存区。展开讨论之前,回顾一下Cortex内核运行机制,发生中断或者Trap时,程序需要先保护现场信息,将现场信息压栈(Push),之后程序跳转到中断服务例程执行对应程序,处理完异常后,程序返回现场,恢复现场信息,即:出栈(Pop)。现场信息的压栈和出栈示意如下:
不同于Cortex内核的处理机制,Tricore使用CSA方式保存和恢复上下文信息。本文,针对CSA,讨论如下几点:
- CSA是什么呢?
- CSA如何管理呢?
- CSA使用示例?
提示:本文基于TriCore架构讨论
1、CSA是什么呢?
程序执行过程中,发生Task抢占/外部中断/Function Call时,CPU程序通过存储/恢复上下文(Context)信息返回原有程序。如下图,分析如下:某个CPU在执行主程序(Code A)过程中,T0时刻,需要调用另一个程序(Code B)获取某个结果再继续执行主程序。如果CPU要准确的返回到原来主程序,CPU在执行Code B之前,需要先记录CPU此时运行的一些状态信息(eg:当前主程序运行地址、当前程序运行状态等),这个过程就是上下文保存(Save),保存的这些信息,是程序能回到原始状态的基础。当CPU执行完Code B以后,回到原来的运行状态时,需要恢复之前CPU的状态,这个过程称为上下文恢复(Restore)。
(一)CSA区域划分
如上的过程中,如果要存储Context信息,就需要一块地址存放此信息。这个地址如何划分?
答:用户自定义一块RAM区。工程中,针对不同的CPU,根据实际情况划分每个CPU的CSA区域大小与起始地址。
在TriCore架构中,一个CSA区域占用64 Bytes。具体一个CPU分配多少个CSA,取决于硬件资源及用户的自定义分配。举例:假设为CPU0分配8KBytes的CSA空间,起始地址:0x70019C00,一共可以分配81024 / 64 = 128个CSA。
CSA的起始地址可以在链接文件中(eg:.lsl)指定,如下定义CPU0的CSA0大小为8K,起始地址:0x70019C00,64Byte对齐。
CORE_SEC(.csa) (LCF_DSPR0_START + LCF_CSA0_OFFSET):
{
PROVIDE(__CSA0 = .);
. = . + LCF_CSA0_SIZE;
PROVIDE(__CSA0_END = .);
} > dsram0
(二)每个CSA存储什么?
每个CSA要么存储上文(Upper Context),要么存储下文(Lower Context)。上文和下文对应的内容如下所示:
不管上文还是下文,存储内容实质就是CPU的特定寄存器内容。对于32 bit位宽的CPU,如上每个寄存器占用4 Bytes,上文或者下文分别由16个寄存器组成,因此,每个CSA存储16*4 = 64 Bytes信息。
提示:对于外部中断、Trap以及Function Call,硬件自动保存Upper Context到CSA区域,Lower Context由程序员视情况保存/恢复。
2、CSA管理
既然CSA有多个,也就意味着,程序运行过程中,即使嵌套多层,也能依赖CSA返回主程序。如果要使用多个CSA,就意味着上下文的多次保存和恢复,如果要CPU正确运行,就需要确保上下文保存的顺序和恢复的顺序。在TriCore架构中,不管上文还是下文,均包含一个PCXI寄存器,而CSA的地址信息管理通过PCXI/Link Word管理。
每个Link Word占用4 Byte,Link Word的Segment和offset存放在PCXI寄存器。如下图:bit16~bit19存储Link Word的段(Segement),bit0~bit15存储Link Word的偏移(Offset)。
举例:假设PCXI此时寄存器的信息为0x00170670。其中,1表示存储上文信息,7表示CSA信息存放在段7,0670表示此CSA在段7偏移0x670*64 = 0x19C00,即:0x70019C00处存放Upper Context。PCXI的位域信息如下所示:
单个CSA容易维护,多个CSA如何管理呢?答:使用单链表方式。在TriCore架构架构中,有三个寄存器用于CSA信息的管理:
-
PCX:previous context list,指向使用了的CSA链表;
-
FCX:free context list,指向当前可用的CSA链表;
-
LCX:Last context,指向链表最后一个CSA区域。
1、Context Save
比如:某个CPU的运行状态如下,此时,有3个CSA可用,FCX指向CSA3,CSA3通过Link Work指向CSA4,CSA4通过Link Work指向CSA5,CSA5指向空(没有可用的CSA)。有2个CSA已经被使用,PCX指向CSA2,CSA2通过Link Word指向CSA1,如下所示:
当程序再次被外部中断/Trap/Function Call时,FCX指向CSA4,上/下文信息保存到CSA3,PCX指向CSA3,CSA3链接到CSA2,如下所示:
2、Context Restore
当CPU返回时,通过CSA3恢复上/下文信息,CPU返回到上次的运行状态,PCX重新指向CSA2,FCX重新指向CSA3,CSA3再次通过Link Word指向CSA4,如下所示:
(一)Context异常
CSA使用过程中,如果发生溢出等错误时,程序进行异常处理,在Tricore中,Context异常对应3类异常(Class 3),具体故障细分如下所示:
提示:如果使能调用深度检查(Call Depth),最大可以设置调用深度64,该参数设置可以操作PSW(CPUx Program Status Word)寄存器的CDC(Call Depth Counter,bit0~bit6)位域。
如果深度最大64,意味着最多可以保存64个CSA,至少需要划分64*64 = 4096 Byte = 4KByte空间。如果想设置更大的Stack空间,则关闭CDC的检查(设置PSW.CDC = 111111B)。
3、示例
工程开发中,程序在启动时,每个CPU会有一个CSA初始化接口(eg:IfxCpu_initCSA()),此接口执行后,上文或者下文的Link Work则建立好链接(64 Byte对齐),示例如下:
1、程序运行到目标Function Call之前,
CPU0寄存器信息如下,此时PCXI指向空,即:没有使用CSA,如下所示:
CSA起始位置(0x70019C00)信息,如下所示(之前被使用过):
2、CPU0调用目标Function,PCXI指向被使用的CSA区域,FCX指向下一个空闲的CSA区域,LCX指向最后一个CSA区域,如下所示:
被使用的CSA区域存储的上文信息如下所示,即:没有调用目标Function之前的上文信息。