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<ehoff+`EHD)) in_hdt <= True; else in_hdt <= False; x <= x + 1; if (((`VD+`VFP)<=y)&&(y<(`VD+`VFP+`VSP))) in_xvs <= False; else in_xvs <= True; if ((evoff<=y)&&(y<evoff+`EVD)) in_vdt <= True; else in_vdt <= False; endaction // for -> 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の最適化により定数となっており、レジスタは存在しませんでした。結論として、マクロで定数定義してもレジスタ宣言しても、オーバヘッドは変わりません。