1 简介
如图1 所示,KeyStone 器件提供了多种协助客户建立鲁棒性应用的特性。
图1KeyStone 器件鲁棒系统
如图1 所示,在LL2、L1P 及L1D 中集成了内存保护模块;LL2、SL2 及DDR 控制器中集成了错误检查纠正模块;L1P 集成了错误检测模块。
MPAX 和MPU 模块附在总线上,用于监控检测以避免非法的总线访问。
每个DSPCorePac 有一个独立的MPAX 用于监控与MSMC 连接的总线。
对于系统中其他的master,根据权限ID 进行分类。对每个权限ID,在MSMC 中集成了2 个MPAX 用于监视与该权限ID 相关的访问。其中一个是SES MPAX 用于保护对DDR3 的访问,另一个是SMS MPAX 用于保护对SL2 的访问。关于每个master 对应的权限ID,参考相应的器件手册。
某些外设的配置端口上添加了MPU,用于保护对该外设配置区域的非法访问。但是并非所有的外设都受MPU 的保护,具体参考相应器件手册中受MPU 保护的外设列表。
每个CorePac 有一个看门狗定时器用于监视其活动,如果该核死机,看门狗可以触发不可屏蔽中断或者复位信号。
EMC 可以避免DSPcore 访问没有映射的的配置空间,XMC 则可以避免DSPcore 访问没有映射的数据空间。
所有这些功能都由硬件模块实现,使用这些功能对系统性能基本上没有影响。使用EDC 会对存储器的访问性能稍有影响,但从整个系统层面上看,它几乎是微不足道的。
在出现问题时,所有这些模块可以向DSPcore 触发异常,DSPcore 的异常监控模块可以记录这些状态并触发异常服务程序执行相应的操作。
本文讨论这些特性的应用,并给出相关基于寄存器层CSL 实现的例程。代码使用如下方式定义寄存器指针。
上述各种特性具体描述分布于各自子系统的文档中,本文最后的参考章节中列出了所有相关的文档。在看本文之前,假设客户已经阅读了相关属性对应的文档,所以本文旨在提供相关的补充信息。
本文适用于KeyStone 1 系列DSP,例程在TCI6614EVM,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 表项之前需要先执行如下操作:
将MPAX 表项对应的存储空间内容从cache 中剔除出去。即使对于属性为不可写的存储空间,应该使用CACHE_wbInvL2()而非CACHE_inv L2()。
如果对受影响的存储器空间使能了预取功能,则需要对预取缓存执行失效操作。
执行“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。由于TCI6614MPU 事件是电平中断而非脉冲中断事件,所有必须首先清除MPU 事件标志,然后才可以清CIC 标志。对于脉冲中断事件,必须首选清CIC 标志,然后清源标志。
另外,只有在通过PSC 使能BCP 后,才可以访问TCI6614中用于BCP 的MPU5。即在访问TCI6614中MPU5 寄存器时,如果此时BCP 没有被使能,则该访问将触发访问错误。
2.4 预留区域保护
预留区域(非法地址)被自动保护。对非法地址进行读操作时将返回垃圾数据,写操作则会被阻止。对预留区域的访问可以产生异常,这有益于捕获软件bug。
由于DSPcore 的访问会经过L1D 控制器,所以DSPcore 对非法地址的访问会触发L1D 内存保护异常。
DSPcore 从非法地址执行时将触发指令获取异常。
对于非法写操作,触发的异常取决于相应的目的地址。
DMA 对非法地址访问时,DMA 模块会上报总线错误。DMA 错误事件可以作为异常路由到DSPcore。
3 EDC
EDC(Error Detection and Correction)用于存储器软错误 (Soft Error) 。软错误是一个错误的信号或数据,但是并不意味着硬件被破坏。在观测到一个软错误后,并不意味着系统可靠性会下降。在宇宙飞船中这种类型的错误称为单一事件扰乱。在内存系统中,一个软错误会改变程序中的一条指令或者一个数据值。软错误通常可以通过器件的重启进行纠正,而硬件错误通常不能通过重启来恢复。软错误不会对系统硬件造成破坏;仅仅会对处理的代码或数据造成错误。产生软错误的原因有:
阿尔法粒子辐射及宇宙射线产生能量中子及质子。发生的概率取决于器件的地理位置及周围环境。通常,一个器件在几年中才会出现几次。
软错误也可由随机噪声、干扰或信号完整性错误引发,如板载电感应或电容串扰。如果软错误发送概率高于上述条目1 中的理论值,则应该检查硬件设计找出其他原因。一个常见的原因是供电电源电压低于预期,导致器件对噪声或干扰的影响更敏感。
KeyStone 器件各级memory 中都实现了EDC 机制,下表对不同memory 模块的实现机制进行了比较。
3.1 L1P错误检测
关于L1P 及LL2 EDC 基本信息参考“TMS320C66xDSPCorePac 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 逻辑会核对校验信息。更多信息参考“TMS320C66xDSPCorePac User Guide(SPRUGW0)”。
错误检查及纠正配置:器件复位后默认情况下LL2 EDC 特性是被关闭的。与某些C64+DSP不同的是,KeyStoneDSP不能对内存分块使能EDC。一旦EDC 使能,EDC 逻辑对整个CorePac L2 内存生效。然而,可以对不同的内存访问请求者分别使能,如L1D 控制器、L1P 控制器或DMA 控制器。例如,如果用户只需要对代码段使用EDC,需要使能下面三个域:
设置L2EDCMD 寄存器中的EN bit 以使能LL2 EDC 逻辑;
设置L2EDCEN 寄存器中的PL2SEN 比特以使能L1 SRAM 的EDC 逻辑对L1P 访问的检查;
设置L2EDCEN 寄存器中的PL2CEN 比特以使能L2 cache 的EDC 逻辑对L1P 访问的检查。
从关闭到使能状态转变时,LL2 EDC 逻辑不会初始化校验RAM。因此,在进入使能状态后,校验RAM 中的值是随机值,需要用户软件对其进行初始化,。对L2 EDC 的配置必须遵循“TMS320C66xDSPCorePac User Guide(SPRUGW0)”中阐述的EDC 配置顺序。下面是从例程中摘录的L2 EDC 使能函数参考代码:
对来自L1D 控制器的访问错误处理:在经过L1D cache 从LL2 中获取数据时,对所有这些数据会进行错误检查,但是不会有任何的纠正。不管是1-bit 或者是多bit 错误,将会通过#117 号系统事件(L2_ED2:不可纠正比特错误检测)上报给DSPcore。
对来自L1P 及DMA 控制器的访问错误处理:1-bit 错误可以被纠正并通过#116 号系统事件(L2_ED1:可被纠正的比特错误)上报。2-bit 错误可以被检测,并通过#117 号系统事件上报该错误。
下表列出对于不同存储器访问请求者,相应的1-bit 错误处理细节。
错误计数器(L2EDCPEC, L2EDNPEC)非常有用,可用于在长时间运行的系统中评估校验比特错误发生的种类与概率。
下表列出对不同存储器访问请求者,相应的2-bit 错误处理细节。
对于大于2 bits 的错误,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 对齐,并且整块的内存范围长度必须是128 bits 的整数倍。在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 校验信息的产生与检测:
对任意master 发起的256-bit 内存段的写操作时,校验信息会被更新并设置为有效。小于256 bits 的写操作会使校验信息失效。当DSPmaster 发起256-bit 内存段的的读操作时,校验信息会被检查。
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 的读操作增加1 cycle 的时延(访问流水线增加了一级),不过访问吞吐量并不会降低。
下面是使能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 存储内容与校验信息的一致性,从而测试检测纠正逻辑。具体的顺序如下:
向测试bank 中的某一个位置写一个已知值,这样可以正确地为这个位置初始化一个校验值。
向SMEDCSTST 对应的PF 比特写1 以冻结该校验值。
向上述被写的位置写任意字节来改变该位置的数值,如果检验纠正功能则写一个1-bit 差异的值,如果检验检测功能则写一个存在2-bit 差异的值。此时该位置的校验值与其存储的数值没有同步。
读回该位置的值,将会产生所选类型的校验错误。
与本文对应的例程中提供了相应的代码用于验证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 中断丢失检测
中断丢失或遗漏是实时系统中常见也是常被忽略的问题。中断丢失检测是一种用于捕捉这种异常的有效方法。对基本的中断丢失检测信息参考“TMS320C66xDSPCorePac User Guide(SPRUGW0)”中“中断错误事件”章节。
软件系统应该对路由到DSPcore 且有对应软件服务的中断使能中断丢失检测。在所有中断配置完毕后可以添加如下代码使能中断丢失检测:
注意,当使能中断丢失检测并在CCS/Emulator 下使用断点或单步进行调测时,由于在仿真停止时中断没有被响应,所有此时中断丢失错误上报的概率很高。如果想忽略它,可以在调测时暂时对某些或全部中断关闭中断丢失检测,但是注意不要忘记在正式发布的程序中重新使能该功能。
5 异常处理
关于异常处理的基本信息参考“TMS320C66xDSPCPU and Instruction Set Reference Guide(SPRUGH7)”中“CPU 异常”一节。
关于中断或异常事件路由的基本信息参考“TMS320C66xDSPCorePac User Guide(SPRUGW0)”中“中断控制器”章节。
5.1 异常事件路由
所有源自或由CorePac 触发的错误事件均直接路由到CorePac 的中断控制器。常被当作异常处理的错误如下表所示。
一些其他非致命的错误事件,如可纠正的LL2 EDC 错误,应该被路由到中断而非异常。
源自或者由器件中共享模块触发的错误事件被路由到CIC。CIC 基本信息参考“KeyStone Architecture Chip Interrupt Controller(CIC) User Guide(SPRUGW4)”。
CIC 事件中常被当作异常处理的事件如下表所示。
每种这样的异常事件只能路由到一个CorePac。通常所有的这些事件被路由到一个CorePac。下图描述DSPcore 内部控制异常处理的开关。
图2DSP核异常控制开关
一旦软件置位TSR.GEE 及IER.NMIE,不能再由软件清除,只能在复位后被清除。
TSR.XEN 可以由软件置位并清除。XEN 可以在进入异常服务函数中由硬件自动清除,并在退出异常服务函数时自动恢复原来的状态。
因此,默认情况下,在中断服务函数中,TSR.GEE=1,IER.NMIE=1 及TSR.XEN=0.
5.2 异常服务函数
异常函数中应该记录或上报异常原因及相关信息,用于故障分析。
关键的记录信息是NRP。NRP 是异常返回指针,通常用于确定异常触发的位置。
实际上,非法操作与NRP 捕获之间的时延大概在10~100 个DSPCore cycles 之间,具体的时延取决于很多因素,如操作类型,产生异常事件的模块等等。例如对于向一个被MPU 保护的寄存器执行写操作,其时延包括:从DSPcore 到寄存器的写指令时延;错误事件从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 记录异常发生时的DSPcore状态。记录的TSCL/TSCH 用于确定异常发生前器件运行的时长。
对于外部异常,通过检查INTC 及CIC 标志寄存器来决定异常原因。对应一个特定的异常,往往有特定的状态寄存器可以检查、记录或上报。例如对应内存保护异常,需要记录的关键信息是故障地址。参考各模块的用户指南了解相关状态或标志的更多细节。
通常,异常服务函数将这些异常信息保存在一个类似如下的数据结构中。
可以在异常服务函数中将这些数据结构中的信息传递给主机,或者将其导出来进行错误分析。
通常异常服务函数处理的错误是致命的,用户不应该期望从异常服务函数中返回。另外,软件也不总是能从异常服务函数中安全返回,阻止从异常中安全返回的条件有:
被异常终止的SPLOOPs 不能正确地重新开始。在返回前应该核实NTSR 中的SPLX 比特数值为0.
中断被堵塞时发生的异常不能正确地重新开始。在返回前应该核实NTSR 中的IB 比特数值为0.
在不能被安全中断的代码处(如一个保护多个赋值的紧凑循环)发生的异常不能正确地返回。编译器通常会在代码中的这些地方关闭中断;查看NTSR 中的GIE 比特值为1 来验证满足这个条件。
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 例程
本文相关的例程可以在TCI6614EVM, C6670 EVM 及C6678 EVM 上跑通。例程可以从以下链接下载:
DSP_arm/c6000_multicore/f/53/t/47664.aspx">http://www.deyisupport.com/question_answer/DSP_arm/c6000_multicore/f/53/t/47664.aspx
如下为工程目录结构:
图3 例程目录结果
“common“文件夹中包含通用代码如DDR 初始化及DMA、定时器、多核导航器、SRIO 驱动等。内存保护初始化代码、EDC 及异常处理的代码包含在KeyStone_common.c.
“src”文件夹中的每个c 文件包含一个测试用例代码。主函数在 “Robust_System.c”. 在“Robust_System.c“的开头有一些宏开关,每个开关用于使能或关闭一个测试用例。
如果使能多个测试用例,每个用例会依次执行。由于程序并不能总是安全地从异常服务程序中返回,因此有可能在一个测试用例后输出如下信息,然后测试流程被终止。
如果出现这种情况,可以关闭这个测试用例然后重新测试其他的用例。
在EVM 上运行例程的步骤如下:
解压例程,将CCS workspace 切换到解压后的文件夹;
在workspace 中导入工程;
如果发生代码修改对工程重新编译,也许需要在编译选项中修改CSL 保护路径;
设置EVM 板上的器件加载模式为No boot 模式;
将代码加载到DSPcore0,运行;
查看CCS stdout 窗口浏览测试结果。
如下为TCI6614上的测试结果。