本文约13,000字,建议收藏阅读
作者 | 林Nova
出品 | 汽车电子与软件
本文主要介绍Infineon Aurix™ TC3XX系列芯片中断系统及异常处理系统的硬件原理(包括IR模块、CPU中断处理机制)、MCAL相关配置和使用示例。
#01
1.1 中断
嵌入式系统的核心特性之一是其出色的实时性能,这与桌面系统形成鲜明对比。由于嵌入式系统通常集成于各类硬件设备中,这些设备往往需要毫秒乃至微秒级别的快速响应能力,特别是在汽车电子控制单元中,某些硬件功能对实时性的要求更是达到了极致。
要实现这样的实时性能,中断机制起着关键作用。从计算机体系结构的角度来看,中断是一种能够中断常规程序执行流程的重要机制。它使系统能够暂停当前正在处理的任务,及时响应那些具有更高优先级或需要紧急处理的事件。这些事件可能源自外部设备(例如传感器信号、定时器触发等)或系统内部异常(如运算错误、内存访问冲突等)。
广义的中断概念包括以下几个关键要素:
-
中断请求(Interrupt Request,IRQ):当外部设备或系统内部需要触发中断时,会向处理器发送中断请求(Interrupt Request, IRQ)。每个中断源通常分配有一个唯一的IRQ 编号,以便系统识别和管理不同的中断信号。
-
中断向量表(Interrupt Vector Table,IVT):计算机系统通过中断向量表来存储各个中断的处理程序入口地址。当中断触发时,处理器会根据中断类型查询该表,快速跳转到对应的中断处理程序执行。
-
中断服务程序(Interrupt Service Routine,ISR):中断服务程序是专门用于响应和处理特定中断的代码段。当中断发生时,处理器会暂停当前任务,转而执行相应的 ISR,完成必要的操作(如读取设备数据、清除中断标志等)。
-
中断控制器(Interrupt Controller):为了高效管理多个中断源,系统通常采用中断控制器来协调不同中断的优先级和触发顺序。常见的控制器包括可编程中断控制器(PIC)和高级可编程中断控制器(APIC),它们负责仲裁中断请求,确保高优先级事件得到及时响应。
中断机制的引入显著提升了计算机系统的实时响应能力和并行处理效率,使系统能够迅速应对突发的高优先级任务。作为连接操作系统与底层硬件的关键桥梁,中断机制有效协调各类事件的执行顺序,保障整个系统高效、有序地运行。
1.2 异常
从广义的角度来说,异常是一种特殊的中断。计算机系统可能面临多种类型的异常,包括硬件故障(如内存错误、CPU错误)、软件错误(如无效指令、内存溢出)、外部事件(如IO错误、网络中断)以及系统资源耗尽等。这些异常可能会导致程序崩溃、数据丢失或系统崩溃,因此异常处理机制的设计对于确保计算机系统的稳定性和可靠性至关重要。
针对异常的处理通常包括以下要素:
-
异常检测:计算机硬件和操作系统需要能够及时检测到发生的异常情况。硬件可以通过各种机制,如中断、故障检测电路等来检测异常,而操作系统则通过监控程序执行状态、资源利用情况等来检测异常。
-
异常处理:一旦异常被检测到,计算机系统会根据异常的类型和严重程度采取相应的处理措施。这可能包括中断当前正在执行的程序、调用异常处理程序、记录异常信息以便后续分析等。
-
异常处理程序:针对不同类型的异常,计算机系统通常会预先定义一系列的异常处理程序。这些程序可以是操作系统内核中的一部分,也可以是应用程序提供的特定处理逻辑。异常处理程序的任务是尽可能地使系统恢复到正常工作状态,或者至少保证系统在异常发生时能够以安全的方式继续运行。
-
异常记录和分析:为了便于系统管理员和开发人员对异常进行跟踪和分析,计算机系统通常会记录异常事件的相关信息,包括异常类型、发生时间、引发异常的程序或进程、异常处理过程中的状态等。
-
安全性和可靠性:异常处理机制设计的一个重要目标是确保系统在异常情况下能够以安全可靠的方式继续运行。这包括对系统的保护机制,防止异常情况导致系统崩溃或数据丢失,并尽可能地提供恢复机制以确保系统的可用性。
2.1 系统结构
TC3XX系列的中断系统涉及到外设、中断控制器、CPU、DMA等的交互,每个模块内部都有与中断相关的控制寄存器,因而这里称其为中断系统,下面我们将从整体上,先阐述TC3XX系列芯片中断系统的交互逻辑。
熟悉ARM的朋友都知道,在ARM系统中,中断向量表中存放了每个中断服务例程(也就是中断函数)的地址,当出现中断信号时,由向量中断控制器VIC接收该信号,然后进入统一的中断枢纽例程中,在中断枢纽例程中查询向量表然后进行中断服务例程的跳转。(下图为ARM PL190 VIC框图)
TC3XX系列的中断响应路线其实和这个类似,不同的是TC3XX中没有中断枢纽例程,CPU接收中断信号后直接跳转到中断向量表,在中断向量表中每个中断有最多32字节的代码空间,一般是在向量表中放跳转指令,跳转到实际执行的中断服务例程中。下图是TC3XX中断系统的硬件框图:
从图中我们可以看到整个中断系统包含来自软硬件的中断源,一个中断路由器IR,和对应的服务提供者,服务提供者可以是CPU或者是DMA,所以在这里中断请求的术语称之为Service Request,服务请求,因为它可能触发一个CPU中断,也可能是触发一个DMA转换。
它的大体响应流程是这样的:当外部中断源如ADC等模块产生了服务请求信号,会发送给IR,IR根据配置的优先级进行仲裁,并将胜出的服务请求路由至对应的服务提供者,如果服务提供者是CPU则是中断服务,如果是DMA则是某路DMA通道的转换使能信号。
下面我们展开介绍各个模块在中断系统中的运行逻辑。
2.2 中断源
中断源是由外设模块、软件模块等进行的服务请求,每个外设模块都有不同的请求事件,比如ADC模块当某个Group的转换完成之后会发出对应的服务请求,CAN或Eth模块在收到外部数据时会发出接收服务请求。具体的资源情况是硬件固定的,使用时需要查询对应模块的说明。
一般情况下,外设的中断信号没有开关,对应的事件发生之后就会向IR发送请求,而中断的控制逻辑由后续的IR模块和CPU来负责处理。
2.3 中断路由器IR
中断路由器IR负责接收系统的服务请求,然后进行优先级仲裁,并将仲裁胜出的请求路由至对应的服务提供者。它的输入是各个外设模块,输出则是CPU或DMA模块。IR模块主要包括服务请求节点(Service Request Node, SRN)、中断控制单元(Interrupt Control Unit,ICU)和外设总线接口(Bus Peripheral Interface,BPI)。IR有很多的SRN,每个SRN对应一个硬件的服务请求,比如ADC0有4个服务请求,则在IR中就有与其对应的4个SRN。中断控制单元负责与对应的服务提供者进行连接。外设总线接口则负责外设总线的连接。
2.3.1 服务请求节点SRN
每个SRN对应一个硬件的服务请求,外设模块的服务请求信号在硬件上一般是直接连接到其对应的SRN的,具体的中断控制如优先级、使能、状态查询等都是在SRN中进行的。每个SRN有一个服务请求控制寄存器(Service Request Control Register,SRC),如SRC_VADC_G3_SR0表示ADC3的SRC寄存器。
SRC寄存器中包含了这个服务请求的控制和状态量,下面我们详细介绍其中的每个成员。
-
Service Request Priority Number (SRPN):服务请求优先级,也就是我们常说的中断优先级,它决定了服务请求在IR中仲裁的结果;同时它也是和CPU中断向量表对应,这个稍后在CPU中断逻辑中会介绍;另外当路由对象是DMA时,它指示对应的DMA通道;
-
Enable Bit (SRE):也就是中断使能位,如果使用中断则需要打开,决定IR是否将该请求路由到对应的服务提供者,如果使能位没有打开,也可以通过软件轮询SRR中断状态位来执行对应服务例程;
-
Type-Of-Service Control (TOS):服务类型,也就是选择将服务路由到哪个ICU中,CPU0、CPU1或者DMA;
-
Service Request Flag (SRR):中断标志位,该位在服务提供者响应服务的时候会自动清除,所以TC3XX中不需要软件去清除中断标志位,但如果没有打开SRE使用的轮询,则需要每次执行后用CLRR清除中断标志位;
-
Clear Bits(CLRR):中断标志清除位,用来清除SRR,读的话一直是0;
-
Set Bits(CLRR):软件中断请求位,可以通过软件来请求一次中断;
-
Interrupt Trigger Overflow Bit (IOV):中断溢出标志位,如果一个中断没有被接收或者清除,又进行了一次触发,则该位会置位;
-
Interrupt Trigger Overflow Clear Bit (IOVCLR):中断溢出标志清除位,清除中断溢出标志,读的话一直是0;
-
SW Sticky Bit (SWS):软请求粘滞位,如果调用过SETR接口进行过软触发,则该位会置位直到清除;
-
SW Sticky Clear Bit (SWSCLR):软请求粘滞清除位,用来清除软请求粘滞位,读的话一直是0;
SRC是中断系统中非常重要的寄存器,比如我们要判断硬件是否发出服务请求,就看SRR位。查看是否路由到CPU,就看SRE使能位,当然CPU中也有中断的使能和仲裁逻辑,我们下面会介绍。
2.3.2 Interrupt Control Unit (ICU)
每个SRN的服务请求会路由到一个ICU中,一个ICU连接一个CPU或者DMA,每个ICU内部进行服务请求的仲裁,并将仲裁的结果发送到连接的服务提供者。ICU中一般不需要配置,与CPU和DMA的连接相对比较固定。我们可以通过查询ICU的寄存器查看仲裁或路由情况,包括LASRx、ECRx等,这些寄存器一般是只读的,不需要用户配置,这里就不展开介绍了。
2.4 CPU as Interrupt Service Provider
当服务请求被路由到CPU的时候,CPU会接收IR的信号并作应答,然后执行对应的服务例程,也就是我们常说的中断。接下来我们从CPU的角度介绍中断的处理流程。在此之前,我们先介绍两个重要的寄存器,中断向量表基址指针(Base Interrupt Vector Table Pointer,BIV)和ICU中断控制寄存器(ICU Interrupt Control Register,ICR)。这里需要注意的是,这两个寄存器属于CPU寄存器,在多核系统中,每个核都有一套自己的CPU寄存器,所以会存在CPU0_ICR,CPU1_ICR,而前文提到的IR模块寄存器属于外设寄存器,一个系统里只有一套。CPU寄存器不能直接读写,要通过ENABLE、DISABLE、MTCR、MFCR等内核指令访问。
如上图是中断向量表基址指针寄存器,包括中断向量表基址BIV和向量空间选择位VSS。
-
BIV:Base Address of Interrupt Vector Table,中断向量表基址,TC3XX的中断向量表是存放在Flash中的连续地址,因此需要该寄存器来表示向量表基地址;
-
VSS:Vector Spacing Select,向量空间选择位,该位表征每个中断向量所占的内存空间,0表示每个中断向量占据32字节,1表示8字节,默认为32字节;
ICR是CPU主要的中断控制寄存器,其中包括全局中断使能位IE,当前CPU中断优先级位CCPN和等待中断优先级位PIPN。
-
IE:Global Interrupt Enable Bit,全局中断使能位,我们所使用的关闭所有中断操作,就是将该位置0,因为每个CPU是独立的,也有自己的ICR控制器,所以IE只控制当前核的中断开关;
-
CCPN:Current CPU Priority Number,当前CPU中断优先级,在非中断程序执行时,该位为0,进入中断时,该位会被硬件设置为所触发中断的优先级;
-
PIPN:Pending Interrupt Priority Number,等待中断优先级,表征当前CPU收到IR路由但还未处理的中断优先级。
2.4.1 中断向量表
众所周知,早期的单片机系统中,硬件触发中断后,进入同一的中断入口函数,在入口函数中查找中断源,然后选择对应的中断服务例程。这样的系统结构中断的响应效率较低,后面才出现了向量中断。向量中断的核心亮点在于由硬件进行中断入口的查找和跳转,这样可以大大降低中断的响应时间,达到真正的高实时性。
TC3XX的中断向量表存放在PFlash(代码段)中的一段连续地址中,默认一个向量的存储空间是8 word(32字节),通过BIV指向其基地址。这里有一个非常巧妙的逻辑,中断向量的位置是和中断优先级绑定的,也就是前文IR模块中的SRC.SRPN,比如优先级为80的中断,它的向量地址就是基址加上偏移为BIV+80*32,CPU在接收该中断信号后第一行运行的代码也就是这个位置。这样设计省去了从中断源到中断入口地址的链接开销,只需要在编译过程中对中断向量表进行编址,CPU在接收到IR的中断信号之后直接按照对应的优先级进行跳转。中断向量表在内存中的结构如下图:
如图我们还可以看到,向量表是支持跨优先级占用的。因为一般情况下32字节放不下中断服务例程,只能放跳转指令,但是如果代码量不那么大,可以通过占有多优先级的方式,省去一次跳转。上图中优先级2~5放置了一个中断,通过优先级2的中断源触发,这里3~5因为被占据,所以要注意不能再给其他中断使用了。
2.4.2 CPU中断控制策略
前面我们提到,IR模块中有SRC.SRE位表征到达IR的服务请求信号是否会被路由,但是路由到CPU之后也并非能够直接执行,还需要判断相关的条件。
我们都知道,系统在运行过程中有时需要进入临界区时,防止中断程序或者OS调度程序破坏数据一致性。而根据数据的保护需求,对于临界区的实现可能是关闭所有中断,或者关闭OS中断(也就是关闭某一优先级以下中断)。
对于TC3XX来说,如果要关闭所有中断,那么操作方法就是将ICR.IE置0,此时所有路由到该CPU的中断都处于Pending状态,不会被接收。而如果要关闭OS中断,将所有OS优先级以下的中断全部屏蔽,则可将ICR.CCPN设置为目标门槛优先级,小于或等于该优先级的中断都将被屏蔽。
所以当中断信号到达CPU的时候,如果ICR.IE为1,且该中断优先级大于ICR.CCPN中指示的当前CPU中断优先级,CPU会接收该中断,向IR发出ACK信号,则IR中的中断标志位SRC.SRR会被硬件清除,然后CPU开始执行中断响应步骤。
2.5 DMA as Interrupt Server Provider
除了CPU接收IR的服务请求信号执行中断响应程序以外,TC3XX的中断系统还有一个亮点,那就是将服务请求信号路由到DMA中,硬件触发一次DMA转换。这种连接,我们只需要将SRC.TOS设置为DMA,然后将SRC.SRPN设置为目标DMA通道号,即可实现路由。
我们都知道,根据摩尔定律,计算机的计算能力每18个月能翻一倍,而存储器的性能每年只能提升7%,所以经过这么多年的发展,内存已经成为了计算机计数的瓶颈,现在CPU的Cache不断增大,也就是为了照顾这一短板。
而DMA的主要目的是承担CPU数据读写的任务,从而解放CPU的算力。TC3XX中实现了将外设的中断信号连接到DMA中,我们就可以通过设计外设触发链路,不经过CPU就能完成外设数据的处理。比如通过ADC中断触发DMA,能够直接将ADC采样数据取到RAM中;通过将SPI的中断信号路由到DMA,就能完成后台异步SPI发送。
2.6 CPU中断处理流程
当所有条件满足,CPU就要开始响应中断了,下面我们将介绍CPU对于中断的响应和退出流程。
在CPU决定响应中断时,并不是直接去按向量表执行其中断服务例程,而是先由硬件执行一系列操作进行上下文保存,也就是保护现场,而在退出中断之后也需要加载上下文,也就是恢复现场。下面我们先详细介绍CPU对于中断的硬件响应步骤:
1. 第一步是保存上部分上下文,TC3XX系列芯片中存在硬件CSA机制,分为上、下两部分上下文,主要包括通用寄存器和链接地址等信息,其中上部分是由中断或者异常时由硬件自动保存,并在退出时使用RFE指令加载回来,下图为CSA结构图,关于CSA的机制本文就不展开讨论了,后续会出专题进行介绍;
2. 保存返回地址寄存器A[11],也就是我们常说的链接寄存器;
3. 如果此时CPU的中断栈使能开关PSW.IS没有打开,则硬件会将栈寄存器A[10](SP指针)指向中断栈指针ISP,以使用中断的栈,同时将PSW.IS置位;
4. 将I/O模式(外设访问权限)设置为Supervisor模式;
5. 将当前保护寄存器集设置为PSW.PRS=000B,TC3XX每个核有6套内存保护的设置集,如果使用了内存保护则这里表示会切换到PSW.PRS=000指向的保护策略;
6. 调用深度计数器PSW.CDC清零,并且调用深度限制被设置为64;
7. 调用深度检查打开:PSW.CDE=1;
8. PSW安全位被设置为SYSCON中的值:PSW.S = SYSCON.IS;
9. 全局地址寄存器A[0], A[1], A[8], A[9]的写入会被禁用:PSW.GW=0;
10. 当前全局中断使能位ICR.IE被保存到PCXI链接字寄存器中的先前全局中断使能位PCXI.PIE中,然后将ICR.IE置0,关闭所有中断;
11. 当前中断优先级ICR.CCPN被保存到PCXI链接字寄存器中的先前中断优先级位PCXI.PCPN(这里顺便一提,PCXI在上部分上下文中,第一步已经保存到CSA中了,因此在嵌套处理时,此处PCXI可以写入,以实现中断嵌套);
12. 等待中断优先级位ICR.PIPN被写入到当前CPU中断优先级位ICR.CCPN;
13. 从中断向量表中取第一条指令,然后开始执行中断服务函数,至此硬件的处理流程结束。
回看整个中断响应流程,其实CPU在响应中断的时候,主要做的事情就是保护上下文、切换系统模式、切换中断环境,这在任何一个系统中都是必需的,而TC3XX得益于其独特的硬件上下文自动保存机制,省去了软件压栈的时间,对中断的实时性有较好的优化作用。
中断执行完毕之后自然需要退出中断,退出中断需要使用RFE指令,然后硬件会执行相应的步骤:
1. 将链接字寄存器中的先前中断优先级位PCXI.PCPN写入到ICR.CCPN中,这样就恢复了中断进入之前的系统中断优先级门槛;
2. 将链接字寄存器中的PCXI.PIE被恢复到ICR.IE中;
3. 从CSA中加载进入中断时保存的上部分上下文。
从中断退出流程我们可以看到,其实主要的硬件处理是还是处理上下文和中断环境。TC3XX的中断是可以直接嵌套的,我们可以直接在中断服务例程中使能全局中断,打开嵌套,得益于其完善的硬件上下文保存机制,软件不需要像ARM一样执行系统模式切换和额外的上下文保存工作,从而提升了系统执行效率。
2.7 不可屏蔽中断 NMI
TC3XX还提供了一种不可屏蔽中断机制(Non-Maskable Interrupt,NMI) ,一般是SMU模块或者外部ESR0/ESR1引脚触发,用于响应紧急事件。这种机制虽然叫不可屏蔽中断,但是它的本质走的是异常处理系统,也就是Trap系统,而不是本文中的中断系统,因此IR模块或CPU的中断控制位都不会对其响应产生影响,从而实现不可屏蔽中断的响应逻辑。
2.8 中断使用示例
讲完了中断的内部逻辑,下面我们就进行实操。本文实现的一个示例是利用Aurix™的MCAL,在EB Tresos中进行配置,实现一个ADC的采样中断配置。ADC这里我们使用ADC3的Source0,关于ADC内部的配置这里就不展开介绍了。
2.8.1 配置及代码
1. 首先我们来到EB工具中的Irq模块中,找到IrqAdcConfig页面进行Adc的中断配置(注意TC3XX中有DSADC模块,用于Delta-Sigma转换,本文用的是EVADC),这里我们来到Adc3,将Catogary配置为CAT1(CAT2为OS中断),然后设置中断优先级,这里使用120,然后TOS选CPU0,由CPU0执行中断函数;
2. 然后需要在代码中进行中断使能和初始化,这里IrqAdc_Init是MCAL中定义好的函数,主要目的将我们配置的优先级和路由目的地写入到SRC寄存器中;
C
IrqAdc_Init();
SRC_VADCG3SR0.B.SRE = 1; /* Enable EVADC Group3 Source0 INT */
voidIrqAdc_Init(void)
{
IRQ_SFR_MODIFY32 (SRC_VADCG3SR0.U, IRQ_CLEAR_MASK,
((uint32)IRQ_ADC3_SR0_TOS | (uint32) IRQ_ADC3_SR0_PRIO));
IRQ_SFR_MODIFY32 (SRC_VADCG3SR1.U, IRQ_CLEAR_MASK,
((uint32)IRQ_ADC3_SR1_TOS | (uint32) IRQ_ADC3_SR1_PRIO));
IRQ_SFR_MODIFY32 (SRC_VADCG3SR2.U, IRQ_CLEAR_MASK,
((uint32)IRQ_ADC3_SR2_TOS | (uint32) IRQ_ADC3_SR2_PRIO));
IRQ_SFR_MODIFY32 (SRC_VADCG3SR3.U, IRQ_CLEAR_MASK,
((uint32)IRQ_ADC3_SR3_TOS | (uint32) IRQ_ADC3_SR3_PRIO));
}
3. 这里再介绍以下中断函数的编址,因为TC3XX中中断向量表的位置是和中断优先级一一对应的,因此需要在编译链接过程中进行编址,不同的编译器有不同的指令(本文使用Tasking编译器),这里MCAL中有中断实现,如果要手动定义中断函数,可以参考格式;
C
#define IFX_INTERRUPT(isr, vectabNum, prio)
void __interrupt(prio) __vector_table(vectabNum) isr(void)
IFX_INTERRUPT(ADC3SR0_ISR, 0, IRQ_ADC3_SR0_PRIO)
{
/* Enable Global Interrupts */
ENABLE();
/* Call Adc Interrupt function*/
Adc_RS0EventInterruptHandler(3U);
}
到这里一个ADC Group3 Source0的中断就配置完毕了,然后我们编译烧录运行。
2.8.2 调试运行
前文说到,中断向量表的向量地址就是基址加上偏移(BIV.VSS=0,向量宽度32字节),这里我们优先级为120,向量地址就是BIV+120*32,我们查看寄存器得知BIV=0x802FE000,则目标中断向量地址为0x802FEF00。
然后我们来到这个地方查看汇编,并在开头打上断点,系统停在了这里,就说明我们的中断成功触发了,并且CPU响应了该中断。然后我们观察汇编代码,发现这里是一个跳转指令,跳转到0x80007DDE。
然后我们来到Map中查询发现这个地址就是Adc3中断函数的地址,也就是正如前文所说,向量表32字节的空间一般是存放跳转指令,用于向中断服务例程跳转。
然后我们继续单步执行,就来到了我们的ADC中断函数中,我们的示例到此就演示完成了。
2.8.3 常见问题排查
我们在配置中断过程中,难免会遇到一些问题,这里介绍下排查的思路。
-
首先就是中断没有进的情况,这时候我们需要排查硬件中断源是否触发了,需要查看SRC.SRR寄存器,如果这里不是1,则表示硬件配置有问题,要向前排查外设模块是否配置无误,这也是最常遇到的情况;
-
其次就是SRE没有在代码里使能,这个是配置代码中没有的,用户除了调用初始化函数以外,每个中断是需要自己手动打开的,打开之后IR模块才会进行路由;
-
还有就是中断优先级是否配置正确,可以用前面的计算方法根据BIV的值和SRC.SRPN的值查询中断向量地址,查看向量表中对应代码是否正确;
-
然后就是SRC.TOS是否配置正确,如果配置为DMA的话这里是要在配置中修改为DMA的。
#03
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编写不同的处理、记录逻辑。
3.1 异常向量表
TriCore™中异常处理是使用异常向量表来进行管理的,用户需要在PFlash中分配8*32字节的内存空间(开头需256字节对齐),用来存放异常向量表,每个异常类占据32字节的空间。向量表中用来存放跳转指令,用来跳转到用户定义的异常处理程序中。
以下是Infineon官方MCAL中向量表示例代码:
C
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向量表起始地址:
C
/* 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中。
3.2 Trap 响应流程
若Trap发生后系统能够处理该异常,并在处理后恢复正常运行(且后续不再触发同类Trap),则此类Trap属于可恢复类型。如果发生该Trap后系统后续已经无法继续运行了,则属于不可恢复的Trap。
可恢复Trap发生后,内核会执行如下步骤:
1. Trap发生后的第一个动作是保存上部分上下文,这和中断系统的处理流程是类似的;
2. 返回地址寄存器A[11]会更新为,导致该Trap的指令位置;
3. Trap的子类TIN值被保存到通用寄存器D[15];
4. 如果中断栈寄存器没有被使用,则栈寄存器A[10]会被设置为中断栈指针地址ISP,并将PSW.IS置1;
5. I/O模式被切换到Supervisor Mode,也就是最高级的特权模式;
6. 保护寄存器集被设置为0:PWS.PRS=0;
7.调用深度计数器(CDC)被清零,并设置调用深度限值为64,PSW.CDC=0000000B;
8. 调用深度检查使能:PSW.CDE=1;
9. PSW的安全位被设置为SYSCON寄存器中的值PSW.S=SYSCON.TS;
10. A[0], A[1], A[8], A[9]的访问被禁止:PSW.GW=0;
11. 全局中断位被关闭:ICR.IE=0;
12. 按照中断类在中断向量表中查询入口地址,并开始执行。
Trap的响应流程整体上和中断系统是类似的,硬件的主要任务还是保护上下文、切换系统模式、关中断等。同样得益于该硬件机制,系统能够迅速地执行用户定义的Trap处理程序,实现问题发生时的高实时性。
另外有一个Trap是无法恢复的,那就是上下文使用超限——FCU。其响应流程有些不同,只有如下几个步骤:
1. Trap的子类TIN值被保存到通用寄存器D[15];
2. 如果中断栈寄存器没有被使用,则栈寄存器A[10]会被设置为中断栈指针地址ISP,并将PSW.IS置1;
3. I/O模式被切换到Supervisor Mode,也就是最高级的特权模式;
4. 保护寄存器集被设置为0:PWS.PRS=0;
5. PSW的安全位被设置为SYSCON寄存器中的值PSW.S=SYSCON.TS;
6. 全局中断被关闭:ICR.IE=0;
7. 按照中断类查询中断向量表并开始执行。
3.3 Trap 处理程序
Trap处理程序是由用户自定的,用户可以在其中编写中断处理代码,进行异常修复、记录等事务。这里需要注意的是,所有的8类Trap都需要编写代码进行捕捉识别,否则出现之后无法定位。
虽然TriCore™中几乎所有Trap都是可恢复的,Trap处理函数返回后系统能够继续执行后续代码,但是通常我们认为发生了Trap,代表软硬件系统已经出现了故障,其执行结果已经不可靠了,且继续向下执行代码也很可能再次发生此类Trap,因此嵌入式领域中通常的做法是将Trap的相关信息进行保存,然后执行复位,以尝试最大限度地恢复系统。
以下是Infineon原厂MCAL对于Trap的DEMO处理程序,主要还是用于辅助调试。
C
/* 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 = %dn", CoreId);
print_f("nClass 0: Virtual Address Fill Trap occurredn");
Mcal_ReleaseSpinlock(&Trap_lock);
get_char();
break;
case 1:
Mcal_GetSpinlock(&Trap_lock, CPU_SPINLOCK_TIMEOUT_TRAP);
print_f("nCoreId = %dn", CoreId);
print_f("nClass 0: Virtual Address Protection Trap occurredn");
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指令返回。
3.4 Trap 退出流程
Trap的退出仅仅是通过一条RFE指令实现的,但是这里还是想说一下,方便大家理解Trap的恢复。
1. 将返回地址加载到PC中,注意此时仍指向发生Trap的那条指令,所以如果问题没有修复,仍然会重新进Trap,这也是为什么一般都采取复位的Trap处理方式;
2. 恢复全局中断使能位:ICR.IE = PCXI.PIE;
3. 恢复中断优先级位:ICR.CCPN = PCXI.PCPN;
4. 将保存的高上下文恢复。
至此Trap处理结束,程序将重新执行先前发生Trap的那条指令,如果在Trap中修复了问题,那程序将继续正常运行。
3.5 同步Trap和异步Trap
Trap还分为同步Trap和异步Trap,对于某些Trap,如指令异常等,在执行过程中即可被捕捉并响应,属于同步Trap。但是如果我们打开了数据缓存(Data Cache),数据的写入其实是异步的。也就是说,在没有使用内存屏障的前提下,我们对数据的写入其实只是往Cache里写,然后程序继续执行后续指令,由Cache负责把数据写入到内存中。而Trap只有在Cache把数据往内存里写的时候才会被检测到,此时我们的程序已经执行到后面某个位置了,所以是异步的。
对于异步的Trap,因为其返回地址已经没有参考性了,所以此时最好的办法是关闭Cache,使Trap变为同步Trap,然后就很好定位了。
3.6 Trap优先级
当多个Trap同时发生时,或者在指令流水执行时存在多个Trap,则有一定的优先级仲裁逻辑。
1. 首先来说,异步Trap的优先级要高于同步Trap,其次高于所有中断的优先级;
2. 其次指令序列中导致Trap的指令,最先进入序列的具有更高优先级;
3. 上下文空间溢出,也就是CSA空间用尽导致的Trap——FCU,其优先级高于所有其他Trap;
4. 当同一条指令触发了多个同步Trap之后,其优先级参考文档中的Table 9,此处就不一一列出了。
3.7 Trap相关寄存器
Trap系统除了Trap基址寄存器BTV以外,还有一些辅助寄存器,用户Trap发生后的辅助排查,主要都是内存访问相关的。
3.7.1 Program Synchronous Error Trap Register (PSTR)
PSTR包含了程序存储系统的同步Trap信息。该寄存器被更新以存储PSE Trap的信息,以帮助定位异常。只有在检测到Trap并且寄存器中没有已设置的位时,寄存器才会被设置。它通过CSFR写入(与数据值无关)来清除。
3.7.2 Data Synchronous Error Trap Register (DSTR)
DSTR包含了数据存储系统的同步Trap信息。该寄存器被更新以存储DSE Trap源信息,以帮助定位异常。每当检测到有效的Trap并且寄存器中没有已设置的位时,寄存器就会被更新。它通过写入(与数据值无关)来清除。
3.7.3 Data Asynchronous Error Trap Register (DATR)
DATR包含了数据存储系统的异步Trap信息。该寄存器被更新以存储DAE Trap的信息,以帮助定位故障。每当检测到有效的Trap并且寄存器中没有已设置的位时,寄存器就会被更新。它通过写入(与数据值无关)来清除。如果DATR寄存器非零,DAE Trap将被抑制。
3.7.4 CPUx Data Error Address Register(DEADD)
DEADD包含了数据存储系统的Trap地址信息。该寄存器更新为MEM、ALN、DSE或DAE的Trap信息,以帮助定位故障。只有在检测到陷阱并且DATR或DSTR寄存器没有任何位已经设置时,才会设置该寄存器。只有当DATR或DSTR寄存器非零时,寄存器内容才有效,因此在清除这些寄存器之前应先读取其内容。
3.8 Trap系统小结
如前文所述,Trap系统的设计还是比较简单的,用户只需要做两件事:分配对应的Trap向量表,初始化Trap基址寄存器BTV。当Trap发生时按照对应的Class,通过查询Trap向量表的入口地址,执行对应的Trap,并通过通用寄存器D[15]获取TIN号。然后数据访问相关的Trap还有一些辅助寄存器,用来帮助定位Trap。
下面我们就通过一个示例,介绍下Trap的处理流程,以及相关的定位技巧。
3.9 Trap调试示例
这里我们创造一个简单的Trap,通过读取一段不存在的内存的数据,使系统发生最常见的Trap4-2,也就是数据同步访问异常(DSE)。如果有条件,建议读者亲手编码调试,走一遍这个流程,对于日后问题的处理大有帮助。
C
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的定位和信息获取。
#04
英飞凌在2024年发布了新一代的Aurix™ TC4XX系列芯片,使用TriCore™ 1.8的内核架构。相对于TC3XX系列有较大升级,接下来我们就针对中断系统介绍其不同的特性。
4.1 ISP类型及数量
从IR的系统框图中可以看出,中断服务提供方类型增加了GTM和PPU等,在TC3XX系列中GTM仅通过SRN发送中断信号,而在TC4XX中同时也可以作为ISP接收中断路由。
而DMA数量最大扩充到了两个,因此ISP也增加了对应的DMA模块。
并且我们注意到,中断是可以直接路由到CPUCS核的,因此在信息安全相关应用场景,其与主核的交互更加方便。
4.2 中断锁步检查
TC4XX中的IR中断系统中,增加了中断锁步功能,进一步增强了功能安全特性。可以通过锁步自检寄存器LCLTEST寄存器进行中断锁步自检。
当锁步出现故障时,会向SMU模块发送ALARM_LCL信号,SMU根据相应的故障配置执行复位、中断等故障动作。
4.3 SRN数量
TC4XX系列的SRN数量增加到了2048个,而通过查询寄存器,在上一代TC3XX旗舰款的TC39X系列最多支持904个SRN。因此在新一代TC4XX上,支持更加丰富的中断连接。
4.4 虚拟化下的中断服务
TC4XX系列芯片支持虚拟化服务,能够在一个CPU硬件中虚拟化出多个Virtual Machine。而相应的,中断服务在路由的过程中也需要考虑虚拟化的问题。
在TC3XX系列芯片中,我们仅需要配置SRC.TOS寄存器位,将中断路由到特定的CPU中。而在TC4XX中,新增了SRC.VM寄存器位,以支持将中断路由到特定的虚拟机中。
#05
本文介绍了Infineon Aurix™ TC3XX系列芯片的中断系统和异常处理系统,描述了包括外设、中断路由器IR和CPU在内的中断交互逻辑,并从IR模块内部描述了中断路由和控制机制,从CPU角度描述了中断的响应流程、开关逻辑,并结合示例对配置和代码进行了调试。同时对异常处理系统及其处理流程进行了详细的介绍,并模拟出异常场景并进行实操演示,帮助读者掌握异常处理的调试过程。
原文始发于微信公众号(AI赋能汽车):英飞凌Aurix™ TC3XX 中断及异常处理系统详解
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论