1. 電圧 → 周波数変換(VCO部)
VCOは入力電圧に応じて発振周波数を変化させる回路。
3段または5段のリング発振器で構成される。
f_osc = f_0 + K_vco * V_in
- f_osc : 出力発振周波数 [Hz]
- f_0 : バイアス時の基準周波数 [Hz]
- K_vco : 電圧-周波数変換感度 [Hz/V]
- V_in : 入力アナログ電圧 [V]
入力電圧が高くなるとMOSインバータの駆動電流が増加し、
遅延時間 t_d が短くなるため、発振周期 T_osc = 2π / ω_osc が短縮し
f_osc が増加する。
2. 周波数 → デジタル変換(カウンタ部)
一定の観測時間 T_s の間に、VCO出力のパルス数をカウントする。
N_count = f_osc * T_s
出力デジタルコード D_out はこのカウント数に等しい。
D_out = N_count
すなわち、入力電圧に比例して出力コードが増加する。
3. 位相積分によるノイズシェーピング
VCO出力の位相 θ(t) は周波数を時間積分したものとして表される。
θ(t) = 2π * ∫[0→t] f_osc(τ) dτ
位相の離散化(カウント)によって量子化誤差が発生するが、
この誤差は積分過程により高周波側に押しやられる。
結果としてΔΣ(デルタシグマ)変調に類似したノイズシェーピング特性を得る。
4. 回路動作の全体式
VCO-ADCの信号変換を一連の関係式で表すと次のようになる。
1. V_in → f_osc : f_osc = f_0 + K_vco * V_in
2. f_osc → N_count : N_count = f_osc * T_s
3. Digital Output : D_out = N_count = (f_0 + K_vco * V_in) * T_s
よって、出力コードは線形式で表せる。
D_out = f_0 * T_s + K_vco * T_s * V_in
5. ノイズ・分解能・非線形性
(1) 位相ジッタ(周期ゆらぎ)
σ_jitter ≈ (ΔT / T_osc) * f_osc
位相ジッタ σ_jitter がSNRを制限する。
(2) 線形誤差(非線形VCO特性)
Error_lin = D_out_actual - (f_0 * T_s + K_vco * T_s * V_in)
補償手法としては、
- デジタルキャリブレーション
- ルックアップテーブル補正
などが用いられる。
6. 設計パラメータの関係式
| 項目 | 式 | 説明 |
|---|---|---|
| VCO感度 | K_vco = Δf / ΔV | 周波数変化率 |
| サンプリング期間 | T_s = 1 / f_sample | 測定ウィンドウ |
| 分解能 | LSB = 1 / (K_vco * T_s) | 1ビットあたりの電圧幅 |
| SNR(ジッタ支配) | SNR = 20 * log10(1 / (2π * f_in * σ_jitter)) | 周波数ゆらぎの影響 |
7. 動作フロー(プレーンテキスト図)
アナログ入力電圧 V_in
│
▼
[Voltage-Controlled Oscillator]
│ f_osc = f_0 + K_vco * V_in
▼
[Counter / Integrator]
│ D_out = f_osc * T_s
▼
デジタル出力コード D_out
8. まとめ(要点)
-
アナログ電圧を時間領域の発振周波数に変換し、
その周期情報をカウントしてデジタル化する方式。 -
オペアンプ不要、全デジタル構成に近い。
-
微細化CMOS、低電圧環境に適する。
-
ΔΣ型に似た積分的ノイズ整形特性を持つ。
-
ジッタ・非線形性・温度ドリフトへの補償が設計上の要点。
-
Design of VCO‑based ADCs — V. Unnikrishnan (2016) (diva-portal.org)
-
Highly Digital Second‑Order VCO ADC — A. Jayaraj 他 (Lab Groups)
-
Ring‑VCO‑based ADC design for low‑energy smart … — P. Srikram (2023) (北大学術資源リポジトリ)
-
Analysis and Design of Voltage‑Controlled Oscillator‑Based ADCs — S. Naraghi (2009) (Deep Blue)
-
VCO‑based ADCs Design Techniques for Communication … — VMM Huamán 他 (Core)
import numpy as np
import matplotlib.pyplot as plt
# ==============================
# 0) Global Parameters / 変数一元管理
# ==============================
VDD = 1.2 # [V] Supply (基準)
VIN_MIN = 0.0 # [V] Input min
VIN_MAX = 1.2 # [V] Input max
N_VIN_POINTS = 201 # [-] Number of Vin samples
f0 = 10e6 # [Hz] Base frequency at bias (f_0)
Kvco_lin = 20e6 # [Hz/V] Linear VCO gain (K_vco)
alpha2 = -8e6 # [Hz/V^2] Quadratic nonlinearity term (2次非線形)
VIN_MID = 0.6 # [V] Vertex for nonlinearity (非線形の中心電圧)
Ts = 10e-6 # [s] Counter window / sampling period (カウント窓)
fs_sample = 1.0 / Ts # [Hz] Sample rate (= 1/Ts)
N_bits_cnt = 16 # [-] Counter width (量子化幅は参考)
jitter_rms = 1.0e-12 # [s_rms] Period jitter (位相ジッタ標準偏差)
fin_min = 10e3 # [Hz] Input tone for SNR_jitter plot (min)
fin_max = 2e6 # [Hz] Input tone for SNR_jitter plot (max)
N_FIN_POINTS = 200 # [-] Frequency points for SNR curve
# ==============================
# 1) VCO frequency model / VCO周波数モデル
# f_osc(Vin) = f0 + Kvco_lin*Vin + alpha2*(Vin - VIN_MID)^2
# 2次非線形項で実機の曲がりを近似
# ==============================
Vin = np.linspace(VIN_MIN, VIN_MAX, N_VIN_POINTS)
f_osc_ideal = f0 + Kvco_lin * Vin
f_osc_nl = f0 + Kvco_lin * Vin + alpha2 * (Vin - VIN_MID)**2
# Guard: limit to physical positive frequency
f_osc_nl = np.clip(f_osc_nl, 1.0, None)
# ==============================
# 2) Transfer characteristic / 伝達特性
# D_out = floor( f_osc * Ts ) 近似(単純カウント)
# LSB_voltage = 1/(Kvco_lin*Ts) を理想LSBと定義
# ==============================
D_out_ideal = f_osc_ideal * Ts
D_out_nl = f_osc_nl * Ts
LSB_V = 1.0 / (Kvco_lin * Ts) # [V/LSB] ideal voltage LSB
# ==============================
# 3) Jitter-limited SNR model / ジッタ支配SNR
# SNR_jitter[dB] = -20*log10( 2*pi*fin*jitter_rms )
# 周波数が高いほどSNR低下(時間ゆらぎ→位相誤差)
# ==============================
fin = np.logspace(np.log10(fin_min), np.log10(fin_max), N_FIN_POINTS)
SNR_jitter_dB = -20.0 * np.log10(2.0 * np.pi * fin * jitter_rms)
# ==============================
# 4) Optional Monte Carlo for code dispersion / ジッタによるコード分散(簡易MC)
# N_count ≈ f_osc*Ts + ε。周期ジッタを独立と仮定し分散を近似。
# ここでは簡便に正規雑音を加えてばらつきを可視化
# ==============================
rng = np.random.default_rng(42)
Vin_mc = np.linspace(VIN_MIN, VIN_MAX, 41)
f_mc = f0 + Kvco_lin * Vin_mc
# 近似: counts = f*Ts、周期ジッタσ_tがN周期で √Nスケールの時間誤差 → counts誤差 ~ (σ_t / T)^*√N
T_mc = 1.0 / f_mc
Ncyc = np.maximum(f_mc * Ts, 1.0)
sigma_counts = (jitter_rms / T_mc) * np.sqrt(Ncyc)
D_mc = f_mc * Ts + rng.normal(0.0, sigma_counts)
# ==============================
# 5) Plots (one figure per chart) / 図示(各図1プロット)
# ==============================
# 5-1) VCO frequency vs Vin
plt.figure(figsize=(8, 4))
plt.plot(Vin, f_osc_ideal/1e6, label="f_osc ideal [MHz]")
plt.plot(Vin, f_osc_nl/1e6, label="f_osc with nonlinearity [MHz]", linestyle="--")
plt.title("VCO Frequency Characteristic")
plt.xlabel("Input Voltage Vin [V]")
plt.ylabel("Oscillation Frequency [MHz]")
plt.grid(True)
plt.legend()
plt.tight_layout()
plt.show()
# 5-2) Transfer characteristic: D_out vs Vin
plt.figure(figsize=(8, 4))
plt.plot(Vin, D_out_ideal, label="D_out ideal = f_osc(Vin)*Ts")
plt.plot(Vin, D_out_nl, label="D_out with nonlinearity", linestyle="--")
plt.scatter(Vin_mc, D_mc, s=12, label="Monte Carlo (jittered counts)")
plt.title("VCO-ADC Transfer Characteristic")
plt.xlabel("Input Voltage Vin [V]")
plt.ylabel("Digital Output Code D_out [counts]")
plt.grid(True)
plt.legend()
plt.tight_layout()
plt.show()
# 5-3) Jitter-limited SNR vs input frequency
plt.figure(figsize=(8, 4))
plt.semilogx(fin, SNR_jitter_dB, label="SNR limited by jitter")
plt.title("Jitter-Limited SNR vs Input Frequency")
plt.xlabel("Input Tone Frequency f_in [Hz]")
plt.ylabel("SNR_jitter [dB]")
plt.grid(True, which="both")
plt.legend()
plt.tight_layout()
plt.show()
# ==============================
# 6) Key metrics print / 指標出力
# ==============================
ENOB_jitter_at_100k = (SNR_jitter_dB[np.argmin(np.abs(fin-100e3))] - 1.76)/6.02
print("=== Key Metrics ===")
print(f"Ideal voltage LSB = {LSB_V:.6f} V/LSB")
print(f"SNR_jitter@100kHz = {SNR_jitter_dB[np.argmin(np.abs(fin-100e3))]:.2f} dB")
print(f"ENOB_jitter@100kHz = {ENOB_jitter_at_100k:.2f} bits")