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