7 |
Pongの作成 (2) |
サウンドROMデータの作成
以下のコマンドにより、Vivadoの読めるCOEファイルを作成します。
echo 'memory_initialization_radix=16;' > srom.coe
echo -n 'memory_initialization_vector=' >> srom.coe
cat s?o.wav | \od -An -t x1 -v >> srom.coe
echo ';' >> srom.coe
サウンドステートマシン
サウンドステートマシンは以前作成したものを流用します。Space Invadersの開発の際には4多重音のため、4個のサウンドステートマシンを使用しましたが、Pongは単純なので1個で十分です。そのため自分と他人のサウンドコードを見分ける必要がないため、キューへの書き込みを示す!emptyで起動します。またミキサーも無くなるため、従来後段のミキサーに入れていた符号拡張と桁調整を本モジュールに組み込みました。
以下にソースコードを示します。コメントは一部ChatGPTにより作成してもらいました。
// 波形ファイルを読み込み、オーディオDACにサウンドデータを出力するFSMの定義 import StmtFSM.*; // FSMを生成するためのユーティリティモジュールのインポート // サウンドイベントと無音を表すマクロ定義 `define SOUND1 1 // 発射音 `define SOUND2 2 // パドルとの衝突音 `define SOUND3 3 // 壁との衝突音 `define SOUND4 4 // アウトの際の音 `define NULL 'h80 // 無音を表す値(8ビットPCMで中間値) // 必要な型定義 typedef UInt#(13) Addr_t; // メモリアドレス用の13ビット符号なし整数 typedef UInt#(8) Data_t; // 8ビットデータ用の符号なし整数 typedef Bit#(16) Sound_t; // 16ビット符号付PCMサウンドデータ typedef Bit#(3) Code_t; // サウンドコード // FSMのインターフェース定義。外部からアクセスするためのメソッドが定義されています。 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 Sound_t sdout(); // 音声出力データの出力メソッド method Bool soundon(); // 音声が再生中かどうかを示す出力メソッド method Bool fifo_ren(); // FIFOの読み出し要求の出力メソッド endinterface (* synthesize,always_ready,always_enabled *) module mkSoundFSM(FSM_ifc); // 内部ワイヤとレジスタの定義 Wire#(Code_t) code <- mkWire, // コードを格納するワイヤと現在のコードを保持するレジスタ current <- mkRegU; Wire#(Bool) lrclk <- mkWire; // 左右のクロック同期用のワイヤ Reg#(Data_t) romdata <- mkRegU; // ROMから読み込まれたデータを保持するレジスタ Reg#(Data_t) dout <- mkReg(`NULL); // データ出力用のレジスタ(初期値は無音) Reg#(UInt#(32)) workd <- mkRegU; // 32ビット作業用データレジスタ Reg#(UInt#(13)) dcount <- mkRegU; // 再生カウント用の13ビットレジスタ Reg#(Addr_t) worka <- mkRegU, // アドレス計算用の作業用アドレスレジスタ romaddr <- mkRegU, // ROMのアドレスレジスタ addr <- mkRegU; // 出力用アドレスレジスタ Reg#(UInt#(8)) ii <- mkReg(0); // ループカウンタ用の8ビットレジスタ Reg#(Bool) son <- mkReg(False), // サウンド再生中フラグ用のレジスタ sonEarly <- mkReg(False), // 早期サウンド開始フラグ用のレジスタ ren <- mkReg(False), // FIFO読み込み要求フラグ用のレジスタ emptyf <- mkReg(True); // FIFOが空かどうかを示すフラグ用のレジスタ // subfunctions // READ MEM サブ関数:メモリからの読み出し // input: worka // output: romdata; // function Stmt readmem; return (seq addr <= worka; delay(2); 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 // Mainloop メインループの定義 // Stmt main = seq while(True) seq action dout <= `NULL; sonEarly <= False; son <= False; ren <= False; endaction await(!emptyf); action ren <= True; // consume 1 entry of Q current <= code; endaction await(emptyf); ren <= False; // Sync to LRCLK // await(lrclk); await(!lrclk); delay(4); // Format decoding // action case (current) `SOUND1: romaddr <= 0 + 16; `SOUND2: romaddr <= 1610 + 16; `SOUND3: romaddr <= (1610 + 900) + 16; `SOUND4: romaddr <= (1610 + 900 + 872) +16; endcase endaction readcount; romaddr <= romaddr + extend(dcount) + 4; readcount; romaddr <= romaddr - 1; // play loop while (dcount != 0) seq // Play 0 if (sonEarly == False) seq // 1cycle目 readmem; action sonEarly <= True; son <= False; dout <= `NULL; endaction endseq else seq // 2cycle目以降 readmem; action son <= True; dout <= romdata; endaction endseq // if delay(11); action romaddr <= romaddr + 1; worka <= romaddr + 1; dcount <= dcount - 1; endaction endseq // while(!終了条件) endseq // while(True) endseq; // Stmt mkAutoFSM(main); // FSMを生成し実行 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 Sound_t sdout(); let bdout = pack(dout); // 現在のオーディオデータ(dout)をパックし、16ビットのデータ(bdout)に変換します。 let s = ~bdout[7]; // 8ビット目(MSB)を反転させてサインビット(s)を生成します。 return {{s,s},bdout[6:0],{7'h0}}; // オーディオデータをsignedに変換します。 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 endmodule: mkSoundFSM