FPGAのRiver Raid

FPGAでRiver Raidをまだ実行していませんか? OK、それからやります。









最近では、FPGAは私にとってブラックボックスでしたが、Verilogはまったく魔法のように思えました。論理要素に回路を構築するプログラムを作成するにはどうすればよいでしょうか。 私はこれを長い間勉強するつもりでしたが、本当の鉄片なしでは始めたくさえありませんでした。



そして最近、Cyclone IVをベースにした安価で優れたデバイスがAliexpressから来ましたが、(当時)致命的な欠陥がありました:中国語のドキュメント。 私は落胆し、Habrについてここでアドバイスを求めさえしました。 私の力を集めて、私はまだ中国人から原始的なプログラムを実行することに成功しました。 デバイスはLEDを点滅させ、私は自分に「万歳」と叫びました。 残りのサンプルをざっと見てみると、私でさえ初期レベルにいたのですが、彼らは真実を語っていることに気づきました。中国のコードはひどいものです。 曲がったコードを勉強するつもりはなかったので、手がかゆくて、すぐに簡単なプログラムを書きたいと思いました。 私はそれをピンポンにすることに決めました。アルゴリズムは原始的であり、結果は壮観です。 FPGA-Tetrisについて記事 (これらのモジュールの作者のおかげ)で、ハブでVGAとキーボードを操作するためのモジュールを見ましたが、残りはすでに「技術的な問題」です。



ピンポンに数時間を費やしました。 正直なところ、私はよく頭を壁にぶつけたいと思っていました。なぜなら、いくつかのことがうまくいかなかった理由がわからなかったからです。



ところで、Quartusの機能は、ソースコードを使用して論理回路を描画するのに非常に役立ちました。 条件、サイクルなどが「ハードウェアで」実装される方法を理解しました。



ピンポンを終えた後、私はタスクが単純すぎて、もっと面白いものを選ぶ必要があるという結論に達しました。 2番目の「プロジェクト」もゲームになることは明らかです。それを作成するのは興味深いことです。すでに述べたように、結果は効果的です。 子供の頃から、リバーレイドをプレイしている他の子供たちの様子を思い出しました-プレイする余裕がなく、高価でした。 YouTubeでゲームプレイのビデオを見た後、子供の頃の夢に気付き始めました。 今後、私が結果として得たものは次のとおりです。







私はすぐに言わなければなりません:私はこの業界で数週間の経験を持っているので、私はアドバイスをしません-私は間違っているかもしれません、間違っているかもしれません。 コメントでは、経験豊富な仲間に、私が間違っていることを修正するように頼みます。 この記事はやる気になります。 彼らは「神は鍋を燃やさない」と言っているので、できます。



私の困難は何ですか?



コンパイラーが条件を明確に示すことができない場合、コードの異なる場所にあるレジスターに何かを取り出して書き込むことはできません。 これが起こる理由は理解できます。レジスタへの入力は1つだけであり、1つの入力にロジックなしで2つの信号を送信することは不可能です。



プログラマは、コードをクラス、ルーチンなどに分割するのが大好きです。 1つのモジュール内の1つのエンティティの形成(verilogの観点から)と、別のモジュール内の別のエンティティの形成のブロックを取り除くことは論理的です。 しかし、両方のモジュールが同じ変数の値を変更した場合はどうなりますか?



正しいアーキテクチャは、このような問題を解決します。 これが2番目の難易度です。 私の知る限り、ベリログの正しいアーキテクチャは、従来のプログラミング言語よりもほぼ重要です。 敵(飛行機、船など)の作業単位を認識したとき、コンパイル後に関連するfpga要素の数が数千増加したことを覚えています。これにより、要素と最小限の機能が不足する恐れがありました。 アーキテクチャをやり直す必要がありました。



良いルールは、変数のビット深度も含め、すべてを定義することです。 残念ながら、私はこの規則を完全に遵守していなかったため、突然画面解像度を上げたい場合は、数十個の変数を変更する必要があります。



プロジェクトについて少し









デバイスには、ビデオドライバーもビデオメモリもありません。 ビデオ信号は「手動」で生成されます。 これを実装するモジュールは、2つの動的パラメーターを提供します。現在のxとyは、特定の時間の画面上の対応する位置です。 何かを描画するには、これら2つのパラメーターを常に監視し、適切なタイミングで特定のピクセルカラーをRGBモニターに送信する必要があります。



ご存知のように、VGAは色にアナログ信号を使用しますが、中国人は確保しています-1つのデジタル出力をRGBに接続しています。 その結果、武器庫には8色しかありません。 もちろん、私はこれを画面解像度で補正しようとしましたが、それでも8色の許容可能な画像は機能しませんでした。 私は演奏しようとしました:あるパスで、ある色でポイントを描き、別の色で-別のパスで半音を得るために、良いものは何も出ませんでした。まばたきは悩まされました。



また、私はアーキテクチャがあまり好きではないことを言いたいと思います。「画面を描画する」ロジックは計算と密接に関連しているため、アーキテクチャをモジュールに分割することができませんでした。 おそらく、休憩をとるなら、やがて私はそれを(学び、)修正するでしょう(アーキテクチャ)。



まず、川をスクロールしました。 このために、スクロールの位置と、この位置から始まる川の拡大(縮小)の速度が示されている配列があります。 各フレームIは、この配列を初期位置(本質的にはフローティングウィンドウ)から「ウォーク」し、この配列内の現在のY座標と現在のデータに従って川岸の現在のX位置を変更します。 川と島(彼らにとっては別個の配列)は、(元のゲームのように)私と対称的です。そのため、川と島の右側がミラーリングされています。



最初は敵のスプライトをアレイに保存していましたが、fpga要素はすぐに十分ではなくなりました-ROM Cyclone IVに転送する必要がありました。 後者にはわずかな問題があります。 事実、ROMは同期的であるため、スプライトのピクセルアドレスを設定するには、クロック(またはネガエッジが使用されている場合はハーフビート)ごとにオブジェクトの左上隅に対する現在のポイントの座標を知る必要があります。 もちろん、これは実行可能です。比較するときに座標を1ピクセル左に移動して、画面上の現在のポイントとオブジェクトの座標の交点を探すだけです。 このようなことはループで行われるため、この追加ロジックは100個の要素をスローします。 私はお金を節約することにしました(要素がお尻に残ったため)。 その結果、スプライトは実際よりも1ピクセル右に描画されます。



また、互いに重なり合う敵のバグがあります(他のオブジェクトではすべて問題ありません)-すべての敵にはスプライト用のROMが1つあるため、オブジェクトの交差の場合に使用するスプライトのアドレスを計算するには、現在どのオブジェクトのピクセルが透明であるかを知る必要があります。 かなり複雑なロジックがあるので、私も気にしないことにしました。



コードを可能な限りエンティティに分割するために、ある種のコンベヤー(ステートマシン)を実装しました。最初の段階では、川の方向を変更する価値があるかどうかを確認し、2番目の段階ではFIFOに新しい敵を配置する価値があります。3番目の段階では、敵を移動する必要があります。



スプライトの写真はGoogleから親切に提供され、mifファイルはpythonのスクリプトによって作成されました。



ゲームの1分ほどで、疲れただけで、世界はかなりトレースされました。このプロジェクトは、夜間の非就業時間と非家族時間の間に見送られました。 誰かが腺の上を走ることができたら-私は追加できます



» Githubプロジェクト



以来、 ishevchukに感謝します 彼のおかげで、私はfpgaのことがどれほど強力かを理解しました



All Articles