STIL语言入门:STIL语言结构、时序定义、Pattern定义、宏定义
各位同学,今天我们来聊聊STIL语言。说实话,我刚接触STIL那会儿,觉得它就是个“怪胎”——既不像C语言那样直观,又不像Verilog那样有硬件对应关系。但干久了才发现,STIL其实是ATE测试的“通用语言”,你想想看,没有它,不同厂商的测试机怎么交换测试向量?
我个人习惯把STIL比作“测试界的PDF”。它不关心你芯片内部怎么设计,只关心你给什么激励、期望什么响应。嗯,这个比喻可能不太准确,但意思到了就行。
STIL语言结构:骨架要搭好
STIL文件的结构其实很清晰,就像盖房子,先搭骨架再填砖。一个完整的STIL文件,通常包含以下几个部分:
- Header:文件头,声明版本、日期、作者等信息
- Signals:信号声明,定义所有用到的端口
- Timing:时序定义,描述波形和时序关系
- PatternBurst:Pattern组,组织多个Pattern
- Pattern:具体的测试向量
- MacroDefs:宏定义,复用常用操作
我给大家画个结构图,这样更直观:
看到没?这就是STIL的骨架。每个部分都有自己明确的职责,不能乱。我记得刚带新人时,有个小伙子把Timing写到了Signals前面,结果解析器直接报错。嗯,顺序很重要。
时序定义:测试的“节拍器”
时序定义是STIL里最核心的部分之一。说白了,就是告诉测试机:什么时候拉高、什么时候拉低、什么时候采样。
一个典型的时序定义长这样:
Timing {
WaveformTable "default" {
Period '100ns';
Waveforms {
CLK { 0 { '0ns' D; } 1 { '50ns' D; } }
DATA { 0 { '0ns' D; } 1 { '0ns' D; } Z { '0ns' Z; } }
OEN { 0 { '0ns' D; } 1 { '0ns' D; } }
}
}
}
这里有几个关键点:
- Period:定义时钟周期,比如100ns
- Waveforms:定义每个信号的波形形状
- D:表示驱动(Drive),测试机主动输出
- Z:表示高阻,常用于三态总线
为什么会这样设计?因为ATE测试机执行Pattern时,是按周期来跑的。每个周期里,所有信号的行为都严格按照WaveformTable来执行。你想想看,如果没有这个统一的“节拍器”,不同Pattern之间的时序怎么对齐?
Pattern定义:测试向量的“剧本”
Pattern就是测试向量的具体内容。它告诉测试机:每个周期,每个信号应该是什么状态。
来看个简单的例子:
Pattern "write_test" {
V { '100ns' 'default'; }
W {
// 写操作
CLK = 0; DATA = 1; OEN = 1;
CLK = 1; DATA = 1; OEN = 1;
CLK = 0; DATA = 0; OEN = 1;
CLK = 1; DATA = 0; OEN = 1;
}
}
这里:
- V:设置电压和时序
- W:具体的波形向量
- 每一行代表一个周期
Pattern定义里还有个重要的概念叫“Compare”。测试机不光要驱动信号,还要在特定时刻采样DUT的输出,和期望值做比较。比如:
Pattern "read_test" {
V { '100ns' 'default'; }
W {
CLK = 0; DATA = Z; OEN = 0; // 让DUT输出
CLK = 1; DATA = Z; OEN = 0;
CLK = 0; DATA = C; OEN = 0; // C表示Compare,采样数据
}
}
看到那个C了吗?它告诉测试机:在这个周期,我要采样DATA引脚,和期望值做比较。如果对不上,就报Fail。
宏定义:让Pattern“瘦身”
宏定义是STIL里最实用的功能之一。说白了,就是把重复出现的操作封装起来,用的时候直接调用。
我给大家看个例子:
MacroDefs {
"write_cycle" {
W {
CLK = 0; DATA = 1; OEN = 1;
CLK = 1; DATA = 1; OEN = 1;
}
}
"read_cycle" {
W {
CLK = 0; DATA = Z; OEN = 0;
CLK = 1; DATA = C; OEN = 0;
}
}
}
Pattern "burst_test" {
V { '100ns' 'default'; }
Call "write_cycle" { DATA = 1; }
Call "write_cycle" { DATA = 0; }
Call "read_cycle" {};
Call "read_cycle" {};
}
你看,原来要写8行,现在4行就搞定了。而且宏定义可以带参数,比如上面的DATA = 1就是传参。
宏定义还有一个妙用——做“测试序列”。比如你要做一组连续的读写操作,用宏定义可以轻松实现:
MacroDefs {
"write_read_check" {
W {
// 写数据
CLK = 0; DATA = 1; OEN = 1;
CLK = 1; DATA = 1; OEN = 1;
// 读数据
CLK = 0; DATA = Z; OEN = 0;
CLK = 1; DATA = C; OEN = 0;
// 检查结果
CLK = 0; DATA = Z; OEN = 0;
CLK = 1; DATA = C; OEN = 0;
}
}
}
这样,一个“写-读-检查”的完整操作,只需要一行Call "write_read_check" {}就搞定了。
好了,STIL的四个核心部分就讲到这里。说实话,STIL入门不难,难的是写出高效、可维护的Pattern。我建议大家多动手写,多看看别人的Pattern是怎么组织的。嗯,实践出真知。
记住,STIL不是编程语言,它是描述语言。它的目标不是“计算”,而是“描述”——描述测试机该怎么动、DUT该怎么响应。想明白这一点,你就入门了。