はじめに
本記事では、1.5ビット構成のパイプラインADCの動作を段階的に解析し、特に第2ステージの残差電圧 $V_{\mathrm{out2}}$ の導出を、補助キャパシタ $C_2$ を一般化した形で行います。Pythonコードと数式を併用して、理解しやすく記述します。
1. システムパラメータの定義
以下に主要パラメータを定義します:
記号 | 内容 | 単位 | 備考 |
---|---|---|---|
$V_{\mathrm{DD}}$ | 電源電圧 | [V] | |
$V_0$ | 比較基準電圧 | [V] | $V_0 = \frac{V_{\mathrm{DD}}}{2}$ |
$C_1$ | 主キャパシタ | [F] | |
$C_2$ | 補助キャパシタ | [F] | ※ 一般化。元構成では $C_2 = \frac{C_1}{4}$ |
$V_1$ | スイッチ注入用補助電圧 | [V] | 通常は $V_1 = \frac{V_{\mathrm{DD}}}{2}$ |
$D_1, D_2$ | ビット判定出力 | 0 or 1 |
2. ステージ1:MSB(D₁)の生成
電荷保存則に基づく残差出力
最初の段階では、サンプリングした電荷と保持時の電荷を等価とし、
$$
C_1\left(V_{\mathrm{in}} - \frac{V_{\mathrm{DD}}}{2}\right) = C_1(V_0 - V_{\mathrm{out1}})
$$
と書けるため、整理して:
$$
V_{\mathrm{out1}} = V_{\mathrm{DD}} - V_{\mathrm{in}}
$$
MSBビット $D_1$ の判定則
$$
D_1 =
\begin{cases}
1 & (V_{\mathrm{in}} \geq V_0) \
0 & (V_{\mathrm{in}} < V_0)
\end{cases}
$$
3. ステージ2:LSB(D₂)の生成とVout₂の一般化
補助項の定義
スイッチ構成により挿入される電圧は:
$$
\mathrm{aux_term} = D_1 \cdot V_1 + (1 - D_1) \cdot (-V_1) = (2D_1 - 1)V_1
$$
残差電圧Vout₂の電荷保存式
充電と保持の電荷バランスより:
$$
C_1(V_{\mathrm{in}} - \frac{V_{\mathrm{DD}}}{2}) + C_2(-\frac{V_{\mathrm{DD}}}{2})
= C_1(V_0 - V_{\mathrm{out2}}) + C_2(\mathrm{aux_term} - V_{\mathrm{out2}})
$$
展開して:
$$
(C_1 + C_2) V_{\mathrm{out2}}
= C_1 V_0 - C_1(V_{\mathrm{in}} - \frac{V_{\mathrm{DD}}}{2})
- C_2 \cdot \frac{V_{\mathrm{DD}}}{2}
- C_2 \cdot \mathrm{aux_term}
$$
整理されたVout₂の一般式
$$
V_{\mathrm{out2}} =
\frac{-C_1 V_{\mathrm{in}} + C_1 V_{\mathrm{DD}} + \tfrac{C_2 V_{\mathrm{DD}}}{2} - C_2 (2D_1 - 1) V_1}
{C_1 + C_2}
$$
4. 判定ビット D₂ の決定
$$
D_2 =
\begin{cases}
1 & (V_{\mathrm{out2}} < V_0) \
0 & (V_{\mathrm{out2}} \geq V_0)
\end{cases}
$$
# Program Name: pipeline_adc_1.5stage_ramp.py
# Creation Date: 20250821
# Overview: Simulate and visualize a 1.5-stage pipeline ADC operation using a ramp input waveform.
# Usage: Run the script to observe residue voltages and bit outputs (D1, D1_bar, D2, D2_bar), and detect threshold crossings.
import numpy as np
import matplotlib.pyplot as plt
# --- Parameter Definitions ---
VDD = 1.0
C1 = 1.0
C4 = 1.0
V0 = VDD / 2
V1 = 1.0
Vin = np.linspace(0, VDD, 100000)
# --- Stage 1 ---
D1_bar = (Vin < V0).astype(int)
D1 = 1 - D1_bar
Vout1 = V0 - (Vin - VDD / 2)
# --- Stage 2 ---
aux_term = D1 * V1 + D1_bar * (-V1)
numerator = C1 * (Vin - VDD / 2) + (C1 / 4) * (-VDD / 2) - (C4 / 4) * aux_term
denominator = C1 + (C4 / 4)
Vout2 = (C1 * V0 - numerator) / denominator
D2 = (Vout2 < V0).astype(int)
D2_bar = 1 - D2
# --- Print Formulas ---
print("D1_bar = 1 if Vin < V0 else 0")
print("D1 = 1 - D1_bar")
print("Vout1 = V0 - (Vin - VDD/2)")
print("aux_term = D1 * V1 + D1_bar * (-V1)")
print("Vout2 = (C1*V0 - [C1*(Vin - VDD/2) + (C1/4)*(-VDD/2) - (C4/4)*aux_term]) / (C1 + C4/4)")
print("D2 = 1 if Vout2 < V0 else 0")
print("D2_bar = 1 - D2")
# --- Plotting ---
plt.figure()
plt.plot(Vin, label='Vin (Input Ramp)')
plt.axhline(V0, color='red', linestyle='--', label='Threshold V0')
plt.xlabel('Sample Index')
plt.ylabel('Vin [V]')
plt.title('Input Ramp Waveform')
plt.legend()
plt.grid(True)
plt.figure()
plt.plot(Vin, Vout1, label='Vout1')
plt.axhline(V0, color='gray', linestyle='--', label='Threshold V0')
plt.xlabel('Vin [V]')
plt.ylabel('Vout1 [V]')
plt.title('Stage 1 Residue Vout1 vs Vin')
plt.legend()
plt.grid(True)
plt.figure()
plt.plot(Vin, Vout2, label='Vout2')
plt.axhline(V0, color='gray', linestyle='--', label='Threshold V0')
plt.xlabel('Vin [V]')
plt.ylabel('Vout2 [V]')
plt.title('Stage 2 Residue Vout2 vs Vin')
plt.legend()
plt.grid(True)
plt.figure()
plt.plot(Vin, D2, label='D2')
plt.xlabel('Vin [V]')
plt.ylabel('D2')
plt.title('D2 Output vs Vin')
plt.legend()
plt.grid(True)
# --- Find Vin where Vout2 ≈ VDD/2 ---
target_value = VDD / 2
tolerance = 1e-3
indices = np.where(np.isclose(Vout2, target_value, atol=tolerance))[0]
Vin_at_half_Vout2 = Vin[indices]
print(f"\nVout2 ≈ {VDD/2} となる Vin の値(許容誤差 {tolerance}):")
for v in Vin_at_half_Vout2:
print(f"{v:.5f} V")
# --- Detect D2 transitions ---
d2_diff = np.diff(D2)
rising_edges = np.where(d2_diff == 1)[0] + 1
falling_edges = np.where(d2_diff == -1)[0] + 1
print("\n--- D2のクロック変化タイミング ---")
for idx in rising_edges:
print(f"↑ Rising Edge at Vin = {Vin[idx]:.5f} V (index {idx})")
for idx in falling_edges:
print(f"↓ Falling Edge at Vin = {Vin[idx]:.5f} V (index {idx})")
# --- Plot D2 transitions ---
plt.figure()
plt.plot(Vin, D2, label='D2')
plt.plot(Vin[rising_edges], D2[rising_edges], 'ro', label='Rising Edge')
plt.plot(Vin[falling_edges], D2[falling_edges], 'bo', label='Falling Edge')
plt.xlabel('Vin [V]')
plt.ylabel('D2')
plt.title('D2 Output with Edge Detection')
plt.legend()
plt.grid(True)
plt.show()