对于复杂的系统,鲁棒性是非常重要的。为了协助客户建立鲁棒性系统,KeyStone器件提供了多种硬件保护机制,如内存保护、EDC。本文介绍如何利用这些特性在KeyStone器件上建立一个鲁棒的系统。同时提供了与文档配套的例程。
1 简介
如图1所示,KeyStone器件提供了多种协助客户建立鲁棒性应用的特性。
图1 KeyStone器件鲁棒系统
如图1所示,在LL2、L1P及L1D中集成了内存保护模块;LL2、SL2及DDR控制器中集成了错误检查纠正模块;L1P 集成了错误检测模块。MPAX和MPU模块附在总线上,用于监控检测以避免非法的总线访问。每个DSP CorePac有一个独立的MPAX用于监控与MSMC连接的总线。
对于系统中其他的master,根据权限ID进行分类。对每个权限ID,在MSMC中集成了2个MPAX用于监视与该权限ID 相关的访问。其中一个是SES MPAX 用于保护对DDR3的访问,另一个是SMS MPAX 用于保护对SL2的访问。关于每个master对应的权限ID,参考相应的器件手册。
某些外设的配置端口上添加了MPU,用于保护对该外设配置区域的非法访问。但是并非所有的外设都受MPU的保护,具体参考相应器件手册中受MPU保护的外设列表。每个CorePac有一个看门狗定时器用于监视其活动,如果该核死机,看门狗可以触发不可屏蔽中断或者复位信号。
EMC可以避免DSP core访问没有映射的的配置空间,XMC则可以避免DSP core访问没有映射的数据空间。所有这些功能都由硬件模块实现,使用这些功能对系统性能基本上没有影响。使用EDC会对存储器的访问性能稍有影响,但从整个系统层面上看,它几乎是微不足道的。
在出现问题时,所有这些模块可以向DSP core触发异常,DSP core的异常监控模块可以记录这些状态并触发异常服务程序执行相应的操作。本文讨论这些特性的应用,并给出相关基于寄存器层CSL 实现的例程。代码使用如下方式定义寄存器指针。
上述各种特性具体描述分布于各自子系统的文档中,本文最后的参考章节中列出了所有相关的文档。在看本文之前,假设客户已经阅读了相关属性对应的文档,所以本文旨在提供相关的补充信息。
本文适用于KeyStone 1系列DSP,例程在TCI6614 EVM,C6670 EVM,C6678 EVM上进行了验证。对于其他的KeyStone器件包扩KeyStone 2系列,基本功能都是一样的,一些细节上的些许差异请参阅相应器件手册。
2 内存保护
文档“Memory Protection On KeyStone Devices(SPRWIKI9012)”中讨论了KeyStone器件上的内存保护属性,其中包括其它文档中没有的很多有用信息,本节在其基础上做一些总结和补充。
表1 总结列出不同内存保护模块的差异。
系统中有多个master和slave,位于slave输入端口的保护模块用于阻止来自其他master对该slave的非法访问;位于master输出端口的保护模块用于阻止该master对所有其他slave的非法访问。每个内存页、分片或范围的保护属性都是可编程的。
2.1 L1及LL2内存保护
关于L1及LL2内存保护的基本信息参考“TMS320C66x CorePac User Guide(SPUGW0)”中内存保护章节。
L1及LL2内存保护只区分7个外部请求ID,但是系统可能有16个权限ID。默认情况下,系统权限ID 0~5映射到CorePac AID 0~5,所有其他的权限ID均映射到AIDx。
CorePac AID与系统权限ID之间的映射关系可由EMC编程配置,具体参考“TMS320C66x CorePac User Guide(SPUGW0)”中“外部存储控制器(EMC)”章节。注意,IDMA的AID与其所属CorePac的数值一致,EDMA传输的权限ID与配置并发起这个传输的核的编号一致。
通常L1被配置为cache,此时所有L1相关的内存保护属性寄存器应该清零从而阻止其他master的对L1的访问。
CorePac内部内存保护模块(保护L1,LL2及XMC/MPAX)的寄存器被一个锁保护起来。默认情况下,这些寄存器没有被锁住,用户软件可以使用自定义的密钥锁住这些寄存器,然后,只有用该密钥进行解锁后才可以访问这些寄存器。
2.2 共享内存保护–MPAX
关于CorePac共享内存保护的基本信息参考“TMS320C66x CorePac User Guide(SPRUGW0)”中“扩展存储控制器(XMC)”章节;关于系统中其他master的共享内存保护基本信息参考“KeyStone Architecture Multicore Shared Memory Controller User Guide (SPRUGW7)”中“内存保护及地址扩展(MPAX)”章节。如下是例程中关于XMC/MPAX的配置样例,每一行代表MPAX中的一个分片配置。
逻辑地址低于0x0C00_0000 的地址访问不会进入XMC。对地址空间0x0000_0000~0x07FF_FFFF
进行访问时,在C66x CorePac内部进行地址解析。这块地址范围包括内部及外部配置总线,及L1D、L1P、L2存储空间。对位于0x0C00_0000~0x0FFF_FFFF区间的逻辑地址访问时,会经过L1 cache,并且在读操作时会经过预取缓存,与该地址范围对应的内存属性配置寄存器MAR是硬件拉死的,不可修改。也就是说对该逻辑地址空间的访问在进入XMC MPAX之前不会经过L2 cache,所以这块逻辑地址空间称为“快速SL2 RAM 路径”。对大于等于0x1000_0000的逻辑地址访问会首先经过L2 cache控制器,然后经过XMC MPAX,这种常规路径会增加一个cycle 的时延。
根据上述配置例子,在访问SL2时,采用逻辑地址0x0C00_0000的访问速率高于使用重映射后的逻辑地址0x1800_0000。但是0x1800_0000对应的内存属性寄存器MAR 是可编程的,因此可以配置通过0x1800_0000访问的SL2为non-cacheable及non-prefetchable。
注意替换地址RADDR是36-bit物理地址>>4。常见的DDR3错误配置如下:
或者
注意DDR3起始物理地址为0x8:0000_0000,而0x9:0000_0000相对起始地址有4GB的偏移,在大多数系统中这是一个非法的地址。
在真实系统中,应该充分利用好MPAX的所有片段更好地将存储空间划分成尽可能多的小片,并仔细设定各个分片的访问限定属性。
不用的地址不应该映射,MPAX会拒绝对未映射的地址访问并上报异常事件,从而有助于捕获软件错误。
SMS/MPAX只允许对SL2的访问,如下为其配置例子:
SES/MPAX是用于保护对DDR3的访问,如下为其配置例子:
当两个master通过共享memory交换数据时,应该确保两个master使用的逻辑地址映射到相同的物理地址。注意EDMA的权限ID是继承于对其配置的CorePac。
警告:
在修改一条MPAX表项时,需要确保此时没有对该表项所覆盖地址的访问。在修改之前,需要先将该表项覆盖地址对应的cache及预取缓存中的数据进行回写及失效操作。
对于MPAX的配置,推荐在程序开始之初且没有使用任何共享存储空间之前完成。用于CorePac MPAX配置的代码和数据应该放在LL2。
如果要运行时动态修改一个MPAX表项,安全的方法是先将新的配置写到一个未使用的编号高度表项,然后清掉旧的表项。这是由于编号高度表项的优先级高于编号低端表项。
在修改MPAX表项之前需要先执行如下操作:
1.将MPAX表项对应的存储空间内容从cache中剔除出去。即使对于属性为不可写的存储空间,应该使用CACHE_wbInvL2()而非CACHE_inv L2()。
2.如果对受影响的存储器空间使能了预取功能,则需要对预取缓存执行失效操作。
3.执行“MFENCE”确保回写及失效操作完成。
CorePac的MPAX寄存器受CorePac的内存保护寄存器锁保护。SES及SMS的MPAX内存保护属性寄存器被MSMC内部分别用于SES及SMS的锁保护。MSMC内部其他寄存器被MSMC内部用于非MPAX 的锁保护。
2.3 外设配置端口保护–MPU
关于MPU的基本信息参考“KeyStone Architecture Memory Protection Unit User Guide (SPRUGW5)”。
MPU0、MPU1、MPU2及MPU3对所有KeyStone 1器件是相同的。但是对于不同的器件,其附加MPU的个数,每个MPU 支持的地址范围表项数,MPU的默认配置均有所差异。具体可参考相关器件手册的“内存保护单元(MPU)”章节。
MPU与MPAX的区别在于,如果访问地址不在MPU任何一个地址范围内,则该地址访问是允许的;而当该地址与MPAX中任意表项地址范围不匹配时,则该地址访问被拒绝。
注意,如果没有被MPPA的设置所拒绝,MPU单元默认所有的访问都是许可的。对于一个地址访问,MPU首先将访问的权限ID与MPPA寄存器的AID bit配置进行核对,如果与权限ID对应的AID bit为0,则不需要核对地址范围,该访问被许可。如MPPA=0则允许所有的对该空间的访问,如果要拒绝任意对该空间的访问则需要将MPPA配置为0x03FFFC00。L1及LL2内存保护的MPPA设置则有所不同,当MPPA中AID bit为0是拒绝相应的访问。
当传输与MPU中多个地址范围匹配时,所有重叠的范围必须允许其访问,否则该访问会被拒绝。最终赋予的访问权限与所有匹配表项中最低的权限等级一致。如某传输与2个表项匹配,其中一个是RW,另一个是RX,则最终的权限是R。这与MPAX也是不一样的。如果一个地址落入多个MPAX表项,编号高的表项优先于编号低的表项。MPAX 只会用编号最高的表项决定权限,并忽略其他匹配的表项。
如下与本文对应例程中一个对MPU1的配置例子。每行代表MPU中一个配置范围。
如上配置知,队列保护如下:
队列0~2047只可由AID0~7进行写(PUSH)操作;
队列2048~6143可由AID11以外所有的AID进行写(PUSH)操作;
队列6144~8191只可由AID8~15(AID11除外)进行写(PUSH)操作。
TCI6614上的MPU6用于避免ARM对DDR3的非法操作。注意,MPU6是用于低32-bit DDR物理地址范围的保护。注意,为了清除MPU异常/中断事件,必须在服务程序的最后向EOI寄存器写0。
TCI6614的MPU事件与其他KeyStone器件有所不同。TCI6614中所有的MPU0~7事件被合并为一个事件并作为一个系统事件连接到CIC0。由于TCI6614 MPU事件是电平中断而非脉冲中断事件,所有必须首先清除MPU事件标志,然后才可以清CIC标志。对于脉冲中断事件,必须首选清CIC标志,然后清源标志。
另外,只有在通过PSC使能BCP后,才可以访问TCI6614中用于BCP的MPU5。即在访问TCI6614中MPU5寄存器时,如果此时BCP没有被使能,则该访问将触发访问错误。
2.4 预留区域保护
预留区域(非法地址)被自动保护。对非法地址进行读操作时将返回垃圾数据,写操作则会被阻止。对预留区域的访问可以产生异常,这有益于捕获软件bug。
由于DSP core的访问会经过L1D控制器,所以DSP core对非法地址的访问会触发L1D内存保护异常。DSP core从非法地址执行时将触发指令获取异常。对于非法写操作,触发的异常取决于相应的目的地址。DMA对非法地址访问时,DMA模块会上报总线错误。DMA错误事件可以作为异常路由到DSP core。
3 EDC
EDC(Error Detection and Correction)用于存储器软错误(Soft Error)。软错误是一个错误的信号或数据,但是并不意味着硬件被破坏。在观测到一个软错误后,并不意味着系统可靠性会下降。在宇宙飞船中这种类型的错误称为单一事件扰乱。在内存系统中,一个软错误会改变程序中的一条指令或者一个数据值。软错误通常可以通过器件的重启进行纠正,而硬件错误通常不能通过重启来恢复。软错误不会对系统硬件造成破坏;仅仅会对处理的代码或数据造成错误。产生软错误的原因有:
1.阿尔法粒子辐射及宇宙射线产生能量中子及质子。发生的概率取决于器件的地理位置及周围环境。通常,一个器件在几年中才会出现几次。
2.软错误也可由随机噪声、干扰或信号完整性错误引发,如板载电感应或电容串扰。如果软错误发送概率高于上述条目1 中的理论值,则应该检查硬件设计找出其他原因。一个常见的原因是供电电源电压低于预期,导致器件对噪声或干扰的影响更敏感。
KeyStone器件各级memory中都实现了EDC机制,下表对不同memory模块的实现机制进行了比较。
3.1 L1P错误检测
关于L1P及LL2 EDC基本信息参考“TMS320C66x DSP CorePac User Guide(SPRUGW0)”。校验比特生成与核对:校验比特在进行64-bit对齐的DMA写或L1P cache缓存时生成。非64-bit对齐的DMA 访问将使校验信息失效。在256-bit对齐的程序读取或64-bit对齐的DMA读操作时,L1P EDC逻辑会核对校验信息。
错误检查设置:器件复位后默认情况下L1P错误检查特性是关闭的。一旦L1PEDCMD寄存器中的“EN”bit被置位,所有L1P memory中的ED逻辑被使能。下面是从应用代码中摘录的L1P ED功能使能例子。
注意:要使L1P ED功能工作正常,必须同时使能L2 EDC。
对L1P cache访问时的错误处理: 对从L1P cache中获取程序产生的校验错误,没有专用的系统事件,然而,错误检测逻辑会发送一个直接的异常事件给DSP(IERR.IFX事件),然后用户可以使用内部异常事件获取这个错误。L1PEDSTAT寄存器的PERR bit会被置位。L1PEDARRD寄存器会记录包含错误bit的的地址信息。在L1P错误对应的异常处理服务函数中,需要对包含错误地址的cache line进行失效操作。
对DMA访问的错误处理:对DMA/IDMA访问产生的校验错误,对应#113号系统事件。用户可以使用这个事件获取错误。L1PEDSTAT寄存器的DERR比特位会被置位,并且L1PEDARRD寄存器会记录包含错误bit的地址信息。
L1P EDC功能验证:通过置位LPEDCMD寄存器中的SUSP比特可以暂停L1P EDC逻辑。使用该特性,可以软件模仿EDC错误并验证EDC功能。与本文对应的例程中提供了验证L1P EDC功能的代码,对应函数L1P_ED_test()。
3.2 LL2错误检查与纠正
校验比特生成与核对:在对L2以128 bits为单元进行内存写操作时会产生相应的校验信息。非128-bit对齐或者小于128 bits的写操作会使校验信息失效。对128-bit对齐的memory读操作时,LL2 EDC逻辑会核对校验信息。更多信息参考“TMS320C66x DSP CorePac User Guide(SPRUGW0)”。
错误检查及纠正配置:器件复位后默认情况下LL2 EDC特性是被关闭的。与某些C64+DSP不同的是,KeyStone DSP不能对内存分块使能EDC。一旦EDC使能,EDC逻辑对整个CorePac L2内存生效。然而,可以对不同的内存访问请求者分别使能,如L1D控制器、L1P控制器或DMA控制器。例如,如果用户只需要对代码段使用EDC,需要使能下面三个域:
1.设置L2EDCMD寄存器中的EN bit以使能LL2 EDC逻辑;
2.设置L2EDCEN寄存器中的PL2SEN比特以使能L1 SRAM的EDC逻辑对L1P访问的检查;
3.设置L2EDCEN寄存器中的PL2CEN比特以使能L2 cache的EDC逻辑对L1P访问的检查。
从关闭到使能状态转变时,LL2 EDC逻辑不会初始化校验RAM。因此,在进入使能状态后,校验RAM中的值是随机值,需要用户软件对其进行初始化。对L2 EDC的配置必须遵循“TMS320C66x DSP CorePac User Guide(SPRUGW0)”中阐述的EDC配置顺序。下面是从例程中摘录的L2 EDC使能函数参考代码:
对来自L1D控制器的访问错误处理:在经过L1D cache从LL2中获取数据时,对所有这些数据会进行错误检查,但是不会有任何的纠正。不管是1-bit或者是多bit错误,将会通过#117号系统事件(L2_ED2:不可纠正比特错误检测)上报给DSP core。
对来自L1P及DMA控制器的访问错误处理:1-bit错误可以被纠正并通过#116号系统事件(L2_ED1:可被纠正的比特错误)上报 。2-bit错误可以被检测,并通过#117号系统事件上报该错误。下表列出对于不同存储器访问请求者,相应的1-bit错误处理细节。
错误计数器(L2EDCPEC, L2EDNPEC)非常有用,可用于在长时间运行的系统中评估校验比特错误发生的种类与概率。下表列出对不同存储器访问请求者,相应的2-bit错误处理细节。
对于大于2bits的错误,EDC逻辑可能会检测并报告为1-bit或2-bit错误,或者EDC根本检测不到该错误。所以说,KeyStone系列EDC硬件逻辑只能保证检测2-bit错误或纠正1-bit错误。
通常软错误出现的概率很低,首先出现1-bit错误,在相对长时间后,第二个错误bit也许会产生。由于1-bit 错误可以被纠正,而2-bit错误不能被纠正,所以我们应该尽可能在第二个比特错误出现前纠正好第一个比特错误。
纠正1-bit错误的操作通常称为“刷新”。为了刷新一块存储器,可以使用IDMA,把IDMA的源地址与目的地址设为相同的地址;字节长度设置为期望覆盖的内存块。地址访问必须是128-bit对齐,并且整块的内存范围长度必须是128bits的整数倍。在IDMA从LL2读取数据时,对于存在有效校验信息的128-bit字,EDC硬件会纠正可能存在于其中的1-bit错误。当IDMA把数据回写到相同的地址时,EDC会对数据产生校验信息并标识其为有效。
刷新操作通常是在1-bit错误中断服务函数中进行。但是在1-bit错误发生之后2-bit错误发生之前,某些数据也许不会被访问,在没有访问时1-bit错误是不会被自动上报的。为了避免这种情况,应该周期性地刷新整块存储器区间来纠正潜在的1-bit错误。下面是一段LL2 EDC刷新的代码例子。
通常,这个函数可以在一个定时中断中调用。如在一个600秒周期的定时中断中调用该函数。
这样, 1MB的存储区间会每7天被刷新一遍。由于刷新操作会与正常的内存操作相竞争,因此会影响正常内存操作的性能。所以刷新操作不能太频繁,但是必须在2-bit错误产生前完成。在设计时必须权衡考虑。LL2 EDC功能验证:通过设置L2EDCMD寄存器中的SUSP比特可以暂停LL2 EDC逻辑。使用该特性,可以软件模仿EDC错误并验证EDC功能。与本文对应的例程中提供了验证LL2 EDC功能的代码,对应函数LL2_ED_test()。
3.3 SL2错误检测与纠正
对共享存储器SL2的基本信息,参考“KeyStone Architecture Multicore Shared Memory Controller User Guide(SPRUGW7)”。校验比特产生与核对:有两种机制用于MSMC校验信息的产生与检测:
1.对任意master发起的256-bit内存段的写操作时,校验信息会被更新并设置为有效。小于256bits的写操作会使校验信息失效。当DSP master发起256-bit内存段的的读操作时,校验信息会被检查。
2.MSMC包含一个后台错误纠正硬件称作刷新引擎,用于周期刷新存储器的内容。刷新的周期数可以通过SMEDCC 寄存器中的REFDEL比特域来配置,每次刷新会读取并回写大小是4个32字节的块。在检测并纠正1-bit或者检查到2-bit错误时,刷新引擎还会上报EDC错误。在MSMC用户手册中有具体的机制细节描述。
DSP复位后,MSMC硬件会使校验信息失效,并重新初始化校验信息。在第一次读MSMC存储器时,软件必须先检查SMEDCC中的PRR比特(校验RAM是否准备好的状态信息)。
错误检测与纠正配置:DSP复位后SL2 EDC逻辑的刷新引擎被使能,并且会在后台产生校验信息。软件不需要像LL2 EDC一样使用DMA进行存储器刷新,只需要查询SMEDCC寄存器中的PRR(校验RAM准备)比特位来确认校验比特已经产生。为了使能错误纠正,SMEDCC中的ECM比特同样应该使能。请注意,错误纠正逻辑会对从SL2的读操作增加1cycle的时延(访问流水线增加了一级),不过访问吞吐量并不会降低。
下面是使能MSMC EDC功能的例程:
错误上报机制:MSMC用户手册中有详细的错误上报机制信息,这里总结如下表。
请注意,由刷新引擎上报的错误地址是从0开始的地址偏移,而为非刷新访问记录的错误地址是器件中从0x0C000000开始的SL2地址。
MSMC EDC功能验证:可以通过设置SMEDCTST寄存器中的PFn比特位(bit0~3)来暂停MSMC EDC逻辑。SMEDCTST 的地址偏移是0x58。每个SL2 RAM bank对应PFn中一个比特(PF0~3与bank0~3依次对应),每个比特可以用于禁止对校验RAM的写操作。这样可以冻结bank对应的校验RAM,因此可以通过故意注入错误来破坏SL2存储内容与校验信息的一致性,从而测试检测纠正逻辑。具体的顺序如下:
1.向测试bank中的某一个位置写一个已知值,这样可以正确地为这个位置初始化一个校验值。
2.向SMEDCSTST对应的PF比特写1以冻结该校验值。
3.向上述被写的位置写任意字节来改变该位置的数值,如果检验纠正功能则写一个1-bit差异的值,如果检验检测功能则写一个存在2-bit差异的值。此时该位置的校验值与其存储的数值没有同步。
4.读回该位置的值,将会产生所选类型的校验错误。
与本文对应的例程中提供了相应的代码用于验证SL2 EDC功能,对应的函数为SL2_EDC_test()。
4 其它鲁棒性特性
4.1看门狗定时器
对应看门狗定时器的基本知识,请参考“KeyStone Architecture Timer64 User Guide(SPRUGV5)”中“看门狗定时器模式”章节。
定时器0~(N-1)可用于N个core的看门狗。在TCI6614中定时器8是ARM的看门狗定时器。在看门狗模式下,定时器倒计时到0时产生一个事件。需要由软件在倒计时终止前向定时器写数,然后计数重新开始。如果计数到0,会产生一个定时器事件。看门狗定时器事件可以触发本核复位、器件复位或者NMI异常,这可以通过配置相应器件手册中描述的“复位复用寄存器(RSTMUXx)”来选择。
使看门狗事件触发NMI异常具有更高的灵活性,在NMI异常服务函数中,错误的原因及某些关键的状态信息可以被记录下来,或者上报给上位机来进行故障分析,然后如果它不能自恢复则可以再由软件来复位器件。
4.2 EDMA错误检测
关于基本的EDMA CC错误信息可以参考“KeyStone Architecture Enhanced Direct Memory Access(EDMA3)Controller User Guide(SPRUGS5)”中的“错误中断”章节。
关于基本的EDMA TC错误信息可以参考“KeyStone Architecture Enhanced Direct Memory Access(EDMA3) Controller User Guide(SPRUGS5)”中的“错误产生”章节。
所有的EDMA错误事件可作为异常被路由到CorePac。事件丢失错误是一种最常见的EDMA CC错误,意味着EDMA不能按要求及时完成数据的传输,或者错误的事件触发了不应该的EDMA传输。总线错误是一种最常见的EDMA TC 错误,通常意味着EDMA访问了错误的地址(如预留地址或受保护的地址)。
4.3 中断丢失检测
中断丢失或遗漏是实时系统中常见也是常被忽略的问题。中断丢失检测是一种用于捕捉这种异常的有效方法。对基本的中断丢失检测信息参考“TMS320C66x DSP CorePac User Guide(SPRUGW0)”中“中断错误事件”章节。
软件系统应该对路由到DSP core且有对应软件服务的中断使能中断丢失检测。在所有中断配置完毕后可以添加如下代码使能中断丢失检测:
注意,当使能中断丢失检测并在CCS/Emulator下使用断点或单步进行调测时,由于在仿真停止时中断没有被响应,所有此时中断丢失错误上报的概率很高。如果想忽略它,可以在调测时暂时对某些或全部中断关闭中断丢失检测,但是注意不要忘记在正式发布的程序中重新使能该功能。
5 异常处理
关于异常处理的基本信息参考“TMS320C66x DSP CPU and Instruction Set Reference Guide(SPRUGH7)”中“CPU 异常”一节。
关于中断或异常事件路由的基本信息参考“TMS320C66x DSP CorePac User Guide(SPRUGW0)”中“中断控制器”章节。
5.1 异常事件路由
所有源自或由CorePac触发的错误事件均直接路由到CorePac的中断控制器。常被当作异常处理的错误如下表所示。
一些其他非致命的错误事件,如可纠正的LL2 EDC错误,应该被路由到中断而非异常。源自或者由器件中共享模块触发的错误事件被路由到CIC。CIC基本信息参考“KeyStone Architecture Chip Interrupt Controller(CIC) User Guide(SPRUGW4)”。
CIC事件中常被当作异常处理的事件如下表所示。
每种这样的异常事件只能路由到一个CorePac。通常所有的这些事件被路由到一个CorePac。下图描述DSP core 内部控制异常处理的开关。
图2 DSP核异常控制开关
一旦软件置位TSR.GEE 及IER.NMIE,不能再由软件清除,只能在复位后被清除。TSR.XEN可以由软件置位并清除。XEN可以在进入异常服务函数中由硬件自动清除,并在退出异常服务函数时自动恢复原来的状态。因此,默认情况下,在中断服务函数中,TSR.GEE=1,IER.NMIE=1及TSR.XEN=0.
5.2 异常服务函数
异常函数中应该记录或上报异常原因及相关信息,用于故障分析。关键的记录信息是NRP。NRP是异常返回指针,通常用于确定异常触发的位置。实际上,非法操作与NRP捕获之间的时延大概在10~100个DSP Core cycles 之间,具体的时延取决于很多因素,如操作类型,产生异常事件的模块等等。例如对于向一个被MPU保护的寄存器执行写操作,其时延包括:从DSP core到寄存器的写指令时延;错误事件从MPU到CIC然后到CorePac异常模块的路由时延。因此,当我们获得NRP后,应从NRP指向的位置向后搜索大概10~100cycles来找有问题的操作。
不过,某些异常NRP是没有意义的,例如,对于指令获取异常及非法操作码异常。这通常发生在当程序跳转到一个非法的地址时,这时NRP也指向一个非法的地址。我们真正想知道的是在程序跳转到非法地址前到底发生了什么,但是这并不能从NRP推导出来。在这种情况下,寄存器B3,A4,B4,B14及B15也许会有所帮助。B3可能还保存着上次函数调用的返回指针;A4及B4也许保存着上次函数调用的参数;B15是栈指针;B14是指向某些全局变量的数据指针。更多的细节可以参考“TMS320C6000 Optimizing Compiler User Guide(SPRUG187)” 中“7.4 函数结构及调用约定”章节。根据这些信息,我们也许可以推导出在程序跳转到非法地址前发生了什么。注意,B3,A4,B4可能在异常发生前已经被修改用于保存其它信息,所以它们也许不是有用的。实际上,B3,A4,B4包含有价值信息的概率还是很高的,所以这些寄存器是值得记录并分析的。
通用寄存器的值不能用C 代码记录,而必须用汇编代码来记录。下面的例子是将B3,A4,B4,B14,B15 寄存器记录在“exception_record”中,然后调用 “Exception_service_routine”。
其它需要记录的基本信息有:EFR,IERR,NTSR,TSCL/TSCH.EFR用于判决异常类型:内部、外部或是NMI。对于内部异常,内部异常的原因记录在IERR。NTSR记录异常发生时的DSP core状态。记录的TSCL/TSCH用于确定异常发生前器件运行的时长。
对于外部异常,通过检查INTC及CIC标志寄存器来决定异常原因。对应一个特定的异常,往往有特定的状态寄存器可以检查、记录或上报。例如对应内存保护异常,需要记录的关键信息是故障地址。参考各模块的用户指南了解相关状态或标志的更多细节。
通常,异常服务函数将这些异常信息保存在一个类似如下的数据结构中。
可以在异常服务函数中将这些数据结构中的信息传递给主机,或者将其导出来进行错误分析。
通常异常服务函数处理的错误是致命的,用户不应该期望从异常服务函数中返回。另外,软件也不总是能从异常服务函数中安全返回,阻止从异常中安全返回的条件有:
1.被异常终止的SPLOOPs不能正确地重新开始。在返回前应该核实NTSR中的SPLX比特数值为0.
2.中断被堵塞时发生的异常不能正确地重新开始。在返回前应该核实NTSR中的IB比特数值为0.
3.在不能被安全中断的代码处(如一个保护多个赋值的紧凑循环)发生的异常不能正确地返回。编译器通常会在代码中的这些地方关闭中断;查看NTSR中的GIE比特值为1来验证满足这个条件。
4.NRP不是一个合法的地址。
所以通常异常服务函数以一个while(1)循环作为结束。默认情况下在异常服务程序中,TSR.GEE=1,IER.NMIE=1 及TSR.XEN=0.即在异常服务程序中NMI及内部异常是使能的。
当一个使能的异常发生在第一个异常服务程序中时,复位向量指向的程序会被执行。这时NTSR和NRP不会发生改变。TSR复制到ITSR,此时的PC复制到IRP。此时为了避免其他外部异常,硬件将TSR设置为默认的异常处理值,NMIE中的IER比特被清零。
通常中断服务表中的复位向量是跳转到程序起始位置如_c_int00,这样,嵌套异常会重启程序。然而这并非大部分用户所期望的,我们通常期望的是异常发生时在异常服务程序执行完后结束程序。为了避免嵌套异常导致程序重启,可以给嵌套异常添加一个额外的异常服务程序,用户可以修改复位向量跳转到嵌套异常服务程序。在KeyStone器件中,加载程序不依赖于复位向量启动程序,所以修改复位向量不会影响程序的加载。
6 例程
本文相关的例程可以在TCI6614 EVM, C6670 EVM及C6678 EVM上跑通。如下为工程目录结构:
图3 例程目录结果
“common“文件夹中包含通用代码如DDR 初始化及DMA、定时器、多核导航器、SRIO驱动等。内存保护初始化代码、EDC及异常处理的代码包含在KeyStone_common.c。
“src”文件夹中的每个c文件包含一个测试用例代码。主函数在 “Robust_System.c”。在 “Robust_System.c“的开头有一些宏开关,每个开关用于使能或关闭一个测试用例。
如果使能多个测试用例,每个用例会依次执行。由于程序并不能总是安全地从异常服务程序中返回,因此有可能在一个测试用例后输出如下信息,然后测试流程被终止。
如果出现这种情况,可以关闭这个测试用例然后重新测试其他的用例。在EVM上运行例程的步骤如下:
1.解压例程,将CCS workspace切换到解压后的文件夹;
2.在workspace中导入工程;
3.如果发生代码修改对工程重新编译,也许需要在编译选项中修改CSL保护路径;
4.设置EVM板上的器件加载模式为No boot模式;
5.将代码加载到DSP core0,运行;
6.查看CCS stdout窗口浏览测试结果。
如下为TCI6614上的测试结果。
评论
查看更多