4. 测试程序结构

做 ATE 测试开发,说白了就是搭积木。但很多人一上来就写代码,结果程序乱成一锅粥。我见过不少新人,把几百个测试项全塞在一个文件里,调试的时候想死的心都有。

这一章,我们就来拆解 ATE 测试程序的四个核心积木块:Pattern、Test Method、Test Suite 和 Test Flow。搞懂它们各自是什么、怎么配合,你的程序才能既清晰又高效。

1. 四个概念,一张图说清

先看这张结构图,你就能明白它们之间的层级关系:

ATE测试程序四层结构关系图 ATE 测试程序四层结构 Test Flow(测试流程) Test Suite 1 (一组相关的测试项,如 DC 测试套件) Test Method(测试方法) (如:measure_voltage、measure_current、measure_frequency) Pattern(测试向量) (如:scan_pattern、func_pattern、mbist_pattern) 每一层都是下一层的容器,上层调度下层,下层为上层提供具体能力

嗯,这张图你看懂了,这一章就掌握了一半。从上到下,是调度关系;从下到上,是封装关系。

2. Pattern:最底层的“指令集”

Pattern 是什么?说白了,就是告诉测试机台「什么时候拉高、什么时候拉低、什么时候采样」的时序指令集。

我习惯把 Pattern 比作乐高积木里最小的那个颗粒。单个颗粒没什么用,但组合起来就能搭出城堡。

Pattern 的核心要素:

  • 向量(Vector):每个周期 pin 脚的状态(H/L/Z/X)
  • 时序(Timing):什么时候驱动、什么时候比较
  • 电平(Levels):VIH/VIL/VOH/VOL 的具体电压值
  • 标签(Label):跳转标记,用于控制流程

举个例子,一个简单的 func pattern 片段:

// Pattern: func_basic.pat
// 功能:验证基本读写功能
// 作者:我(2024-03-15)

// 时序集:t1 (周期 100ns)
// 电平集:lv1 (VDD=1.8V, VIH=1.6V, VIL=0.2V)

// Vector 格式:CLK  DATA  ADDR  CS   WE   OE   DQ
START:          L     L     L     H    H    H    Z
                P     H     L     L    H    H    Z   // 写使能
                P     L     H     L    L    H    Z   // 写地址
                P     H     L     L    H    L    Z   // 读使能
                P     Z     Z     H    H    H    H   // 读数据
                P     L     L     H    H    H    Z   // 空闲
JMP START       // 循环

我的经验:Pattern 文件一定要加版本号和作者信息。有一次我调试一个 bug,发现是用了旧版 pattern,浪费了整整一天。从那以后,我每个 pattern 文件头部都加了个「修改记录」块。

3. Test Method:可复用的“测试函数”

Test Method 是真正干活的代码。它调用 Pattern,设置机台资源,采集数据,然后判断 Pass/Fail。

我见过最糟糕的写法,是把所有逻辑都写在一个巨大的 Test Method 里,几千行代码,谁都不敢动。正确的做法是:一个 Test Method 只做一件事。

Test Method 名称 功能描述 输入参数 输出结果
measure_voh 测量输出高电平电压 pin, pattern, 电流负载 VOH 值 (V)
measure_vol 测量输出低电平电压 pin, pattern, 电流负载 VOL 值 (V)
measure_iddq 测量静态漏电流 电压, 等待时间 IDDQ 值 (A)
run_scan_test 执行扫描链测试 scan_pattern, 频率 Pass/Fail + 故障位
measure_freq 测量时钟频率 clock_pin, 采样数 频率值 (Hz)

你看,每个 Test Method 的职责都很清晰。这样写的好处是:换个项目,这些函数直接拿过来用。我手头有个库,积累了 50 多个通用 Test Method,新项目 80% 的测试方法都不用重写。

4. Test Suite:把相关的测试打包

Test Suite 就是把一组相关的 Test Method 和 Pattern 打包在一起。为什么要打包?因为测试不是一个个跑的,是一批批跑的。

举个例子,DC 测试套件:

// Test Suite: DC_Tests
// 包含所有直流参数测试

TestSuite DC_Tests {
    // 测试项 1:VOH 测试
    Test {
        test_method = measure_voh;
        pattern = func_basic.pat;
        params {
            pin = "DQ[0:7]";
            i_load = -1.0mA;  // 拉电流
            voh_min = 1.4V;
            voh_max = 1.8V;
        }
    }
    
    // 测试项 2:VOL 测试
    Test {
        test_method = measure_vol;
        pattern = func_basic.pat;
        params {
            pin = "DQ[0:7]";
            i_load = 1.0mA;   // 灌电流
            vol_min = 0.0V;
            vol_max = 0.4V;
        }
    }
    
    // 测试项 3:IDDQ 测试
    Test {
        test_method = measure_iddq;
        pattern = idle_pattern.pat;
        params {
            vdd = 1.8V;
            wait_time = 10ms;
            iddq_max = 10uA;
        }
    }
}

我曾经踩过的坑:把功能测试和 DC 测试混在一个 Suite 里。结果 DC 测试失败时,功能测试的 pattern 还在跑,把芯片状态搞乱了。后来我强制规定:一个 Suite 只做一类测试,DC 归 DC,功能归功能,扫描归扫描。

5. Test Flow:整个测试的“总指挥”

Test Flow 是最高层的调度器。它决定:先跑哪个 Suite,失败后怎么办,要不要跳转到其他测试。

你想想看,一颗芯片可能有几百个测试项。如果全部跑完才发现前面某个测试失败了,那后面的测试全是浪费。Test Flow 就是用来解决这个问题的。

一个典型的 Test Flow 结构:

// Test Flow: Main_Flow
// 主测试流程

Flow Main_Flow {
    // 第一步:接触测试
    RunSuite(Contact_Tests) {
        on_fail: abort;  // 接触失败,直接终止
    }
    
    // 第二步:DC 测试
    RunSuite(DC_Tests) {
        on_fail: goto_cleanup;  // DC 失败,跳转到清理流程
    }
    
    // 第三步:功能测试
    RunSuite(Func_Tests) {
        on_fail: goto_cleanup;
    }
    
    // 第四步:扫描测试
    RunSuite(Scan_Tests) {
        on_fail: goto_cleanup;
    }
    
    // 第五步:MBIST 测试
    RunSuite(MBIST_Tests) {
        on_fail: goto_cleanup;
    }
    
    // 正常结束
    label cleanup:
    RunSuite(Cleanup_Tests);
    result: pass;
}

Test Flow 的设计原则(我总结的):

  • 先粗后细:先跑接触测试、DC 测试,再跑功能测试
  • 失败早退:关键测试失败,后面的就别跑了,省时间
  • 有进有退:每个分支都要有 cleanup,别让机台处于异常状态
  • 可读性第一:Flow 是给人看的,命名要清晰,注释要到位

6. 四者的关系:一个完整的例子

为了让你更直观地理解,我拿一个实际项目来说。假设我们要测试一颗 MCU 芯片:

  1. Pattern 层:我们有 contact.pat(接触测试向量)、dc_meas.pat(DC 测量向量)、func_core.pat(核心功能向量)、scan_top.pat(扫描向量)
  2. Test Method 层:我们有 check_contact()、measure_vdd()、run_func()、run_scan()
  3. Test Suite 层:我们把它们打包成 Contact_Suite、DC_Suite、Func_Suite、Scan_Suite
  4. Test Flow 层:最终流程是:Contact → DC → Func → Scan → Cleanup

你看,每一层都只关心自己的事。Pattern 只管时序,Test Method 只管测量逻辑,Suite 只管打包,Flow 只管调度。这就是分层解耦的思想。

我的习惯:每次写新项目,我先画 Test Flow 的流程图,再拆 Suite,再写 Test Method,最后才写 Pattern。从上往下设计,从下往上实现。这样思路最清晰,返工最少。

7. 避坑指南

最后,分享几个我踩过的坑,你遇到了能少走弯路:

  • 别把 Pattern 路径写死:用变量或配置文件管理路径,否则换个目录就得改代码
  • Test Method 别写太长:超过 200 行的 Test Method,基本可以拆成两个
  • Suite 之间要独立:前一个 Suite 跑完,要恢复机台默认状态,别影响后面的测试
  • Flow 里加日志:每个 Suite 开始和结束都打印时间戳,调试时能定位问题
  • 版本控制:Pattern、Test Method、Suite、Flow 都要纳入 Git 管理,别问我为什么

嗯,这一章的内容就到这里。四层结构搞清楚了,你的测试程序就有了骨架。后面几章,我们会往这个骨架里填肉——具体怎么写 Pattern、怎么封装 Test Method、怎么设计高效的 Test Flow。