第四章:时钟与复位——芯片的“心跳”与“重启键”
时钟和复位,是数字芯片里最基础、也最容易出问题的两个信号。我常说,搞不定时钟和复位,后面的验证工作就像在沙滩上盖楼。今天咱们就聊聊时钟怎么生成、复位怎么处理,以及分频倍频那些事儿。
核心要点:时钟是时序逻辑的基准,复位是初始状态的保证。两者处理不当,芯片功能必然紊乱。
4.1 时钟生成方法
时钟信号怎么来?说白了,要么从外部晶振直接进来,要么通过PLL(锁相环)在芯片内部生成。我个人习惯在验证环境里用两种方式:
- 直接生成法:用initial块或always块产生周期信号。适合简单测试。
- 系统时钟模型:模拟真实PLL行为,包含锁定时间、抖动等。适合系统级验证。
先看一个最基础的时钟生成代码:
// 基础时钟生成 - 50MHz,周期20ns
reg clk;
initial begin
clk = 0;
forever #10 clk = ~clk; // 每10ns翻转一次
end
嗯,这里要注意:forever必须放在initial块里,否则仿真器会报错。我在项目中遇到过有人把forever直接写在always块外面,结果仿真直接卡死。
小技巧:如果需要多个不同频率的时钟,可以用一个基础时钟通过计数器分频得到。这样能保证所有时钟同源,避免跨时钟域问题。
4.2 复位信号的同步与异步处理
复位信号,你想想看,它决定了芯片上电后所有寄存器从什么状态开始跑。处理不好,芯片可能永远进不了正常工作状态。
复位分两种:
| 类型 | 特点 | 适用场景 |
|---|---|---|
| 异步复位 | 复位信号不依赖时钟,立即生效 | 上电复位、紧急复位 |
| 同步复位 | 复位信号需在时钟沿采样后才生效 | 功能复位、软件复位 |
我曾经在一个项目中,设计人员用了异步复位,但没做同步释放处理。结果复位撤销时刚好碰上时钟沿,导致部分寄存器复位失败,芯片功能时好时坏。排查了整整两天才找到原因。
正确的做法是——异步复位,同步释放:
// 异步复位同步释放电路
reg rst_n_sync1, rst_n_sync2;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
rst_n_sync1 <= 1'b0;
rst_n_sync2 <= 1'b0;
end else begin
rst_n_sync1 <= 1'b1;
rst_n_sync2 <= rst_n_sync1;
end
end
// 使用同步后的复位
always @(posedge clk or negedge rst_n_sync2) begin
if (!rst_n_sync2)
data_out <= 'b0;
else
data_out <= data_in;
end
警告:千万不要在RTL里直接用异步复位驱动多个触发器!每个触发器到复位源的距离不同,会导致复位撤销时间不一致,引发亚稳态。
4.3 时钟分频与倍频
分频和倍频,说白了就是改变时钟频率。分频用计数器就能搞定,倍频通常需要PLL。
4.3.1 时钟分频
偶数分频最简单,计数器计到一半翻转就行。奇数分频稍微麻烦点,需要同时用上升沿和下降沿触发。
// 3分频电路(占空比50%)
reg clk_div3;
reg [1:0] cnt;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) cnt <= 0;
else if (cnt == 2) cnt <= 0;
else cnt <= cnt + 1;
end
always @(posedge clk or negedge rst_n) begin
if (!rst_n) clk_div3 <= 0;
else if (cnt == 0) clk_div3 <= ~clk_div3;
end
为什么这样写?因为3分频需要3个时钟周期完成一个完整周期。计数器从0到2,在0时刻翻转一次,就能得到50%占空比。我刚开始做分频时,总想着用if (cnt == 1)翻转,结果占空比怎么调都不对。
4.3.2 时钟倍频
倍频在FPGA里通常调用PLL IP核,在ASIC里用模拟PLL。验证时我们一般用行为模型模拟:
// PLL行为模型 - 2倍频
reg pll_clk;
real period = 10.0; // 输入时钟周期10ns
always begin
#(period/4) pll_clk = 1;
#(period/4) pll_clk = 0;
end
这个模型很简单,但忽略了PLL的锁定时间。实际项目中,PLL从启动到稳定输出需要几百微秒。我曾经在验证时没等PLL锁定就直接用时钟,结果前1000个时钟周期全是乱的,仿真波形惨不忍睹。
避坑指南:验证环境中一定要模拟PLL的锁定过程。可以用一个pll_locked信号,在锁定时间到达后才拉高,所有逻辑只有在pll_locked为高时才工作。
4.4 本章知识体系
下面这张图总结了时钟与复位的核心知识点,我建议你保存下来,写代码前先过一遍:
这张图把时钟生成、复位处理、分频倍频三个模块串起来了。你写代码时,先想清楚用的是哪种时钟源、复位怎么同步、分频系数是多少,基本就不会出大问题。
个人经验:我习惯在验证环境里加一个时钟和复位的checker,自动检查时钟频率是否稳定、复位释放是否同步。这样跑回归测试时,一旦时钟或复位出问题,第一时间就能发现,不用等到波形分析才发现。
好了,时钟和复位就聊到这儿。记住一句话:时钟是芯片的心跳,复位是芯片的重启键。这两样搞定了,你的验证工作就成功了一半。