|
21 |
GameFSMの改良 (18) |
さて、過去記事の続きです。GameFSM(ゲームシナリオ)とSoundFSM(サウンドプレーヤ)の間をOneStageというセマフォで接続していて、それを最適化しようとしたところ、ChatGPTにCDCを考慮していないと怒られてしまいました。「なら作って」と言って作ってもらった(実際には大変だったが)コードを示します。
マルチクロック設計で、2つの非同期クロックドメインにまたがる1段の非同期FIFOを用いています。
GSBridge.bsv:
package GSBridge;
import Clocks::*;
typedef Bit#(4) SoundCode_t;
// Game 側 IF:busy を見て !busy のときだけ setReq する前提
interface GSBridgeGameIfc;
method Action setCode(SoundCode_t code); // このサイクルのコード値
method Action setReq (Bool fire); // このサイクルで発行するなら True
// method Bool busy; // バッファ占有中なら True
endinterface
// Sound 側 IF:valid が立ったサイクルで code を取り込む
interface GSBridgeSoundIfc;
method SoundCode_t code; // 取り込まれたコマンド値
method Bool valid; // 新コマンド到着 1 サイクルパルス
endinterface
interface GSBridgeIfc;
interface GSBridgeGameIfc game;
interface GSBridgeSoundIfc sound;
endinterface
(* synthesize, always_ready, always_enabled, no_default_clock, no_default_reset *)
module mkGSBridge#(Clock gameClk, Reset gameRst,
Clock sndClk)
(GSBridgeIfc);
// gameRst を sndClk ドメインに同期させたリセット
Reset sndRst <- mkSyncReset(2, gameRst, sndClk);
// Game→Sound の 4bit コマンド用 Sync FIFO(深さ1)
SyncFIFOIfc#(SoundCode_t) fifo
<- mkSyncFIFO(1, gameClk, gameRst, sndClk);
// Game ドメイン側入力(必ず gameClk/gameRst にぶら下げる)
Wire#(SoundCode_t) w_code <- mkWire(clocked_by gameClk, reset_by gameRst);
Wire#(Bool) w_req <- mkWire(clocked_by gameClk, reset_by gameRst);
// Sound ドメイン側の出力レジスタ(sndClk/sndRst ドメイン)
Reg#(SoundCode_t) r_code <- mkRegU (clocked_by sndClk, reset_by sndRst);
Reg#(Bool) r_valid <- mkReg(False, clocked_by sndClk, reset_by sndRst);
//-------------------------
// Game ドメイン: fire かつ FIFO に空きがあるときだけ enq
//-------------------------
rule rl_enq (w_req && fifo.notFull);
fifo.enq(w_code);
endrule
//-------------------------
// Sound ドメイン: FIFO から 1 件取り出して r_code にラッチ
// valid を 1 サイクルだけ立てる
//-------------------------
rule rl_deq (fifo.notEmpty && !r_valid);
r_code <= fifo.first;
fifo.deq;
r_valid <= True;
endrule
rule rl_clear (r_valid);
r_valid <= False;
endrule
//-------------------------
// Game 側サブインタフェース実装
//-------------------------
interface GSBridgeGameIfc game;
// このサイクルのコード値を保持
method Action setCode(SoundCode_t code);
w_code <= code;
endmethod
// このサイクルで発行するなら fire=True
method Action setReq(Bool fire);
w_req <= fire;
endmethod
// ★ busy は「未消費のコマンドが FIFO にあるかどうか」
// = 元の 1bit セマフォと等価
// method Bool busy;
// return fifo.notEmpty;
// endmethod
endinterface
//-------------------------
// Sound 側サブインタフェース実装
//-------------------------
interface GSBridgeSoundIfc sound;
// 直近に取り込んだコマンド値(valid が 1 のサイクルに有効)
method SoundCode_t code;
return r_code;
endmethod
// 新コマンドが届いたサイクルだけ 1 になるパルス
method Bool valid;
return r_valid;
endmethod
endinterface
endmodule
endpackage
Leave a Comment