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的测试流程大概是这样的:

  1. 进入测试模式,所有触发器连成扫描链
  2. PRPG生成一组向量,通过扫描链灌入
  3. 施加一个系统时钟,让逻辑正常运算
  4. 捕获结果到扫描链,再移出到MISR
  5. 重复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不是银弹,但它绝对是芯片测试里最实用的技术之一。