6 |
BSVによるサウンドFSMの再設計 (7) |
BSVソース
完成したBSVのソースを貼り付けます。1つのソースで4種のFSMを合成し分けているため、やや複雑になっています。
SoundFSM.bsv: // ステートマシンライブラリのインポート import StmtFSM::*;
// サウンドコードの定義 `define SOUND1_ON 1 // 自弾発射音_ON `define SOUND2_ON 2 // 自機爆発音_ON `define SOUND3_ON 3 // インベーダ爆発音_ON `define SOUND4_ON 4 // インベーダ歩行音1_ON `define SOUND5_ON 5 // インベーダ歩行音2_ON `define SOUND6_ON 6 // インベーダ歩行音3_ON `define SOUND7_ON 7 // インベーダ歩行音4_ON `define SOUND8_ON 8 // UFO爆発音_ON `define SOUND9_ON 9 // 自機増加音_ON `define SOUND10_ON 10 // UFO飛行音_ON `define SOUND10_OFF 11 // UFO飛行音_OFF `define NULL 'h80 // 無音 // 各FSMが起動する条件を定義 `define COND_FSM0 !emptyf && (code == `SOUND1_ON || code == `SOUND2_ON || code == `SOUND9_ON) `define COND_FSM1 !emptyf && (code == `SOUND3_ON) `define COND_FSM2 !emptyf && (code == `SOUND4_ON || code == `SOUND5_ON || code == `SOUND6_ON || code == `SOUND7_ON) `define COND_FSM3 !emptyf && (code == `SOUND8_ON || code == `SOUND10_ON || code == `SOUND10_OFF) // 型定義 typedef UInt#(15) Addr_t; // アドレス型 typedef UInt#(8) Data_t; // データ型 typedef Bit#(4) Code_t; // サウンドコード型 // インターフェース定義 interface FSM_ifc; method Action sound(Code_t code); // サウンドコードを受け取る method Action rom_data(Data_t indata); // ROMデータを受け取る method Action sync(Bool lrclk); // シンクロ信号を受け取る method Action empty(Bool flag); // FIFOが空かどうかのフラグを受け取る method Addr_t rom_address(); // 現在のROMアドレスを返す method Data_t sdout(); // 現在のサウンドデータを返す method Bool soundon(); // サウンドがONかどうかを返す method Bool fifo_ren(); // FIFOから読み出し可能かどうかを返す endinterface // サウンドFSMの生成 (* synthesize,always_ready,always_enabled *) `ifdef FSM0 module mkSoundFSM0(FSM_ifc); `elsif FSM1 module mkSoundFSM1(FSM_ifc); `elsif FSM2 module mkSoundFSM2(FSM_ifc); `elsif FSM3 module mkSoundFSM3(FSM_ifc); `endif // ワイヤとレジスタの宣言 Wire#(Code_t) code <- mkWire, // サウンドコード用のワイヤ current <- mkRegU; // 現在のサウンドコード用のレジスタ Wire#(Bool) lrclk <- mkWire; // 左右クロック用のワイヤ Reg#(Data_t) romdata <- mkRegU, // ROMデータ用のレジスタ data <- mkRegU, // データ一時保存用 dout <- mkReg(`NULL); // 出力用データ Reg#(UInt#(32)) workd <- mkRegU; // 作業用データ Reg#(UInt#(15)) dcount <- mkRegU; // データカウント用 Reg#(Addr_t) worka <- mkRegU, // 作業用アドレス romaddr <- mkRegU, // ROMアドレス用 addr <- mkRegU; // 一時アドレス用 Reg#(UInt#(8)) ii <- mkReg(0); // ループカウンタ Reg#(Bool) son <- mkReg(False), // サウンドONフラグ sonEarly <- mkReg(False), // 早期サウンドONフラグ ren <- mkReg(False), // 読み取り許可フラグ emptyf <- mkReg(True); // 空フラグ // FSM3専用のUFOフラグ `ifdef FSM3 Reg#(Bool) fUFO <- mkReg(False); `endif // サブ関数:メモリからデータを読み取る // READ MEM // input: worka // output: romdata; function Stmt readmem; return (seq addr <= worka; // アドレスをセット noAction; // アクションなし(データセットアップタイム) data <= romdata; // データを読み取る endseq); endfunction // サブ関数:カウント値を読み取る // READ COUNT // input: romaddr // output: (romaddr,...,romaddr+3) => dcount; // romaddr + 4 => romaddr; function Stmt readcount; return (seq workd <= 0; for (ii <= 0; ii <= 3; ii <= ii + 1) seq worka <= romaddr + extend(3-ii); readmem; // メモリからデータを読み取る if (ii == 3) dcount <= truncate(workd<<8) | extend(romdata); else workd <= workd<<8 | extend(romdata); endseq romaddr <= romaddr + 4; // アドレスを更新 endseq); endfunction // メインのステートマシン Stmt main = seq while(True) seq // 初期化アクション action dout <= `NULL; sonEarly <= False; son <= False; ren <= False; endaction // 条件に応じて待機 `ifdef FSM0 await(`COND_FSM0); action ren <= True; current <= code; endaction `elsif FSM1 await(`COND_FSM1); action ren <= True; current <= code; endaction `elsif FSM2 await(`COND_FSM2); action ren <= True; current <= code; endaction `elsif FSM3 await(`COND_FSM3 || fUFO); // FSM3はUFOフラグも考慮 if (`COND_FSM3) action fUFO <= (code == `SOUND10_ON); // UFOフラグをセット ren <= True; current <= code; endaction else if (fUFO) action current <= `SOUND10_ON; // UFOフラグがTrueならUFO音を継続 endaction `endif // FIFOが空でないことを確認 await(emptyf); ren <= False; // UFO音のオフコマンド処理(FSM3専用) `ifdef FSM3 if (code == `SOUND10_OFF) continue; `endif // LRクロックのエッジにシンクロ await(lrclk); await(!lrclk); delay(4); // サウンドコードに基づいてROMアドレスを設定 action case (current) `ifdef FSM0 `SOUND1_ON: romaddr <= 0 + 16; `SOUND2_ON: romaddr <= 3422 + 16; `SOUND9_ON: romaddr <= 16150 + 16; `elsif FSM1 `SOUND3_ON: romaddr <= 0 + 16; `elsif FSM2 `SOUND4_ON: romaddr <= 0 + 16; `SOUND5_ON: romaddr <= 1266 + 16; `SOUND6_ON: romaddr <= 2836 + 16; `SOUND7_ON: romaddr <= 4406 + 16; `elsif FSM3 `SOUND8_ON: romaddr <= 0 + 16; `SOUND10_ON: romaddr <= 25968 + 16; `endif endcase endaction // カウント値を読み取り、次のROMアドレスを計算 readcount; romaddr <= romaddr + extend(dcount) + 4; // 再度カウント値を読み取り、ROMアドレスを調整 readcount; romaddr <= romaddr - 1; // サウンドデータの再生 while (!((dcount == 0) || `ifdef FSM0 (`COND_FSM0 && current !=`SOUND9_ON))) seq `elsif FSM1 (`COND_FSM1)))seq `elsif FSM2 (`COND_FSM2))) seq `elsif FSM3 (`COND_FSM3))) seq `endif if (sonEarly == False) seq readmem; // データ読み出し(3 clock) action sonEarly <= True; son <= False; // サウンドオフ dout <= `NULL; // データ無効 endaction endseq else seq readmem; // データ読み出し(3 clock) action son <= True; // サウンドオン dout <= romdata; // データ出力 endaction endseq delay(11); // readmemが3クロック、その次のactionが1クロックで計4クロック。 // さらにdelay()後の終端処理の1クロックを加えて、whileループが16クロックに // なるように11クロック遅延を挿入 action romaddr <= romaddr + 1; worka <= romaddr + 1; dcount <= dcount - 1; endaction endseq // UFOフラグをリセット(FSM3専用) `ifdef FSM3 if ((code == `SOUND8_ON || code == `SOUND10_OFF) && !emptyf) fUFO <= False; `endif endseq endseq; // 自動ステートマシン生成 mkAutoFSM(main); // メソッド実装 method Action sound(Code_t incode); code <= incode; endmethod method Action rom_data(Data_t indata); romdata <= indata; endmethod method Addr_t rom_address(); return addr; endmethod method Data_t sdout(); return dout; endmethod method Bool soundon(); return son; endmethod method Action sync(Bool inlrclk); lrclk <= inlrclk; endmethod method Bool fifo_ren(); return ren; endmethod method Action empty(Bool flag); emptyf <= flag; endmethod `ifdef FSM0 endmodule: mkSoundFSM0 `elsif FSM1 endmodule: mkSoundFSM1 `elsif FSM2 endmodule: mkSoundFSM2 `elsif FSM3 endmodule: mkSoundFSM3 `endif
これをverilogに合成するには、FSM0であれば、
bsc -verilog -D FSM0 SoundFSM.bsv
のようにマクロ定義により行います。