PLL回路の設計 — 位相同期ループの実装と応用

スマートフォンでWi-Fi(2.4 GHz)に接続しながらLTE(例えば1.8 GHz)で通話し、Bluetoothイヤホン(2.4 GHz帯の別チャネル)で音楽を聴く — これらすべてに必要な精密な周波数の信号を、たった1つの水晶発振器(通常26 MHzや38.4 MHz)から生み出しているのがPLL(Phase-Locked Loop, 位相同期ループ)回路です。

PLLの基本理論は前回の記事(位相同期ループの理論)で解説しました。本記事では、理論から一歩踏み込み、実際のPLL回路の実装と設計に焦点を当てます。現代の集積回路で最も広く使われているチャージポンプPLLの構成要素を一つずつ解説し、周波数シンセサイザの設計例を通じて、PLLが実際にどのように動作するかをPythonシミュレーションで確認します。

本記事の内容

  • チャージポンプPLLの全体構成
  • 位相周波数比較器(PFD)の動作原理
  • チャージポンプとループフィルタの設計
  • デジタルPLL(DPLL/ADPLL)の概要
  • 周波数シンセサイザの設計例(分数分周PLL)
  • PLLのロック時間とジッタ
  • Pythonによるチャージポンプ PLLのシミュレーション

前提知識

この記事を読む前に、以下の記事を読んでおくと理解が深まります。

チャージポンプPLLの全体構成

PLLの基本ブロック図

PLLは帰還制御系の一種であり、出力信号の位相を基準信号の位相に一致させることで周波数を制御するシステムです。基本的なブロック構成は次のとおりです。

  1. 位相周波数比較器(PFD: Phase-Frequency Detector): 基準信号と帰還信号の位相差を検出
  2. チャージポンプ(CP): 位相差に応じた電流パルスを出力
  3. ループフィルタ(LPF): 電流パルスを平滑化して制御電圧に変換
  4. VCO(Voltage-Controlled Oscillator): 制御電圧に応じた周波数の信号を出力
  5. 分周器(÷N): VCO出力周波数を $N$ 分の1に分周して帰還

このループが閉じると、VCO出力の位相が基準信号に同期(ロック)し、VCOの出力周波数は次のようになります。

$$ f_{out} = N \cdot f_{ref} $$

$N$ の値を変えるだけで、1つの基準周波数 $f_{ref}$ から任意の周波数を合成できます。これが周波数シンセサイザの基本原理です。

なぜチャージポンプか

初期のPLLでは、アナログの乗算器型位相比較器やXOR型位相比較器が使われていましたが、これらにはいくつかの問題がありました。XOR型は位相差が $\pi$ を超えるとロックを外してしまい、乗算器型は出力にVCO周波数の2倍成分が含まれ、ループフィルタの設計が難しくなります。

チャージポンプPLLは、PFD + チャージポンプの組み合わせにより、$\pm 2\pi$ の広い位相差検出範囲(周波数差の検出も可能)と、理論的にゼロの定常位相誤差を実現します。この優れた特性のため、現代の集積回路PLLのほぼすべてがチャージポンプ方式を採用しています。

PLLの全体像を把握したところで、次は各構成要素の詳細な動作を見ていきましょう。まずは位相差を検出するPFDから始めます。

位相周波数比較器(PFD)

PFDの動作原理

PFD(Phase-Frequency Detector)は、2つの入力信号の位相差と周波数差の両方を検出できる回路です。XOR型位相比較器が位相差しか検出できないのに対し、PFDは周波数が異なる場合でもどちらが速いか(遅いか)を判定できるため、初期ロックの獲得が確実です。

PFDは2つのDフリップフロップとANDゲート(リセット用)で構成されます。動作を状態遷移で説明します。

状態1(初期状態): 両方のフリップフロップの出力 UP, DOWN がともに Low

基準信号の立ち上がりエッジ到着: UP が High に遷移。VCO(分周後)のエッジが来るまでUPを維持

VCO信号の立ち上がりエッジ到着: – もし UP = High なら、UP と DOWN が両方 High になった瞬間にリセットが発動し、両方が Low に戻る – もし UP = Low なら、DOWN が High に遷移。基準信号のエッジが来るまでDOWNを維持

この動作の結果、次のような出力が得られます。

  • 基準信号が先に到着(位相進み): UP パルスが出力される。パルス幅は位相差に比例
  • VCO信号が先に到着(位相遅れ): DOWN パルスが出力される。パルス幅は位相差に比例
  • 位相差ゼロ(ロック状態): UP も DOWN も出力されない(理想的には)

PFDの伝達特性は、位相差 $\Delta\phi = \phi_{ref} – \phi_{div}$ に対して次のようになります。

$$ \Delta t_{pulse} = \frac{\Delta\phi}{2\pi} \cdot T_{ref} $$

検出範囲は $-2\pi < \Delta\phi < 2\pi$ で、この範囲では線形の特性を示します。

デッドゾーン問題

実際のPFDにはデッドゾーンと呼ばれる問題があります。位相差が非常に小さい場合、UPまたはDOWNパルスの幅がゲートの遅延時間よりも短くなり、チャージポンプが正しく応答できません。

デッドゾーンがあると、ロック状態付近で位相誤差の補正が効かなくなり、VCOが基準信号に完全に同期できず、位相ノイズやジッタが増大します。

対策として、リセットパスに意図的な遅延素子を挿入する手法が広く使われています。リセットに遅延 $\tau_d$ を加えると、位相差がゼロの場合でもUPとDOWNの両方に幅 $\tau_d$ のパルスが出力されます。チャージポンプでは同じ幅のUPとDOWNの電流が相殺し、ネットの電荷注入はゼロになります。これによりデッドゾーンが解消されます。

PFDの動作が理解できたところで、次はPFDの出力パルスを制御電圧に変換するチャージポンプとループフィルタについて見ていきましょう。

チャージポンプとループフィルタ

チャージポンプの動作

チャージポンプは、PFDのUP/DOWN信号に応じて一定の電流 $I_{CP}$ をループフィルタに注入/吸出しする回路です。

  • UP = High: 電流源が $I_{CP}$ をループフィルタに注入(制御電圧上昇 → VCO周波数上昇)
  • DOWN = High: 電流シンクが $I_{CP}$ をループフィルタから吸出し(制御電圧低下 → VCO周波数低下)
  • UP = DOWN = Low: ハイインピーダンス(電流ゼロ)

チャージポンプの出力は、位相差に比例した電荷量(電流 $\times$ パルス幅)です。1つの基準周期あたりの平均電流は次のとおりです。

$$ \bar{I}_{CP} = \frac{I_{CP}}{2\pi} \cdot \Delta\phi $$

したがって、PFDとチャージポンプを合わせた位相-電流変換利得 $K_{PD}$ は次のようになります。

$$ K_{PD} = \frac{I_{CP}}{2\pi} \quad [\text{A/rad}] $$

ループフィルタの設計

ループフィルタの役割は2つあります。(1) チャージポンプの電流パルスを平滑化してVCOの制御電圧に変換すること、(2) PLLの安定性と過渡応答を決定するループ特性を設定すること。

最も一般的なのは2次受動フィルタで、次の構成です。

  • $C_1$: 主積分コンデンサ(ループの積分動作を担う)
  • $R_1$: 安定化抵抗(ゼロを追加して位相余裕を確保)
  • $C_2$: リップル抑制コンデンサ($C_1$ に比べて小さい値)

このフィルタのインピーダンスは次のとおりです。

$$ Z(s) = \frac{1 + s R_1 C_1}{s(C_1 + C_2)(1 + s R_1 \frac{C_1 C_2}{C_1 + C_2})} $$

$C_2 \ll C_1$ の近似では、次のように簡略化されます。

$$ Z(s) \approx \frac{1 + s R_1 C_1}{s C_1 (1 + s R_1 C_2)} $$

ゼロ点は $\omega_z = 1/(R_1 C_1)$、極は $\omega_p = 1/(R_1 C_2)$ です。

PLLの開ループ伝達関数

PLL全体の開ループ伝達関数 $G(s)$ は、各要素の伝達関数の積です。

$$ G(s) = \frac{K_{PD} \cdot Z(s) \cdot K_{VCO}}{s \cdot N} $$

$K_{VCO}$ はVCO利得($\text{rad/s/V}$)、$N$ は分周比、$1/s$ はVCOの周波数→位相変換(位相は周波数の積分)を表します。

2次フィルタを代入して整理すると、$C_2$ を無視した近似では次のようになります。

$$ G(s) = \frac{I_{CP} K_{VCO}}{2\pi N} \cdot \frac{1 + s R_1 C_1}{s^2 C_1} $$

この伝達関数は $s^2$ の極(二重積分器)と1つのゼロを持つタイプII、3次PLLです。

設計パラメータの決定

PLLの設計は、所望のループ帯域幅 $\omega_c$(開ループ利得交差周波数)と位相余裕 $\phi_m$ から、フィルタ部品値を決定するプロセスです。

利得交差周波数で $|G(j\omega_c)| = 1$ とし、位相余裕 $\phi_m$ を確保するように設計します。

$C_2$ を無視した近似では、位相余裕は次の式から求まります。

$$ \phi_m = \arctan(\omega_c R_1 C_1) – 90° $$

これは $\omega_c R_1 C_1 = \tan(\phi_m + 90°)$ を意味しますが、もう少し整理しましょう。ゼロ点 $\omega_z = 1/(R_1 C_1)$ を使うと次のようになります。

$$ \phi_m = \arctan\left(\frac{\omega_c}{\omega_z}\right) – 90° $$

位相余裕 $\phi_m = 60°$ を実現するためには、ゼロ点を利得交差周波数の $1/\tan(150°)$ 倍に設定します。実用的な設計手順を示します。

ステップ1: 所望の $\omega_c$ と $\phi_m$ を決定する。$\omega_c$ は $2\pi f_{ref}/10$ 程度(基準周波数の1/10以下がリファレンススプリアス抑制のため推奨)

ステップ2: ゼロ点の位置を決定する。

$$ \omega_z = \frac{\omega_c}{\tan(\phi_m)} $$

$\phi_m = 60°$ の場合、$\omega_z = \omega_c / \tan(60°) = \omega_c / \sqrt{3}$

ステップ3: $|G(j\omega_c)| = 1$ から $C_1$ を求める。

$$ C_1 = \frac{I_{CP} K_{VCO}}{2\pi N \omega_c^2} \cdot \sqrt{1 + \left(\frac{\omega_c}{\omega_z}\right)^2} $$

ステップ4: $R_1$ を求める。

$$ R_1 = \frac{1}{\omega_z C_1} $$

ステップ5: $C_2$ を設定する。リファレンススプリアス抑制のため、高周波の極 $\omega_p = 1/(R_1 C_2)$ を $\omega_c$ の数倍以上に配置する。一般に $C_2 = C_1/10$ 程度。

ループフィルタの設計手順が整理できました。次は、従来のアナログPLLとは異なるアプローチであるデジタルPLLについて概要を見ていきましょう。

デジタルPLL(DPLL/ADPLL)

デジタルPLLの動機

現代のSoC(System-on-Chip)では、アナログ回路の面積を最小化し、できるだけデジタルで実装したいという強い動機があります。アナログのチャージポンプPLLには次のような課題があります。

  • ループフィルタのコンデンサ $C_1$ が大きく(数百pF〜数nF)、チップ面積を占有する
  • アナログ回路はプロセス微細化の恩恵を受けにくい(むしろ電源電圧低下で性能劣化)
  • VCOのチューニング範囲とリニアリティの確保が難しい

これらの課題を解決するために、PLLのデジタル化が進んでいます。

ADPLL(All-Digital PLL)の構成

ADPLLは、PLL全体をデジタル回路で実装した構成です。主な構成要素は次のとおりです。

  • TDC(Time-to-Digital Converter): アナログのPFD+CPに代わり、基準信号とVCO出力の時間差を直接デジタル値に変換
  • デジタルループフィルタ: アナログのRC回路に代わり、デジタルフィルタ(IIRまたはFIR)で制御値を計算
  • DCO(Digitally-Controlled Oscillator): アナログVCOに代わり、デジタル制御ワードで周波数を設定する発振器

ADPLLの利点は、ループフィルタが大きなコンデンサを必要とせずデジタルレジスタで実現できること、プロセス微細化により面積と消費電力が縮小すること、帯域幅やダンピングの設定をレジスタの書き換えだけで変更できる柔軟性があることです。

一方で、TDCの量子化ノイズがPLLの位相ノイズに直接寄与するため、高分解能のTDC設計が重要な課題です。

デジタルPLLの概要を理解した上で、次はPLLの最も重要な応用である周波数シンセサイザの設計について詳しく見ていきましょう。

周波数シンセサイザの設計

整数分周PLLの限界

基本的なPLLでは分周比 $N$ は整数なので、出力周波数は $f_{ref}$ の整数倍に制限されます。

$$ f_{out} = N \cdot f_{ref} $$

周波数ステップ(チャネル間隔)は $f_{ref}$ に等しくなります。例えば、携帯電話で200 kHzのチャネル間隔が必要な場合、$f_{ref} = 200 \, \text{kHz}$ に設定する必要があります。1.8 GHzの出力を得るには $N = 9{,}000$ という非常に大きな分周比が必要です。

大きな $N$ は2つの問題を引き起こします。まず、ループ帯域幅は $f_{ref}/10$ 以下に制限されるため、$f_{ref} = 200 \, \text{kHz}$ ではループ帯域幅はわずか20 kHz以下になります。狭いループ帯域幅はロック時間の長期化とVCO位相ノイズの抑制不足を意味します。次に、PFDの位相ノイズは帰還ループ内で $20\log_{10}(N)$ だけ増幅されるため、$N = 9{,}000$ では約79 dBもの増幅を受けます。

分数分周PLL(Fractional-N PLL)

分数分周PLLは、分周比を時間的に切り替えることで平均的に非整数の分周比を実現します。

基本的なアイデアはシンプルです。$N$ と $N+1$ を交互に切り替え、$N+1$ の割合を $k/M$ とすると、平均分周比は次のようになります。

$$ N_{avg} = N + \frac{k}{M} $$

例えば、$N = 90$, $k = 1$, $M = 4$ とすると、$N_{avg} = 90.25$ です。$f_{ref} = 20 \, \text{MHz}$ なら $f_{out} = 90.25 \times 20 = 1{,}805 \, \text{MHz}$ が得られ、5 MHzステップ($= f_{ref}/M$)の周波数合成が可能です。

しかし単純な分周比切り替えには問題があります。分周比が周期的に変化するため、PFDの出力にも周期的なパターンが現れ、VCOの制御電圧にスプリアス(偽信号)が発生します。これを分数スプリアスと呼びます。

シグマデルタ変調器による分数スプリアスの低減

現代の分数分周PLLでは、分周比の切り替えパターンをシグマデルタ(ΣΔ)変調器で制御します。ΣΔ変調器は量子化ノイズ(分周比の整数丸めによる誤差)を高周波側にシフトするノイズシェーピングを行います。

ΣΔ変調器の出力する分周比列は擬似ランダムであり、周期的なパターンが抑制されるため、分数スプリアスが大幅に低減されます。さらに、ノイズシェーピングにより高周波に押し上げられた量子化ノイズは、PLLのループフィルタで除去されます。

$L$ 次のΣΔ変調器を使用した場合、帯域内の量子化ノイズの位相ノイズ密度は次のように近似されます。

$$ S_{\phi,QN}(f) = \frac{(2\pi)^2}{12 f_{ref}} \cdot \left(2\sin\frac{\pi f}{f_{ref}}\right)^{2L} $$

次数 $L$ が高いほど、低周波(帯域内)でのノイズ抑制が強力になりますが、高周波でのノイズは増大し、安定性の問題も生じます。実用的には $L = 3$ または $L = 4$ が広く使われています。

周波数シンセサイザの設計原理が理解できました。次は、PLLの重要な性能指標であるロック時間とジッタについて解説します。

PLLのロック時間とジッタ

ロック時間

ロック時間は、PLLが電源投入時や周波数変更後にロック状態に到達するまでの時間です。ロック時間はPLLのループ帯域幅 $\omega_c$(自然周波数 $\omega_n$)に反比例します。

2次PLLの場合、位相がロック判定条件(例えば $\pm 0.1 \, \text{rad}$ 以内)に収まるまでの時間は、ダンピング係数 $\zeta$ に依存しますが、おおよそ次のように見積もれます。

$$ t_{lock} \approx \frac{2\pi}{\omega_n} \cdot \frac{\ln(\Delta\phi_0 / \Delta\phi_{tol})}{\zeta} $$

$\Delta\phi_0$ は初期位相差、$\Delta\phi_{tol}$ はロック判定の許容値です。ダンピング $\zeta = 0.707$(臨界制動に近い値)のとき、ループ帯域幅の逆数の数倍〜10倍程度がロック時間の目安です。

ジッタと位相ノイズ

ジッタは、出力信号のエッジ位置の時間的な揺らぎです。理想的なクロックでは立ち上がりエッジが等間隔に来ますが、実際にはランダムな時間変動(ジッタ)があります。

ジッタの主な原因は次のとおりです。

  1. VCOの位相ノイズ: VCO自体が持つ固有のノイズ。ループ帯域外ではPLLで抑制できない
  2. 基準信号の位相ノイズ: 基準水晶発振器のノイズ。ループ帯域内で $20\log_{10}(N)$ だけ増幅される
  3. チャージポンプのノイズ: 電流源のショットノイズとフリッカーノイズ
  4. ΣΔ変調器の量子化ノイズ: 分数分周PLLでの追加ノイズ源
  5. 電源ノイズ: VCOやチャージポンプの電源変動が周波数変動として現れる

RMSジッタ $\sigma_t$ は、位相ノイズ密度 $S_\phi(f)$ を積分して求められます。

$$ \sigma_t = \frac{1}{2\pi f_{out}} \sqrt{2 \int_{f_1}^{f_2} S_\phi(f) \, df} $$

積分範囲 $[f_1, f_2]$ はアプリケーションに依存します(例: 12 kHz〜20 MHz)。

PLLの性能指標についての理解が深まったところで、次はこれまでの理論をPythonシミュレーションで統合的に確認しましょう。

Pythonによるチャージポンプ PLLのシミュレーション

ループフィルタの設計計算

まず、設計仕様からループフィルタの部品値を算出します。

import numpy as np
import matplotlib.pyplot as plt

# PLL設計仕様
f_ref = 20e6         # 基準周波数 [Hz]
f_out = 1.8e9        # 出力周波数 [Hz]
N = int(f_out / f_ref)  # 分周比
I_CP = 500e-6        # チャージポンプ電流 [A]
K_VCO = 2 * np.pi * 100e6  # VCO利得 [rad/s/V]
phi_m = 60           # 位相余裕 [deg]

# ループ帯域幅(基準周波数の1/10以下)
f_c = f_ref / 10     # 2 MHz
omega_c = 2 * np.pi * f_c

# ステップ2: ゼロ点位置
phi_m_rad = np.radians(phi_m)
omega_z = omega_c / np.tan(phi_m_rad)

# ステップ3: C1の決定
T1 = 1 / omega_z
C1 = I_CP * K_VCO / (2 * np.pi * N * omega_c**2) * np.sqrt(1 + (omega_c/omega_z)**2)

# ステップ4: R1の決定
R1 = 1 / (omega_z * C1)

# ステップ5: C2の決定(C1/10)
C2 = C1 / 10

# 極周波数
omega_p = 1 / (R1 * C2)

print("=" * 50)
print("PLL設計パラメータ")
print("=" * 50)
print(f"基準周波数:   f_ref = {f_ref/1e6:.0f} MHz")
print(f"出力周波数:   f_out = {f_out/1e9:.1f} GHz")
print(f"分周比:       N = {N}")
print(f"CP電流:       I_CP = {I_CP*1e6:.0f} μA")
print(f"VCO利得:      K_VCO = {K_VCO/(2*np.pi*1e6):.0f} MHz/V")
print(f"ループBW:     f_c = {f_c/1e6:.0f} MHz")
print(f"位相余裕:     φ_m = {phi_m}°")
print("-" * 50)
print("ループフィルタ部品値")
print("-" * 50)
print(f"C1 = {C1*1e12:.1f} pF")
print(f"R1 = {R1:.0f} Ω")
print(f"C2 = {C2*1e12:.1f} pF")
print(f"ゼロ周波数: f_z = {omega_z/(2*np.pi)/1e6:.2f} MHz")
print(f"極周波数:   f_p = {omega_p/(2*np.pi)/1e6:.2f} MHz")

設計計算の結果、具体的な部品値が得られました。$C_1$ は数百pF程度、$R_1$ は数kΩ程度で、集積回路に実装可能な値です。ゼロ周波数がループ帯域幅の約 $1/\sqrt{3}$ 倍になっているのは、位相余裕60°の設計条件を反映しています。

開ループ特性のボード線図

次に、設計したPLLの開ループ伝達関数のボード線図を描いて安定性を確認します。

import numpy as np
import matplotlib.pyplot as plt

# 設計値(前のセルから)
f_ref = 20e6
N = 90
I_CP = 500e-6
K_VCO = 2 * np.pi * 100e6
f_c = f_ref / 10
omega_c = 2 * np.pi * f_c
phi_m_rad = np.radians(60)
omega_z = omega_c / np.tan(phi_m_rad)

C1 = I_CP * K_VCO / (2 * np.pi * N * omega_c**2) * np.sqrt(1 + (omega_c/omega_z)**2)
R1 = 1 / (omega_z * C1)
C2 = C1 / 10
omega_p = 1 / (R1 * C2)

# 周波数範囲
freq = np.logspace(3, 9, 2000)
s = 1j * 2 * np.pi * freq

# ループフィルタのインピーダンス
Z = (1 + s * R1 * C1) / (s * (C1 + C2) * (1 + s * R1 * C1 * C2 / (C1 + C2)))

# 開ループ伝達関数 G(s) = (I_CP/(2π)) * Z(s) * K_VCO / (s * N)
G = (I_CP / (2 * np.pi)) * Z * K_VCO / (s * N)

gain_dB = 20 * np.log10(np.abs(G))
phase_deg = np.angle(G, deg=True)

# 利得交差と位相交差の検出
idx_gc = np.argmin(np.abs(gain_dB))
f_gc = freq[idx_gc]
PM = 180 + phase_deg[idx_gc]

fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 9), sharex=True)

# ゲイン
ax1.semilogx(freq, gain_dB, 'b-', linewidth=2)
ax1.axhline(y=0, color='r', linestyle='--', alpha=0.7)
ax1.axvline(x=f_gc, color='g', linestyle=':', alpha=0.7,
            label=f'$f_c$ = {f_gc/1e6:.1f} MHz')
ax1.axvline(x=omega_z/(2*np.pi), color='orange', linestyle=':', alpha=0.7,
            label=f'$f_z$ = {omega_z/(2*np.pi)/1e6:.2f} MHz')
ax1.axvline(x=omega_p/(2*np.pi), color='purple', linestyle=':', alpha=0.7,
            label=f'$f_p$ = {omega_p/(2*np.pi)/1e6:.1f} MHz')
ax1.set_ylabel('Open-Loop Gain [dB]', fontsize=12)
ax1.set_title('PLL Open-Loop Bode Plot', fontsize=14)
ax1.legend(fontsize=10)
ax1.grid(True, which='both', alpha=0.3)

# 位相
ax2.semilogx(freq, phase_deg, 'b-', linewidth=2)
ax2.axhline(y=-180, color='r', linestyle='--', alpha=0.7)
ax2.axvline(x=f_gc, color='g', linestyle=':', alpha=0.7)

# PM表示
ax2.annotate('', xy=(f_gc, -180), xytext=(f_gc, phase_deg[idx_gc]),
            arrowprops=dict(arrowstyle='<->', color='g', lw=2))
ax2.text(f_gc * 1.5, (phase_deg[idx_gc] - 180) / 2,
         f'PM = {PM:.1f}°', fontsize=12, color='g')

ax2.set_xlabel('Frequency [Hz]', fontsize=12)
ax2.set_ylabel('Phase [deg]', fontsize=12)
ax2.grid(True, which='both', alpha=0.3)
ax2.set_ylim([-270, -90])

plt.tight_layout()
plt.savefig('pll_bode.png', dpi=150, bbox_inches='tight')
plt.show()

print(f"利得交差周波数: f_c = {f_gc/1e6:.1f} MHz")
print(f"位相余裕: PM = {PM:.1f}°")

ボード線図から、設計したPLLの安定性が確認できます。

ゲインプロットでは、低周波側で $-40 \, \text{dB/dec}$ の傾き(二重積分器のため)が見え、ゼロ周波数 $f_z$(橙点線)で $-20 \, \text{dB/dec}$ に緩和されています。このゼロ点がなければ、利得交差で位相が $-180°$ を超えてしまい不安定になります。高周波では $C_2$ による極 $f_p$(紫点線)で再び $-40 \, \text{dB/dec}$ に戻ります。

位相プロットでは、利得交差周波数での位相が約 $-120°$ で、$-180°$ からの余裕(位相余裕)が約60°あることが確認できます。これは設計目標の $\phi_m = 60°$ とほぼ一致しており、設計手順の正しさが検証されました。位相余裕60°は、過渡応答にオーバーシュートが少なく安定した動作を保証します。

チャージポンプPLLの時間領域シミュレーション

PLLの過渡応答(ロック動作)を時間領域でシミュレーションします。

import numpy as np
import matplotlib.pyplot as plt

# PLL パラメータ
f_ref = 20e6
N_initial = 85      # 初期分周比(ロック前)
N_final = 90         # 目標分周比
f_out_initial = N_initial * f_ref
f_out_target = N_final * f_ref
I_CP = 500e-6
K_VCO = 2 * np.pi * 100e6  # rad/s/V
f_vco_center = 1.8e9         # VCO中心周波数
V_ctrl_center = 0.9           # 中心制御電圧

# ループフィルタ(前述の設計値)
omega_c = 2 * np.pi * 2e6
omega_z = omega_c / np.tan(np.radians(60))
C1 = I_CP * K_VCO / (2 * np.pi * N_final * omega_c**2) * np.sqrt(1 + (omega_c/omega_z)**2)
R1 = 1 / (omega_z * C1)
C2 = C1 / 10

# シミュレーション
dt = 1e-9       # 1 ns
t_total = 10e-6  # 10 μs
n_steps = int(t_total / dt)

# 状態変数
phi_ref = 0.0       # 基準信号の位相
phi_vco = 0.0       # VCO出力の位相
phi_div = 0.0       # 分周後の位相
V_C1 = V_ctrl_center  # C1の電圧
V_C2 = 0.0          # C2の電圧(初期値0)
V_ctrl = V_ctrl_center

# 記録用
t_arr = np.zeros(n_steps)
f_out_arr = np.zeros(n_steps)
V_ctrl_arr = np.zeros(n_steps)
phase_err_arr = np.zeros(n_steps)

# 分周比を途中で変更(周波数ステップ応答)
N_switch_time = 1e-6  # 1μsで分周比を切り替え

for i in range(n_steps):
    t = i * dt
    t_arr[i] = t

    # 分周比
    N = N_initial if t < N_switch_time else N_final

    # VCO周波数
    f_vco = f_vco_center + K_VCO / (2 * np.pi) * (V_ctrl - V_ctrl_center)

    # 位相の更新
    phi_ref += 2 * np.pi * f_ref * dt
    phi_vco += 2 * np.pi * f_vco * dt
    phi_div = phi_vco / N

    # 位相誤差
    phase_err = phi_ref - phi_div

    # PFD + チャージポンプ出力(簡略化: 連続近似)
    # 位相差を[-π, π]に正規化
    phase_err_wrapped = ((phase_err + np.pi) % (2 * np.pi)) - np.pi
    I_out = I_CP / (2 * np.pi) * phase_err_wrapped

    # ループフィルタの更新(簡略化: 分離した2つのコンデンサ)
    V_C1 += I_out * dt / C1
    V_R1 = I_out * R1
    V_C2 += I_out * dt / C2

    # 制御電圧
    V_ctrl = V_C1 + V_R1

    # 記録
    f_out_arr[i] = f_vco
    V_ctrl_arr[i] = V_ctrl
    phase_err_arr[i] = phase_err_wrapped

fig, axes = plt.subplots(3, 1, figsize=(12, 10), sharex=True)

# VCO出力周波数
axes[0].plot(t_arr * 1e6, f_out_arr / 1e9, 'b-', linewidth=1)
axes[0].axhline(y=f_out_target / 1e9, color='r', linestyle='--', alpha=0.7,
                label=f'Target: {f_out_target/1e9:.2f} GHz')
axes[0].axvline(x=N_switch_time * 1e6, color='gray', linestyle=':', alpha=0.7,
                label='N switch')
axes[0].set_ylabel('$f_{out}$ [GHz]', fontsize=12)
axes[0].set_title('Charge-Pump PLL: Lock Transient', fontsize=14)
axes[0].legend(fontsize=10)
axes[0].grid(True, alpha=0.3)

# 制御電圧
axes[1].plot(t_arr * 1e6, V_ctrl_arr, 'r-', linewidth=1)
axes[1].axvline(x=N_switch_time * 1e6, color='gray', linestyle=':', alpha=0.7)
axes[1].set_ylabel('$V_{ctrl}$ [V]', fontsize=12)
axes[1].grid(True, alpha=0.3)

# 位相誤差
axes[2].plot(t_arr * 1e6, phase_err_arr, 'g-', linewidth=1)
axes[2].axhline(y=0, color='gray', linestyle=':', alpha=0.5)
axes[2].axvline(x=N_switch_time * 1e6, color='gray', linestyle=':', alpha=0.7)
axes[2].set_xlabel('Time [$\\mu$s]', fontsize=12)
axes[2].set_ylabel('Phase Error [rad]', fontsize=12)
axes[2].grid(True, alpha=0.3)

plt.tight_layout()
plt.savefig('pll_transient.png', dpi=150, bbox_inches='tight')
plt.show()

# ロック時間の推定
locked_idx = np.where(np.abs(f_out_arr[int(N_switch_time/dt):] - f_out_target) / f_out_target < 1e-4)[0]
if len(locked_idx) > 0:
    t_lock = locked_idx[0] * dt
    print(f"ロック時間(0.01%精度): {t_lock*1e6:.2f} μs")

3つのグラフから、PLLのロック動作の過渡応答が読み取れます。

上段のVCO出力周波数を見ると、$t = 1 \, \mu\text{s}$ で分周比が $N = 85$ から $N = 90$ に変更された瞬間、PLLは新しい目標周波数1.8 GHzに向かって周波数を調整し始めます。位相余裕60°の設計により、大きなオーバーシュートなく滑らかに目標に収束していることがわかります。

中段の制御電圧は、周波数変化に対応して変化しています。初期の急激な変化の後、定常値に収束しています。制御電圧の変化量は、周波数変化量をVCO利得で割った値に対応します。

下段の位相誤差は、分周比変更直後に大きな値を示しますが、PLLのループ動作によって徐々にゼロに収束しています。収束の速さはループ帯域幅に依存し、帯域幅が広いほどロック時間は短くなります。

位相ノイズの解析

最後に、PLLの位相ノイズプロファイルを解析します。

import numpy as np
import matplotlib.pyplot as plt

# PLL パラメータ
f_ref = 20e6
N = 90
I_CP = 500e-6
K_VCO = 2 * np.pi * 100e6

# ループフィルタ
omega_c = 2 * np.pi * 2e6
omega_z = omega_c / np.tan(np.radians(60))
C1 = I_CP * K_VCO / (2 * np.pi * N * omega_c**2) * np.sqrt(1 + (omega_c/omega_z)**2)
R1 = 1 / (omega_z * C1)
C2 = C1 / 10

# オフセット周波数
f_offset = np.logspace(2, 8, 1000)
s = 1j * 2 * np.pi * f_offset

# ループフィルタのインピーダンス
Z = (1 + s * R1 * C1) / (s * (C1 + C2) * (1 + s * R1 * C1 * C2 / (C1 + C2)))

# 開ループ利得
G = (I_CP / (2 * np.pi)) * Z * K_VCO / (s * N)

# 閉ループ伝達関数
H_cl = G / (1 + G)       # 基準→出力
H_err = 1 / (1 + G)      # VCOノイズ→出力

# ノイズ源のPSD(典型値、dBc/Hz)
# 基準発振器の位相ノイズ(フロア: -150 dBc/Hz)
L_ref = -150 + 0 * f_offset  # フラット近似

# VCOの位相ノイズ(1/f²特性)
f_vco_ref = 1e6  # 1 MHz offset reference
L_vco_1MHz = -120  # -120 dBc/Hz @ 1 MHz offset
L_vco = L_vco_1MHz + 20 * np.log10(f_vco_ref / f_offset)

# チャージポンプのノイズ(フラット近似)
L_cp = -225 + 0 * f_offset  # A²/Hz → 位相ノイズに変換は複雑なので近似

# 各ノイズ源の出力位相ノイズへの寄与
# 基準ノイズ: N²×|H_cl|²×L_ref
L_out_ref = 20 * np.log10(N) + 20 * np.log10(np.abs(H_cl)) + L_ref

# VCOノイズ: |H_err|²×L_vco
L_out_vco = 20 * np.log10(np.abs(H_err)) + L_vco

# 合成位相ノイズ(パワー加算)
L_out_total = 10 * np.log10(10**(L_out_ref/10) + 10**(L_out_vco/10))

fig, ax = plt.subplots(figsize=(12, 7))

ax.semilogx(f_offset, L_out_ref, 'b--', linewidth=1.5, alpha=0.7,
            label='Reference noise contribution')
ax.semilogx(f_offset, L_out_vco, 'r--', linewidth=1.5, alpha=0.7,
            label='VCO noise contribution')
ax.semilogx(f_offset, L_out_total, 'k-', linewidth=2.5,
            label='Total output phase noise')
ax.axvline(x=omega_c/(2*np.pi), color='g', linestyle=':', alpha=0.7,
           label=f'Loop BW = {omega_c/(2*np.pi)/1e6:.0f} MHz')

ax.set_xlabel('Offset Frequency [Hz]', fontsize=12)
ax.set_ylabel('Phase Noise [dBc/Hz]', fontsize=12)
ax.set_title('PLL Output Phase Noise Profile', fontsize=14)
ax.legend(fontsize=11)
ax.grid(True, which='both', alpha=0.3)
ax.set_ylim([-180, -60])

plt.tight_layout()
plt.savefig('pll_phase_noise.png', dpi=150, bbox_inches='tight')
plt.show()

位相ノイズプロファイルから、PLLの帯域幅が果たす役割が明確に読み取れます。

ループ帯域幅($f_c = 2 \, \text{MHz}$、緑点線)を境に、支配的なノイズ源が切り替わっています。帯域内($f < f_c$)では、基準信号の位相ノイズが $N^2$ 倍されてVCO出力に現れます(青破線)。このため、分周比 $N$ が大きいほど帯域内のノイズが悪化します。帯域外($f > f_c$)では、VCO固有の位相ノイズ(赤破線)が支配的になります。PLLの帰還ループはこの領域ではVCOノイズを抑制できません。

全体の位相ノイズ(黒実線)は、帯域内では基準ノイズ寄与に、帯域外ではVCOノイズ寄与にそれぞれ漸近しています。つまり、PLLの帯域幅は「基準ノイズの増幅」と「VCOノイズの抑制」のトレードオフを決定するパラメータです。帯域幅を広くすればVCOノイズ抑制は向上しますが、基準ノイズの寄与が増加し、さらにリファレンススプリアスの抑制も悪化します。

まとめ

本記事では、PLL回路の実装と設計を実践的に解説しました。

  • チャージポンプPLL: PFD + チャージポンプの組み合わせにより、広い引き込み範囲と理論的にゼロの定常位相誤差を実現。現代PLLの標準構成
  • PFD: 位相差と周波数差の両方を検出可能。デッドゾーン問題はリセット遅延で対策
  • ループフィルタ設計: 所望のループ帯域幅と位相余裕から $C_1$, $R_1$, $C_2$ を系統的に決定。ゼロ点 $\omega_z$ が位相余裕を確保するカギ
  • 分数分周PLL: ΣΔ変調器による分周比制御で高い周波数分解能を実現。量子化ノイズのシェーピングがスプリアス低減のカギ
  • ロック時間: ループ帯域幅に反比例。位相余裕60°の設計で安定かつ高速なロックが可能
  • 位相ノイズ: ループ帯域内は基準ノイズ×$N^2$、帯域外はVCO固有ノイズが支配。帯域幅はこのトレードオフを決定

PLLは「位相の帰還制御系」という普遍的な原理に基づいており、その設計手法は制御工学の理論と密接につながっています。PLLを核とした周波数シンセサイザは、無線通信、クロック生成、計測、レーダーなど、あらゆる電子システムの基盤技術です。

次のステップとして、以下の記事も参考にしてください。