- 易迪拓培训,专注于微波、射频、天线设计工程师的培养
自动实现半导体器件系统强化测试的方法
检验芯片的各项功能需要编写诊断软件,目的是确保器件的各项功能按要求工作,但是无论各项测试有多么详尽,它都无法找出大部分系统层次的问题。因此,要利用系统级强化测试(stress testing)来使多种功能以高强度和随机的方式共同执行,从而迫使这些设计问题浮出表面。本文将探讨用于检验半导体器件设计的系统强化测试方法。
强化测试
强化测试是指将产品置于比实际正常应用苛刻得多的条件下使用,其目的是通过各种方式对设备进行考验,迅速发现其设计缺陷。例如,对于一件机械产品,可能让它 面对恶劣的环境条件,如高压、高温和震动。如果该产品能在这样的条件下成功经受一个月的考验,那么在正常使用条件下,它应该能工作长得多的时间。
同样的概念也适用于芯片的设计。此时,强化测试的表现形式是执行诊断软件,其目的是使大量的芯片功能同时、连续、随机、密集地工作,其程度远远超过芯片正常 使用时的状况。这些测试通常连续运行,大多数时候无人照管,夜间和周末也不停止。通过这些测试,可以在产品发货之前迫使隐藏的系统问题尽快地暴露出来。
为什么强化测试对半导体设计如此重要呢?为了解释这一问题,假设一个半导体器件常常为了不同用途而应用在各种电子系统之中,因此,这些电子产品将被几千甚至 几百万用户使用,每个用户使用产品的方式各不相同。在如此大量的使用中,当芯片上的功能和事件出现某种在器件测试过程中从未遇到过的顺序或组合时,可能就 会发生问题。这些问题通常表现为:不时被锁住、数据出错或其它难以复现的异常现象。如果这些问题出现太多(或者在执行重要任务的产品中仅仅偶然出现),用 户将会认为这些产品是不可靠甚至不稳定的。
强化测试概念的引出
最初尝试创建系统 强化测试时,方法是研究和编写直接激活尽可能多的芯片功能并使它同时执行软件,使用定时器中断并让它执行随机测试,同时主程序不停地复制数据,并随机地不 时启动其它芯片操作。当想出向芯片内引入更多无序和随机事件的招数时,便将它添加到软件中去。很快就可以发现,虽然陆续发现了许多问题,但它大多发生在自 己编写的软件上。
由于不时使用同一块存储区域或外设资源,测试软件中的测试功能相互干扰。例如,当一个测试正在使用某一通用I/O时,另一个随机启动的测试却更改了它的状态。由此不时导致的故障耗用了大量的调试时间。
第二次,尝试开发组织得非常好的强化测试软件。它安排随机选择的测试功能轮流运行,在许多地方将中断锁住,以防止软件冲突和保证其它安全措施。这种谨慎的编 程使得测试非常稳定,但与直接测试相比,同样不能有效地发现芯片的问题,因为它是按照一种很大程度上可预期的顺序方式来执行测试。毕竟测试的目的是为了测 试半导体器件的设计,而不是为了获得稳定的测试软件。
在理论上希望避免这两种极端情况。测试的根本目的是为了暴露芯片中系统 级的相互作用问题,为了达到这个目的,就必须将操作搞乱,使得许多不同事件以随机方式发生。其中的诀窍是,以一种有组织的方式来保持测试的无序特点,从而 获得可靠的测试软件,并可进行扩展,还可以尽可能地帮助调试。要达到这些目的,需要仔细进行考虑和规划。这就是构建一个强化测试框架的思路。
测试框架
本文介绍一个软件框架(framework)的设计,读者可以使用这个框架来为半导体器件构建一个可扩展的强化测试。虽然本文没有提供完整的框架,但这些内容应当能够激发读者的灵感来帮助构建自己的强化测试框架。
软件框架通过提供一个公共操作引擎和环境来简化特定类型的应用开发工作,具体的应用经由一个定义好的接口来与框架连接。强化测试框架也采用了同样的思路,它提供一个软件引擎来处理强化测试的基本操作和公共功能。
强化测试框架本身不测试任何芯片功能,但它允许嵌入测试事件模块。测试事件模块是实现一项具体的半导体器件测试或事件的函数,通常要使用一组随机产生的测试参数。执行强化测试时,框架引擎将随机选择并以不同方式来启动这些测试事件模块。
这个框架还为测试事件模块提供了基本服务供其使用,包括保留系统资源的功能、选择随机测试参数的功能和数据记录功能。大部分预先规划和设计都位于框架本身内 部,因为框架处理强化测试复杂和“混乱无序”的一面。然后,根据需要增加单个测试事件模块,扩展强化测试。由于框架负责处理公共的强化测试软件部分,测试 事件模块的开发人员可以专注于为目标芯片功能编写优秀的测试程序。图1为强化测试框架各个组成部分的高层次示意图。
随机测试池是强化测试的核心部分,实际的半导体器件测试就位于其中。它是为了对芯片的某项具体功能启动事件和(或)执行测试而编写的测试事件模块的集合。开发新的 测试事件模块时,可将它添加到随机测试池中,以便在下一次运行强化测试时将其包含在内。本文后面还将更详细地介绍测试事件模块,因为这是为芯片开发并由框 架启动和支持的实际半导体器件测试。
执行过程中,强化测试引擎随机地从池中选择和运行测试事件模块。为了使测试变得更加杂乱无序(从而对器件设计施加更大的强化测试),框架采用异步时隙来按照任意时间间隔随机选择和启动测试。
“时隙”使用一个硬件定时器来实现,器件或测试板卡上的多个定时器可以拥有各自的时隙。框架为每个时隙定时器选择随机的时间间隔,当时隙截止时,引擎中断当前 的测试模块,定时器的中断处理程序将选择另一个模块来执行,同时引擎为下一个时隙操作选择另一个随机的时间间隔。这样,测试将以一种交织的风格执行,引发 各种各样的执行方式,同时还实现了执行的并行性,这一点将在后面看到。
选择和启动一项测试的框架代码很简单,因为测试事件模块只不过是一个C语言函数而已。列表1是启动程序的一段代码。一般而言,框架保持有一组C函数,这些函数即为测试模块,读者可以选择任意一个索引来查看函数并加以执行。
(列表1)
测试模块
如前所述,测试事件模块是一个函数,它针对半导体器件的某个具体特性执行一个动作或一次检验。虽然框架将测试模块作为一个C函数来激活,但如果需要,模块也 可以调用汇编级代码。框架的编写方式使得设计人员可以开发新的模块并添加到测试池中,供强化测试期间框架随机选择。后面框架实例中的测试模块是void C函数,它不需要任何参数。在框架支持功能的帮助下,模块本身可以启动测试并调用相应的功能来记录结果。
测试事件模块共有三种类型:
1. 纯事件
2. 检验测试
3. 延迟检验测试
正如其名称所表明的那样,纯事件模块启动一个系统事件后不做任何检验工作,包括以某种方式改变系统或启动某项后台功能。那么,启动一个事件而不执行任何检验 有什么意义呢?为了解释这个问题,来考察一个测试事件模块,它仅仅切换处理器高速缓存的当前设置。每次框架随机选择这个模块时,高速缓存就从1切换到0或 从0切换到1。这增加了一个变化因素,因为对于运行时需要用到这个高速缓存的测试模块而言,它的值有时为1有时为0。
第二种测试事件模块是检验测试,它执行一个动作,并在测试模块函数退出之前对这个动作进行检验。例如,这种测试可以用任意数据来填充随机选择的存储器区域,然后采用任意CPU读写宽度来将这个区域复制到一个随机的目的存储器区域,再检验其结果。
第三种测试模块是延迟检验测试,这是功能最强的测试,虽然通常只能用于可以对总线进行控制的芯片功能(换句话说,运行在CPU的后台)。这种测试模块将启动 一个测试然后退出,使得在原测试继续运行时,其它测试模块可以执行。在大多数情况下,测试的完成用一个中断来表示。当中断发生时,中断处理程序将检验测试 的结果。
延迟检验测试的例子如DMA (直接存储器访问)传输。可以编写一个测试模块来选择随机的源、目的存储器区域和DMA通道。这个测试模块可以用任意数值来填充源存储器,并配置一个用来 检验测试结果的DMA结束中断处理程序。这个中断处理程序即为“延迟检验测试”,它可访问包含有被启动测试的详细信息的数据结构,在被调用时,它还将检验 测试的结果。
测试框架实例
为了展示这些测试的强大功能,来考察一个简单的测试框架实例,它包含以下四个测试模块。
1) 简单的基于CPU的存储器复制(TE_CPU_COPY):
将随机数据样式填充到一个任意大小的随机源地址存储器块中,然后使用一种随机混合的读写宽度来将数据样式复制到任意选择的目的地址。复制完成后,检验目的区域中的数据是否包含了先前选择的数据样式。
2) DMA存储器---存储器复制(TE_DMA_M2M):
将任意数据样式填充到随机源地址的任意大小的存储器块中,然后任意选择一个存储器---存储器DMA通道,编程为将源地址的数据样式复制到随机选择的目的地址。当DMA结束中断发生时,检查两个存储器区域是否匹配。
3) DMA---外设(TE_DMA_M2P):
将任意数据样式填充到随机源地址的任意大小的存储器块中,然后任意选择一个存储器---存储器DMA通道,编程为将数据样式复制到特定外设的任意端口号中。当DMA结束中断发生时,检查外设的FIFO中是否包含了正确的数据(或者被发送到外设)。
4) 高速缓存切换(TE_CACHE):
将高速缓存从1切换到0或从0切换到1并退出。这个测试模块是一个纯事件测试模块,运行时不执行检验操作。
假设框架有三个时隙定时器用来启动测试事件模块,那么启动它的main( )例程位于时隙0,具体运行时可能与列表2所示类似。
(列表2)
运行开始时执行存储器复制测试。执行过程中,时隙2定时器在复制中途定时截止。此时,高速缓存测试事件模块被启动,它切换高速缓存的状态并退出。复制测试在 高速缓存处于新设置的状况下继续执行。在测试完成之前,时隙1定时器又截止,此时一个DMA存储器---端口测试模块被启动,它设置任意的DMA通道并退 出。现在,DMA已在后台执行,而原来的复制测试又重新运行。随后又发生一个时隙3定时器中断,使用DMA通道5启动存储器---存储器传输并退出。这种 安排还可以继续下去,但已经可以看到,通过这个测试,可以让所有的DMA通道同时运行,高速缓存随机切换,而且CPU在测试过程中随机访问不同的存储器区 域。
测试参数
测试事件模块将使用一个或多个随机选择的测试参数来执行测试操作。常见的测试参数如:源和目的存储器地址、块大小、数据样式、读写宽度(如8、16或32位)、DMA通道和UART端口。
虽然随机地选择测试事件函数看起来比较简单,但究竟应当如何选择随机测试参数值,同时确保这些值对系统有效,而且不与其它测试发生交叠从而导致干扰呢?例 如,如果某个测试选择一个随机的存储器地址和大小,需要知道(1)存储器确实存在;(2)其它测试没有选择与这片存储器交叠的存储器区域(因为一个测试有 可能改写另一个测试的存储器并导致故障)。另一个资源竞争的例子是,两个测试都随机地选中同一个DMA通道来进行数据传输。
框架提供的支持功能使测试模块可以安全地选择任意测试参数。为了帮助理解这一点,这里举一个最常用的测试参数的例子:随机选择的系统存储器块。随机选择的存 储器块有一个起始地址值和一个大小值,它都是随机数值。这个存储器块可以位于CPU地址空间的任何位置,包括在SRAM、PCMCIA或Flash存储器 中。
在列表3所示的例子中,memPool数组中填入了存储器段的起始地址和大小,它位于系统的地址空间中,可供测试进行随 机选择。每个条目都有一个保留标志(inUse项)。为了选择一个随机存储器块,函数首先选择一个随机的指向存储器段数组的索引。然后在这个段中选择一个 随机的起始地址和大小。最大块的大小值是存储器段的结束地址减去所选的起始地址。一旦随机块的起始地址和大小选定了,整个存储器段就通过设置保留标志被标 记为保留。
列表3:随机存储器块选择
============================
typedef struct
{
uint32_t start_addr;
uint32_t size;
bool inUse;
} memSeg;
memSeg memPool[] =
{
/* start addr size inUse */
{ 0x10000000,0x00040000, 0},
{ 0x10040000,0x00040000, 0},
{ 0x10080000,0x00040000, 0},
{ 0x100c0000,0x00040000, 0},
{ 0x30000000,0x00040000, 0},
{ 0x30040000,0x00040000, 0},
{ 0x30080000,0x00040000, 0},
{ 0x300c0000,0x00040000, 0},
{ 0x30100000,0x00008000, 0},
{ 0x40004000,0x00004000, 0},
{ 0x40008000,0x00004000, 0},
{ 0x4000c000,0x00004000, 0},
{ 0xa0000000,0x00002000, 0},
{ 0xb0000000,0x00008000, 0},
{ 0xb0008000,0x00008000, 0},
{ 0xb0010000,0x00004000, 0}
};
int SelectAndReserveBlock(unsigned int *addr, unsigned int *size)
{
int retries=0; /* limit the number of times we try to get a segment */
int seg;
while(retries < MAX_RETRY_COUNT)
{
/* Select a random memory segment (index into memPool array) */
seg = GetRandomNumber(sizeof(memPool)/sizeof(memSeg));
if (memPool[seg].inUse)
{
retries++;
continue;
}
/* reserve the segment */
memPool[seg].inUse = 1;
/* select random block in segment, and don't let it run past the end
of the segment. */
*addr = memPool[seg].start_addr + GetRandomNumber(memPool[seg].size);
max_size = memPool[seg].size - (*addr - memPool[seg].start_addr);
if (max_size > MAX_BLOCK_SIZE)
max_size = MAX_BLOCK_SIZE;
*size = GetRandomNumber(max_size);
break;
}
/* return the segment number so it can be used to free it later,
or return -1 if we gave up looking for a free segment */
return (retries >= MAX_RETRY_COUNT) ? -1 : seg;
}
void FreeMemorySegment(int seg)
{
memPool[seg].inUse = 0;
}
===============================================
这种方法的一个缺点是,使用存储器段中的某个随机存储器块时,按照存储器段数组中的定义方式,整个存储器段都将被保留。如果所有存储器段都被保留了,还有存 储器可用吗?解决的办法是将存储器分为更小的段。可能希望以不同的方式来分割存储器段,以便消除跨越数据宽度和类型各不相同的多个存储器器件来选择存储器 块的可能性。注意这些方案都比较简单,也可以加强它的功能,关键只是直接控制指示哪些存储器区域可以使用。
管理其它资源的选 择和保留可能更加简单。例如,保留标志数组可用来管理DMA通道,数组的索引代表通道。设计人员可以编写一个ReserveDmaChannel()函 数,它返回一个随机DMA通道号,这个通道号处于芯片上的DMA通道数目范围之内,并且其保留标志未被设置。然后,函数将设置保留标志来保留这个通道。测 试完成时,可以调用FreeDmaChannel()来清除该标志。
调试技巧
调试和分离与系统相互作用有关的问题可能比较困难,在这个过程中可以通过设计强化测试框架来提供帮助。根据运行是在芯片生产前的仿真环境下进行,还是在一个安装有实际芯片的板卡上进行,所遇到的问题将会有差异。
在仿真环境下运行时,速度是最大的问题,仿真CPU的运行速度要比实际器件慢得多。仿真环境的优势在于仿真工具可以输出芯片设计内部信号的详细记录,这将简 化调试工作,但它也有一个很大的缺点,即问题复现困难。如果为了暴露问题要花费太长的时间来运行强化测试,这将是难以接受的。
解决的办法是将强化测试作为许多较短(例如30分钟)的测试来运行,这可以通过用一组测试事件模块来创建不同的测试来实现。此外,还应该给每个测试分配一个专门的随机数种子(seed),以便使用这个种子的其它测试每次运行时,都能重新产生准确的测试事件和测试参数序列。
好的运行脚本可以随机产生种子值,然后创建一个测试并加以运行。种子值应当与运行结果一起记录下来。为了复现一个故障,可以用这个种子重新创建测试(因为可能不希望保留所有的可运行测试程序,其数量可能达到几千个),然后在短时间内复现这个问题。
在实际的芯片上可以运行更多强化测试,因为其执行速度要快得多。但在理论上,它可能还是要花费同样长的时间,因为比较常见的问题应该在芯片生产前的强化测试 中就已经暴露出来了。在这里,问题的复现同样是一个很大的困难,读者可以尝试在芯片生产前的测试中应用所介绍的技术,但它可能不会同样有效,因为实际系统 可能不如在仿真环境下那样确定。换句话说,两个采用同一随机测试种子运行的测试可能不会每次都获得精确相同的运行序列。另外,此时也无法记录内部信号的行 为,因此应该依靠一些技术,如将检测点的信息记录到存储器或高速端口、插入各种逻辑分析仪触发事件,以及采用其它技术来帮助深入跟踪问题的详细情况。
在实际芯片上调试系统问题时,没有什么通用的方法。设计人员应当周密考虑,并象即将遇到难以捉摸的故障一样来进行计划,编写程序代码,为分析问题提供尽可能多的线索。
此外,测试框架还应该包含这样一种能力,它创建的测试运行程序能将某些测试模块排除在外,从而使强化测试无需在已知的问题上停止下来而得以继续运行。另一方面,将其余功能排除在外也将使设计人员能够对某些特定的芯片功能进行重点测试。
框架需要组织良好的软件代码来生成种子、创建测试、运行、存储测试记录和后续处理,并向设计人员报告所遇到的问题。这个过程应当是完全自动化的,这样就可长时间运行测试而无需人员维护。
记录测试过程
强化测试框架最少应该记录哪个测试模块在运行,以及发生的所有故障。完善的记录将包括更多的信息,如选择的随机参数,各个测试事件是何时启动、退出和检验 (针对延迟检验函数)的。这样,设计人员就可以了解各个测试是如何相互联系地运行的。后续处理软件可以生成一些有用的报告,以显示测试的覆盖范围。
开发记录功能时,应使强化测试软件尽可能简单方便地记录事件。在仿真环境下,可编写HDL代码来提供一个记录外设,这样设计人员只需写下一个数值记号, HDL就将产生完整的记录信息。在实际芯片上,可以将字节记号和数值发送到存储器板卡或输出到调试端口。关键之处是要避免记录操作本身过多占用宝贵的测试 运行时间,并导致过度干扰。通过后续处理软件,可以使记录更清楚、更美观。
本文小结
由于框架将涉及对中断处理程序所读写的共享数据结构的使用,因此访问这些数据结构时,应在软件代码中对重要的存储器段进行保护。例如,如果一个测试事件刚刚 读取了一个存储器段的inUse标志并发现它未被使用,但在它将该存储器段设置为保留之前,又发生了一个时隙中断,中断启动的测试模块结束时,可能将同一 存储器段设置为保留,因为此时该存储器段仍表现为未被使用。在这种类似的重要全局数据访问期间,应该将时隙定时器中断予以禁止。
设计一个强化测试框架时,有许多扩展可能性。如可以调节测试事件模块的概率权重,或调整测试参数以便对强化测试进行精密调节。举个例子,高速缓存切换事件可 能是100个测试事件模块之一,但测试时可能希望它运行比1%多得多的时间,此时便可通过调整测试参数来实现。规划、设计和实现性能优良可靠的半导体器件 强化测试框架是一项重大投入,虽然需要花费大量的时间,但它可以帮助提高产品的质量。
作者: Steve Babin
IBM公司负责开发灵巧电话软件的深入计算事业部担任顾问职务。在嵌入式系统领域拥有16年以上的工作经验,Steve
Babin持有路易斯安那州立大学的电子工程学士学位。
Email: sbabin@austin.rr.com