3、PLL配置激励编写:寄存器模型、配置序列、频率切换测试、分频比设置
好,咱们直接进入正题。PLL的配置,说白了就是通过寄存器告诉它:「嘿,给我输出这个频率!」。但实际写激励的时候,你会发现事情没那么简单。我刚开始做PLL验证那会儿,就吃过配置顺序不对的亏——PLL死活锁不住,折腾了两天才发现是复位释放的时机搞反了。
这一节,咱们就聊聊PLL配置激励到底该怎么写。我会把寄存器模型、配置序列、频率切换测试和分频比设置这几个核心点串起来讲。
3.1 寄存器模型:你的PLL遥控器
做PLL验证,第一件事就是建寄存器模型。你想想看,PLL通常有十几个寄存器:控制寄存器、分频寄存器、锁定检测寄存器……如果每次都用直接写地址的方式去操作,代码会变得又臭又长。
我个人习惯用UVM的寄存器模型(RAL)。为什么?因为它能让你用名字去访问寄存器,而不是记一堆十六进制地址。比如这样:
// 通过寄存器模型配置PLL
pll_regs.pll_ctrl_reg.write(status, 32'h0000_0001); // 使能PLL
pll_regs.pll_div_reg.write(status, 32'h0000_000A); // 设置分频比
pll_regs.pll_lock_reg.read(status, lock_value); // 读取锁定状态
嗯,这里要注意:寄存器模型里的字段定义一定要和RTL保持一致。我在项目中遇到过,有人把分频比的位宽定义错了,结果仿真跑了一整天,频率全都不对。
核心要点:寄存器模型不是摆设,它是你控制PLL的遥控器。建好了,后面所有测试用例都能复用。
3.2 配置序列:先做什么,后做什么
PLL配置是有严格顺序的。你不能上来就给分频比,得先让PLL上电、复位释放、然后才能配置。我总结了一个标准配置序列:
- 上电与复位:先拉高PLL的电源,再释放复位信号。顺序错了,PLL可能进入未知状态。
- 配置分频比:设置输入分频、反馈分频、输出分频。这一步决定了最终输出频率。
- 使能PLL:让PLL开始工作,进入锁定过程。
- 等待锁定:轮询锁定状态寄存器,或者等锁定信号拉高。
- 使能输出:锁定之后,再打开输出时钟。
写成Sequence大概是这样:
class pll_config_seq extends uvm_sequence;
// ...
task body();
// 1. 上电与复位
pll_reset_seq::start(m_sequencer);
// 2. 配置分频比
pll_regs.pll_div_reg.write(status, div_value);
// 3. 使能PLL
pll_regs.pll_ctrl_reg.set_field("pll_en", 1);
pll_regs.pll_ctrl_reg.write(status);
// 4. 等待锁定
repeat(1000) begin
pll_regs.pll_lock_reg.read(status, lock_val);
if(lock_val[0]) break;
#100ns;
end
// 5. 使能输出
pll_regs.pll_ctrl_reg.set_field("out_en", 1);
pll_regs.pll_ctrl_reg.write(status);
endtask
endclass
警告:千万不要在PLL未锁定时就使能输出。我曾经见过有人这么干,结果输出时钟频率一直在跳变,后级电路直接崩溃。
3.3 频率切换测试:动态调整的艺术
实际应用中,PLL经常需要在不同频率之间切换。比如芯片在低功耗模式下跑低频,高性能模式下跑高频。这时候,你的激励就要能动态改变分频比。
频率切换测试的核心是:先停输出,再改配置,等重新锁定,再开输出。我建议你这样写:
// 频率切换测试
task freq_switch_test(input int new_div);
// 关闭输出
pll_regs.pll_ctrl_reg.set_field("out_en", 0);
pll_regs.pll_ctrl_reg.write(status);
// 等待输出稳定(通常几个时钟周期)
repeat(10) @(posedge clk_ref);
// 修改分频比
pll_regs.pll_div_reg.write(status, new_div);
// 等待重新锁定
wait(pll_lock_signal == 1);
// 重新使能输出
pll_regs.pll_ctrl_reg.set_field("out_en", 1);
pll_regs.pll_ctrl_reg.write(status);
endtask
你可能会问:为什么要先关输出?因为PLL在重新锁定的过程中,输出频率是不确定的。如果后级电路对时钟边沿敏感,可能会采到错误数据。
3.4 分频比设置:算清楚再写
分频比设置是PLL配置中最容易出错的地方。PLL的输出频率公式是:
Fout = Fin × (M / N) / K
其中M是反馈分频比,N是输入分频比,K是输出分频比。举个例子:
| 参数 | 值 | 说明 |
|---|---|---|
| Fin | 50 MHz | 输入参考时钟 |
| N | 2 | 输入分频 |
| M | 40 | 反馈分频 |
| K | 4 | 输出分频 |
| Fout | 250 MHz | 最终输出 |
写激励的时候,我建议你做一个函数,自动计算分频比:
function automatic int calc_div_value(int fin_mhz, int fout_mhz);
// 简化版:假设N=1, K=1
return fout_mhz / fin_mhz;
endfunction
小技巧:分频比寄存器通常有范围限制。比如M不能超过255,N不能超过7。写激励前,先查清楚PLL的规格书,别超限。
3.5 知识体系总览
下面这张图,是我画的PLL配置激励的核心逻辑。你可以把它当作写代码时的路线图:
嗯,这张图把咱们刚才讲的内容串起来了。你写激励的时候,就照着这个流程走,基本不会出大问题。
最后说一句:PLL配置激励写得好不好,直接决定了你的验证效率。我建议你花点时间把寄存器模型和配置序列封装好,后面所有测试用例都能复用。别像我刚开始那样,每个测试都重新写一遍配置代码——累死不说,还容易出错。