Article #1043

既に発行済みのブログであっても適宜修正・追加することがあります。
We may make changes and additions to blogs already published.

GameFSMの改良 (18)

posted by sakurai on November 21, 2025 #1043

さて、過去記事の続きです。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

Your email address will not be published.

You may use Markdown syntax. If you include an ad such as http://, it will be invalidated by our AI system.

Please enter the numbers as they are shown in the image above.