Comments
Description
Transcript
FPGAとLCDを用いたゲームマシンの設計
卒 業 研 究 報 告 題 目 FPGA と LCD を用いたゲームマシンの設計 指 導 教 員 橘 昌良 助教授 報 告 者 笠原 拓二 提出日 平成 18 年 2 月 20 日 高知工科大学 電子・光システム工学科 1 目次 はじめに・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・1 第1章 ゲームの内容・・・・・・・・・・・・・・・・・・・・・・・・・・・・・2 1.1 テトリス・・・・・・・・・・・・・・・・・・・・・・・・・・・2 1.2 ルール・・・・・・・・・・・・・・・・・・・・・・・・・・・・2 第2章 設計方法・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・3 2.1 設計の概要・・・・・・・・・・・・・・・・・・・・・・・・・・3 2.1.1 FPGA について・・・・・・・・・・・・・・・・・・・・・4 2.1.2 VHDL について・・・・・・・・・・・・・・・・・・・・・4 2.1.3 LCD・・・・・・・・・・・・・・・・・・・・・・・・・・4 2.1.4 SRAM・・・・・・・・・・・・・・・・・・・・・・・・・4 2.2 設計の流れ・・・・・・・・・・・・・・・・・・・・・・・・・・5 第3章 ゲームマシン仕様定義・・・・・・・・・・・・・・・・・・・・・・・・・7 3.1 仕様検討・・・・・・・・・・・・・・・・・・・・・・・・・・・7 3.1.1 テトリス本体の仕様・・・・・・・・・・・・・・・・・・・・7 3.1.2 テトリスの動作周波数・・・・・・・・・・・・・・・・・・・8 3.1.3 テトリスのアルゴリズム・・・・・・・・・・・・・・・・・・9 3.1.4 SRAM の動作・・・・・・・・・・・・・・・・・・・・・・11 3.1.5 ディスプレイの仕様・・・・・・・・・・・・・・・・・・・13 3.2 大まかな機能分割・・・・・・・・・・・・・・・・・・・・・・15 第4章 HDL設計・・・・・・・・・・・・・・・・・・・・・・・・・・・・・17 4.1 VHDL による記述・・・・・・・・・・・・・・・・・・・・・・17 4.1.1 VHDL 宣言部・・・・・・・・・・・・・・・・・・・・・・18 4.1.2 同期回路設計とリセット信号・・・・・・・・・・・・・・・19 4.2 ディスプレイ制御回路・・・・・・・・・・・・・・・・・・・・20 4.2.1 同期信号用のカウンタ設計・・・・・・・・・・・・・・・・21 4.2.2 アドレス計算・・・・・・・・・・・・・・・・・・・・・・23 4.2.3 LCD_controller の状態遷移・・・・・・・・・・・・・・・・24 4.2.4 LCD_controller のシミュレーション結果・・・・・・・・・・25 4.3 SRAM 制御部の設計・・・・・・・・・・・・・・・・・・・・・26 2 4.3.1 SRAM アクセスタイミング・・・・・・・・・・・・・・・・27 4.3.2 メモリーコントローラの状態遷移・・・・・・・・・・・・・29 4.3.3 Memory_controller のシミュレーション結果・・・・・・・・30 4.4 テトリス本体の設計・・・・・・・・・・・・・・・・・・・・・31 4.4.1 テトリスの状態遷移回路・・・・・・・・・・・・・・・・・31 4.4.2 データ構造・・・・・・・・・・・・・・・・・・・・・・・33 4.4.3 アルゴリズム詳細・・・・・・・・・・・・・・・・・・・・35 4.4.4 テトリスのモジュール構成・・・・・・・・・・・・・・・・39 おわりに・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・40 謝辞・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・41 参考文献・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・42 付録1 付録2 LCD_controller の VHDL 記述・・・・・・・・・・・・・・・・・・・・43 Memory_controller の VHDL 記述・・・・・・・・・・・・・・・・・・48 3 はじめに 携帯電話に代表される情報端末やカーエレクトロニクスなどでは、ソフトウェアとハー ドウェアから構成されている組み込みシステムとして製品化されている。このような組み 込みシステムでは、小型化、低価格化、低消費電力化への要求から、回路の高機能化とと もに複雑化が進んできた。そのため、システム LSI の設計には、大規模な回路を短期間で 設計し、さらに設計資産を再利用できるような設計方法が考えられてきた。 近年の LSI 設計・開発現場ではハードウェア記述言語(HDL:Hardware Description Language)による設計が主流になってきている。HDL は、プログラミングの構文に似て いて、回路の機能を言語レベルで記述するだけで論理合成することができ、容易に回路の 設計、シミュレーションを行うことができる。これにより、従来のゲート・レベルでの設 計よりも大幅な設計期間の短縮がかなった。[1] 本研究では、HDL による回路設計を学ぶために、プログラムによる回路の作成・書き換 えが可能な FPGA を用いて、テトリスというゲームマシンの設計を行なった。研究の概要、 経過について本論文に報告する。 本論文は全 4 章構成になっている。まず、第 1 章で設計するゲームの内容について説明 し、次の第 2 章で設計の流れ、各装置の概要などを説明する。第 3 章は 2 章の内容をより 具体化したもので、第 4 章から実際の設計の内容についての解説になっている。最後に、 付録として VHDL のソースを添付する。 4 第1章 ゲームの内容 この章では、今回製作するゲーム『テトリス』の概要を簡単に紹介する。 1.1 テトリス テトリス(Tetris, 露Тетрис)は、1980 年代末から 1990 年代初めにかけ、世界各国 で大流行したコンピューターゲームである。もともとは旧ソ連の科学者アレクセイ・パジ トノフ(w:Alexey Pajitnov)が教育用ソフトウェアとして開発したものであったが、その後ラ イセンス供給が様々なゲーム制作会社に対してなされ、各種のプラットフォーム上で乱立 する状態になった。なお、テトリスのもつ数学性、動的性、ならびに知名度から、テトリ スをゲームプログラミングの練習題材として用いられる例がしばしばみられた。 ゲームの内容は、1 つの画面を次々と落下してくるブロックを積み上げ、それをひたす ら消していくという単純なアルゴリズムのパズルゲームである。次に、ゲームのルールに ついて解説する。[10] 1.2 ルール フィールドは格子状のブロックを敷き詰めることが可能な領域からなる。 片面型テトロミノ状のブロックピースがフィールド上方から 1 つずつ落下してくる。 プレイヤーは、落下中のブロックピースを 90 度単位で回転させるか、格子単位で左右 に移動させるか、ブロックピースを高速に落下させるかのいずれかの操作をすること ができる。 ブロックピースがフィールド最下段、または他のブロックの上に着地すると、そのブ ロックピースはブロックとしてフィールドに固定される。そして新しいブロックピー スがフィールド上方に出現し、再び同じ処理が繰り返される。 格子の特定の行がすべてブロックで埋め尽くされると、その行にあるブロックが消滅 し得点となる。同時に多くの行を消去するほど高得点が得られる。 (特に 4 段消しを「テ トリス」と呼ぶ。 )消滅した行の上にあったブロックは、消えた行数分落下する。 固定されたブロックがフィールドの最上段まで積み重なるとゲームオーバーとなる。 以上がテトリスの基本的なルールである。詳しいアルゴリズムについては第 3 章から解 説する。 5 第2章 設計の方法 この章では、設計に関する概要や使用したデバイスについて説明する。 2.1 設計の概要 本研究では、近年、デジタル回路設計手法として一般的になってきたハードウェア記述 言語の 1 つである VHDL を用いて回路の機能設計を行う。設計した回路の実装には、源発 振 30MHz ほどで動作する FPGA を用いる。また、ゲーム画面表示用として LCD、フレー ムバッファ用として SRAM を使用する。 (図 2.1 参照) テトリスの動作 全体の制御 VHDL で記述 LCD FPGA SRAM Controller 図 2.1 全体の構成イメージ 6 2.1.1 FPGA について FPGA(Field Programmable Gate Array)とは PLC(Programmable Logic Device)の 一種で、その名前のとおり内部の倫理を回路設計者がプログラムできる LSI である。 プログラムによって容易に内部倫理を再構成できるため、回路実装後の動作シミュレ ーションをした後でも機能の変更、追加を行うことができる。[1] 2.1.2 VHDL について VHDL(Very high speed integrated circuit Hardware Description Language)とは アルゴリズム・レベルからゲート・レベルまでの抽象度の広い範囲においてデジタル システムをモデル化することができるハードウェア記述言語である。言語レベルでの 記述から、自動設計(Design Automation : DA)ツールにより論理合成、配置配線を自動 化できるため、従来の回路図入力に比べ大幅に設計期間を短縮することができる。ま た、大きな特徴として C 言語のようにプログラムを先頭から順に処理するソフトウェ ア記述と違い、ハードウェアを設計する上で欠かすことのできないデジタル回路での 並列動作を、正確にモデリングすることができる。詳しい記述方法については第 4 章 で解説する。[1][2] 2.1.3 LCD(Liquid Crystal Display) 液晶を利用した表示装置。2 枚の偏光板と液晶分子を利用して光の透過率を調整する ことにより、画像を表示する。液晶分子自体は発光しないため光源となるバックライ トが必要となる。 CRT ディスプレイや PDP など他の表示装置に比べて薄くて軽いので、携帯用コンピ ュータや省スペースデスクトップパソコン等によく使われている。以前まで LCD を代 表とする表示デバイスはコストが高く、自動車や民生機器などの組み込み機器で使わ れることはあまりなかったが、携帯電話や薄型テレビなどの普及により LCD のコスト がかなり下がった。そのため、最近では組み込み機器向けに必要十分な機能に絞り込 んだグラフィックス LSI も生産されている。 2.1.4 SRAM(Static Random Access Memory) プログラムやデータを記録保持する装置の一種。記憶素子としてフリップフロップ 回路を用いるもので、アクセス速度が速く消費電力が小さいという特徴があるが、回路 が複雑になり集積度を上げにくいという欠点を持つ。電源を切ると記憶内容は失われる。 非同期 SRAM、シンクロナス(同期)SRAM、デュアル・ポート SRAM などいろいろ なバリエーションがあるが、本研究ではもっとも一般的な非同期 SRAM を使用する。 7 2.2 設計の流れ 工 程 概念設計 仕様検討 動作設計 ブロック分割 HDL コーディング データパス設計 コンパイル 設計データ 仕様書 ブロック図 ソースコード テストベンチ RLT シミュレーション 論理設計 論理合成・配置配線 物理設計 FPGA ダウンロード 製造 実機検証 ネットリスト 回路情報 図 2.1 FPGA 開発フロー 図 2.1 は FPGA 設計の基本的な流れである。上位レベルの工程から順に設計を行ってい くが、下位レベルで問題がでたら上位レベルに戻って検討しなおす必要がある。次に、順 を追いながら各工程について簡単に解説していく。[6] 1. 仕様検討 これから作成する回路の機能を決める。実際の設計では要求仕様が提示されるので、 制約事項を踏まえて設計に着手できるレベルまで仕様を詳細に決めていく。この段階 で、どの機能をどのように実現するかのイメージがあれば、後の工程がスムーズに進 められる。 8 2. ブロック分割 仕様が決定してもいきなり HDL で記述するのは困難なので、機能別にいくつかのブ ロックに分割する。機能の切り分けが終わったら、それぞれの機能ブロック間でどの ようにデータ、制御信号が流れているのかブロック図を書いて明らかにする。 3. HDL コーディング 分割した機能モジュールごとに、HDL を用いて RTL(Register Transfer Level ; レ ジスタ転送レベル)で記述する。 4. コンパイル FPGA 化できない HDL コードをシミュレーションしても意味がないので、まずは記 述したコードに問題がないかどうかを FPGA 開発ツールでチェックする。 5. RTL シミュレーション テストベンチを作成して、シミュレーションで HDL コードの論理検証を行う。 6. 論理合成・配置配線 論理合成ツールを使用して RTL の HDL コードをゲート・レベルのネットリストに 変換する。生成されたネットリストに従って、FPGA 開発ツールで部品を配置し、配 線する。必要に応じてバック・アノテーション(配置配線後の遅延情報を反映したシ ミュレーション)を実施する。 7. FPGA ダウンロード FPGA 開発ツールで FPGA へ渡す回路情報(ビットストリーム)を生成し、ダウン ロード・ケーブルを介して回路情報を FPGA へ転送する。 8. 実機検証 FPGA を実際に動作させて検証する。 以上の手順で、次章から実際にテトリスを製作した流れにそって解説していく。また、 本論文では工程 1∼2 が第 3 章、工程 2∼5 が第 4 章、工程 6∼8 が第 5 章にそれぞれ対応 する。 9 第3章 ゲームマシン仕様定義 この章では、本研究で使用したデバイスの動作の仕組み、制約条件や、テトリスの動作 仕様について大まかに説明する。 3.1 仕様検討 初めに、ゲームのルールや動作、各デバイスの仕様を解説する。 3.1.1 テトリス本体の仕様 まず、ユーザーインターフェースの部分について下図に示す。 左 右 下 回転 Start Reset 図 3.2 入力インターフェース 図 3.1 テトリス画面 図 3.3 使用するブロック 10 図 3.1 は実行中のゲーム画面である。ゲームフィールド内は 10*24 段で構成され、画面 の右上に次のブロック、右下に各データを表示する。データの意味はそれぞれ次のとおり である。 SCORE : ゲームの得点。ブロックを消すと加算される。一度に消す段数に比例して 多くの点数が加算される。 Lines : 消去した段数をカウントする。一定値に達すると Level を加算する。 Level : ブロックピースの自動落下速度に影響する。 図 3.2 はゲームコントローラーである。以下に説明する5つのキーで、回路全体を制御す る。 Right_key : ゲーム実行中において、ブロックピースの右方向移動を入力する。 Left_key : ゲーム実行中において、ブロックピースの左方向移動を入力する。 Down_key : ゲーム実行中において、ブロックピースの下方向移動を入力する。 Turn_key : ゲーム実行中において、ブロックピースの回転を入力する。 Start/Stop_key : ゲーム待機状態の時、ゲーム開始信号 Start を入力する。また、ゲーム 実行中の場合、ゲーム内容を保持したままゲーム待機状態へ遷移する。 その間ディスプレイの表示はオフになる。 Reset_key : ゲームを初期状態にもどす。 図 3.3 はゲーム内で使用するブロックである。基本となる各色 7 種に回転後のブロックも 含め、計 19 個のブロックが登場する。回転後のブロックデータもそれぞれ SRAM に記憶 しておく。 3.1.2 テトリスの動作タイミング ブロックの自動落下速度を 100ms∼500ms くらいとして回路設計を考えた場合、内部 クロック(30MHz=33.3ns)に比べて極端に遅い。また、キー入力による操作を考えても、あ まり高速で動作する回路だと設計しづらくなる。よって、テトリス本体の回路には内部ク ロックを分周した 1KHz 程度(1ms)で設計する。 11 3.1.3 テトリスのアルゴリズム ここでは、テトリスの動作について解説する。テトリスのアルゴリズムについては、す でに Web で C 言語によるソースコードが紹介されているので、それを参考にアルゴリズム 確認、動作検証をおこなった。[9] まず、テトリスの主な処理として、以下のようにまとめられる。 ○ メイン…トップモジュールとして、各処理の呼び出しとゲーム実行中か否かの判定 を行う。 ○ 初期化処理…ゲーム内の各データを初期化する。 ○ ブロック生成処理…新しいブロックをランダムに生成する。 ○ ブロック落下処理…ブロックの自動落下の処理。 ○ ブロック操作処理…キー入力に応じた制御を行う。 ○ ブロック移動処理…ブロックを移動させる処理。 ○ ブロック回転処理…ブロックを回転させる処理。 ○ 重なり検査処理…移動、回転後のブロックが壁と重なってないか判定する。 ○ 画面描画処理…画面の表示を更新する。 ○ ブロック固定処理…壁と接触したブロックを固定する。 ○ 消去判定・消去処理…フィールド全段において消去判定を行い、ブロックの横 1 列 揃ったラインを消去し、得点を加える。 図 3.4 はテトリスのプログラム構造を大まかにまとめた関係図である。SPD(Structured Programming Diagrams)という表記法を使用している。基本的な処理の流れとしては、各 ブロックを結んだ線上を上>右>下の優先順位で進んでいき、枝の端まできたら最初の枝 の下方向へ進んでいく。[3] 12 レベル1 レベル2 レベル3 前処理 ゲーム初期化処理 テトリス 各初期値の設定 (WHILE : 否ゲームオーバー) (IF : キー入力検出) ブロック操作処理 then else (IF : カウント一定未満) ブロック作成処理 ブロック座標初期化 画面更新処理 乱数発生 入力キー判定 ブロックデータ再配置 (SWITCH : 入力キー) (IF : 重なり検査) 右 (IF : 重なり検査) 時間をカウント ブロック落下処理 移動処理 ゲームオーバー処理 ブロック座標更新 左 (IF : 重なり検査) カウントの初期化 画面更新処理 後処理 ゲーム終了 移動処理 下 (IF : 重なり検査) 移動処理 回転後ブロックロード (IF : 重なり検査) 回転 回転処理 (IF : 重なり検査) 回転前ブロックに戻す 画面更新処理 移動処理 ブロック固定処理 (IF : 消去判定) ブロック作成処理 消去処理 画面更新処理 得点計算 画面更新処理 図 3.4 テトリスプログラム SPD 13 3.1.4 SRAM の動作 メモリとして、512k×8 ビット構成の非同期 SRAM を使用した。アドレスバス幅 19 ビ ット、データバス幅 8 ビットで、CS(チップセレクト) 、OE(アウトプット・イネーブル) 、 WE(ライト・イネーブル)の 3 つの制御信号により動作を決定する。各制御入力と動作状態 の関係は表 3.1 のようになっている。[8] 表 3.1 FUNCTIONAL DESCRIPTION(データシートより) CS OE WE I/O Pin Mode Power H X X High-Z Deselected Standby L H H High-Z Output disabled Active L L H Dout Read Active L X L Din Write Active CS はデバイスの選択状態をあらわす。制御信号は負極性であるので“L”のとき信号が オンとなり、デバイスが選択状態になる。選択状態でないとき、ほかの入力ピンはすべて 無視される。OE はデータ出力バッファを開く信号を意味し、WE は SRAM への書き込み 信号を意味する。ただし、WE と OE の両方を“L”にした場合 WE が優先されるため、リ ード動作を行うときは WE を“H”にしておかなくてはならない。 非同期 SRAM はその名のとおり、 特定のクロック信号に同期して動くようなことはなく、 入力信号の状態に対応して動作する。また、リード時に有効なデータが確定したことや、 ライト時にデータを受け取ったということを示す信号はないので、データシートのタイミ ング図から読み取りながら設計する必要がある。リード・ライトサイクルについて、それ ぞれ 2 パターン計 4 種類のアクセス方法を解説していく。 14 ○OE コントロールド・リード アドレスを制定させて、CS=”L”、WE=”H”とした状態で OE をアサート(L レベル)するこ とによりデータを I/O ピンに出力する。また、CS、WE、OE がリード状態の条件を満たさ なくなると、SRAM は I/O ピンのドライブをやめ、ハイ・インピーダンスになる。 (図 3.8 参照) ○アドレスコントロールド・リード 上記の CS=”L”、WE=”H”、OE=”L”の状態のままアドレスを変化させることにより、新 しいアドレスのデータを I/O ピンに出力させる。つまり、アクセス状態のままアドレスだけ を変えて違うアドレスのデータを読み込む。ただし、高速 SRAM の一部にはデバイスが選 択状態でアドレス変化をさせると誤動作するような物もあるので、この使い方が許される か否かは事前の確認が必要である。 ○WE コントロールド・ライト アドレスを制定させて、CS=”L”の状態で WE をアサート(L レベル)すると、I/O ピンはハ イ・インピーダンス状態になり、その後ホストがデータを確定させる。データの書き込み 動作は WE の立ち上がりエッジで行われる。 ○CS コントロールド・ライト WE をあらかじめアサートした状態で、CS を利用してデータを書き込む。WE が先にア サートされているので、デバイスが選択状態になるのと同時にライト状態になる。 15 3.1.5 ディスプレイの仕様 本研究では、SHARP 製 5.5 インチ パッシブカラー液晶モジュール LM32C041 を使用し た。LCD の外形は 150mm(W)×116mm(H)×25mm(D)。表示画素は 320(縦)×240(横)× RGB。間引き式階調コントロール回路を内蔵しており、垂直同期信号、水平同期信号、ド ットクロック、RGB 各ビットの表示データ入力により RGB 各 8 階調 512 色を表示する。 表 3.2 はタイミングの定格値、図 3.5、3.6 はそれぞれ同期信号に対するデータ入力のタイ ミングを示している。[7] 表 3.2 インターフェースタイミング定格表 項 目 定 格 値 MIN TYP MAX フレーム周期 12.5ms - 20ms クロック周期 100ns - - HSYNC”H”レベル幅 376DCK - - HSYNC”H”レベル幅 248HSYNC - - ”L”レベル HSYNC 幅 3DCK - - ”L”レベル VSYNC 幅 3HSYNC - - VSYNC HSYNC 3H 4H 240H +4H Y=1 Line data Y=240 (a) 水平方向のタイミング HSYNC DCK RGB data 3DCK 48DCK 320DCK X=1 +8DCK X=320 (b) 垂直方向のタイミング 図 3.5 データ入力タイミング図 16 y 座標 HSYNC (240.1) (1.1) 320DCK (1.1) (1.320) x 座標 240H (2.1) (239.320) (240.1) (240.320) VSYNC (240.320) (a)通常の配置画面 (320,1) (b)ゲームでの配置 図 3.6 画面上での動作 ○タイミング図の動作の説明 VSYNC は画面縦方向の同期信号、HSYNC は横方向の同期信号を表している。データ入 力の流れとしては、図 3.5(a)に示すように、まず VSYNC の立ち上がりから HSYNC4 周期 (379DCK)をカウントし、その後 HSYNC1 周期ごとに 1 行分のデータ入力が開始される。 1 行分のデータ入力に関しては、 図 3.5(b)に示すように HSYNC の立ち上がりから DCK(dot clock)48 周期をカウントし、 その後 DCK(に同期して 1 画素のデータが LCD に入力される。 この流れを繰り返しながら、図 3.6(a)に示すように画面左上隅から掃引を開始し、右下隅で 1 画面分の掃引を完了する。 ○本研究での使い方 テトリスのゲーム画面は縦長であるので、LCD を 90 度 時計回りに回転した配置で使用 し、よって、x,y 座標の進み方は図 3.6(b)のようになる。 また、デジタル入力の液晶の場合、アナログ入力の液晶と違い画像データを送り続けな くても画面を保持し続けることができる。図 3.4 を見てもわかるとおり、テトリスの場合、 画面更新処理はそう多くない。したがって、通常時 LCD の同期信号を止めておき、テトリ ス本体回路からの画面描画要求があった時のみ1フレーム分動作するよう設計する。画面 を連続して描画し続ける場合、DCK の 1 周期を LCD からの SRAM アクセスとテトリス本 体からの SRAM アクセスに二分して割り当てなくてはならないため、連続描画よりかなり 設計を簡素化できる。LCD のフレーム周期 12.5ms∼20ms と、テトリス本体の動作周波数 (1KHz=1ms)を考えると、ゲーム動作への影響も少ないと思われる。 V シンク(VSYNC:vertical synchronous)、 H シンク(HSYNC : horizontal synchronous) 17 3.2 大まかな機能分割 前節で得られた要求仕様をもとに、大まかな機能分割を行い、入出力を明確にする。 1 つのモジュールだけで記述することもできるが、大規模で複雑な機能だと機能記述が散乱 し、不具合が発生しやすくなりデバッグも大変になってしまう。設計データの再利用性を 高めるという意味でも、見通しのよい機能ブロックへ分割していく。 まず、最上位ブロックの入出力を明らかにする。(図 3.7 参照) 図 3.7 のブラックボックスを、いくつか機能ブロックに分割したものが図 3.8 である。内部 の主な回路はほとんど順序回路であるので、複数の有限状態機械(finite state machine : FSM)で構成される。 h_sync Right key v_sync Left key DCK Down key LCD data bus(9bit) LCD Turn key FPGA Start/Stop CS Reset key OE WE Clock SRAM data bus(8bit) SRAM address bus(19bit) 図 3.7 最上位ブロックの入出力仕様 LCD スイッチ テトリス LCD・メモリ パルス制御部 メイン回路部 制御部 SRAM 図 3.8 大まかな機能ブロック図 18 FSM DFF LCD LCD 制御 DFF tetris FSM DFF DFF Memory 制御 DFF FSM SRAM Controller DFF T_CLK GEN 図 3.8 全体の機能ブロック図 図 3.8 について、右のモジュールから順に簡単に説明する。 ○LCD 制御 FSM テトリス本体からの制御信号により LCD 同期信号を発生し、画面を描画したり非表示に したりする。L_con(LCD_controller)というモジュール名で設計する。 ○メモリ制御 FSM テトリス本体からの SRAM アクセスと、LCD からのアクセスに対しバス調停を行い、 SRAM アクセス用の制御信号を操作する。Mem_con(Memory_controller)というモジュー ル名で設計する。 ○tetris FSM テトリスの動作を記述します。図 3.4 のアルゴリズムを参考に、さらにいくつかのステー トマシンで構成される。Tetris_main というトップモジュールから設計していく。 ○DFF・T_CLK コントローラーからの入力信号のチャタリングを防止するとともに、入力パルスを DFF によって保持する。T_CLK では、源発振からのクロックを分周してテトリス本体用のクロ ック(1kHz 程度)を生成する。 19 第4章 HDL設計 この章では、まず VHDL の基本的な記述のしかたについて説明する。その後、各機能ブ ロックについて RTL で記述できるところまで詳細に設計する。[1][2][4] 4.1 VHDL による記述 VHDL の基本構造は図 4.1 に示すように、ライブラリ宣言とパッケージ宣言で始まり、 エンティティ宣言で入出力ポートを定義し、アーキテクチャ宣言に動作を記述していく。 library ライブラリ名; ライブラリ宣言 use ライブラリ.パッケージ名.all; entity エンティティ名 is port( 信号 1 : in std_logic; エンティティ宣言 信号 2 : out std_logic); end エンティティ名; architecture インプリメンテーション名 of signal node1 : std_logic; エンティティ名 is 内部信号宣言部 begin 動作記述部 end インプリメンテーション名; アーキテクチャ宣言 図 4.1 VHDL の言語構造 20 4.1.1 VHDL 宣言部 ○ライブラリ宣言 ライブラリは、あらかじめ定義されたデータの集まりである。このライブラリを使用す るための宣言を行う。IEEE ライブラリは、VHDL 記述において必須であり最初に必ず library IEEE; と記述する。 ○パッケージ呼び出し パッケージは、データ・タイプなどをまとめたものである。とりあえず基本的な型が定 義されている std_logic_1164 は必須。また、最後の all はパッケージ中のすべてを使うとい う意味である。 ○エンティティ宣言 エンティティは、回路の入出力ポートを定義する。 entity エンティティ名 is と end エンティティ名; の間に信号の方向と属性を列挙する。 信号の方向は、入力(in)、出力(out)、双方向(inout)の 3 種類、属性はビット(std_logic)と バス(std_logic_vector)を主に使用する。std_logic_vector ではデータ幅を指定する。このと き、downto で定義すると最上位ビットが番号の大きい信号に割り当てられる。to で定義し た場合逆になる。 ○アーキテクチャ宣言 アーキテクチャ宣言は、 architecture インプリメンテーション名 of エンティティ名 is で記述する。本体は、ノード宣言部と動作記述部からなる。ノード宣言部では、動作記述 部で用いるノード名(モジュール内部でのみ使い、外部には出力しない内部信号名)を宣 言する。 signal ノード名 : 属性; 動作記述部は、回路の動作について記述していく部分である。begin と end で囲み、この間 に記述する。 21 4.1.2 同期回路設計とリセット信号 ○同期回路 大規模な回路を確実に動作させるためには、すべてを同期回路で設計する必要がある。 同期回路とは、一定周波数の信号(クロック)に従ってすべてのフリップフロップが同じ タイミングで動作する回路を意味する。よって、一定の順序で処理していく回路やフリッ プフロップを使うような回路では、プロセス文のセンシティビティリストに clk を記述する。 プロセス文については下記に説明している。[1][2] ○リセット回路 回路の電源投入時の過度状態では回路の内部状態がどう定まるかが全く不明である。ま た、プログラムが暴走して制御不能になった場合、回路を再び初期状態にしてリスタート する必要がある。このような目的で回路を初期化するのがリセット信号である。上記の clk と同じくセンシティビティリストに加えて記述する。 ○同期信号と非同期信号 D フリップフロップの一般的な記述を下に示す。 1 process( CLK, reset ) 2 begin 3 if ( reset = ‘0’ ) then 4 5 Q <= ‘0’; elsif ( CLK ’event and CLK = ‘1’ ) then 6 Q <= D; 7 end if; 8 end process; 動作記述部の begin から end の間は全て同時に実行されるが、process 文で囲まれた部分 は上から順に実行され、下の process 文で最初に戻る。1 行目の()内はセンシティビティ リストと呼ばれ、 ()内に記述された信号の値が変化した時のみ process 文が実行される。 5 行目の識別子 event は遅延属性というもので、 信号 CLK が変化しているとき真(TRUE) を返す。すなわち、5 行目によりクロックの立ち上がりを検出し、クロックに同期して入力 を出力に伝盤させている。一方 reset 信号はクロックに同期した信号ではないため、if 文の 最初に記述している。 なお、同期信号によるデータ入力では、入力信号を保持・確定するための時間(セット アップ・タイム)と(ホールド・タイム)が必要であるため、クロックの周波数とデータ の入力タイミングに注意しなければならない。[1] 22 4.2 ディスプレイ制御回路の設計 第 3 章で分割した機能のうち、画面表示を制御するモジュール(LCD_controller)について 解説する。図 4.2 は信号入出力を表したブロック図であり、信号の内容については表 4.1 に 示している。 clk v_sync h_sync reset T_main DCK LCDRD_en LCD_controller DISP_en SRAM SD(8bit) DISP RGBdata(9bit) LCDRD_en_off T_main A_count(17bit)_N Mem_con 図 4.2 LCD_controller のエンティティ図 表 4.1 LCD_controller の入出力仕様 信号 意味 clk システム内部クロック(30MHz) reset リセット入力 LCDRD_en テトリス本体からの画面更新要求 DISP_en テトリス本体からの画面表示 off 要求 SD SRAM 直結のデータ入力(8bit) A_count_N 表示画素アドレスの SRAM 出力(17bit) LCDRD_en_off テトリス本体への画面更新終了出力 v_sync LCD の垂直同期信号 h_sync LCD の水平同期信号 DCK LCD の画素同期信号 DISP ディスプレイ表示オフ出力 LCD_red 赤色画素データ(3bit) LCD_green 緑色画素データ(3bit) LCD_blue 青色画素データ(3bit) 23 LCD 内部の動作は、テトリス本体の制御信号によるステートマシンと、v_sync1 周期分のカウ ンタで構成される。SRAM へのデータアクセスについては、SRAM をリード状態にしたま まアドレスの値を変更することで 1 フレーム分のデータを連続で読み出す構成としている。 また、LCD へ出力するデータ幅は 9bit であるが、SRAM のデータ幅が 8bit であるため 青色出力 3bit のうち B1 と B2 に同じ値を出力するようにしている。[5] 4.2.1 同期信号用のカウンタ設計 LCD の画面表示は VSYNC、 HSYNC、 DCK の 3 つの同期信号により動作しているため、 それぞれの同期信号用の 3 つのカウンタが必要になる。また、表示させる画素を選ぶため の、SRAM のフレームバッファ対するアドレスカウンタも必要である。そのため、モジュ ールの内部に次のような 4 つのカウンタを設計した。 ○S_count(2bit) DCK を発振させるためのシステム内部クロックの 2bit カウンタ。clk の立ち上がりに同 期して1ずつ加算されていき、0~3 の範囲を遷移していく。S_count0~1 の間 DCK=0、 S_count2~3 の間 DCK=1 という条件付信号代入により、DCK を発生させる。システムク ロックが 30MHz なので DCK は 7.5MHz となる。 133.3ns DCK S_count(3bit) 0 1 2 3 0 1 2 3 System clock(30MHz) 33.3ns 図 4.3 DCK 発振タイミング図 24 ○D_count(10bit) HSYNC 用の DCK カウンタ。563 個のドットクロックをカウントする。D_count0~2 の 時のみ HSYNC=0 として HSYNC を発生させる。 HSYNC DCK 3DCK 48DCK … 562, 0, 1, 2, 3, D_count … 320DCK 50, 51, … 192DCK 370,371 … 図 4.4 HSYNC 発生タイミング図 ○H_count(8bit) VSYNC 用の HSYNC246 進カウンタ。DCK カウンタ 562→0 により HSYNC をインク リメントする。247 カウントで 1 画面の表示終了であるので、ここで全てのカウンタを初期 化状態にして VSYNC と HSYNC を 1 に固定する。 VSYNC HSYNC H_count 3H 4H 240H 0, 1, 2,3,4, 5,6, 7, 8 … 246 図 4.5 VSYNC 発生タイミング図 ○A_count(17bit) 読み込む画素データのアドレスを計算する。LCD の画素数が 320×240 であるため 320 ×240 のカウンタを作ればよいが、テトリス本体から SRAM にアクセスする時のアドレス 計算を簡潔にするため、 512×240 までカウントする。 画像データの表示は VSYNC、 HSYNC の立ち上がりからそれぞれ 4H、48DCK 後となるため、図 4.4、4.5 のカウンタのタイミン グ図を参照に D_count=51,H_count=7 からカウントを開始する。 25 4.2.2 アドレス計算 SRAM のアドレス幅は 19bit であり 0~524288 番地までデータを格納することができる。 本研究では x 座標に下位 9 ビットを割り当て、上位 10 ビットを y 座標に割り当てて使用す る。すなわち横方向 512 画素、縦方向 1024 画素のメモリ空間が作成される。(図 4.6 参照) こうすることにより、アクセスしたいアドレスの計算はy座標の値を左に 9 ビットシフ トした値とx座標の値の or をとるだけで、アクセスしたいアドレスを生成することができ る。(図 4.7 参照) (0.0) (319,0) アクセスしたいアドレス(10,1) (511,0) x 座標 1 フレーム ”000001010” or (0,239) y 座標 “0000000001000000000” = 他画像 格納用、その他 “0000000001000001010” アドレスの深さ 10 進数 512+8+2=522 番地 (0,1023) y× 512 + (511,1023) 図 4.6 メモリ領域 x = アドレス 図 4.7 アドレス計算例 上記のアドレス計算を満たすため、アドレスのカウント(A_count)は、x 座標 9bit512 分までカウントしている。 (x,y) 深さ x座標 9bit y座標 8bit (0,0) (319,9) 表示領域 (511,0) 0 (0,0) 1 (1,0) 2 (2,0) 320 非表示領域 表示領域 (320,0) 非表示領域 511 (551,0) (511,0) 512 (0,1) 512*240-1 (511,239) 図 4.8 フレームバッファのアドレス空間 26 4.2.3 LCD_controller の状態遷移 LCD_controller の状態遷移図を下に示す。通常 LCD には同期信号をとめた Idle 状態で あり、画面には前の画面更新処理で表示された画像が保持されている。 テトリス本体からの画面更新要求(LCDRD_en=1)が入ると活性化状態となり、各種カウ ンタにより同期信号を発生し、1 画面表示処理をおこなう。1 画面の処理が終わるとテトリ ス本体へ終了信号(LCDRD_en_off=1)を返し、(LCDRD_en=0)の入力により Idle 状態へ遷 移する。 また、ユーザーインターフェース部のコントローラーの stop 信号入力により、ディスプ レイ表示オフ要求(DISP_en)が入力されると DISP_off 状態へ遷移する。テトリスの実行が 停止している間、画面の表示がオフになる。 Idle 待機 DISP_en=1 LCDRD_en=0 DISP_en=0 LCDRD_en=1 not DISP_en=0 not LCDRD_en=0 DISP_off LCD_act 画面表示 画面描画 オフ 実行 図 4.9 LCD コントローラーの状態遷移図 27 4.2.4 LCD_controller のシミュレーション結果 VHDL コーディング、論理合成、シミュレーションには ALTERA 社の PLD 総合開発ツ ール QuartusⅡ WebEdition(v5.1)を使用した。LCD_controller のシミュレーション結果を 以下に示す。なお、VHDL コードについては論文末の付録 1 に示している。 (A) 0~1μs のシミュレーション結果 (B) 0~2ms のシミュレーション結果 図 4.10 LCD_controller のシミュレーション結果 28 4.3 SRAM 制御部の設計 SRAM アクセスに関する制御信号の発生、テトリス本体とLCDコントロール部からの SRAM アクセスの際のバス調停を行う回路を設計する。この回路の入出力は以下のように なる。 LCDRD_en CS TR_en OE TW_en WE Tetris TD(8bit) TA(19bit) A_count_N(17bit) SRAM SD(8bit) Memory contoroller SA(19bit) Clock Reset LCD_controller 図 4.11 メモリコントローラーブロック図 表 4.2 メモリコントローラーの入出力仕様 信号 意味 clk システム内部クロック(30MHz) reset リセット入力 LCDRD_en テトリス本体からの画面更新要求 TR_en テトリス本体から SRAM へのデータ要求 TW_en テトリス本体から SRAM への書き込み要求 TD テトリス本体とのデータバス(8bit) TA テトリス本体とのアドレスバス(19bit) A_count_N LCD_cont からのフレームバッファアドレス(17bit) CS チップセレクト(非極性) OE アウトプットイネーブル(非極性) WE ライトイネーブル(非極性) SD SRAM とのデータバス(8bit) SA SRAM とのアドレスバス(19bit) 29 4.3.1 SRAM アクセスタイミング SRAM へのアクセスタイミングとして、データシートのタイミング定格値を参考に次の ようなアクセスサイクルを設計した。[8] リードサイクル、ライトサイクル共に3ステートでクロックに同期した制御信号の操作 を行い、全4クロックサイクルでの SRAM アクセスとなっている。また、LCD によるリー ドサイクルにはアドレスコントロールドリードで行っているため、テトリス本体からのリ ードアクセスとは別のステートにより操作する。 R0 R1 R2 Idle R0 R1 R2 clk 33.3ns trc=70ns~ Address taa=~70ns CS OE Data Valid Data out 図 4.12(a) テトリス本体からのリードサイクル 30 Idle W0 W1 W2 Idle W0 W1 W2 clk Address CS WE Data in Data Valid 図 4.12(b) テトリス本体からのライトサイクル clk DCK Address Data Valid Data out CS OE 図 4.12(c) 図 4.12 LCD からのリードサイクル SRAM アクセスタイミング図 31 Idle 4.3.2 メモリコントローラーの状態遷移 図 4.12 を参考に状態遷移図でまとめたものが図 4.13 である。 W0 CS=L WE=L Idle TW_en=1 CS=H OE=H WE=H TR_en=1 W1 R0 CS=L OE=L R1 LCDRD_en=0 LCDRD_en=1 W2 LCD_act R2 CS=H CS=L OE=H WE=H OE=L 図 4.13 メモリーコントローラーの状態遷移図 32 4.3.3 Memory_controller のシミュレーション結果 Memory_controller のシミュレーション結果を以下に示す。なお、VHDL コードについ ては論文末の付録 2 に示している。 図 4.14 Memory_controller の単体シミュレーション結果 33 4.4 テトリス本体の設計 第 3 章でまとめたテトリスの仕様、大まかなアルゴリズムをもとに、HDL で記述できる までに詳細に分析していく。 4.4.1 テトリスの状態遷移回路 第 3 章では処理の内容、順序についてまとめてプログラム構造図(図 3.4)を作成した。 しかし、逐次実行のソフトウェアプログラムから作成したものであり、すべての回路が同 時に動作するように記述する HDL でそのまま構成を設計するのは困難である。そのため、 HDL では外部信号によって内部の状態が遷移する、状態遷移回路によって構成を考えてい く。 本状態遷移回路は、以下のような 11 種類の状態とする。状態遷移図を図 4.15 に示す。 ① Init – 各信号、レジスタの値を初期化する。 ② Idle – ゲーム開始の待機状態。画面表示はオフになっている。 ③ Run – ゲーム実行状態。各種入力信号により複数のステートに分岐する。 ④ R_shift – Right_key の入力により、ブロックを右に移動させる。 ⑤ L_shift – Left_key の入力により、ブロックを左に移動させる。 ⑥ Rot – Turn_key の入力により、ブロックを回転させる。 ⑦ Drop – Down_key 入力、もしくは一定時間経過により、ブロックを下移動する。 ⑧ Check_L – ブロックが横一列揃ったラインがないか調査する。 ⑨ Delet_L – 横一列揃ったラインを消去し、上のブロックを 1 段ずらす。 ⑩ Create_B – 新しいブロックを作成し、画面初期位置へ配置する。 ⑪ End – ゲームオーバー画面を表示し、初期化の実行を待つ。 各状態の詳しい処理については、4.4.3 節より順番に解説していく。 34 ① Init 初期化 start_key 初期化完了 ② ⑪ End Idle 再開待機 実行待機 ③ ブロック start_key Run 配置 error 実行状態 ブロック ⑩ ゲーム準備中サイクル ゲーム実行中サイクル Right_key 配置完了 Create_B ④ R_shift ブロック生成 Left_key 消去列なし ⑧ ⑤ 移動完了 Turn_key Check_L 時間経過 line 調査 消去列 消去完了 ⑨ 右移動 左移動 or ⑥ Down_key 移動 error Rot 発見 ⑦ Delete_L Drop line 消去 下移動 回転 図 4.15 テトリスの状態遷移図 35 L_shift 4.4.2 データ構造 各状態についての解説の前に、まず、テトリスの画像データの扱い方について解説する。 ゲーム画面の構成を図 4.16、ブロックの構成について図 4.17 に示す。 ○画面構成 ゲーム画面は LCD を時計周りに 90°回転させた配置であるから、ブロックの落下方向 は x 座標系で考える事になる。すなわち、ブロックの下移動=x加算、右移動=yの減算、 左移動=yの加算、という具合になる。また、ブロックの移動範囲は(40,100)~(279,199)の 格子内とする。ゲーム画面の右上は、次に出現するブロックを表示する。(図 4.16 参照) x 座標 320 dot y 座標 240 dot (0,0) (0,239) (319,0) (40,100) (279,100) (40,199) (279,199) (319,239) 図 4.16 ゲーム画面の構成 36 ○ブロックデータの構成 ブロックの取り扱いに関しては、図 4.17 左上のように 10×10 ドットを 1 セルとし、こ のセルを 4 つ組み合わせたものとして考える。各セルごとにそれぞれ位置情報を示すアド レスを持ち、ブロックの移動・回転はこのセル単位ごとに管理する。ブロックデータをフ レームバッファに書き込む際も、1 セル分の処理を 4 回繰り返すことによりデータを書き込 んでいく。 また、ブロックには 7 つの種類があり、さらにそれぞれ対応する回転後のデータがある ため、ブロックの種類ごとに 7 つの識別子を用意する。この識別子は、ブロックの回転動 作だけでなくブロックの色分けの判定にも使用する。 1 8 ドット 1 (0,0) (10,0) (0,10) (10,10) 保持する 1 セル 座標 灰データ 黄データ 白データ 画素構成 図 4.17 ブロックの構成 37 4.4.3 アルゴリズム詳細 図 4.15 に示した状態遷移をもとに、各状態での処理内容について順に解説していく。 ① Init 回路起動時、リセット信号入力時、または End 状態から start 入力があった時に実行す る回路初期化状態。リセット信号のトグルにより各内部信号の値を初期化し、SRAM のフ レームバッファに対し初期画面データを書き込む。各レジスタに初期値を与え初期ブロッ クの作成が終わると、次の Idle 状態へと遷移する。 ② Idle Init による初期化の終了、もしくは Run 状態から start 入力があった時の状態。LCD に 対して画面表示オフ信号(DISP_en)を出力する以外には特に何も処理をしない。start 入力 が入るまでゲームの実行を待機する。 ③ Run ゲーム実行中のメインとなる状態。start 入力でゲーム一時停止状態へ、各ブロック操作 キー入力、および一定時間経過で、それぞれ④∼⑦のブロック制御状態へ遷移する。なお、 時間経過による状態遷移にはカウンタを用いるが、時間のカウントは Run 状態時だけでな く移動・回転処理状態にもカウントしていくことにする。こうすることで、移動信号を入 力し続けることによるブロックの落下停止を防ぐことができる。各状態への遷移の制御と 時間のカウントを許可するだけで、メインとはいえ特に複雑な処理はしない。 ④ R_shift ブロック消去 Run 状態から Right_key の入力により、 ブロックを 1 セル右方向へ移動させる。 座標更新 すなわち、レジスタに保持してある 4 つの アドレスに対し、y座標−10の演算を行う。 否背景色 座標戻し しかし、移動後の位置に壁や他のブロックが あった場合、移動を中止しなければならない。 そのため、右図 4.14 に示すような流れで処理 を行う。 画素検査 背景色 ブロック書き込み LCD 出力 図 4.18 ブロック移動処理の流れ 38 まず、制御中ブロックに対して重なり判定が起こらないように、R_shift に状態遷移した 時点で保持している 4 つのセルアドレスを参照して、ブロックを消去する。そして 4 つの アドレスに対しy座標+10 の演算を行う。演算後のアドレスが移動後のブロックの座標に なるので、SRAM からこの 4 つのアドレスの画素データをリードしてくる。その画素デー タが 4 つとも背景データ(黒)だった場合、移動先にブロックはないと判断できるので、 このアドレスに対し画像データを書き込み、LCD への出力を実行する。4 つの内どれか 1 つでも背景以外のデータが検出された場合、制御中ブロックと他のブロック・壁との重な りがおきてしまうので、4 つのアドレスに対しy座標−10 の演算を行い、もとの座標にも どす。その後、再びブロック画像データを書き込み、Run 状態へと遷移する。 ⑤ L_shift R_shift と移動方向が逆なだけで処理の内容は同じであるので、セルアドレスy座標に対 する演算を+−逆転させるだけでよい。 ⑥ Rot Run 状態から turn_key の入力により、ブロックの回転を実行する。処理の流れは図 4.14 とほぼ同じであるが、R/L_shift より座標の更新が少しだけ複雑になる。 回転動作の座標の計算方法につい A B C A B D て、左のようなブロックを例に説明す C る。 D 前節で解説したとおり、制御中のブ ロックに対し 4 つのアドレスを各セル block No=1 ごとに持っている。ブロックのAセル address register の位置座標が(50,150)の時、それを基準 A (0,0) + (50,150) → (50,150) に各セルのアドレスが計算される。各 B (10,0) + (50,150) → (60,150) セルの位置はブロックの種類により異 C (20,0) + (50,150) → (70,150) なっているため、全 19 種のブロック識 D (0,10) + (50,150) → (50,160) 別子を参照に計算を行う。 回転前アドレス 移動処理ではこのアドレスに対し同 if (block No=1) then じ数値の加算になるが、回転動作では address register セルごとの位置が変更されるため、も A (50,150) + (0,0) → (50,150) う一度ブロック識別子を参照してブロ B (60,150) + (0,0) → (60,150) ックごとの演算を行う。 C (70,150) + (−10,10) → (60,160) その後、移動処理と同じように重な D (50,160) + (10,10) → (60,170) り判定を行い、ブロックのデータを書 回転後アドレス き込んでいく。 39 ⑦ Drop Run 状態から Down_key 入力、もしくはゲーム実行サイクルにおける時間(テトリス用の クロック)のカウントが一定数になった時、ブロックを下方向へ移動させる。これもアドレ スのx軸に対する加算になるだけで、あとの処理は R_shift と同じ流れである。ただし、ブ ロックの移動エラーが起きた場合、座標を移動前に戻してブロックの再描画を行った後、 Run 状態ではなく Check_L 状態へと遷移する。 ⑧ Check_L この状態へ遷移したときに保持している 4 つのアドレスの x 座標に消える可能性のある ラインが含まれているので、x 座標アドレスを別のレジスタへ格納しておき、このアドレス をもとにそれぞれ 1∼4 のラインに対し消去判定を行う。 消去ラインの判定にはカウンタを用いる。ゲームフィールド内のセルの y 軸に取りうる 値の範囲は(100,110,120,130,140,150,160,170,180,190)だけであるので、この y 座標と上記 で格納したx座標のアドレスをもとに、SRAM からそれぞれ画素データをリードしてくる。 このときの画素データが背景色データでない時(ブロックのセルデータが存在する場合)、 消去カウンタをカウントアップし、カウンタの値が 10 の時、Delete_L へと遷移する。 格納したx座標の走査が終わると Create_B へと遷移する。 y 走査対象アドレスの保持 x y=100 否背景色 画素検査 (保持 x,y) A B count+1 C 背景色 D y=190 (A or B,C,D)の 3 つのx座標で count=10 ライン検査 y,count 値 y+10 else y=190 count≠10 Delete_L へ 次のライン or Create_B へ 図 4.19 消去判定フロー 40 ⑨ Delete_L 消去判定がでたラインに対して消去を行う。まず、Check_L で消去判定がでたx座標よ り上のフィールドデータを SRAM の空き領域へコピーする。そして、コピーしたデータを 1 段ずらしたところに上書きしていき、最後にゲームフィールド最上段を消去する。 消去判定と消去処理は 1 ラインずつおこなうため、Check_L で格納したx座標アドレス を 1 セル分ずらして、再び Check_L へ遷移する。 ⑩ Create_B 1 度目の実行でも、すでに Init で次に生成されるブロックは決定されてるため、次の次 のブロックを乱数により決定する。その後、ブロックの位置を示す 4 つのセルアドレスを 次のブロックを表示する領域に書き換え、次の次に生成されるブロックを書き込む。そし て、ブロックのアドレスを初期位置へもどし、初期配置位置の画素を検査して重なりがな いか調べる。初期位置に他のブロックがないことが確認できれば、新しいブロックをフレ ームバッファに書き込む。ここまでで一連の処理が終わり、再び Run 状態へ遷移する。も し、初期配置位置で重なり判定が起きた場合、Game over となり End 状態へ遷移する。 ⑪ End Game over を知らせるメッセージを表示して、start_key によるゲーム初期化の実行を待つ。 41 4.4.4 テトリス内部のモジュール構成 図 4.15 の状態遷移図をもとに、テトリス本体のモジュール構成を以下のようにした。 コンポーネントによっては、さらに下位レベルのステートマシン、サブコンポーネントに より構成され、階層化したステートマシンによってテトリスの動作を実現する。 clk T_clk reset start turn down Left Right メイン FSM アドレス REG 画素 ROM Create Delete Tetris Control Block Block Initialize Block FSM FSM FSM FSM address BUS Data BUS DISP_en LCDRD_en TR_en TW_en 図 4.20 テトリス本体の機能ブロック図 42 TA TD おわりに 本研究の目標としては、回路を実装して動作確認をするところまでであったが、結局回 路の構造や処理をまとめるだけで終わってしまった。処理の順序がわかってもゲート回路 で信号がどのタイミングでどのように伝盤していくのか、といったソフトウェアとハード ウェアの動作の違いがなかなか呑み込めなかった。しかし、そのまま強引に下位レベルの 設計へと進めようとしてしまったために、後の段階で回路の整合性がとれなくなってしま うなど、無駄が多かったように思える。HDL により抽象的な段階からロジック回路を生成 できるとはいえ、トップレベルから具体的な機能ブロックとロジック回路を作っていくに は、基礎的な知識が十分に必要であると感じた。 全工程の半分程しか進められなかったが、あきらめずできるとこまで取り組んだことに よって知識や解析力が身についただけでなく、ハードウェアの設計に関する興味も強くな った。ゲームの設計には様々な要素が含まれるので、論理設計の学習に非常に有効である と思う。今後、完成させる事はもちろん、アルゴリズムの最適化や回路規模の小型化、ゲ ーム性の向上など、時間があれば考えていきたい。 43 謝辞 今回の卒業研究に際して、丁寧なご助言、ご鞭撻下さりました橘 昌良助教授に心から感 謝いたします。就職活動や大学生活についてご指導下さいました杉田 彰久教育講師はじめ、 本学入学以来ご教授賜りました電子・光システム工学科の教員・職員の皆様と共に、ここ に厚くお礼申し上げます。 また、共に最後まで卒業研究に励んだ橘研究室の皆さん、同期生諸氏にお礼を申し上げ ます。 平成 18 年 2 月 20 日 44 参考文献 [1]「VHDL によるマイクロプロセッサ設計入門」仲野 巧 著、CQ 出版 [2]「VHDL によるハードウェア設計」長谷川 裕恭 著、CQ 出版 [3]「構造化プログラム設計図法 SPD」遠藤 裕香 著、共立出版 [4]「Design Wave magazine」2002 年 5 月号、CQ 出版 [5]「Design Wave magazine」2003 年 1 月号、CQ 出版 [6]「Design Wave magazine」2005 年 4 月号、CQ 出版 [7]「5.5 インチ LCD LM32C041 仕様書」 [8]「CMOS SRAM K6T4008C1B Family 仕様書」 [9]ゲーム&その他【プログラミング研究所】 「http://www13.plala.or.jp/kymats/study/game_other.html」 [10]テトリス − Wikipedia 「http://ja.wikipedia.org/wiki/%E3%83%86%E3%83%88%E3%83%AA%E3%82%B9」 45 付録 1 LCD_controller の VHDL 記述 library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; entity LCD_controller is port ( clk : in std_logic; reset : in std_logic; LCDRD_en : in std_logic; DISP_en : in std_logic; SD : in std_logic_vector(7 downto 0); A_count_N : out std_logic_vector(16 downto 0); LCDRD_en_off : out std_logic; v_sync : out std_logic; h_sync : out std_logic; DCK : out std_logic; DISP : out std_logic; LCD_red : out std_logic_vector(3 downto 1); LCD_green : out std_logic_vector(3 downto 1); LCD_blue : out std_logic_vector(3 downto 1) ); end LCD_controller; architecture Behavior of LCD_controller is type LCDstatus is ( idle, DISP_off, LCD_act); signal state : LCDstatus; -- LCD Sequencer state signal DCK_I : std_logic; -- Internal dot clock -- clock counter signal S_count : std_logic_vector(1 downto 0); -- system clock count(0 to 3) signal A_count : std_logic_vector(16 downto 0); --address count(0 to 512*240-1) 46 signal D_count : std_logic_vector(9 downto 0); -- DCK count(0 to 562) signal H_count : std_logic_vector(7 downto 0); -- HSYNC count(0 to 246) begin -- LCD data out LCD_red <= SD(7 downto 5); LCD_green <= SD(4 downto 2); LCD_blue <= SD(1 downto 0) & SD(0); -- LCD state machine ------------------------------------process(clk,reset) begin if( reset = '1' ) then state <= idle; DISP <= '1'; elsif( CLK'event and CLK = '1') then case state is when idle => if ( LCDRD_en = '1' ) then state <= LCD_act; elsif ( DISP_en = '1' ) then state <= DISP_off; DISP <= '0'; end if; when LCD_act => if ( LCDRD_en = '0' ) then state <= idle; end if; when DISP_off => if ( DISP_en = '0' ) then state <= idle; DISP <= '1'; end if; when others => 47 null; end case; end if; end process; -- state LCD_active ====================================== -- DCK generation,S_COUNT(0~3)---------------------------process(clk, reset, LCDRD_en) begin if( reset = '1' or LCDRD_en = '0') then S_count <= (others => '0'); elsif( clk'event and clk='1' ) then if( state = LCD_act ) then if( S_count = 3 ) then S_count <= (others => '0'); else S_count <= S_count + 1; end if; end if; end if; end process; DCK_I <= S_count(1); DCK <= DCK_I; -- H_COUNT(0~246),D_COUNT(0~562),A_COUNT(0~512*240-1) ---process(DCK_I, reset, LCDRD_en) begin if( reset = '1' or LCDRD_en = '0') then D_count <= (others => '0'); H_count <= (others => '0'); A_count <= (others => '0'); LCDRD_en_off <= '0'; elsif( DCK_I'event and DCK_I = '1' ) then if( state = LCD_act ) then if ( D_count = 562 ) then 48 D_count <= (others => '0'); H_count <= H_count + 1; else D_count <= D_count + 1; end if; if ( A_count = 122690 ) then -- (512*240-190) LCDRD_en_off <= '1'; elsif ( H_count >= 7 and D_count >= 51 ) then A_count <= A_count + 1; end if; end if; end if; end process; A_count_N <= A_count; -- HSYNC,VSYNC generation---------------------------------process (DCK_I, reset, LCDRD_en) begin if( reset = '1' or LCDRD_en = '0') then h_sync <= '1'; v_sync <= '1'; elsif( DCK_I'event and DCK_I ='1' ) then if( state = LCD_act ) then if ( D_count >= 0 and D_count < 3) then h_sync <= '0'; else h_sync <= '1'; end if; if ( H_count >= 0 and H_count < 3) then v_sync <= '0'; else v_sync <= '1'; end if; end if; end if; 49 end process; end Behavior; 50 付録 2 Memory_controller の VHDL 記述 library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; entity Memory_controller is port ( clk : in std_logic; reset : in std_logic; LCDRD_en : in std_logic; TR_en : in std_logic; TW_en : in std_logic; TD : inout std_logic_vector(7 downto 0); TA : in std_logic_vector(18 downto 0); A_count_N : in std_logic_vector(16 downto 0); CS : out std_logic; WE : out std_logic; OE : out std_logic; SD : inout std_logic_vector(7 downto 0); SA : out std_logic_vector(18 downto 0) ); end Memory_controller; architecture Behavior of Memory_controller is type MEMstatus is ( idle, R0, R1 ,R2, W0, W1, W2, LCD_act); signal state : MEMstatus; -- SRAM Sequencer state signal TD_OE_I : boolean; -- Tetris data bus output enable signal SD_OE_I : boolean; -- SRAM data bus output enable begin 51 -- tri state logic TD <= SD when TD_OE_I else (others => 'Z'); SD <= TD when SD_OE_I else (others => 'Z'); -- Mem_con state machine ------------------------------------process(clk,reset) begin if( reset = '1' ) then state <= idle; CS <= '1'; OE <= '1'; WE <= '1'; TD_OE_I <= false; SD_OE_I <= false; elsif( CLK'event and CLK = '1') then case state is when idle => if ( LCDRD_en = '1' ) then state <= LCD_act; CS <= '0'; OE <= '0'; WE <= '1'; elsif ( TW_en = '1' ) then state <= W0; CS <= '0'; OE <= '1'; WE <= '0'; elsif ( TR_en = '1' ) then state <= R0; CS <= '0'; OE <= '0'; WE <= '1'; end if; -- LCD active -----------------when LCD_act => if (LCDRD_en = '0') then 52 state <= idle; CS <= '1'; OE <= '1'; end if; -- Read cicle -----------------when R0 => state <= R1; when R1 => state <= R2; OE <= '1'; TD_OE_I <= true; when R2 => state <= idle; CS <= '1'; TD_OE_I <= false; -- Wright cicle ---------------when W0 => state <= W1; SD_OE_I <= true; when W1 => state <= W2; CS <= '1'; WE <= '1'; when W2 => state <= idle; SD_OE_I <= false; when others => null; end case; end if; end process; process(A_count_N,LCDRD_en,TA) begin if (LCDRD_en = '1') then SA <= ("00") & A_count_N(16 downto 0); 53 else SA <= TA; end if; end process; end Behavior; 54