16 |
BSVによるSpace Invadersの変更 (4) |
Graphic Controlerの再設計
完成したIP diagramを510.1に示します。3個のサブモジュールをまとめたので、graphic階層はなくなりました。
BSVソース
GraphicFSM.bsv:
// グラフィックディスプレイコントローラー、BSVによる実装
import StmtFSM::*;
// 各種タイミングパラメータの定義
`define HD 800 // 水平解像度
`define HFP 16 // 水平フロントポーチ
`define HSP 80 // 水平同期期間
`define HBP 160 // 水平バックポーチ
`define HO `HFP + `HSP + `HBP // 水平オフセット
`define HL `HD + `HO // 一行当たりのピクセル数
`define VD 600 // 垂直解像度
`define VFP 1 // 垂直フロントポーチ
`define VSP 3 // 垂直同期期間
`define VBP 21 // 垂直バックポーチ
`define VO `VFP + `VSP + `VBP // 垂直オフセット
`define VL `VD + `VO // 一画面当たりの行数
// その他の定義
`define EHD 512 // 有効水平解像度
`define EVD 512 // 有効垂直解像度
// ログ幅の定義
`define HW 11 // log2(1056) = 10.04439
`define VW 10 // log2(628) = 9.294621
// アドレス型の定義
typedef Bit#(16) Addr_t;
// インターフェースの定義
interface GraphicFSM_ifc;
method Bool xhs(); // 水平同期信号出力
method Bool xvs(); // 垂直同期信号出力
method Addr_t address(); // VRAMアドレス出力
(* prefix="" *)
method Action idata(Bit#(4) indata); // VRAMデータ入力
(* prefix="" *)
method Action expl(Bool exp); // 爆発入力
method Bit#(1) rd(); // 赤出力
method Bit#(1) gd(); // 緑出力
method Bit#(1) bd(); // 青出力
endinterface
// モジュールの定義
(* synthesize,always_ready,always_enabled *)
module mkGraphicFSM(GraphicFSM_ifc);
Reg#(UInt#(`HW)) x <- mkRegU; // 水平方向のカウンタ
Reg#(UInt#(`VW)) y <- mkRegU; // 垂直方向のカウンタ
Reg#(Bool) in_xhs <- mkReg(False), // 水平同期信号フラグ
in_xvs <- mkReg(False), // 垂直同期信号フラグ
in_hdt <- mkReg(False),
in_vdt <- mkReg(False);
UInt#(`HW) ehoff = (`HD-`EHD)/2;
UInt#(`VW) evoff = (`VD-`EVD)/2;
Reg#(Bit#(4)) in_data <-mkRegU; // VRAMデータ
Reg#(Bool) in_exp <- mkReg(False); // 爆発フラグ
// メインループの定義
Stmt main = seq
while(True) seq
// for (y <= 0; y < `VL; y <= y+1) seq
y <= 0;
while (y < `VL) seq
// for (x <= 0; x < `HL; x <= x+1) action --- for consumes two cycles, then we like to use while
x <= 0;
while (x < `HL) action
if (((`HD+`HFP)<=x)&&(x<(`HD+`HFP+`HSP))) in_xhs <= False;
else in_xhs <= True;
if ((ehoff<=x)&&(x while
y <= y + 1;
endseq // for -> while
$display("%3d %3d", y, x);
endseq // while(True)
endseq; // Stmt // xから水平オフセット(ehoff)を減算してパックし、右に1ビットシフトする。
Bit#(`HW)xx = pack(x-ehoff)>>1;
// yから垂直オフセット(evoff)を減算してパックし、右に1ビットシフトする。
Bit#(`VW)yy = pack(y-evoff)>>1;
// xxとyyを8ビットに切り詰める。
Bit#(8)xxx = truncate(xx);
Bit#(8)yyy = truncate(yy);
// xxxとyyyを合成して16ビットのアドレスを作成。
Bit#(16) in_addr = {yyy, xxx};
// 水平データタイミング(in_hdt)と垂直データタイミング(in_vdt)をANDで合成。
Bool in_dt = in_hdt && in_vdt;
// 爆発フラグ(in_exp)に基づいて赤色成分のデータを処理。
Bit#(1) in_rd = !in_exp ? in_data[2] & pack(in_dt) : (in_data[2] | in_data[1] | in_data[0]) & pack(in_dt);
// 爆発フラグ(in_exp)に基づいて緑色成分のデータを処理。
Bit#(1) in_gd = !in_exp ? in_data[1] & pack(in_dt) : 1'b0;
// 爆発フラグ(in_exp)に基づいて青色成分のデータを処理。
Bit#(1) in_bd = !in_exp ? in_data[0] & pack(in_dt) : 1'b0;
// ステートマシン生成
mkAutoFSM(main);
// メソッド定義
method Bool xhs();
return in_xhs;
endmethod
method Bool xvs();
return in_xvs;
endmethod
method Addr_t address();
return in_addr;
endmethod
method Action idata(Bit#(4) indata);
in_data <= indata;
endmethod
method Action expl(Bool exp);
in_exp <= exp;
endmethod
method Bit#(1) rd();
return in_rd;
endmethod
method Bit#(1) gd();
return in_gd;
endmethod
method Bit#(1) bd();
return in_bd;
endmethod
endmodule: mkGraphicFSM
- actionからendactionまでは1サイクル実行です。
- verilogと同様、"<="はノンブロッキング代入でDFFが、"="はブロッキング代入で組み合わせ回路がそれぞれ生成されます。
当初、水平のオフセットを表す定数EHOFFは、上記のような
UInt#(`HW) ehoff = (`HD-`EHD)/2;
という変数ではなく、define文により
`define EHOFF (`HD-`EHD)/2
のように定義していたのですが、defineの中でカッコや乗除算は使用できないようなので、変数としました。
ところが、生成されたVerilogを確認したところ、bscの最適化により定数となっており、レジスタは存在しませんでした。結論として、マクロで定数定義してもレジスタ宣言しても、オーバヘッドは変わりません。