打印
[AURIX™]

TriCore异常处理机制(Trap)

[复制链接]
32|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
nawu|  楼主 | 2024-11-30 11:03 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
1. 异常处理机制——Trap系统介绍
1.1 计算机异常处理机制简介
计算机的异常处理机制是一种操作系统和硬件结合的策略,用于处理在程序执行过程中发生的各种异常情况,包括硬件错误、软件错误以及其他意外情况。

计算机系统可能面临多种类型的异常,包括硬件故障(如内存错误、CPU错误)、软件错误(如无效指令、内存溢出)、外部事件(如IO错误、网络中断)以及系统资源耗尽等。这些异常可能会导致程序崩溃、数据丢失或系统崩溃,因此异常处理机制的设计对于确保计算机系统的稳定性和可靠性至关重要。

针对异常的处理通常包括以下要素:

异常检测:计算机硬件和操作系统需要能够及时检测到发生的异常情况。硬件可以通过各种机制,如中断、故障检测电路等来检测异常,而操作系统则通过监控程序执行状态、资源利用情况等来检测异常。
异常处理:一旦异常被检测到,计算机系统会根据异常的类型和严重程度采取相应的处理措施。这可能包括中断当前正在执行的程序、调用异常处理程序、记录异常信息以便后续分析等。
异常处理程序:针对不同类型的异常,计算机系统通常会预先定义一系列的异常处理程序。这些程序可以是操作系统内核中的一部分,也可以是应用程序提供的特定处理逻辑。异常处理程序的任务是尽可能地使系统恢复到正常工作状态,或者至少保证系统在异常发生时能够以安全的方式继续运行。
异常记录和分析:为了便于系统管理员和开发人员对异常进行跟踪和分析,计算机系统通常会记录异常事件的相关信息,包括异常类型、发生时间、引发异常的程序或进程、异常处理过程中的状态等。
安全性和可靠性:异常处理机制设计的一个重要目标是确保系统在异常情况下能够以安全可靠的方式继续运行。这包括对系统的保护机制,防止异常情况导致系统崩溃或数据丢失,并尽可能地提供恢复机制以确保系统的可用性。
1.2 TriCore的异常处理机制简介
TriCore的异常处理机制称为Trap系统,TriCore的硬件在运行过程中,会对内存的处理、指令取指执行等操作做监控,当检测到异常时,执行相应的异常动作。

TriCore将异常分为8个类,包括内存、指令、数据处理等,每个类包括不同数量的异常:





每个Class中的Trap都有一个二级类号,称为TIN(Trap Identification Number)。比如我们说Trap 4-2,就是指上表中的Trap Class4 TIN2——DSE。

其中Class 6——System Call一般是用于操作系统的系统调用,Class 7——Non-Maskable Interrupt则作为不可屏蔽中断使用,用于处理系统中极其紧急且优先级高的事务,因其可以打断任何非Trap类的程序,所以使用时需要极其谨慎。

和之前提到的中断处理机制类似,TriCore使用异常向量表对异常进行跳转,每一类Trap有一个Trap入口地址,用户可以在入口地址针对不同的Trap编写不同的处理、记录逻辑。

2. Trap系统
TriCore中Trap系统的机制其实并不复杂,因为进入Trap之后,系统应尽可能快速简短地处理异常,并执行用户设定的处理程序。其整体的逻辑就是在中断发生时由硬件进行现场保护,然后通过中断向量表查找入口地址并执行Trap处理程序。最后通过RFE指令返回(如果Trap可恢复)。

2.1 异常向量表
TriCore中异常处理是使用异常向量表来进行管理的,用户需要在PFlash中分配8*32字节的内存空间(开头需256字节对齐),用来存放异常向量表,每个异常类占据32字节的空间。向量表中用来存放跳转指令,用来跳转到用户定义的异常处理程序中。



以下是Infineon官方MCAL中向量表示例代码:

void traptab_cpu0 (void)
{
  __asm (".align 256");
  /* Class 0, MMU Traps: */
  __asm("j       _trap_0");/*  Jump to the trap handler */

  __asm (".align 32");
    /* Class 1, Internal Protection Traps */
  __asm("j       _trap_1");/*  Jump to the trap handler */

  __asm (".align 32");
  /* Class 2, Instruction Error Traps */
  __asm("j       _trap_2");/*  Jump to the trap handler */

  __asm (".align 32");
  /* Class 3, Context Management Traps */
  __asm("j       _trap_3");/*  Jump to the trap handler   */

  __asm (".align 32");
  /* Class 4, System Bus and Peripheral Error Traps */
  __asm("j       _trap_4");/*  Jump to the trap handler   */

  __asm (".align 32");
  /* Class 5, Assertion Traps */
  __asm("j       _trap_5");/*  Jump to the trap handler     */

  __asm (".align 32");
  /* Class 6, System Call Trap */
  __asm("j       _trap_6");/*  Jump to the trap handler     */

__asm (".align 32");
  /* Class 7, Non Maskable Interrupt Traps */·
  __asm("j       _trap_7");/*  Jump to the trap handler       */
}



内存分配完毕之后,在系统启动时还需要告诉硬件,异常向量表的位置。这一步是通过设置异常向量表基址寄存器(**Base Trap Vector Table Pointer)**来实现的。



RES:预留。
BTV:Base Trap Vector Table,异常向量表基址地址,因TriCore有8个Trap类,此处系统要求256字节对齐。
系统启动过程中,需要设置异常向量表指针寄存器,以下是Infineon MCAL原厂代码,其中**__TRAPTAB**是从链接脚本中获取的Trap向量表起始地址:

/* Trap vector table initialization is necessary if it is not same as default value */
Ifx_Ssw_MTCR(CPU_BTV, (unsigned int)__TRAPTAB(0));
/* Base interrupt vector table initialized */
Ifx_Ssw_MTCR(CPU_BIV, (unsigned int)__INTTAB(0));
/* Interrupt stack pointer is configured */
Ifx_Ssw_MTCR(CPU_ISP, (unsigned int)__ISTACK(0));


完成向量表的设定之后,接下来的事情就交给硬件了,系统只需要正常运行就可以了。

当硬件检测到异常发生时,根据不同的Trap类进入相应的入口地址,比如发生了Class4 TIN2,则硬件会按照向量表基址及Trap Class计算入口地址:Entry = BTV + 32*Class,并进行跳转。而Trap的子类TIN则存在通用寄存器D15中。

2.2 Trap响应流程
Trap发生后,如果系统能够处理该类型的Trap,并且返回后系统能够继续运行(后续不再发生该Trap),则属于可恢复的Trap。如果发生该Trap后系统后续已经无法继续运行了,则属于不可恢复的Trap。

可恢复Trap发生后,内核会执行如下步骤:

Trap发生后的第一个动作是保存上部分上下文,这和中断系统的处理流程是类似的;
**返回地址寄存器A[11]**会更新为,导致该Trap的指令位置;
Trap的子类TIN值被保存到通用寄存器D[15];
如果中断栈寄存器没有被使用,则栈寄存器A[10]会被设置为中断栈指针地址ISP,并将PSW.IS置1;
I/O模式被切换到Supervisor Mode,也就是最高级的特权模式;
保护寄存器集被设置为0:PWS.PRS=0;
调用深度计数器(CDC)被清零,并设置调用深度限值为64,PSW.CDC=0000000B;
调用深度检查使能:PSW.CDE=1;
PSW的安全位被设置为SYSCON寄存器中的值PSW.S=SYSCON.TS;
A[0], A[1], A[8], A[9]的访问被禁止:PSW.GW=0;
全局中断位被关闭:ICR.IE=0;
按照Trap类在异常向量表中查询入口地址,并开始执行。
Trap的响应流程整体上和中断系统是类似的,硬件的主要任务还是保护上下文、切换系统模式、关中断等。同样得益于该硬件机制,系统能够迅速地执行用户定义的Trap处理程序,实现问题发生时的高实时性。

另外有一个Trap是无法恢复的,那就是上下文使用超限——FCU。其响应流程有些不同,只有如下几个步骤:

Trap的子类TIN值被保存到通用寄存器D[15];
如果中断栈寄存器没有被使用,则栈寄存器A[10]会被设置为中断栈指针地址ISP,并将PSW.IS置1;
I/O模式被切换到Supervisor Mode,也就是最高级的特权模式;
保护寄存器集被设置为0:PWS.PRS=0;
PSW的安全位被设置为SYSCON寄存器中的值PSW.S=SYSCON.TS;
全局中断被关闭:ICR.IE=0;
按照Trap类查询异常向量表并开始执行。
2.3 Trap处理程序
Trap处理程序是由用户自定的,用户可以在其中编写中断处理代码,进行异常修复、记录等事务。这里需要注意的是,所有的8类Trap都需要编写代码进行捕捉识别,否则出现之后无法定位。

虽然TriCore中几乎所有Trap都是可恢复的,Trap处理函数返回后系统能够继续执行后续代码,但是通常我们认为发生了Trap,代表软硬件系统已经出现了故障,其执行结果已经不可靠了,且继续向下执行代码也很可能再次发生此类Trap,因此嵌入式领域中通常的做法是将Trap的相关信息进行保存,然后执行复位,以尝试最大限度地恢复系统。

以下是Infineon原厂MCAL对于Trap的DEMO处理程序,主要还是用于辅助调试。

/* Trap class 0 handler. */
void _trap_0( void )
{
  uint32 tin;
  uint32 CoreId = Mcal_GetCpuIndex();

  __asm ("svlcx");

  __GETTIN (tin);

  TrapIdentification[0][tin] = 1;
  switch(tin)
  {
    case 0:
      Mcal_GetSpinlock(&Trap_lock, CPU_SPINLOCK_TIMEOUT_TRAP);
      print_f("\nCoreId = %d\n", CoreId);
      print_f("\nClass 0: Virtual Address Fill Trap occurred\n");
      Mcal_ReleaseSpinlock(&Trap_lock);
      get_char();
      break;

    case 1:
      Mcal_GetSpinlock(&Trap_lock, CPU_SPINLOCK_TIMEOUT_TRAP);
      print_f("\nCoreId = %d\n", CoreId);
      print_f("\nClass 0: Virtual Address Protection Trap occurred\n");
      Mcal_ReleaseSpinlock(&Trap_lock);
      get_char();
      break;

    default:
      /* Halt the execution if debug mode enabled.*/
      DEBUG();
      break;
  }
  __asm ("rslcx \n");
  __asm ("rfe \n");
}



我们可以看到程序首先保存了下部分上下文,用于预备执行较为复杂的Trap处理逻辑;然后从通用寄存器D[15]中获取Trap的TIN信息,用于Trap的详细识别。随后进行了Trap相关信息的打印,最后通过RFE指令返回。

2.4 Trap退出流程
Trap的退出仅仅是通过一条RFE指令实现的,但是这里还是想说一下,方便大家理解Trap的恢复。

将返回地址加载到PC中,注意此时仍指向发生Trap的那条指令,所以如果问题没有修复,仍然会重新进Trap,这也是为什么一般都采取复位的Trap处理方式;
恢复全局中断使能位:ICR.IE = PCXI.PIE;
恢复中断优先级位:ICR.CCPN = PCXI.PCPN;
将保存的高上下文恢复
至此Trap处理结束,程序将重新执行先前发生Trap的那条指令,如果在Trap中修复了问题,那程序将继续正常运行。

2.5 同步Trap和异步Trap
Trap还分为同步Trap和异步Trap,对于某些Trap,如指令异常等,在执行过程中即可被捕捉并响应,属于同步Trap。但是如果我们打开了数据缓存(Data Cache),数据的写入其实是异步的。也就是说,在没有使用内存屏障的前提下,我们对数据的写入其实只是往Cache里写,然后程序继续执行后续指令,由Cache负责把数据写入到内存中。而Trap只有在Cache把数据往内存里写的时候才会被检测到,此时我们的程序已经执行到后面某个位置了,所以是异步的。

对于异步的Trap,因为其返回地址已经没有参考性了,所以此时最好的办法是关闭Cache,使Trap变为同步Trap,然后就很好定位了。

2.6 Trap优先级
当多个Trap同时发生时,或者在指令流水执行时存在多个Trap,则有一定的优先级仲裁逻辑。

首先来说,异步Trap的优先级要高于同步Trap,其次高于所有中断的优先级;
其次指令序列中导致Trap的指令,最先进入序列的具有更高优先级;
上下文空间溢出,也就是CSA空间用尽导致的Trap——FCU,其优先级高于所有其他Trap;
当同一条指令触发了多个同步Trap之后,其优先级参考文档中的Table 9,此处就不一一列出了。
2.7 Trap相关寄存器
Trap系统除了Trap基址寄存器BTV以外,还有一些辅助寄存器,用户Trap发生后的辅助排查,主要都是内存访问相关的。

2.7.1 Program Synchronous Error Trap Register (PSTR)
PSTR包含了程序存储系统的同步Trap信息。该寄存器被更新以存储PSE Trap的信息,以帮助定位异常。只有在检测到Trap并且寄存器中没有已设置的位时,寄存器才会被设置。它通过CSFR写入(与数据值无关)来清除。



2.7.2 Data Synchronous Error Trap Register (DSTR)
DSTR包含了数据存储系统的同步Trap信息。该寄存器被更新以存储DSE Trap源信息,以帮助定位异常。每当检测到有效的Trap并且寄存器中没有已设置的位时,寄存器就会被更新。它通过写入(与数据值无关)来清除。







2.7.3 Data Asynchronous Error Trap Register (DATR)
DATR包含了数据存储系统的异步Trap信息。该寄存器被更新以存储DAE Trap的信息,以帮助定位故障。每当检测到有效的Trap并且寄存器中没有已设置的位时,寄存器就会被更新。它通过写入(与数据值无关)来清除。如果DATR寄存器非零,DAE Trap将被抑制。



2.7.4 CPUx Data Error Address Register(DEADD)
DEADD包含了数据存储系统的Trap地址信息。该寄存器更新为MEM、ALN、DSE或DAE的Trap信息,以帮助定位故障。只有在检测到陷阱并且DATR或DSTR寄存器没有任何位已经设置时,才会设置该寄存器。只有当DATR或DSTR寄存器非零时,寄存器内容才有效,因此在清除这些寄存器之前应先读取其内容。



2.8 Trap系统小结
如前文所述,Trap系统的设计还是比较简单的,用户只需要做两件事:分配对应的Trap向量表,初始化Trap基址寄存器BTV。当Trap发生时按照对应的Class,通过查询Trap向量表的入口地址,执行对应的Trap,并通过通用寄存器D[15]获取TIN号。然后数据访问相关的Trap还有一些辅助寄存器,用来帮助定位Trap。

然而我们实际开发过程中,因为开发的代码较为庞大和复杂,涉及到多层嵌套和数据传递,以及各种指针的使用,这些都使得问题的定位变得困难。

下面我们就通过一个示例,介绍下Trap的处理流程,以及相关的定位技巧。

3. 使用示例
这里我们创造一个简单的Trap,通过读取一段不存在的内存的数据,使系统发生最常见的Trap4-2,也就是数据同步访问异常(DSE)。如果有条件,建议读者亲手编码调试,走一遍这个流程,对于日后问题的处理大有帮助。

if(TestFlag)
    TrapTest = *(uint32*)0x78000000;


我们将断点打在if前面,然后单步执行指令。当执行到读取内存指令时,因为地址物理上不存在,因此这条Load指令会产生Trap。






因为这里预先知道会发生Trap4,查看Trap向量表基址寄存器位BTV=0x80000100,根据前文描述的计算方法计算得知,Trap4入口地址位0x80000180,我们在这个位置打个断点,硬件在完成上下文保存之后会进入这个入口地址。



这里会跳转到Trap4的处理程序,处理程序的地址在写代的时候就不用特意分配地址了,放到哪里都行,编译的时候会修改跳转地址的。我们继续单步执行,然后就来到了处理函数中:



然后我们观察通用寄存器D[15],它的值就是当前Trap的TIN值,结合入口地址,我们得出当前发生的是Trap4-2.



然后我们就可以通过手册,来查询该Trap的详细信息了。






然后前文还提到,DSTR寄存器可以用于数据内存访问得辅助定位,我们查询该寄存器值发现LBE位(Load Bus Error)置位,表征内存加载出现问题,指向前面我们的Load指令。



然后我们还可以通过DEADD寄存器,查询错误的内存访问地址,也就是我们指针故意指向的地址:



然后结合这些信息,我们就可以根据程序调用栈,进行问题的详细定位。



到这里Trap的示例展示就完成了,读者可以根据这个流程,针对实际的问题,进行Trap的定位和信息获取。

4. 小结
本文介绍了TriCore的异常处理机制——Trap系统,介绍了其响应和处理流程,以及硬件相关的上下文保护等机制,并通过示例,展示了Trap的定位和排查,对读者在系统问题的处理上,有一定的帮助。

Trap的定位和信息获取,是非常简单的,我们很容易通过一定的寄存器获取一些报错信息。但是如何要通过这些信息,找出代码逻辑的问题,找到问题的根因,还需要对程序前后执行的逻辑进行详细的分析和调试,有时还需要对汇编进行调试排查。下一篇,我们就来介绍下TriCore的汇编语言,带大家了解下TriCore内核的指令集系统。
————————————————

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

原文链接:https://blog.csdn.net/weixin_44000419/article/details/136997381

使用特权

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

本版积分规则

73

主题

3308

帖子

3

粉丝