3、向量格式详解(WGL):从结构到优化

WGL,全称是 Waveform Generation Language。说白了,它就是 ATE 测试机和 EDA 仿真工具之间沟通的「通用语言」。你仿真出来的波形,最终要转成 WGL 才能下到测试机上去跑。

我刚开始接触 WGL 时,觉得它不就是个波形描述文件嘛,有啥好学的?直到有一次,一个 128 管脚的芯片,我生成的 WGL 文件有 2GB 大,测试机加载了整整 40 分钟。嗯,从那以后,我再也不敢小看这个「格式」了。

WGL 向量格式 文件结构 Signal Groups Timing Pattern Burst 时序定义 Waveform Edge Period 优化技巧 减少向量深度 合并 Pattern

3.1 WGL 文件结构:三大核心模块

一个标准的 WGL 文件,由三个部分组成。我个人习惯把它们叫做「骨架、血肉、灵魂」。

3.1.1 Signal Groups(信号分组)

这部分定义了你芯片的所有管脚。但注意,不是简单罗列,而是分组。

核心要点: 把功能相关的信号放在一个组里。比如所有数据总线叫 DATA_BUS,所有地址线叫 ADDR_BUS。这样后面写 Pattern 时,直接引用组名就行,不用一个个敲管脚名。
// 一个典型的 Signal Groups 定义
SignalGroups
  Group DATA_BUS = { D0, D1, D2, D3, D4, D5, D6, D7 };
  Group ADDR_BUS = { A0, A1, A2, A3, A4, A5 };
  Group CTRL_SIG = { CS_N, WE_N, OE_N };
End SignalGroups;

我在项目中遇到过,有人把 200 个管脚全部写在一个组里。结果呢?调试时找个信号要找半天。你想想看,分组就像整理工具箱,螺丝刀和扳手分开放,干活才快。

3.1.2 Timing(时序定义)

这部分我单独拿出来讲,因为它太重要了。WGL 里的时序,说白了就是告诉测试机:什么时候该给信号,什么时候该采样。

3.1.3 Pattern Burst(向量序列)

这是真正的测试向量本体。它描述了每个周期里,每个管脚应该是什么状态。

PatternBurst "func_test"
  Pattern "pattern_001";
  Pattern "pattern_002";
  Pattern "pattern_003";
End PatternBurst;
个人经验: Pattern Burst 的名字最好有意义。别用 test1、test2 这种。我习惯用 func_scan、func_bist、func_speed 这种命名,一看就知道是干什么的。

3.2 时序定义详解:Waveform、Edge、Period

这部分是 WGL 的核心,也是很多人容易搞混的地方。我尽量用大白话讲清楚。

3.2.1 Period(周期)

Period 就是测试机的一个时钟周期。比如你芯片跑 100MHz,那 Period 就是 10ns。

Timing "timing_100mhz"
  Period 10ns;
  // ... 其他定义
End Timing;
注意: Period 一旦定义,整个 Pattern 里所有向量都按这个周期来。如果你需要变周期,必须定义多个 Timing 块。

3.2.2 Waveform(波形)

Waveform 定义了每个信号在一个周期内的行为。它由多个 Edge 组成。

Waveform "write_cycle"
  // 格式:信号名 波形类型 @ 时间点
  CLK:    PULSE  @ 0ns, 5ns;
  DATA:   NRZ    @ 2ns;
  CS_N:   NRZ    @ 1ns;
  WE_N:   NRZ    @ 1ns;
End Waveform;

这里有个关键点:波形类型。常用的有:

  • NRZ(Non-Return-to-Zero):电平信号,保持到周期结束。适合数据信号。
  • PULSE:脉冲信号,在指定时间点跳变。适合时钟。
  • RZ(Return-to-Zero):归零信号,跳变后回到初始状态。

为什么会这样设计?其实很简单。NRZ 适合数据,因为数据需要保持稳定让测试机采样。PULSE 适合时钟,因为时钟就是靠跳变沿触发的。

3.2.3 Edge(边沿)

Edge 是 Waveform 里的最小单位。它定义了信号在什么时间点发生什么变化。

// 一个完整的时序定义示例
Timing "timing_slow"
  Period 20ns;
  
  Waveform "read_cycle"
    CLK:    PULSE  @ 0ns, 10ns;
    DATA:   NRZ    @ 5ns;
    ADDR:   NRZ    @ 2ns;
    CS_N:   NRZ    @ 1ns;
    OE_N:   NRZ    @ 1ns;
  End Waveform;
  
  Waveform "write_cycle"
    CLK:    PULSE  @ 0ns, 10ns;
    DATA:   NRZ    @ 3ns;
    ADDR:   NRZ    @ 2ns;
    CS_N:   NRZ    @ 1ns;
    WE_N:   NRZ    @ 1ns;
  End Waveform;
End Timing;
避坑指南: 我曾经犯过一个错误——把采样沿和驱动沿搞反了。结果测试机采到的数据全是错的。记住:驱动沿是测试机给芯片发信号的时间点,采样沿是测试机从芯片读数据的时间点。这两个时间点之间要有足够的建立时间。

3.3 WGL 向量优化技巧

这部分是实战干货。我做了这么多年量产,总结出两个最有效的优化方向。

3.3.1 减少向量深度

向量深度,说白了就是 Pattern 文件里有多少个向量周期。深度越大,测试时间越长,测试机内存占用也越大。

怎么减少?我常用的方法:

  1. 去除冗余等待周期:很多仿真波形里会有 NOP(空操作)周期。这些在量产测试时完全可以去掉。
  2. 合并连续相同状态:如果连续 10 个周期所有信号状态都一样,用 Repeat 指令代替。
  3. 使用 Loop 循环:重复的测试序列用循环实现,而不是展开。
// 优化前:100个重复周期
V { DATA=XXXX; CLK=P; }
V { DATA=XXXX; CLK=P; }
// ... 重复98次

// 优化后:用 Repeat
Repeat 100 {
  V { DATA=XXXX; CLK=P; }
}
我的习惯: 每次生成 WGL 后,我都会用脚本统计一下向量深度。如果超过 10 万周期,我就会看看能不能优化。量产测试中,每减少 1% 的向量深度,就是实打实的成本节省。

3.3.2 合并 Pattern

合并 Pattern 的核心思想是:把多个小 Pattern 合并成一个大 Pattern,减少 Pattern 切换的开销。

测试机在执行 Pattern 时,切换 Pattern 需要时间(通常几十微秒)。如果你有 1000 个 Pattern,每个切换 50μs,光切换时间就 50ms。对于量产测试来说,这个时间不可忽视。

// 不推荐:频繁切换 Pattern
PatternBurst "bad_example"
  Pattern "init";
  Pattern "write_001";
  Pattern "read_001";
  Pattern "write_002";
  Pattern "read_002";
  // ... 更多
End PatternBurst;

// 推荐:合并成几个大 Pattern
PatternBurst "good_example"
  Pattern "init_and_write_all";
  Pattern "read_all";
End PatternBurst;

你想想看,测试机每秒钟要测几十颗芯片,省下来的时间就是产能。我见过一个项目,通过合并 Pattern,测试时间从 3.2 秒降到了 2.8 秒。别小看这 0.4 秒,一个月下来能多测好几万颗芯片。

注意: 合并 Pattern 时,要确保不同 Pattern 的时序定义一致。如果时序不同,不能简单合并。我建议先统一时序,再合并。

好了,关于 WGL 格式的核心内容就这些。记住三个关键词:结构清晰、时序准确、优化到位。做到这三点,你的 WGL 向量就能在量产测试中跑得又快又稳。


公众号:蓝海资料掘金营,微信deep3321