4、BIST(内建自测试):Memory BIST、Logic BIST、MBIST控制器设计
各位同学,咱们今天聊点硬核的——BIST。说白了,就是让芯片自己给自己做体检。
你想想看,一颗GPU芯片里动辄几百个SRAM、几十个FIFO,再加上密密麻麻的逻辑单元。要是靠ATE(自动测试设备)一个个测,那测试成本能把人吓哭。我当年刚入行时,就见过一个项目因为测试时间太长,导致芯片单价硬生生贵了2毛美金——别小看这2毛,量产后就是几百万的利润啊。
所以,BIST就派上用场了。它让芯片在内部自己产生测试向量、自己比对结果,最后只输出一个Pass/Fail信号。嗯,这里要注意:BIST不是万能的,但它绝对是降低测试成本的神器。
4.1 Memory BIST:SRAM的自我救赎
Memory BIST,简称MBIST,是BIST家族里最成熟、最常用的成员。GPU里的显存、缓存、寄存器堆,全得靠它来测。
我个人习惯把MBIST分成三块:
- 测试算法:比如March C-、March 13N,这些是核心
- 控制器:负责生成地址、数据、控制信号
- 响应分析器:比对读出数据,判断对错
我在项目中遇到过最典型的场景:某款GPU的L2缓存有512KB,分成32个Bank。如果不用MBIST,ATE得逐个Bank写读验证,光这一步就要花掉3秒。用了MBIST后,所有Bank并行测试,0.1秒搞定。你想想看,这差距有多大?
核心要点:MBIST的测试算法决定了故障覆盖率。March C-能覆盖99%的固定故障和转换故障,是工业界的主流选择。
常见的March算法长这样:
// March C- 算法伪代码
// 假设Memory有N个地址,每个地址1bit
for (addr = 0; addr < N; addr++) {
write(addr, 0); // 写0
}
for (addr = 0; addr < N; addr++) {
read(addr, 0); // 读0
write(addr, 1); // 写1
}
for (addr = N-1; addr >= 0; addr--) {
read(addr, 1); // 读1
write(addr, 0); // 写0
}
// ... 后续步骤类似,反向再走一遍
嗯,这里要注意:实际实现时,地址生成器要支持递增和递减两种模式。我曾经见过一个新手工程师,把地址计数器写成了只增不减,结果March C-的后半段全废了。
4.2 Logic BIST:数字逻辑的体检医生
Logic BIST,简称LBIST,比MBIST复杂得多。它主要测的是组合逻辑和时序逻辑——说白了,就是那些不规则的逻辑门。
LBIST的核心思想是:用伪随机向量去刺激逻辑,然后用压缩器(比如MISR)把响应压缩成一个签名。最后比对签名,就知道逻辑有没有问题。
我建议你记住这几个关键模块:
| 模块 | 作用 | 常见实现 |
|---|---|---|
| PRPG | 生成伪随机测试向量 | LFSR(线性反馈移位寄存器) |
| MISR | 压缩响应,生成签名 | 多输入移位寄存器 |
| 相位器 | 控制扫描链的使能 | 简单的状态机 |
避坑指南:我曾经在LBIST的PRPG多项式选择上栽过跟头。如果多项式选得不好,生成的向量会有大量重复,导致故障覆盖率上不去。建议用工业标准的多项式,比如IEEE 1149.1里推荐的那些。
LBIST的测试流程大概是这样的:
- 进入测试模式,所有触发器连成扫描链
- PRPG生成一组向量,通过扫描链灌入
- 施加一个系统时钟,让逻辑正常运算
- 捕获结果到扫描链,再移出到MISR
- 重复N次,最后比对签名
你想想看,这个过程完全不需要ATE干预。芯片自己就能跑完几万个向量,最后只输出一个Pass/Fail。这就是LBIST的魅力。
4.3 MBIST控制器设计:从零开始搭一个
好了,咱们来点实际的。MBIST控制器到底怎么设计?
我个人习惯把控制器分成四个状态:
- IDLE:空闲状态,等待测试启动信号
- INIT:初始化,比如清空地址计数器
- TEST:执行March算法,这是核心
- DONE:测试完成,输出结果
下面是一个简化版的MBIST控制器Verilog代码:
module mbist_controller (
input clk,
input rst_n,
input start, // 启动测试
output reg done, // 测试完成
output reg pass, // 测试通过
output reg [9:0] addr, // 地址总线
output reg we, // 写使能
output reg [7:0] wdata,// 写数据
input [7:0] rdata // 读数据
);
// 状态定义
localparam IDLE = 2'b00;
localparam INIT = 2'b01;
localparam TEST = 2'b10;
localparam DONE = 2'b11;
reg [1:0] state, next_state;
reg [9:0] addr_cnt;
reg [2:0] march_step; // March算法步骤
// 状态机
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
state <= IDLE;
else
state <= next_state;
end
// 组合逻辑:下一状态
always @(*) begin
case (state)
IDLE: next_state = start ? INIT : IDLE;
INIT: next_state = TEST;
TEST: next_state = (addr_cnt == 1023 && march_step == 5) ? DONE : TEST;
DONE: next_state = IDLE;
default: next_state = IDLE;
endcase
end
// 地址计数器
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
addr_cnt <= 0;
else if (state == INIT)
addr_cnt <= 0;
else if (state == TEST)
addr_cnt <= addr_cnt + 1;
end
// 输出赋值
assign addr = addr_cnt;
// ... 其他逻辑省略
endmodule
注意:实际项目中,MBIST控制器要处理多Bank并行测试、时钟域同步、测试模式切换等复杂问题。上面的代码只是一个教学示例,千万别直接拿去流片。
嗯,这里有个小技巧:在设计MBIST控制器时,我建议把March算法的每一步都做成独立的子状态机。这样调试起来方便,也容易扩展新的算法。我曾经在一个项目里,就因为把算法写死了,结果客户要求换March C+,我硬是改了两周。
4.4 实战经验:BIST的常见坑
最后,分享几个我踩过的坑:
- 时钟问题:BIST模式下,时钟频率可能和正常模式不同。记得做时钟域隔离,否则时序分析会报一堆violation。
- 功耗问题:所有Bank同时测试,瞬间电流能飙到正常值的3倍。我建议分时启动,或者加一个功耗控制模块。
- 签名比对:MISR的初始值要小心。如果初始值设错了,好芯片也会报Fail。我一般用全0或全1,然后仿真验证。
- 测试时间:LBIST的向量数量要权衡。向量太少,覆盖率不够;向量太多,测试时间太长。我一般用故障仿真来定这个数。
好了,关于BIST的内容就讲到这里。下一章咱们聊聊DFT(可测试性设计)的完整流程,到时候会把这些知识点串起来。记住,BIST不是银弹,但它绝对是芯片测试里最实用的技术之一。