2、PLL测试环境搭建:验证平台结构、时钟生成模块、复位逻辑、配置接口(SPI/I2C)
好,咱们直接进入正题。PLL的测试环境搭建,说白了就是给你的DUT(待测设计)搭个台子,让它能跑起来、能被控制、能被观察。我做了这么多年验证,发现很多新手一上来就急着写测试用例,结果环境没搭稳,后面debug debug到怀疑人生。所以这一章,咱们把地基打牢。
2.1 验证平台整体结构
一个典型的PLL验证平台,我习惯把它分成这么几块:
- 时钟生成模块:给PLL提供参考时钟,以及给整个平台提供工作时钟。
- 复位逻辑:确保DUT和整个平台在正确的时间点被复位。
- 配置接口:通过SPI或I2C协议,往PLL的寄存器里写配置字。
- DUT本身:就是咱们要测的PLL模块。
- 监测与比对模块:抓输出时钟,算频率、测抖动、看锁定时间。
你想想看,这五块缺一不可。少了时钟,PLL没法工作;少了复位,初始状态不确定;少了配置接口,你连让它输出多少MHz都控制不了。
核心思路:验证平台要能模拟真实芯片的工作环境,同时又要提供比真实环境更强的可控性和可观测性。
我个人习惯用SystemVerilog搭建这个平台,配合UVM的agent和monitor结构。但今天咱们先不扯UVM那套复杂的,先把最核心的模块讲清楚。
2.2 时钟生成模块
时钟生成模块,我通常叫它 clk_gen。它的任务有两个:一是给PLL提供参考时钟(ref_clk),二是给验证平台的其他组件提供工作时钟(sys_clk)。
这里有个坑,我刚开始做的时候踩过。参考时钟的频率和精度必须跟芯片的规格书一致。比如规格书上说参考时钟是25MHz,精度±100ppm,那你就不能随便写个25.001MHz出来。为什么?因为PLL的输出频率 = 参考时钟 × (N/M),参考时钟偏一点,输出就偏一大截。
小技巧:我习惯在时钟生成模块里加一个enable控制。这样可以在仿真过程中随时关掉时钟,模拟时钟失效的场景,测试PLL的hold行为。
下面是一个简单的时钟生成代码示例:
// 时钟生成模块
module clk_gen #(
parameter REF_CLK_PERIOD = 40.0, // 25MHz -> 40ns
parameter SYS_CLK_PERIOD = 10.0 // 100MHz -> 10ns
)(
output reg ref_clk,
output reg sys_clk,
input clk_enable
);
initial begin
ref_clk = 0;
sys_clk = 0;
end
always #(REF_CLK_PERIOD/2.0) begin
if (clk_enable) ref_clk = ~ref_clk;
end
always #(SYS_CLK_PERIOD/2.0) begin
if (clk_enable) sys_clk = ~sys_clk;
end
endmodule
嗯,这里要注意,#(REF_CLK_PERIOD/2.0) 这种写法在有些仿真器里可能不支持实数延迟。我建议用 #(REF_CLK_PERIOD*500) 然后配合时间单位来搞定,或者直接用 #(20ns) 这种硬编码。不过为了可复用性,还是用参数化好一些。
2.3 复位逻辑
复位逻辑,说白了就是让DUT和平台在正确的时间点进入已知状态。PLL的复位通常有两种:异步复位和同步释放。
我个人习惯用异步复位、同步释放的方式。为什么?因为PLL对复位时序比较敏感,异步复位能保证复位信号第一时间生效,同步释放能避免复位释放时出现亚稳态。
警告:千万不要在PLL还没锁定时就给它发配置命令。我曾经见过一个项目,复位释放后立马写寄存器,结果PLL还在自校准过程中,配置根本没写进去。后来查了三天才找到原因。
复位逻辑的代码示例:
// 复位生成模块 - 异步复位,同步释放
module rst_gen #(
parameter RST_CYCLES = 10 // 复位保持10个时钟周期
)(
input clk,
input ext_rst_n, // 外部复位输入
output reg rst_n // 同步复位输出
);
reg [1:0] rst_sync;
always @(posedge clk or negedge ext_rst_n) begin
if (!ext_rst_n) begin
rst_sync <= 2'b00;
rst_n <= 1'b0;
end else begin
rst_sync <= {rst_sync[0], 1'b1};
rst_n <= rst_sync[1];
end
end
endmodule
这里用了两级同步器,把外部异步复位同步到时钟域里。释放的时候,rst_n会在第二个时钟沿之后才变高,这样就避免了亚稳态。
2.4 配置接口:SPI与I2C
配置接口是PLL验证中最关键的一环。大部分PLL芯片都支持SPI或I2C协议来配置内部寄存器。咱们得在验证平台里实现一个对应的master模型。
2.4.1 SPI接口
SPI接口,我习惯用四线制:SCLK、MOSI、MISO、CS_N。PLL通常作为slave,咱们的验证平台作为master。
这里有个经验之谈:SPI的时钟频率不能太高。我见过有人把SPI时钟设到50MHz,结果PLL内部的数字逻辑根本跟不上,数据全错。一般SPI时钟设在10MHz以内比较安全。
SPI配置的时序图大概是这样:
// SPI写操作时序(CPOL=0, CPHA=0)
// SCLK: __|‾‾|__|‾‾|__|‾‾|__|‾‾|__
// CS_N: ‾‾‾‾‾|________________|‾‾‾
// MOSI: XXXX|A7|A6|...|A0|D7|...|D0|XXXX
// MISO: XXXX|Z |Z |...|Z |Z |...|Z |XXXX
我个人习惯把SPI的驱动封装成一个task,方便在测试用例里调用:
task spi_write_byte(
input [7:0] addr,
input [7:0] data
);
begin
cs_n = 0;
// 发送地址字节(最高位为写标志)
for (int i=7; i>=0; i--) begin
mosi = addr[i];
@(posedge sclk);
end
// 发送数据字节
for (int i=7; i>=0; i--) begin
mosi = data[i];
@(posedge sclk);
end
cs_n = 1;
@(posedge sclk);
end
endtask
2.4.2 I2C接口
I2C接口比SPI复杂一些,因为它需要双向数据线(SDA)和时钟线(SCL),还有起始条件、停止条件、应答位这些概念。
我记得第一次写I2C master模型时,被应答位的时序搞得很头疼。后来总结了一个口诀:「发完8位等应答,SCL高时读SDA」。你想想看,I2C的SDA在SCL高电平时被采样,所以应答位必须在SCL高电平时读取。
I2C写操作的流程:
- 发送起始条件(SCL高时SDA从高变低)
- 发送设备地址 + 写位(7位地址 + 1位写标志)
- 等待从设备应答(SCL高时读SDA是否为低)
- 发送寄存器地址(8位)
- 等待应答
- 发送数据(8位)
- 等待应答
- 发送停止条件(SCL高时SDA从低变高)
关键点:I2C的地址是7位的,但很多PLL芯片的数据手册里写的是8位地址(左移了一位)。比如芯片地址是0x50(7位),写操作时发送0xA0(8位)。这个坑我踩过不止一次,每次都要翻数据手册确认。
下面是一个简化的I2C写操作代码:
task i2c_write_byte(
input [6:0] dev_addr,
input [7:0] reg_addr,
input [7:0] data
);
begin
// 起始条件
sda = 1; scl = 1; #1;
sda = 0; #1;
scl = 0; #1;
// 发送设备地址 + 写位
send_byte({dev_addr, 1'b0});
check_ack();
// 发送寄存器地址
send_byte(reg_addr);
check_ack();
// 发送数据
send_byte(data);
check_ack();
// 停止条件
sda = 0; scl = 1; #1;
sda = 1; #1;
end
endtask
2.5 避坑指南与个人经验
做PLL验证环境搭建,有几个坑我几乎每次都会遇到:
- 时钟精度问题:仿真用的时钟是理想时钟,但实际芯片里的时钟有抖动。我建议在时钟生成模块里加一个jitter注入功能,模拟真实场景。
- 复位时序问题:PLL的复位释放后,需要等待一段时间才能开始配置。这个等待时间在数据手册里叫「power-up time」或「lock time」。我习惯在复位释放后加一个固定的延迟,比如100us,再开始发配置命令。
- 配置顺序问题:有些PLL的寄存器有依赖关系,必须先配A再配B。我建议把配置过程封装成一个函数,按顺序调用,不要分散在测试用例里。
个人习惯:我会在验证平台里加一个「配置检查」功能。每次写完寄存器后,再读回来比对一下。这样能及时发现SPI/I2C通信错误,而不是等到最后看输出频率不对了才回头查配置。
好了,这一章的内容就到这里。环境搭好了,下一章咱们就可以开始写具体的测试用例了。记住,环境搭得越扎实,后面debug越省心。
| 模块 | 关键点 | 常见问题 |
|---|---|---|
| 时钟生成 | 频率精度、enable控制 | 时钟频率与规格不符 |
| 复位逻辑 | 异步复位同步释放 | 复位释放后立即配置 |
| SPI接口 | 时钟频率、CPOL/CPHA | 时钟频率过高导致数据错误 |
| I2C接口 | 地址左移、应答位时序 | 7位地址与8位地址混淆 |
可可精品资料库