はじめに
ファミコン世代にとっては当たり前に通じてしまう「リセットボタン」も、だんだん通じにくくなっているという話も聞きます。
今回はFPGAのリセットについて考えてみたいと思います。同期リセットとか非同期リセットとか、負倫理/正論理とかの話ではなく、リセットの必要性そのものの話です。
いわゆる FPGA での普通の書き方を知りたくて検索された方は下記の記事をどうぞ。
リセットとは
ASIC を作るにせよ(作ったことないけど)、CPLD でロジックを作るにせよ、74シリーズ等のロジックICを並べるにせよ、フリップフロップやSRAM/DRAMなどの揮発記憶素子は基本的に電源投入段階ですべて不定値です。
そして基本的にすべての記憶素子が不定値からスタートできるようなシステムは無いわけではないですが、普通のシステムではほぼないかと思います。したがって、何らかの初期化が必要なフリップフロップに対してはリセットが接続され、RAMなどに対しては必要あれば何らかの初期化処理を検討するのが一般的かと思います。
電源オン時の初期化についてはパワーオンリセットといわれ、基板に電源が投入されて電源が安定した後に一定時間リセットをアサートするように回路構成したり専用のリセットICを置くことで実現されます。
そして、このリセットがいろんな意味で便利であるので、しばしパワーオン時だけではなく、システム異常時の復帰や、リセットボタンのようなもので、有無を言わさず初期状態から再実行するのにも使えます。
リセットは不定値の取りうるありとあらゆる状態からの正確な起動を保証しなければなりません。そのありとあらゆる状態は正常な実行中や異常な実行中に取りうるすべての状態も包含しますので、とにかくリセットしたら正常に初期状態になるわけです。 というか、そうなることかが保証されるようにリセットを設計しないといけないわけです。
これがまあファミコン的なリセットボタンですね。
FPGAには初期値がある
一方で FPGA は、パワーオン後にFlashメモリから bitstream を読み出したり、CPUから bitstream をダウンロードして書き換えたりといった起動方法をとることもあり、本来不定値であるはずのフリップフロップやSRAMなどに初期値を設定することができるものがあります。 そもそもFPGAはSRAMの塊でそれらを初期化することで回路を書き換えるので、必然的に初期値を持つのだと思います。
Verilog などのハードウェア記述言語では、レジスタ変数に初期値を与えたり、initial 文で初期化する書き方が可能です。これはもともとはシミュレーション時を想定した文法だったのだと思いますが、FPGA 用のシンセサイザはこれを解釈してレジスタやメモリ(配列)に初期値を設定します。
下記は、AMD の Viado でリセットでレジスタを 1 にする Verilog を合成したものですが、初期値は 0 でした。

一方で、下記のように 1 に初期化すると、

下記のように初期値が 1 になります。

UG970などを見てみると、Attribute に INIT があり、初期値を指定できることがわかります。

(ちなみに、個人的には初期値指定しなかったときはシンセサイザにはリセット値で初期化してほしいところですが、どうも試した範囲では常に0っぽい?)
FPGA ではそもそもリセット要らないのでは?
HLSなどの台頭で、FPGA を GPU のようにアクセラレータとして、次々と書き換えながら利用するケースも増えてきました。
この時、リセットは必須ではないような気すらします。
- ダウンロード時に確実に初期値が設定される
- CPU上のプログラムからロードされ、途中のリセットの必要性がほとんどない
- むしろCPUとの共有メモリなどにアクセスするのでバストランザクション中にリセットする方が危険
などいろいろな要因がありそうに思います。
ASIC と FPGA で同じソースを流用するのは危険?
ASCI向けのソースコードを検証のために FPGA に持ってくることは割とよくあるのかと思います。
この際は、適切にリセットを制御することでもちろん正しく動きます。
一方で、FPGAのことしか考えていないコードは、うっかりASICに持って行ったりすると、シミュレーションでは動くが実機で正しく動かない なんてことになりえません。これは ASIC 系の人からすると恐怖でしかないかと思います。
もっとも FPGAしか想定しておらずASIC化などを完全にスコープ外にして問題ない開発も多いので、そもそも混ざること自体が稀だとは思いますが。
FPGA屋はどうするべきなのか?
そうはいっても、FPGAでしか使わないとはいえ、ある程度行儀よく書いておきたいという思いもあるし、リセット無で正しく動くならその分の配線を削減して性能を上げたいと考えるのもエンジニアのサガかと思います。
ではどうすればいいのでしょう?
思いつく範囲として
- リセット記述は書くだけ書いておいて、非リセットの固定値にして論理圧縮で消えてもらう
- 初期化や initial は ifdef などで取り外して、リセットが効くかのシミュレーションもできるようにしておく
- 動作中にリセットで初期化できるかなどもテストしておく
などでしょうか?
FPGAであっても後から途中リセットが欲しくなるケースもありますし、やはり Verilog のお作法的にリセットはないと気持ち悪い部分もありますし、何らかの形で初期値に頼らない初期化方法は記述しておきたいのも確かだったりします。
おわりに
なんとなく気になって初期値指定のあるなしで合成結果を調べたレベルではあるのですが、実は結構奥深い問題のような気もしてきました。
あらためて「やはりFPGAってソフトウェアだよな」と思った一幕でもありました。

