3. 鉴频鉴相器(PFD)设计

好,咱们今天聊聊锁相环里一个关键模块——鉴频鉴相器,也就是PFD。说实话,我刚入行那会儿,觉得PFD不就是个比较器嘛,有啥难的?后来真做项目了才发现,这里面的坑一个接一个。尤其是那个死区问题,搞不好你的锁相环就锁不住,或者相位噪声差得离谱。

3.1 PFD工作原理

PFD的核心任务,就是比较参考信号和反馈信号的频率和相位差异。它输出两个信号:UP和DN。UP拉高,意味着参考信号领先,需要让VCO跑快点;DN拉高,则相反。

我习惯用三态状态机来理解PFD的工作。它有三个状态:

  • 状态0:UP=0,DN=0。这是空闲态,两个信号对齐了。
  • 状态1:UP=1,DN=0。参考信号上升沿先到,PFD输出UP脉冲。
  • 状态2:UP=0,DN=1。反馈信号上升沿先到,PFD输出DN脉冲。

你想想看,如果参考信号一直领先,PFD就会持续输出UP脉冲,直到反馈信号追上来。这个机制保证了PFD不仅能鉴相,还能鉴频。频率差很大时,PFD也能正常工作,不会像简单的鉴相器那样锁在谐波上。

关键点:PFD的鉴频能力是它最大的优势。传统的乘法器鉴相器只能鉴相,频率差大了就抓瞎。PFD则不同,它能把频率差转化为相位差,最终锁定。

3.2 死区问题与消除方法

嗯,这里要注意。死区是PFD设计里最让人头疼的问题之一。什么叫死区?说白了,就是当参考信号和反馈信号的相位差非常小的时候,PFD的UP和DN脉冲宽度太窄,窄到电荷泵来不及响应。

为什么会这样?因为PFD里的数字逻辑门有延迟。UP和DN脉冲如果太窄,可能还没传到电荷泵,就被逻辑门吃掉了。结果就是,相位差明明存在,但电荷泵没有输出电流,环路增益为零。锁相环在这个小相位差范围内就失控了,相位噪声会急剧恶化。

我在项目中遇到过这种情况。有一次做一款通信芯片,锁相环的带内相位噪声怎么也压不下去。折腾了好久,最后发现是PFD死区在作祟。相位差在几个皮秒以内时,环路根本不起作用。

怎么消除死区?业界最常用的方法就是——插入延迟。具体做法是:

  • 在PFD的复位路径上故意加一些延迟单元
  • 这样即使相位差为零,UP和DN也会同时产生一个最小宽度的脉冲
  • 这个最小脉冲宽度要足够长,确保电荷泵能完全开启和关闭

我的经验:延迟时间一般取5-10个反相器延迟,大约几十到一百皮秒。太短了消除不了死区,太长了又会引入额外的相位噪声。我曾经试过用200ps的延迟,结果锁相环的参考杂散飙得很高,得不偿失。

还有一种方法是用动态逻辑设计PFD。动态逻辑的复位速度更快,可以减小死区。但动态逻辑对工艺和温度比较敏感,设计起来要小心。

警告:不要以为加了延迟就万事大吉。延迟会引入额外的抖动,而且会增大PFD的功耗。你需要根据你的锁相环指标,仔细权衡延迟时间。我一般会在仿真时扫一下延迟参数,看看相位噪声和杂散的trade-off。

3.3 电荷泵PFD结构

PFD通常和电荷泵搭配使用,组成所谓的电荷泵PFD。结构其实不复杂:PFD输出UP和DN信号,控制电荷泵里的电流源和电流沉。

典型的电荷泵PFD结构如下:

  • PFD核心:两个D触发器加一个与门,构成三态状态机
  • 延迟单元:在复位路径上,用于消除死区
  • 电荷泵:由两个电流源(上拉和下拉)和开关组成
  • 环路滤波器:通常是一个二阶RC滤波器,把电流脉冲转换成电压

我个人的习惯是,在电荷泵里加一个单位增益缓冲器。为什么呢?因为电荷泵的开关动作会引起电压波动,这个波动会通过寄生电容耦合到PFD的输出,造成所谓的“电荷共享”问题。单位增益缓冲器可以稳定电荷泵的输出节点,减少这种耦合。

另外,电流源的设计也很关键。上拉和下拉电流必须精确匹配,否则会产生静态相位偏移。我一般会用共源共栅电流源,提高输出阻抗,减小沟道长度调制效应的影响。

参数 典型值 我的建议
电荷泵电流 50μA - 500μA 根据环路带宽和VCO增益来定
电流匹配精度 < 1% 用共源共栅结构,版图注意对称
开关速度 < 100ps 用小尺寸开关管,减小寄生
输出摆幅 0.2V - VDD-0.2V 保证电流源工作在饱和区

3.4 PFD的Verilog-A建模

做系统级仿真时,用晶体管级的PFD太慢了。我习惯用Verilog-A建一个行为级模型,跑PLL的瞬态仿真和相位噪声仿真。

下面是我常用的PFD Verilog-A模型。它包含了死区消除功能,也模拟了电荷泵的非理想特性。

// Verilog-A PFD with charge pump model
`include "disciplines.vams"
`include "constants.vams"

module pfd_cp (ref, fb, vctrl);
  input ref, fb;
  output vctrl;
  electrical ref, fb, vctrl;

  parameter real Icp = 100e-6;    // 电荷泵电流 100μA
  parameter real Vdd = 1.8;       // 电源电压
  parameter real td = 50e-12;     // 死区消除延迟 50ps
  parameter real R = 10e3;        // 环路滤波器电阻 10kΩ
  parameter real C = 100e-12;     // 环路滤波器电容 100pF

  real up, dn;
  integer state;
  real vout;

  analog begin
    @(cross(V(ref) - Vdd/2, +1)) begin
      if (state == 0) begin
        state = 1;
        up = 1;
      end else if (state == 2) begin
        state = 0;
        up = 0;
        dn = 0;
      end
    end

    @(cross(V(fb) - Vdd/2, +1)) begin
      if (state == 0) begin
        state = 2;
        dn = 1;
      end else if (state == 1) begin
        state = 0;
        up = 0;
        dn = 0;
      end
    end

    // 死区消除:强制最小脉冲宽度
    @(cross(up - 0.5, -1)) begin
      @(timer(td)) up = 0;
    end
    @(cross(dn - 0.5, -1)) begin
      @(timer(td)) dn = 0;
    end

    // 电荷泵输出电流
    if (up == 1 && dn == 0)
      vout = Icp * R + V(vctrl);  // 充电
    else if (up == 0 && dn == 1)
      vout = -Icp * R + V(vctrl); // 放电
    else
      vout = V(vctrl);            // 高阻态

    // 环路滤波器(一阶RC)
    I(vctrl) <+ ddt(C * V(vctrl)) + (V(vctrl) - vout) / R;
  end
endmodule

这个模型有几个要点:

  • cross函数检测上升沿,模拟D触发器的行为
  • timer实现延迟,模拟死区消除
  • 电荷泵输出直接驱动一个RC滤波器,简化了环路滤波器的建模

使用建议:跑PLL瞬态仿真时,我一般会把电荷泵电流设成理想值,先验证环路锁定行为。等锁定没问题了,再引入非理想因素,比如电流失配、开关延迟等。这样调试起来效率高很多。

另外,这个模型没有考虑电荷泵的噪声。如果你要做相位噪声仿真,需要在电流源上叠加一个噪声源。我通常会在Icp上加一个white_noise函数,模拟热噪声和闪烁噪声。

好了,PFD的设计要点就这些。记住,死区是头号敌人,延迟插入是常规武器,电荷泵匹配是基本功。下次咱们聊环路滤波器的设计,那个更有意思。