同期式回路で非同期信号を扱う際の注意点







# はじめに


この記事では、あるクロックで動作している同期式回路に対して、そのクロックとは非同期な信号（別のクロックで同期しているなど）を入力する際の注意点を説明します。この内容は、一般的な説明なので、すでに理解している方は読み飛ばしてください。




# 遅延のズレ


あるクロックで動作している同期式回路に対して、そのクロックとは非同期な信号（別のクロックで同期しているなど）を入力する際の注意点の一つ目は「遅延のズレ」です。


## 誤動作する例


例えば、次図の左のような状態遷移を持ち、各状態は one-hot でデコードしている同期式回路があるとします。この回路は、REQ信号によって状態を遷移しています。


![Fig.1 遅延のズレによって誤動作する回路の例の構成](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0%2F24981%2F1a0f8662-c66a-495e-b590-84eb691c8a5e.jpeg "Fig.1 遅延のズレによって誤動作する回路の例の構成")

Fig.1 遅延のズレによって誤動作する回路の例の構成

<br />



この REQ 信号が非同期信号(クロックに同期していない信号)の場合、この回路は誤動作する可能性があります。何故ならば、FPGA や ASIC などには、信号を伝達するための配線があり、その配線を伝って信号の値が伝わるのに遅延が発生します。そしてこの配線遅延は回路の配置によって異なります。また、アンド回路やオア回路などのコンビネーション回路も入力信号に対して出力の遅延が発生します。したがって、REQ 信号の値が配線遅延やコンビネーション回路の遅延を通じてフリップフロップの入力に到達するのにズレが生じます。

もしそのズレが次の図のようにクロックを跨っていると、ステートマシーンがあり得ない状態に陥ってしまいます。


![Fig.2 遅延のズレによって誤動作する回路の例のタイミングチャート](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0%2F24981%2F652111fc-417e-4717-9934-9d043216ba00.jpeg "Fig.2 遅延のズレによって誤動作する回路の例のタイミングチャート")

Fig.2 遅延のズレによって誤動作する回路の例のタイミングチャート

<br />




## 誤動作対策


遅延のズレによって誤動作が生じるのを防ぐには次の二つが考えられます。

  * ステートマシンを工夫する。
  * 非同期信号を一旦一つの Flip-Flop で受ける。




### ステートマシンを工夫する


この方法は、例えば one-hot のステートマシンをやめて、誤動作の起きにくいジョンソンステートマシンを採用するなどがあります。




### 非同期信号を一旦一つの Flip-Flop で受ける


非同期信号をそのまま複数の Flip-Flop の入力に使うから遅延のズレによって 複数のFlip-Flop の間で不正な値になるのですから、非同期信号は一旦一つの Flip-Flop で受けてから複数の Flip-Flop に分配するようにすれば、このような問題は起きないはずです。


![Fig.3 遅延のズレによる誤動作を Flip-Flop で受けることで解消する構成例](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0%2F24981%2Ff3c60e6a-1202-4860-baad-34b52b4fd9ed.jpeg "Fig.3 遅延のズレによる誤動作を Flip-Flop で受けることで解消する構成例")

Fig.3 遅延のズレによる誤動作を Flip-Flop で受けることで解消する構成例

<br />



一旦一つの Flip-Flop で受けることによって、Flip-Flop からの出力遅延と配線遅延とコンピネーション回路の遅延の合計が Flip-Flop のセットアップ時間前ならば、通常の同期式回路設計の範疇内になります。


![Fig.4 遅延のズレによる誤動作を Flip-Flop で受けることで解消する例のタイミングチャート](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0%2F24981%2Fa229b49b-fae9-4ed2-bb42-1b37d6f37295.jpeg "Fig.4 遅延のズレによる誤動作を Flip-Flop で受けることで解消する例のタイミングチャート")

Fig.4 遅延のズレによる誤動作を Flip-Flop で受けることで解消する例のタイミングチャート

<br />



これにて一件落着といきたいのですが、残念ながらそうはいきません。というのも、次の章で説明する「メタステーブル」の問題が控えているからです。


# メタステーブル


あるクロックで動作している同期式回路に対して、そのクロックとは非同期な信号（別のクロックで同期しているなど）を入力する際の注意点の二つ目は「メタステーブル」です。


## メタステーブルとは


Flip-Flop には、入力クロックの立ち上がり（立ち下りの場合もある）タイミングの前後に、データの入力のセットアップに必要な時間とデータ入力が変化してはいけないホールド時間が存在します。この期間内は入力データを変化させてはいけません。


![Fig.5 Flip-Flop のセットアップ時間とホールド時間](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0%2F24981%2F95293d2c-eaad-4498-9851-50abe111a88d.jpeg "Fig.5 Flip-Flop のセットアップ時間とホールド時間")

Fig.5 Flip-Flop のセットアップ時間とホールド時間

<br />



しかし、クロックとは非同期な信号（別のクロックで同期しているなど）を入力する際、必ずそのルールが守られるとは限りません。もしこのルールが守れなかった場合は、Flip-Flop はメタステーブルという状態になります。


![Fig.6 Flip-Flop 通常出力とメタステーブル状態](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0%2F24981%2F2f8f90cc-689e-45a2-a174-5acad3e5f114.jpeg "Fig.6 Flip-Flop 通常出力とメタステーブル状態")

Fig.6 Flip-Flop 通常出力とメタステーブル状態

<br />



Flip-Flop はメタステーブル状態になると、一定の期間（リカバリタイム）、出力の値が不安定になります。メタステーブル状態は時間とともに指数関数的に減衰していくとされています。Flip-Flop がメタステーブル状態におちいった後、時間 T が経過した時点で安定な状態(0 または 1)に回復している確率は、以下の式で表現されます。


```math
P_{\text{stable}}(T) = 1 - e^{-\frac{T}{\tau}}
```




なお、リカバリタイム後、Low または High の何れかに出力が安定しますが、その値は入力データと同じとは限りません。




## メタステーブル対策


メタステーブルの対策には次の二つが考えられます。

  * Flip-Flop の後の回路を工夫する。
  * 非同期信号を複数段の Flip-Flop で受ける。



メタステーブルが発生するのは確率的なことなので、上記の対策を組み合わせて、より安全性を高めます。




### Flip-Flop の後の回路を工夫する


Flip-Flop がメタステーブル状態になるのは仕方がないとあきらめて、メタステーブル状態になっても問題が起きないようにFlip-Flop の後の回路を工夫しします。



例えばグレイコードがその良い例です。グレイコードは、隣り合う値の間で ビットが1ビットだけ変化する 特殊な2進数の表現方法です。このようなコードを利用することで、メタステーブル状態になっても誤動作しないようにしています。




### 非同期信号を複数段の Flip-Flop で受ける


次の図のように、問題となる Flip-Flop の前にもう一つ Flip-Flop を追加します。このようにすることで、DATA を入力している Flip-Flop がメタステーブル状態になったとしても、その次の Flip-Flop のセットアップ時間前までに安定するならば、問題となる Flip-Flop はメタステーブル状態になりません。ただし、問題の Flip-Flop がメタステーブル状態になるのは防げますが、出力される値が DATA と同じであることは限らないことに注意してください。


![Fig.7 メタステーブル対策](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0%2F24981%2F0e0e85b8-acff-4d6c-b271-97b7c8105736.jpeg "Fig.7 メタステーブル対策")

Fig.7 メタステーブル対策

<br />




### MTBF


MTBF（Mean Time Between Failures）とは、システムが故障するまでの平均時間の事です。



なお、ここからは筆者の推論交じりの話になります。ご指摘があればコメントお願いします。



あるクロックで動作している同期式回路に対して、そのクロックとは非同期な信号（別のクロックで同期しているなど）を入力する際、外部信号を Flip-Flop で受けた場合にメタステーブルになる確率を P0 、次の Flip-Flop のセットアップ時間までにメタステーブル状態が回復しない確率をP1とすると、P0およびP1は次のようになります。この式において τ はデバイスの特性によって決まる値です。


```math
P_{\text{0}} = \frac{T_{setup} + T_{hold}}{T_{cycle}}
```



```math
P_{\text{1}} = e^{-\frac{T_{cycle}-T_{setup}-T_{delay}}{\tau}}
```


そして、外部信号が単位時間あたりに変化する確率を Fclk で表すと、MTBF は次のようになります。


```math
MTBF = \frac{1}{Fclk \cdot P_{\text{0}} \cdot P_{\text{1}}} = \frac{T_{cycle}}{Fclk \cdot (T_{setup} + T_{hold})} \cdot e^{\frac{T_{cycle}-T_{setup}-T_{delay}}{\tau}}
```




上記の MTBF の考え方は、非同期信号を１段の Flip-Flop で受けて、その Flip-Flop の出力信号をシーケンサや各種制御用の Flip-Flop に入力する場合です。しかし、これだと、最初の Flip-Flop から出力した信号が入力されたすべての Flip-Flop に対して考慮しなければなりません。

そこで Flip-Flop を２段にして、2段目の Flip-Flop がメタステーブル状態になったら故障と定義して MTBF を定義しなおします。すると MTBF は次のようになります。


```math
MTBF_{(N=2)} = \frac{1}{Fclk \cdot P_{\text{0}} \cdot P_{\text{1}}} = \frac{T_{cycle}}{Fclk \cdot (T_{setup} + T_{hold})} \cdot e^{\frac{T_{cycle}-T_{setup}-T_{delay}}{\tau}}
```




筆者の(古い?)常識ではP1(最初の Flip-Flop のメタステーブル状態が次の Flip-Flop のセットアップ時間を満たさない確率) は限りなく0に近いので、「非同期信号は２段の Flip-Flop で受けろ」と教えられてきました。

しかし、最近のデバイスは高速になり、高い周波数で動作します。もしかしたら、最初の Flip-Flop のメタステーブル状態が２段目の Flip-Flop のセットアップ時間を満たせない確率が無視できない場合も考えられます。もし、２段目の Flip-Flop のセットアップ時間を満たせなかったら、２段目の Flip-Flop もメタステーブル状態になります。そして２段目の Flip-Flop がメタステーブル状態からさらに次の Flop-Flop のセットアップ時間までに回復しない確率を P2 とすれば、


```math
P_{\text{2}} = e^{-\frac{T_{cycle}-T_{setup}-T_{delay}}{\tau}}
```


となり、MTBFは次のようになります。


```math
MTBF = \frac{1}{Fclk \cdot P_{\text{0}} \cdot P_{\text{1}} \cdot P_{\text{2}}} = \frac{T_{cycle}}{Fclk \cdot (T_{setup} + T_{hold})} \cdot e^{2 \cdot \frac{T_{cycle}-T_{setup}-T_{delay}}{\tau}}
```




そこで、非同期信号を受ける Flip-Flop をN段にした場合に、最初の段の Flip-Flop がメタステーブルになる確率を P0 とし、最終段の Flip-Flop がメタステーブル状態になる確率を Pn とすると、Pn は次のようになります。


```math
P_{\text{n}} = e^{(n-1) \cdot -\frac{T_{cycle}-T_{setup}-T_{delay}}{\tau}}
```




したがって、非同期信号を N 段の Flip-Flop で受けて、最終段の Flip-FLop がメタステーブル状態になったら故障と考えたときのMTBF は次のようになります。


```math
MTBF_{(N)}= \frac{1}{Fclk \cdot P_{\text{0}} \cdot P_{\text{n}}} = \frac{T_{cycle}}{Fclk \cdot (T_{setup} + T_{hold})} \cdot e^{(N-1) \cdot \frac{T_{cycle}-T_{setup}-T_{delay}}{\tau}}
```




非同期信号を受ける Flip-Flop の段数が MTBF にどのような影響を与えるかを分かりやすくプロットしたものが下図です。

  __なお、このプロット図を作成するに当たって設定したパラメーターは、傾向を把握するために設定した架空のものです。実際には、使っているデバイスによって大きく結果が変わります。__  






![Fig.8 MTBF vs Number of Flip-Flop stages](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0%2F24981%2F4bc324f3-ed05-478e-a957-ef4fb9d3d596.png "Fig.8 MTBF vs Number of Flip-Flop stages")

Fig.8 MTBF vs Number of Flip-Flop stages

<br />

このプロット図は次のような Python スクリプトを作って作成しました。


```Python:plot-mtfs.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt
# Parameters
Ts  = 0.1  # Tsetup 0.1[nsec]
Th  = 0.0  # Thold    0[nsec]
Td  = 1.0  # Tdelay   1[nsec]
tau = 0.1  #        0.1[nsec]
Fclk_valuse = [100, 200, 300, 400, 500] # [MHz]
N = np.arange(1,9,1)  # N from 1 to 8
# MTBF function
def mtbf(N, fclk, t_cycle, t_setup, t_hold, t_delay, tau):
    p0 = (t_setup + t_hold) / t_cycle
    pn = np.exp((N-1) * (-(t_cycle-t_setup-t_delay)/tau))
    return 1 / (fclk * p0 * pn)

# Plotting
plt.figure(figsize=(10, 6))
for Fclk_MHz in Fclk_valuse:
    fclk    = Fclk_MHz*1000*1000
    t_cycle = 1/fclk
    mtbf_values = mtbf(N, fclk, t_cycle, Ts*1e-9, Th*1e-9, Td*1e-9, tau*1e-9)
    plt.plot(N, mtbf_values, label=f'Fclk = {Fclk_MHz} MHz')
# Log scale for y-axis due to large MTBF values
param_text = (
    f'$T_{{setup}}$ = {Ts} ns\n'
    f'$T_{{hold}}$ = {Th} ns\n'
    f'$T_{{delay}}$ = {Td} ns\n'
    f'$r$ = {tau} ns'
)
mtbf_text = (
    r'$T_{\mathrm{cycle}} = \frac{1}{Fclk}$'
    f'\n'
    r'$P_{\mathrm{0}} = \frac{{T_\mathrm{setup}} + T_{\mathrm{hold}}}{T_{\mathrm{cycle}}}$'
    f'\n'
    r'$P_{\mathrm{N}} = e^{(N-1)\cdot\frac{-(T_{\mathrm{cycle}} - T_{\mathrm{setup}} - T_{\mathrm{delay}})}{r}}$'
    f'\n'
    r'$\mathrm{MTBF} = \frac{1}{Fclk \cdot P_{\mathrm{0}} \cdot P_{\mathrm{N}}}$'
)
plt.text(0.985, 0.75, param_text, transform=plt.gca().transAxes,
         fontsize=10, verticalalignment='top', horizontalalignment='right',
         bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))
plt.text(0.775, 0.55, mtbf_text, transform=plt.gca().transAxes,
         fontsize=10, verticalalignment='top', horizontalalignment='left',
         bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))
plt.yscale('log')
plt.xlabel('Number of Flip-Flop stages')
plt.xticks(N)
plt.ylabel('MTBF')
plt.ylim(1e-8,1e+20)
plt.yticks([1, 60, 3600, 86400, 3.154e+7, 3.154e+10, 3.154e+13, 3.154e+16],['1 sec', '1 min', '1 hour', '1 day', '1 year', '1 K years', '1 M years', '1 B years'])
plt.title('MTBF vs Number of Flip-Flop stages for Different Fclk')
plt.legend()
plt.grid(True)
plt.show()

```



# まとめ


同期式回路で非同期信号を扱う際は、遅延のズレとメタステーブルに注意して設計する必要があります。その基本的な対策は次のとおりです。



  * 誤動作しないように回路を工夫する
  * 非同期信号は必ず一つの Flip-Flop で受ける
  * 非同期信号を受ける Flip-Flop を多段にする   
段数は MTBF と相談


# 参考


* H.J.M. Veendrick, "The Behavior of Flip-Flops Used as Synchronizers and Prediction of their Failure Rate" IEEE Journal of Solid-State Circuits, Vol. SC-15, No. 2, pp. 169?176, April 1980
* T. J. Chaney and C. E. Molnar, "Anomalous behavior of synchronizer and arbiter circuits," IEEE Transactions on Computers, 1973.
* W. J. Dally and J. Poulton, Digital Systems Engineering, Cambridge University Press, 1998.
* P. Higham, "Mean Time Between Failures (MTBF) for Synchronizers", Xilinx XAPP094