...

FPGAとLCDを用いたゲームマシンの設計

by user

on
Category: Documents
8

views

Report

Comments

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
Fly UP