【Serdes】RjとJTFの関係

PCI Expressなどの高速SerdesのSystemの中で、TXのPLLとRXのCDRはジッタを抑制するフィルタの役割を果たす。これは、ジッタトランスファファンクション(Jitter Transfer Function: JTF)として数学的なモデル化が出来る。

下図の青線がUSB3.1 Gen2のリファレンスとして記載されているRX CDRのJTFである。一方、TX PLL側は同図の赤線のようになる。

RX側のHigh Pass FilterとTX側のLow Pass Filterの組み合わせにより、全帯域のJitterは抑制される。ただし、10MHz付近に関してはスイートスポットとなり、この帯域のジッタは排除することが出来ない。

f:id:kent_s:20180729094817p:plain

JTFはDetermistic Jitter(Dj)を抑える上で効果があることは容易に理解できる。Djは周波数分布が限定されており、その分布がJTFの不感帯に入っていなければ、抑制できると分かるからだ。

では、Random Jitterに対して、これらのフィルタは効くのだろうか?このことをPythonを使ったSimulationで検討した。

検証方法

検証モチーフはUSB3.1 Gen2である。手順をいかに示す。

  1. 乱数を使用し、1ps RMSの時系列データを作成
  2. 1をフーリエ変換
  3. 2にCDRのJTFを乗算
  4. 3を逆フーリエ変換し時系列波形を再生
  5. 4の標準偏差を計算

Simulationに使ったPythonのプログラムを本記事の末尾に記載する。

検証結果

結果は下記の通り、JTFによってほとんどジッタは減っていないことが分かった。

  • JTF通過前ジッタ: 1.00006781201 ps RMS
  • JTF通過後ジッタ: 0.996609674857 ps RMS

周波数スペクトルで見ると、下図の通り、JTFによって低周波数成分は明確に減衰していることは分かる。
ランダムジッタは非常に広い帯域で分布しており、かつ、広域側の強度が強い。したがって、低域側だけの減衰では効果が少ないことが原因と思われる。
にしても、RMSでここまで変化量が少ないというのはとても興味深い。

JTF通過前
f:id:kent_s:20180729105559p:plain

JTF通過後
f:id:kent_s:20180729105632p:plain

ソースコード

import numpy as np
import matplotlib.pyplot as plt

#Sample size
intSize = 1000000
#Time step : 1UI = 100ps
fltStepTime = 100e-12
#Freqency step : 10kHz
fltStepFreq = 1/(intSize*fltStepTime)

#Jitter RMS = 1ps
fltJitterRms = 1e-12

#Generate random pattern
arrData = np.random.normal(0, fltJitterRms, intSize)
arrTime = np.arange(0, intSize*fltStepTime, fltStepTime)

#Generate fft pattern
arrFftData = np.fft.fft(arrData)
arrFftFreq = np.fft.fftfreq(intSize,fltStepTime)

#Calc jitter suppressed spectrum
ks = 0.707
w3dB = 2 * np.pi * 1.5 * 10**7
wn = w3dB / (1 + 2 * ks**2 + ((1 + 2 * ks**2)**2 + 1)**0.5)**(-0.5)
arrFltFftData = np.zeros(intSize, complex)
for i in range(intSize):
        s = 2 * np.pi * arrFftFreq[i] * 1j
        arrFltFftData[i] = (s**2 / (s**2 + 2*ks*wn*s + wn**2)) * arrFftData[i]

#Calc jitter suppressed time domain data
arrFltData = np.fft.ifft(arrFltFftData)

#Output jitter RMS value
print np.std(arrData.real)
print np.std(arrFltData.real)

#Plot Raw Jitter @ Time Domain
#plt.figure()
#plt.plot(arrTime, arrData)

#Plot Raw Jitter @ Histgram
#plt.figure()
#plt.hist(arrData)

#Plot Raw Jitter @ Freq Domain
plt.figure()
plt.plot(arrFftFreq, abs(arrFftData))
plt.axis([1e6,max(arrFftFreq),0,max(abs(arrFftData))])
plt.xscale("log")

#Plot Filtered Jitter @ Time Domain
#plt.figure()
#plt.plot(arrTime, arrFltData.real)

#Plot Filtered Jitter @ Freq Domain
plt.figure()
plt.plot(arrFftFreq, abs(arrFltFftData))
plt.axis([1e6,max(arrFftFreq),0,max(abs(arrFftData))])
plt.xscale("log")

plt.show()