Posts Issued on September 6, 2022

posted by sakurai on September 6, 2022 #502

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

のようにマクロ定義により行います。


左矢前のブログ 次のブログ右矢