FPGAでライフゲームを作りました
動画の内容
- ランダムに初期化(see 線形帰還シフトレジスタ - Wikipedia)
- しばらく実行(高速モード:1ステップ3msec。VGA60Hzの画面の更新が17msecなので画面1回更新されるごとに5ステップ進んでいる計算)
- 画面をクリア、低速モード(256倍遅い)に移行
- Rペントミノを配置するボタンを押す
- しばらく走らせる
- 高速モードに変える
実装
- 読み書き用のアドレスをインクリメントする
- 2行+3マス文の323bitのシフトレジスタに読んだデータをpushする(see シフトレジスタ - Wikipedia)
- シフトレジスタの0, 1, 2, 160, 161, 162, 320, 321, 322の9ビットを束ねてアドレスとし、ライフゲームのルールがハードコードされた512bitのROMから1bit読む
- 読んだ値をVRAMに書き込む
各1クロックでできるかな〜と思ったけども、画面が流れるバグ(おそらくアドレス計算が間に合わなくて1つずれたところに書き込んだと思われる)が起きたので今は各2クロックで動かしている。
なので今は 1ピクセルあたり8クロック * 160x120ピクセル / 50MHz で画面1回更新するのに3msecということになる。頑張ればもっと縮む可能性がある。
消費したリソースは、ロジックエレメントが459個(3%)、メモリが57912bit(11%)とのこと。あと9倍くらい余裕があるから、解像度を2倍にしてピクセル数を4倍にするくらい行けそうだけど、この問題をどうやって解決するか: DE0でFPGAのチップ内蔵RAMをVRAMに使おうとしたら上手く行かなかった日記
ソースコード
Verilog HDL素人が書いたソースコードはこちら: https://github.com/nishio/fpga/blob/master/lifegame.v
ちなみにさっき知ったんだけど、Verilog HDLにもfor文があるみたいですよ。(えっ
追記
さすがに酷いので単純なシフトレジスタはfor文を使って書き換えました。512行のルールテーブルは何とかならないかな。
追記
シフトレジスタの実装について、こんなことしなくても
for(i = 0; i < 322; i = i + 1) begin buffer[i + 1] <= buffer[i]; end
この1行でいいじゃん、という指摘を頂きました(thanks @natutan!)
buffer[1:322] <= buffer[0:321];
たしかに…それができることは知っていたのになぜ思いつかなかったか…。たぶん僕の脳の中ではまだC言語の配列みたいなイメージで捉えられているのだろうなぁ。だから「要素が323個ある配列」を1個ずらすことが「代入文1つでできる」という発想を思いつくことができなかった。