みなさん、こんにちは。このコースでは、ある簡単な例題を非同期式回路として FPGA 上に実装し、ツールを適切に使うことで、正しく動作するものを作る、ということ目指しています。前回は、非同期式回路の超入門ということで、非同期式回路のイメージをざっくりと紹介しました。
今回は、非同期式回路の実行制御のかなめとなるハンドシェイクについて少し詳しく紹介したいと思います。
ハンドシェイクとは
ハンドシェイクという用語は、情報通信分野の通信プロトコルなどでよく使われる用語だと思います。その場合、コンピュータなどのノード間のやりとりを示す場合が多いですが、非同期式回路では、素子あるいは回路ブロック間でのやりとりに使われます。
いろいろなハンドシェイク方式がありますが、話を簡単にするために、ここでは要求信号と応答信号という2本の制御信号線のみを考えます。さらにここでは下図のように要求信号は送信側の出力信号、受信側の入力信号であり、応答信号は受信側の出力信号、出力側の入力信号とします。
素子あるいは回路ブロックレベルのやりとりなので、プロトコルは非常にシンプルで、次のようになります。
- [送信側] データを送信側データポートに置く。
- [送信側] 要求信号を出す。
- [受信側] 要求信号を受け取り、受信側データポートのデータが有効であることを知る。
- [受信側] データを受け取り、自分の領域に格納する。
- [受信側] 応答信号を出す。
- [送信側] 応答信号を受け取り、データポートのデータを更新しても良いことを知る。
送信側と受信側は、これを繰り返し、データの受け渡しを行います。このようなハンドシェークを回路上に実装するにあたり、注意すべき重要な点が二つあります。ひとつは、要求(あるいは応答)信号を出すということをいかに実現するか、であり、もう一つは、要求(あるいは応答)信号をいつ出すか、ということです。以下、これらを考えて見ます。
要求・応答信号の実現
要求信号も応答信号もその実現の考え方は同じです。このため、以下では要求信号について考えます。
要求信号の一番簡単な実現方法は、「要求信号が値1をとる時、要求信号が出ている」と考えることです。すなわち、上述のプロトコルの 2. において、送信側は要求信号の値を0から1に変化させるわけです。ただし、これだけでは2回目のデータを送ろうとするときに困ります。要求信号を出そうと思っても、既にその値が1になっているからです。そこで、プロトコルのどこかで要求信号を0に戻す必要があります。このような0に戻す操作を加えると、上述のプロトコルは例えば次のようになります。
- [送信側] データを送信側データポートに置く。
- [送信側] 要求信号を1とする。
- [受信側] 要求信号が1となることにより、受信側データポートのデータが有効であることを知る。
- [受信側] データを受け取り、自分の領域に格納する。
- [受信側] 応答信号を1とする。
- [送信側] 応答信号が1となることにより、要求信号を0とする。
- [受信側] 要求信号が0となることにより、応答信号を0とする。
- [送信側] 応答信号が0となることにより、データポートのデータを更新しても良いことを知る。
このようすを図示すると、次のようになります。
この方式は、要求信号と応答信号が、(1,0)->(1,1)->(0,1)->(0,0)という変化により1サイクルが完了するため、4相ハンドシェイク方式などと呼ばれます。なお、細かな点ですが、上記 5. の応答信号を1とするタイミングは、実は 4. の前に移動しても正しく動作します。これは、送信側は最終的に応答信号が0となるタイミングで次のデータ転送に移るためで、応答信号は早めに1 となっても構わないからです。
もう一つの要求信号の実現方法は、「要求信号の変化(0→1あるいは1→0)により要求信号が出ている」と考えることです。この場合、立ち上がり変化と立ち下がり変化を区別せずに、要求信号が出ている、と考えれば、値を0に戻す必要はありません。ですから、もとのプロトコルをそのまま使えます。すなわち、下記のようになります。
- [送信側] データを送信側データポートに置く。
- [送信側] 要求信号を反転する。
- [受信側] 要求信号を受け取り、受信側データポートのデータが有効であることを知る。
- [受信側] データを受け取り、自分の領域に格納する。
- [受信側] 応答信号を反転する。
- [送信側] 応答信号を受け取り、データポートのデータを更新しても良いことを知る。
このようすを図示すると、次のようになります。
この方式は、要求信号と応答信号が、(1,0)->(1,1) あるいは (0,1)->(0,0)という変化により1サイクルが完了するため、2相ハンドシェイク方式などと呼ばれます。
これらの2つの方式には一長一短があり、一般的には下記のように考えられています。しかし、回路設計の工夫やデバイスの選択により、欠点を克服する工夫も数多く研究されています。
- 4相ハンドシェイク方式の特徴
- 制御回路が簡単になる
- 信号線の値を0に戻す必要があり、性能や電力消費において不利となる
- 2相ハンドシェイク方式の特徴
- 値を覚えておく必要があり、制御回路がやや複雑となる
- 信号変化が少なく、性能および電力消費において有利
要求・応答信号を出すタイミング
上述のプロトコル 3. のように、受信側にとって要求信号は、受信側データポートのデータが有効である、という意味を持ちます。従って、送信側データポートに置かれたデータが組合わせ回路を通り、受信側データポートに確実に到達したのちに要求信号が受信側に届く必要があります。同期式回路では、クロック信号により同様なことを保証しています。
非同期式回路で、このことを実現する方法は、二つあります。一つめの方法は、束データ方式と呼ばれるものです。これは非常に簡単で、「組合わせ回路に相当する遅延値」を持つ遅延素子を用いて、下図のように構成します。
送信側はデータと同じタイミングで要求信号を出しますが、遅延素子により、その要求信号が受信側に到達するのは、データが受信側のデータポートに到達した後となります。データの値によって組合わせ回路の遅延は変わるかもしれませんから、それらの最悪遅延を考慮して、それよりも少し大きな遅延値を決定し、遅延素子を実現する必要があります。
FPGA では、バッファやインバータを組み合わせて遅延素子を作ることが多いですが、この際 (1) 合成ツールの論理最適化によって消されてしまわないようにする、(2) 配線遅延を含めて、データパスの遅延より小さくならないようにする、ことが重要です。これらに関しては、回路や制約の記述により適切にツールを使うことで実現できます。次回以降で、具体的な回路を用いて説明していきたいと思います。
二つめの方法は、符号化方式と呼ばれるものです。この方式では、データを適当な符号を用いて符号化し、データの到着を受信側で識別することで、受信側で要求信号を生成します。典型的な符号としては、2線式符号(Dual-rail code, 1-out-of-2 code 等ともいう)があります。これは、データバスの1ビット分である d を 2 本の信号線 (d1, d0) を用いて次のように表します。
- d=0 のとき、(d1, d0) = (0, 1)
- d=1 のとき、(d1, d0) = (1, 0)
ただし、(d1, d0) = (0, 0) の時は有効なデータではないと定義します。
組合わせ回路で実現される演算回路をこの符号を用いて構成する必要があります。これを2線式論理と呼びます。直感的には、元の回路の各ゲートを下記のように置き換えることで実現できます。
そして、受信側では、各データビットの論理和を取った上で、それらの論理積を求めることで要求信号が得られます。すなわち、下図のようになります。
考え方としては、全てのビットが (0,0) から (0,1) か (1,0) に遷移すれば、受信側にデータが確定するわけですので、それを検出して要求信号を受け取ったと思い、レジスタへの書き込み等をすればいいわけです。
この方式では、一回のデータ転送後に、全ての信号線を (0,0) に戻す相 (休止相と呼ばれます) が必要になります。また、休止相を含めたプロトコルや要求信号の落とし方などに関しては、もう少し詳細な議論が必要ですが、ここでは省略します。
もうひとつ注意すべきこととしては、この符号化方式は基本的に4相ハンドシェイクで使われるということです。2相ハンドシェイクで使うためには、特殊なデータパス構成が必要となります。
ここまで、要求信号を出すタイミングについて考えてきました。一方、応答信号に関しては、基本的に受信側の処理が終了後、直ちに生成し、送信側に送り返すことで実現できます。ただし、受信側のレジスタ書き込みに時間がかかるなどの場合で、送信側データが早く更新されてしまう可能性がある場合には、レジスタのホールド時間を確保するために、応答信号にも小さな遅延素子が必要になることもあります。このようなホールド時間制約についてもツールにより検査できるので、このコース終盤で説明します。
束データ4相ハンドシェイク制御回路の構成
このコースでは、次回以降に、簡単な例題回路を取り上げ、その非同期式回路を具体的に設計します。その際には、最も基本的な方式である束データ4相ハンドシェイク方式を用います。この節では、その実現方法について紹介します。
以下ではパイプライン制御ができる4相ハンドシェイク制御回路を考えます。この制御回路は以下のような信号線を持ちます。
制御回路の動作は、直感的には次のようになります。
- 要求信号入力が1となったら、レジスタにデータを書き込むために書き込み信号を発生させる
- ただし、それは現在レジスタに入っているデータを上書きしても良い場合のみである。データの上書きが許されるのは、次段の処理が完了している、すなわち、次段への要求信号も次段からの応答信号も0の場合である。
- 次段の処理が完了していない場合は、完了するまで待ち、その後レジスタ書き込み信号を発生させる。
- レジスタにデータを書き込んだら、次段への要求信号と前段への応答信号を1とする。
- 要求信号入力が0となったら、前段への応答信号を0とする。
- 応答信号入力が1となったら、次段への要求信号を0とする。
このような動作を行う回路を考えるため、信号線の値により、もう少し動作を詳細化します。以下の記述では、括弧内の事象が起こるまで待ち、起こったらそのあとに記述してある動作を行うことを意味します。また、初期状態では各信号線の値は0とします。
- ireq に関する処理
- (ireq が1となる)次段への処理が完了する(oreq=iack=0である)のを待って、oreq, oack, clk の各信号線を1とする。
- (ireq が0となる)oack, clk の各信号線を0とする。
- iack に関する処理
- (iack が1となる)oackが0となるのを待って、oreq を0とする。
- (iack が0となる)次段への処理が完了する。
これらは並列に起こり得るので、例えば以下の図のように ireq が0となって、次に1となってから iack が1となることもあります。
このような処理は、直感的な動作とほとんど一致していると思います。なお、緑の矢印は制御回路の外 (環境とも言います) の動作を表します。
細かな点として、 oreq を0とするのに、なぜ oack が0となるまで待つ必要があるか、と思われるかもしれません。これは、もし oack が0となるまで待たずに oreq を0としてしまうと、下図のようなことが起こってしまうからです。
すなわち、oreq を0とすることで、iack も0となり、次段への処理が完了している状態が発生します。ですので、ireq が1であることから、上図の太矢印のように、oreq が1になってしまい、次段に同じデータを2回送ってしまうことになります。
さて、いよいよ制御回路の中身を考えていきます。今度は、各出力線を1や0にする条件を考えてみます。まず、oack です。
- oack (=clk) を1にする条件
- ireq = 1, oreq= 0, iack = 0
- oack (=clk) を 0 にする条件
- ireq = 0
- 上記以外の場合
- oack の値を保持する
次に oreq です。
- oreq を1にする条件
- oack = 1
- oreq を0にする条件
- oack= 0, iack = 1
- 上記以外の場合
- oack の値を保持する
これらを回路として実現したのが下図です。oack の部分を G1が、oreq の部分を G2 が実現しています。いずれも値を保持するために、自己ループを作っている点に注意してください。また、初期状態ではすべての信号線が0ですが、G2 の自己ループは初期化できないので、アクティブロー (リセット時0、動作時1を取る) のリセット信号 rstb を使用しています。
まとめ
今回は、「ハンドシェイクとは」と題して、非同期式回路の実行制御を司るハンドシェイクとはどのようなものかについて解説し、束データ4相ハンドシェイク制御回路を具体的に紹介しました。
次回は、この制御回路を用いて、例題回路を具体的に設計し、同期式設計と比較するところまでを予定しています。
国立情報学研究所 米田友洋