第四章 鉴频鉴相器(PFD)设计

好,咱们进入PLL设计里最核心也最容易被忽视的一个模块——鉴频鉴相器。说实话,我见过不少新手一上来就盯着VCO和环路滤波器猛调,结果锁相环要么锁不住,要么锁住了但抖动大得离谱。问题出在哪?十有八九是PFD没设计好。

4.1 PFD工作原理

鉴频鉴相器,名字已经说得很清楚了——它既要鉴频,又要鉴相。说白了,就是同时比较输入参考时钟和反馈时钟的频率差和相位差。

我习惯把PFD理解成一个“三态状态机”。它有三个输出状态:UP、DOWN、以及中间态(什么都不做)。

  • UP有效:参考时钟领先反馈时钟,需要把反馈时钟“拉快”
  • DOWN有效:反馈时钟领先参考时钟,需要把反馈时钟“拉慢”
  • 两者都无效:已经锁定,不需要调整

你想想看,如果两个时钟完全同频同相,PFD的输出就是稳定的中间态。一旦有偏差,它就会输出一个宽度正比于相位差的脉冲。这个脉冲宽度,直接决定了后续电荷泵的充放电时间。

核心要点:PFD的工作频率范围可以很宽,理论上从直流到几百MHz都没问题。但实际设计中,我们更关心的是它在锁定点附近的线性度。

4.2 死区问题与消除方法

死区,这是PFD设计里最坑人的地方。我在项目中遇到过两次因为死区导致PLL锁定失败的案例,后来每次做PFD都会格外小心。

什么是死区?简单说,就是当两个时钟的相位差非常小的时候,PFD“感觉不到”这个差异,输出脉冲宽度为零。这样一来,电荷泵就不会动作,环路就失去了对微小相位误差的校正能力。

为什么会这样?原因在于数字电路的建立时间和保持时间。当UP和DOWN脉冲宽度都小于某个阈值时,后续的电荷泵开关管根本来不及完全开启,脉冲就消失了。

我曾经踩过的坑:有一次做65nm工艺的PLL,仿真时一切正常,流片回来却发现锁定时间比预期长了3倍。查了两个月,最后发现是PFD的死区宽度达到了200ps,导致锁定点附近环路增益骤降。从那以后,我每个PFD设计都会做死区宽度仿真,确保它小于50ps。

消除死区的方法,业内公认最有效的是插入延迟单元。具体做法是:

  • 在PFD的复位路径上加入一个延迟缓冲器
  • 确保UP和DOWN脉冲的最小宽度大于电荷泵开关管的开启时间
  • 延迟时间通常取200ps~500ps,具体看工艺

还有一种方法是用预充电技术,让PFD的输出节点在每次比较前先预充到一个中间电平。这样即使相位差很小,也能产生可检测的脉冲。不过这种方法功耗会大一些,我一般只在高速PLL里用。

方法 优点 缺点 适用场景
延迟单元插入 实现简单,功耗低 增加额外延迟 通用PLL
预充电技术 死区消除彻底 功耗较高 高速PLL
动态逻辑PFD 面积小,速度快 设计复杂 先进工艺

4.3 PFD的Verilog实现

嗯,这里我直接给出一段我常用的PFD代码。注意,这不是行为级模型,而是可综合的RTL代码。

module pfd (
    input  wire clk_ref,    // 参考时钟
    input  wire clk_fb,     // 反馈时钟
    input  wire rst_n,      // 异步复位,低有效
    output reg  up,         // 上升沿有效
    output reg  dn          // 下降沿有效
);

reg ref_dly, fb_dly;
wire reset;

// 延迟单元,用于消除死区
always @(posedge clk_ref or negedge rst_n) begin
    if (!rst_n) ref_dly <= 1'b0;
    else        ref_dly <= #1 1'b1;
end

always @(posedge clk_fb or negedge rst_n) begin
    if (!rst_n) fb_dly <= 1'b0;
    else        fb_dly <= #1 1'b1;
end

// 复位信号:两个延迟信号同时为高时复位
assign reset = ref_dly & fb_dly;

// 状态机:三态输出
always @(posedge clk_ref or posedge reset or negedge rst_n) begin
    if (!rst_n) up <= 1'b0;
    else if (reset) up <= 1'b0;
    else up <= 1'b1;
end

always @(posedge clk_fb or posedge reset or negedge rst_n) begin
    if (!rst_n) dn <= 1'b0;
    else if (reset) dn <= 1'b0;
    else dn <= 1'b1;
end

endmodule

个人建议:代码里的#1延迟是用于仿真验证的,综合时会被工具忽略。实际流片时,这个延迟由工艺决定。我一般会在综合后做后仿真,确认延迟是否足够消除死区。

4.4 PFD的时序约束

PFD的时序约束,很多人会忽略。其实这里门道不少。

首先,PFD的输入是异步的——参考时钟和反馈时钟之间没有固定的相位关系。所以你不能用常规的setup/hold约束来约束它们。我习惯的做法是:

  • 对参考时钟和反馈时钟分别做false_path约束
  • 对PFD内部的复位路径做max_delay约束
  • 对UP/DOWN输出到电荷泵的路径做set_output_delay

举个例子,在SDC文件里我会这样写:

# 参考时钟和反馈时钟之间是异步的
set_false_path -from [get_clocks clk_ref] -to [get_clocks clk_fb]
set_false_path -from [get_clocks clk_fb] -to [get_clocks clk_ref]

# 复位路径约束,确保延迟单元能正常工作
set_max_delay 1.0 -from [get_pins pfd_inst/ref_dly_reg/C] -to [get_pins pfd_inst/reset]

# 输出到电荷泵的路径
set_output_delay -clock clk_ref -max 0.5 [get_ports {up dn}]
set_output_delay -clock clk_ref -min 0.1 [get_ports {up dn}]

你可能会问,为什么复位路径要单独约束?因为PFD的复位信号是由两个延迟信号产生的,如果这个路径太长,会导致复位脉冲宽度不够,死区消除效果就打折扣了。

避坑指南:我曾经在一个28nm项目里,因为忘了约束PFD的复位路径,导致后仿时发现复位脉冲宽度只有80ps,而电荷泵需要至少150ps才能完全开启。结果就是PLL在锁定点附近来回振荡,始终无法稳定。加了max_delay约束后,问题立刻解决。

最后说一句,PFD的时序约束不要做得太紧。留一些余量,因为工艺角变化会影响延迟单元的延迟时间。我一般会留20%的余量,这样在SS corner下也能正常工作。

PFD设计知识体系 工作原理 死区问题 Verilog实现 三态状态机:UP/DOWN/中间态 频率比较 + 相位比较 输出脉冲宽度 ∝ 相位差 小相位差时无输出脉冲 原因:建立/保持时间限制 消除方法:延迟单元插入 可综合RTL代码 延迟单元实现 三态输出逻辑 时序约束要点 异步时钟false_path 复位路径max_delay 输出路径set_output_delay

好了,PFD的设计要点就这些。记住,死区消除是重中之重,时序约束也不能马虎。下一章咱们聊电荷泵设计,那又是另一个容易踩坑的地方。

可可精品资料库