AXIは基本的にReady-Validのハンドシェークで動いているのだけれども、これをひたすら繋いでいくと、ValidとReadyがFFを経由せずにつながってしまい、タイミング的に問題となる。
これを改善するために、各種バスプロトコルにはスライサと呼ばれるFFで区切る部品が存在するのだけれども(一般的スライサと言うのかな?分からない)、いろんな実装方法があって、ただし基本的に考え方は全部同じだと思っている。
FIFOを使ったり、Skid Bufferやストリームレジスタを使う方法があり、私が良く参考にしているのは、ステートマシンでSkid Bufferを構成する方法。以下のページが非常に参考になり、よく勉強させてもらっている。
初めて見たときは少し感動してしまったのだが、要するにいくつかの条件に分けてステートマシンを構成しており、
- マスター側のReady-Valid (mready, mvalid)
- 内部のMasterとSlaveが埋まっているときの一時格納用のTValid (tvalid)
スレーブ側のReady-Valid (sready, svalid)
tvalid=0, mready={0,1}, mvalid=0 : の場合、mvalidが上がっていないというとことは、SkidBuffer内にデータがたまっていないのでSreadyは上げる。Skid Bufferに新しいデータを受け入れ可能。
- tvalid=0, mready=0, mvalid=1 : の場合、mvalidが上がっておりデータを転送したいがmreadyが下がっているのでまだ転送は完了しない。tvalidにデータを格納できるのでSreadyを上げていてもよい。
- tvalid=0, mready=1, mvalid=1 : の場合、mvalidが上がっておりmreadyも上がっている。つまりマスター側は転送完了なのでスレーブ側からデータを受け入れてよい。
- tvalid=1, mready={0,1}, mvalid=0 : マスターが転送したいデータが存在しないにもかかわらず、tvalidが上がることはあり得ない。この条件は無効。
- tvalid=1, mready=0, mvalid=1 : マスターに転送したいデータが存在しているが、まだ転送は完了していない。tvalidも上がっているので、これ以上スレーブ側からはデータを受け入れられない。
- tvalid=1, mready=1, mvalid=1 : マスター側はデータの転送が完了し、tvalidが立ち上がっているのでそのデータをマスター側に移す。スレーブ側は受け入れられるようになるのでsreadyを上げる。
というステートマシンを構成すればよい。これをVerilogで記述したものが紹介されている。
ちなみに、skid bufferの他の実装については、以下にも解説が載っている。
付録の実装はAlteraからIntelに社名が変わった時点で消えてしまっているのだが、誰かがGitHubにアップロードしているのを発見した。ありがたい。