Wafer Process Statistical Control and R2R Management
半導体製造では、ナノメートルオーダーの精密制御が求められます。本章では、 Run-to-Run(R2R)制御、Virtual Metrology(VM)、プロセスドリフト検出など、 ウェハプロセスの統計的管理とAI技術を学びます。
Run-to-Run制御は、前回のウェハ(ロット)の計測結果をフィードバックし、 次のウェハの製造条件を調整する適応制御手法です。
\( u_k \): 第k回の制御入力(プロセスパラメータ)
\( y_k \): 第k回の測定値
\( T \): 目標値
\( K \): 制御ゲイン(0 < K < 1)
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy import stats
import warnings
warnings.filterwarnings('ignore')
plt.rcParams['font.sans-serif'] = ['Arial Unicode MS', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False
class R2RController:
"""Run-to-Run EWMA制御システム"""
def __init__(self, target, initial_input, control_gain=0.5):
"""
Args:
target: 目標値(例: 膜厚 100nm)
initial_input: 初期制御入力(例: 成膜時間 60秒)
control_gain: 制御ゲイン K(0 < K < 1)
"""
self.target = target
self.u = initial_input # 現在の制御入力
self.K = control_gain
self.history = {
'run': [],
'input': [],
'output': [],
'error': []
}
def process_model(self, u, drift=0, noise_std=1.0):
"""
プロセスモデル(簡略化)
Args:
u: 制御入力(成膜時間など)
drift: プロセスドリフト
noise_std: プロセスノイズ標準偏差
Returns:
測定値(膜厚など)
"""
# 線形モデル: y = 1.5 * u + drift + noise
gain = 1.5 # プロセスゲイン
y = gain * u + drift + np.random.normal(0, noise_std)
return y
def update_control(self, measurement):
"""
制御入力の更新(EWMA制御)
Args:
measurement: 測定値
Returns:
次回の制御入力
"""
error = self.target - measurement
# EWMA制御則
u_next = self.u + self.K * error
# 制御入力の制約(物理的制約)
u_next = np.clip(u_next, 30, 90) # 30-90秒の範囲
self.u = u_next
return u_next
def run_simulation(self, n_runs=100, drift_start=50, drift_rate=0.1):
"""
R2R制御シミュレーション
Args:
n_runs: 実行回数(ウェハ枚数)
drift_start: ドリフト開始ラン
drift_rate: ドリフト速度(/run)
"""
for run in range(n_runs):
# プロセスドリフトの計算
if run >= drift_start:
drift = (run - drift_start) * drift_rate
else:
drift = 0
# プロセス実行と測定
y = self.process_model(self.u, drift=drift, noise_std=2.0)
# 履歴記録
error = y - self.target
self.history['run'].append(run + 1)
self.history['input'].append(self.u)
self.history['output'].append(y)
self.history['error'].append(error)
# 次回の制御入力更新
self.update_control(y)
return pd.DataFrame(self.history)
def plot_results(self, df):
"""制御結果の可視化"""
fig, axes = plt.subplots(3, 1, figsize=(14, 10))
# 測定値のトレンド
axes[0].plot(df['run'], df['output'], marker='o', color='#11998e',
linewidth=1.5, markersize=4, label='測定値')
axes[0].axhline(y=self.target, color='red', linestyle='--',
linewidth=2, label=f'目標値 ({self.target}nm)')
axes[0].fill_between(df['run'], self.target - 3, self.target + 3,
alpha=0.2, color='green', label='許容範囲 (±3nm)')
axes[0].set_xlabel('ラン番号(ウェハ)')
axes[0].set_ylabel('膜厚(nm)')
axes[0].set_title('R2R制御による膜厚管理', fontsize=12, fontweight='bold')
axes[0].legend()
axes[0].grid(alpha=0.3)
# 制御入力のトレンド
axes[1].plot(df['run'], df['input'], marker='s', color='#38ef7d',
linewidth=1.5, markersize=4, label='成膜時間')
axes[1].set_xlabel('ラン番号(ウェハ)')
axes[1].set_ylabel('成膜時間(秒)')
axes[1].set_title('R2R制御入力', fontsize=12, fontweight='bold')
axes[1].legend()
axes[1].grid(alpha=0.3)
# 制御誤差
axes[2].plot(df['run'], df['error'], marker='^', color='#f38181',
linewidth=1.5, markersize=4, label='制御誤差')
axes[2].axhline(y=0, color='black', linestyle='-', linewidth=1)
axes[2].fill_between(df['run'], -3, 3, alpha=0.2, color='green',
label='許容誤差 (±3nm)')
axes[2].set_xlabel('ラン番号(ウェハ)')
axes[2].set_ylabel('誤差(nm)')
axes[2].set_title('制御誤差', fontsize=12, fontweight='bold')
axes[2].legend()
axes[2].grid(alpha=0.3)
plt.tight_layout()
plt.savefig('r2r_control_results.png', dpi=300, bbox_inches='tight')
plt.show()
# 実行例
print("=" * 60)
print("Run-to-Run EWMA制御システム(CVD成膜プロセス)")
print("=" * 60)
# R2R制御システムの初期化
r2r = R2RController(
target=100.0, # 目標膜厚 100nm
initial_input=60.0, # 初期成膜時間 60秒
control_gain=0.5 # 制御ゲイン K=0.5
)
# シミュレーション実行
df_results = r2r.run_simulation(
n_runs=100,
drift_start=50, # 50ラン目からドリフト開始
drift_rate=0.1 # 0.1nm/runのドリフト
)
# 性能評価
print(f"\n制御性能評価:")
print(f"平均膜厚: {df_results['output'].mean():.2f} nm")
print(f"膜厚標準偏差: {df_results['output'].std():.2f} nm")
print(f"平均絶対誤差: {df_results['error'].abs().mean():.2f} nm")
print(f"最大誤差: {df_results['error'].abs().max():.2f} nm")
# 規格内率の計算
in_spec = df_results[(df_results['output'] >= 97) & (df_results['output'] <= 103)]
print(f"規格内率 (100±3nm): {len(in_spec)/len(df_results)*100:.1f}%")
# ドリフト補正前後の比較
before_drift = df_results[df_results['run'] < 50]
after_drift = df_results[df_results['run'] >= 50]
print(f"\nドリフト発生前(1-49ラン)平均: {before_drift['output'].mean():.2f} nm")
print(f"ドリフト発生後(50-100ラン)平均: {after_drift['output'].mean():.2f} nm")
# 可視化
r2r.plot_results(df_results)
実装のポイント:
Virtual Metrology(VM)は、プロセス装置のセンサデータ(温度、圧力、流量など)から、 計測器を使わずにウェハの品質特性(膜厚、CD、電気特性など)を予測する技術です。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import train_test_split
from sklearn.metrics import r2_score, mean_squared_error, mean_absolute_error
import warnings
warnings.filterwarnings('ignore')
plt.rcParams['font.sans-serif'] = ['Arial Unicode MS', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False
class VirtualMetrologySystem:
"""Virtual Metrology(仮想計測)システム"""
def __init__(self):
self.model = None
self.feature_names = None
def generate_process_data(self, n_samples=500):
"""
プロセスセンサデータの生成(エッチングプロセスを想定)
Returns:
(センサデータ, CD測定値)
"""
np.random.seed(42)
# プロセス条件(装置センサデータ)
rf_power = np.random.normal(1000, 50, n_samples) # RFパワー(W)
pressure = np.random.normal(50, 5, n_samples) # 圧力(mTorr)
gas_flow_ar = np.random.normal(200, 10, n_samples) # Arガス流量(sccm)
gas_flow_cf4 = np.random.normal(50, 5, n_samples) # CF4ガス流量(sccm)
temp_chamber = np.random.normal(60, 3, n_samples) # チャンバー温度(℃)
etch_time = np.random.normal(120, 5, n_samples) # エッチング時間(秒)
# CD(Critical Dimension)の物理モデル
# CD = f(RF power, pressure, gas flows, temp, time) + noise
cd_base = 90 # ベースCD(nm)
cd = (cd_base
- 0.002 * (rf_power - 1000) # RFパワー↑ → CD↓
+ 0.05 * (pressure - 50) # 圧力↑ → CD↑
- 0.01 * (gas_flow_cf4 - 50) # CF4↑ → CD↓(エッチング促進)
+ 0.005 * (temp_chamber - 60) # 温度↑ → CD↑
- 0.03 * (etch_time - 120) # 時間↑ → CD↓
+ np.random.normal(0, 1, n_samples)) # ノイズ
# データフレーム作成
df = pd.DataFrame({
'rf_power': rf_power,
'pressure': pressure,
'gas_flow_ar': gas_flow_ar,
'gas_flow_cf4': gas_flow_cf4,
'temp_chamber': temp_chamber,
'etch_time': etch_time,
'cd_measured': cd
})
return df
def train_vm_model(self, df, feature_columns, target_column='cd_measured'):
"""
VMモデルの訓練
Args:
df: プロセスデータ
feature_columns: 特徴量カラム
target_column: 予測対象カラム
"""
X = df[feature_columns].values
y = df[target_column].values
# 訓練/テストデータ分割
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=42
)
# Random Forestモデル
self.model = RandomForestRegressor(
n_estimators=100,
max_depth=15,
min_samples_split=5,
random_state=42
)
self.model.fit(X_train, y_train)
self.feature_names = feature_columns
# 予測と評価
y_pred_train = self.model.predict(X_train)
y_pred_test = self.model.predict(X_test)
# 性能指標
r2_train = r2_score(y_train, y_pred_train)
r2_test = r2_score(y_test, y_pred_test)
rmse_test = np.sqrt(mean_squared_error(y_test, y_pred_test))
mae_test = mean_absolute_error(y_test, y_pred_test)
results = {
'X_train': X_train, 'y_train': y_train,
'X_test': X_test, 'y_test': y_test,
'y_pred_train': y_pred_train, 'y_pred_test': y_pred_test,
'r2_train': r2_train, 'r2_test': r2_test,
'rmse_test': rmse_test, 'mae_test': mae_test
}
return results
def predict_cd(self, process_conditions):
"""
プロセス条件からCDを予測
Args:
process_conditions: プロセス条件の配列またはDataFrame
Returns:
予測CD値
"""
if self.model is None:
raise ValueError("モデルが訓練されていません")
return self.model.predict(process_conditions)
def plot_vm_results(self, results):
"""VM結果の可視化"""
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
# 予測精度プロット(訓練データ)
axes[0, 0].scatter(results['y_train'], results['y_pred_train'],
alpha=0.5, s=20, color='#11998e', label='訓練データ')
axes[0, 0].plot([85, 95], [85, 95], 'r--', linewidth=2, label='理想直線')
axes[0, 0].set_xlabel('実測CD(nm)')
axes[0, 0].set_ylabel('予測CD(nm)')
axes[0, 0].set_title(f'訓練データ予測精度 (R²={results["r2_train"]:.4f})',
fontsize=12, fontweight='bold')
axes[0, 0].legend()
axes[0, 0].grid(alpha=0.3)
# 予測精度プロット(テストデータ)
axes[0, 1].scatter(results['y_test'], results['y_pred_test'],
alpha=0.5, s=20, color='#38ef7d', label='テストデータ')
axes[0, 1].plot([85, 95], [85, 95], 'r--', linewidth=2, label='理想直線')
axes[0, 1].set_xlabel('実測CD(nm)')
axes[0, 1].set_ylabel('予測CD(nm)')
axes[0, 1].set_title(f'テストデータ予測精度 (R²={results["r2_test"]:.4f})',
fontsize=12, fontweight='bold')
axes[0, 1].legend()
axes[0, 1].grid(alpha=0.3)
# 予測誤差の分布
errors = results['y_pred_test'] - results['y_test']
axes[1, 0].hist(errors, bins=30, color='#4ecdc4', alpha=0.7, edgecolor='black')
axes[1, 0].axvline(x=0, color='red', linestyle='--', linewidth=2, label='誤差ゼロ')
axes[1, 0].set_xlabel('予測誤差(nm)')
axes[1, 0].set_ylabel('頻度')
axes[1, 0].set_title(f'予測誤差分布 (MAE={results["mae_test"]:.2f}nm)',
fontsize=12, fontweight='bold')
axes[1, 0].legend()
axes[1, 0].grid(alpha=0.3)
# 特徴量重要度
feature_importance = pd.DataFrame({
'feature': self.feature_names,
'importance': self.model.feature_importances_
}).sort_values('importance', ascending=True)
axes[1, 1].barh(feature_importance['feature'], feature_importance['importance'],
color='#f38181', alpha=0.8)
axes[1, 1].set_xlabel('重要度')
axes[1, 1].set_title('特徴量重要度', fontsize=12, fontweight='bold')
axes[1, 1].grid(axis='x', alpha=0.3)
plt.tight_layout()
plt.savefig('virtual_metrology_results.png', dpi=300, bbox_inches='tight')
plt.show()
# 実行例
print("=" * 60)
print("Virtual Metrologyシステム(エッチングプロセス)")
print("=" * 60)
# VMシステムの初期化
vm = VirtualMetrologySystem()
# プロセスデータの生成
df_process = vm.generate_process_data(n_samples=500)
print(f"\n生成データ数: {len(df_process)}")
print(f"CD範囲: {df_process['cd_measured'].min():.2f} - {df_process['cd_measured'].max():.2f} nm")
# 特徴量の定義
feature_cols = ['rf_power', 'pressure', 'gas_flow_ar', 'gas_flow_cf4',
'temp_chamber', 'etch_time']
# VMモデルの訓練
results = vm.train_vm_model(df_process, feature_cols)
print(f"\nVMモデル性能:")
print(f"訓練データ R² = {results['r2_train']:.4f}")
print(f"テストデータ R² = {results['r2_test']:.4f}")
print(f"RMSE = {results['rmse_test']:.2f} nm")
print(f"MAE = {results['mae_test']:.2f} nm")
# 新規ウェハの予測例
print(f"\n新規ウェハのCD予測:")
new_wafer = np.array([[1020, 52, 205, 48, 61, 118]]) # 新しいプロセス条件
predicted_cd = vm.predict_cd(new_wafer)
print(f"予測CD: {predicted_cd[0]:.2f} nm")
# 可視化
vm.plot_vm_results(results)
実装のポイント:
本章では、ウェハプロセスの統計的管理について学びました。