Posts Tagged with "BSV"

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

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


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

posted by sakurai on September 5, 2022 #501

サウンドデータ修正手順

ゲームサウンドの改良のため、サウンドROMの内容を修正します。サウンドROMの作成法は記載していなかったので、その備忘のためでもあります。以下に手順の大略を示します。

  1. オリジナルサウンドを(http://www.classicgaming.cc/classics/space-invaders/sounds)で入手する。Player shootingとKilled Space Invaderのサウンドの表示が誤って入れ替わっているようです。
  2. Windows上のサウンドツールである Audacityにより適宜音量を調整する。そのままだと歩行音とのバランスが悪く歩行音が聞こえにくいため、歩行音以外の音量を下げて平均化する。ただし後述のように、頻繁に鳴る音でなければ多少大き目でも良い。
  3. waveを8bitに圧縮変換する。コマンドは

ffmpeg -i input.wav -ac 1 -ar 11025 -acodec pcm_u8 -fflags +bitexact -flags:v +bitexact -flags:a +bitexact output.wav

ここで、拡張子に.wavが必要。

  1. サウンド毎のwaveバイナリが作成されたので、チャネル毎にwaveをcatでまとめ、ROMイメージのバイナリファイルを作成する。
     Ch.0 --- Code 1, 2, 9
     Ch.1 --- Code 3
     Ch.2 --- Code 4, 5, 6, 7
     Ch.3 --- Code 8, 10
  2. wcによりエントリアドレスを調べておく。エントリアドレスは、直前のROM累積サイズ+16。
  3. make_coe(以下)コマンドにより、バイナリからVivadoの読めるCOEファイルに変換する。

export input=\$1.bin
export output=\$1.coe
echo 'memory_initialization_radix=16;' > \$output
echo -n 'memory_initialization_vector=' >> \$output
od -An -t x1 -v < \$1.bin >> \$output
echo ';' >> $output

修正点

  • Code2 (自機爆発音) ---- 若干延長してフェードアウトエンベロープを加えた。

図%%.1
図501.1 修正後の自機爆発音
  • Code1 (自機弾発射音), 3 (インベーダ爆発音), 9 (自機増加音) ---- 歩行音とのバランスで音量を下げすぎたため、音量を若干上げた。

頻繁に鳴る音、例えばインベーダ破壊音が大きめだとうるさく感じますが、まれな音、例えば自機増加音や自機破壊音は大きめでも問題ないことがわかりました。ゲームバランスと言われますが、音量にもバランスがあるようです。

修正後のサウンドROM構造

表501.1 ROM構成表
Channel Code Sound Start Size [bytes] Entry=Start+16
自機音
チャネル(#0)
1 自機弾発射音 0 3,422 0+16
2 自機爆発音 3,422 12,728 3,422+16
9 自機増加音 16,150 5,500 16,150+16
合計 [bytes] (32KB ROM使用率) 21,650 (66%)
インベーダ音
チャネル(#1)
3 インベーダ爆発音 0 4,622 0+16
合計 [bytes] (32KB ROM使用率) 4,622 (14%)
インベーダ音
チャネル(#2)
4 インベーダ歩行音 1 0 1,266 0+16
5 インベーダ歩行音 2 1,266 1,570 1,266+16
6 インベーダ歩行音 3 2,836 1,570 2,836+16
7 インベーダ歩行音 4 4,406 2,180 4,406+16
合計 [bytes] (32KB ROM使用率) 6,586 (20%)
UFO音
チャネル(#3)
8 UFO爆発音 0 25,968 0+16
10 UFO飛行音 25,968 1,846 25,968+16
合計 [bytes] (32KB ROM使用率) 27,814 (85%)

ROMのエントリポイントは上記のとおりStartアドレスから+16となります。


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

posted by sakurai on August 25, 2022 #500

プチ音の再修正

ゲームFSMからのコマンドにより、サウンドはサウンドFSMで演奏されます。サウンドとサウンドの間は、サウンドが鳴り続けないように、一旦OFFにします。

シミュレーション波形を観測している際に、UFO飛行音は連続して再生されるので、一旦サウンドをOFFにするとプチ音がするのではないかと思い、ONを継続するように変更したところ、却ってプチ音がするようになりました。

確かにステートベース設計の旧版では一旦サウンドをOFFにしていたため、そのように戻したところ、プチ音は聞こえなくなりました。

図%%.1
図500.1 再修正後

修正前はson=False、dout=NULLのためにプチ音が鳴っていると思い、son=True, dout=前データを継続としましたが、却ってプチ音が鳴るようです。波形は再修正後です。

サウンドFSM(UFO音チャネル#3)のアルゴリズム

フラグのON/OFFアルゴリズムを表500.1にまとめました。

表500.1 UFOサウンドアルゴリズム表
No. コード キュー ハンドシェーク(rd_en ) サウンド間 フォーマット&演奏
1 UFO爆発, UFO飛行音 !empty rd_en <= True son <= False Play
2 UFO飛行音 OFF !empty rd_en <= True son <= False -
3 UFOフラグ - rd_en <= False son <= False UFO飛行音Play
4 他FSMコード !empty rd_en <= False son <= False -

上記でプチ音対策で修正した箇所は表500.1の緑色表示の部分です。一旦son <= TrueとONを継続するように修正しましたが、OFFに戻しました。

図%%.2
図500.2 FSM間ハンドシェーク

まず、通常のサウンドNo.1及び3は以下のフローとなります。

  1. GameFSMはempty == Trueの場合にコードを出力し、同時にOneStageFSMに対してwr_enを発行します。
  2. OneStageFSMはempty = False(=Full)とします。
  3. SoundFSMは正当コードかつempty == False (=Full)を待ち、入力されたらrd_en = TrueをOneStageFSMに返します。
  4. OneStageFSMはempty = Trueとします。
  5. GameFSMはempty == Trueを待ち、入力されたら終了です。
  6. SoundFSMもempty == Trueを待ち、入力されたらrd_en = Falseとします。
  7. SoundFSMはUFOオフコマンドならUFOフラグをOFFして終了します。
  8. SoundFSMはUFOオフコマンドでなければフォーマットをデコードします。
  9. SoundFSMはサウンドをカウント分演奏します。

次に、通常のサウンドで無い場合No.2は、UFOフラグがTrueの時であり、GameFSMのコードがキューに無くてもUFO飛行音を演奏し続けます。

  1. GameFSMはなにもしません。
  2. SoundFSMはUFOフラグ==Trueの場合には、実行しますがrd_en = Trueは返しません。これはキューに何もないためです。
  3. SoundFSMは内部的にはUFO飛行音と扱います。
  4. SoundFSMはフォーマットをデコードします。
  5. SoundFSMはサウンドをカウント分演奏します。

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

posted by sakurai on August 22, 2022 #499

サウンドFSM毎に固有のROMを持ちますが、ROMとアクセスアドレス、データの関係表です。各コードは3段のエントリとなっており、それぞれフォーマット格納アドレス、フォーマット長、データ格納アドレス、データ長、サウンド格納アドレス、サウンドデータの順となっています。

表499.1 ROMアクセス表
FSM No. コード アドレス データ
自機音
チャネル(#0)
1 0013, 0012, 0011, 0010 00,00,00,10
002b, 002a, 0029, 0028 00,00,10,b1
002c,002d,002e,002f,... 81,81,82,83,...
2 10f1,10f0,10ef,10ee 00,00,00,10
1109,1108,1107,1106 00,00,22,1b
110a,110b,110c,110d,... 7b,79,7b,79,...
9 3339,3338,3337,3336 00,00,15,4f
3351,3350,334f,334e 00,00,10,b1
3352,3353,3354,3355,... 7c,7c,7b,7c,...
インベーダ音
チャネル(#1)
3 0013, 0012, 0011, 0010 00,00,00,10
002b, 002a, 0029, 0028 00,00,11,e1
002c,002d,002e,002f,... 83,85,84,81,...
インベーダ音
チャネル(#2)
4 0013, 0012, 0011, 0010 00,00,00,10
002b, 002a, 0029, 0028 00,00,04,c5
002c,002d,002e,002f,... 76,77,74,74,...
5 0505,0504,0503,0502 00,00,00,10
051d,051c,051b,051a 00,00,05,f6
051e,051f,0520,0521,... 7a,78,78,77,...
6 0b27,0b26,0b25,0b24 00,00,00,10
0b3f,0b3e,0b3d,0b3c 00,00,05,f6
0b40,0b41,0b42,0b43,... 7a,7b,7a,7a,...
7 1149,1148,1147,1146 00,00,00,10
1161,1160,115f,115e 00,00,08,58
1162,1163,1164,1165,... 78,7a,77,78,...
UFO音
チャネル(#3)
8 0013, 0012, 0011, 0010 00,00,00,10
002b, 002a, 0029, 0028 00,00,65,43
002c,002d,002e,002f,... 7e,78,7b,7b,...
10 6583,6582,6581,6580 00,00,00,10
659b,659a,6599,6598 00,00,07,0a
659c,659d,659e,659f,... 99,86,7a,7a,...

バスアクセスのシミュレーションエラーが出たため、まとめておこうと思い上記の表を作成しましたが、エラーの原因はROMの内容が古かったためでした。


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

posted by sakurai on August 19, 2022 #498

テストケース

設計で最重要なのがテストケース作成です。バグの無い設計のためには、テストケースを網羅的に作成して検証する必要があります。

表498.1 テストケース進捗表
FSM No. テストケース Pass/Fail
No. 内容 V1 (State) V2 (Seq.)
自機音
チャネル(#0)
1 CODE1演奏中にCODE2がプリエンプト可能なこと Pass Pass
2 CODE1演奏中にCODE9がプリエンプト可能なこと Pass Pass
3 CODE9演奏中にCODE1がプリエンプト不可能なこと
(自機増加音が妨げられないこと)
Pass Pass
4 CODE3を無視すること Pass Pass
インベーダ音
チャネル(#1)
5 CODE3演奏中にCODE3がプリエンプト可能なこと
(実際には起こらないため不要)
Pass Pass
6 CODE1を無視すること Pass Pass
インベーダ音
チャネル(#2)
7 CODE4演奏中にCODE5がプリエンプト可能なこと Pass Pass
8 CODE5演奏中にCODE6がプリエンプト可能なこと Pass Pass
9 CODE1を無視すること Pass Pass
UFO音
チャネル(#3)
10 CODE10演奏中にCODE8がプリエンプト可能なこと Pass Pass
11 CODE10演奏後にCODE10OFFが来るまで演奏を継続すること Pass Pass
(12) (11で)CODE10演奏中にプチプチ音が鳴らないこと Pass Pass
13 CODE10演奏中にCODE8がプリエンプトし最後にOFFになること Pass Pass
14 CODE10演奏中にCODE8がプリエンプトしOFFがプリエンプト
した後OFFになること
Pass Pass
15 CODE1を無視すること Pass Pass

プログラムの対称性に鑑み、最小必要パターンのみとしました。パターンは必要に応じ、随時追加削除していきます。


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

posted by sakurai on August 18, 2022 #497

チャネルとコードの対応

表497.1 音データ、コードとサウンドチャネルの関係
サウンドの説明 CODE番号 サウンドチャネル
ON OFF
自機弾発射音 1 自機音チャネル(#0)
自機爆発音 2
自機増加音 9
インベーダ爆発音 3 インベーダ音チャネル(#1)
インベーダ歩行音1 4 インベーダ音チャネル(#2)
インベーダ歩行音2 5
インベーダ歩行音3 6
インベーダ歩行音4 7
UFO爆発音 8 UFO音チャネル(#3)
UFO飛行音 10 10+16

チャネル起動条件

それぞれのサウンドチャネルは別のFSMにより駆動されます。それらの起動条件を上げると、

  • チャネル0(FSM0) ---- (CODE1_ON || CODE2_ON || CODE9_ON) && !empty
  • チャネル1(FSM1) ---- CODE3_ON && !empty
  • チャネル2(FSM2) ---- (CODE4_ON || CODE5_ON || CODE6_ON || CODE7_ON) && !empty
  • チャネル3(FSM3) ---- ((CODE8_ON || CODE10_ON || CODE10_OFF) && !empty) || UFO
  • 例外処理: チャネル3においてはCODE10_ONの場合、OFFが来るまで演奏し続けるようにUFOのフラグを立てますが、起動時にUFOフラグだった場合はコマンドが無くてもCODE10_ONとして起動します。

チャネル終了条件

それぞれのサウンドチャネルは演奏を終了することがあります。カウンタが0になる場合またはプリエンプションです。具体的な終了条件(もしくは割り込み条件)を上げると、

  • カウンタが0になる (|| 以下のプリエンプション)
  • チャネル0(FSM0) ---- ((CODE1_ON || CODE2_ON || CODE9_ON) && !empty) && (current != CODE9_ON)
  • 例外処理: チャネル0(FSM0) ---- CODE9_ONの場合はプリエンプション禁止のため、currentを見てCODE9_ON(自機増加中)だったら割り込まないようにします。
  • チャネル1(FSM1) ---- CODE3_ON && !empty
  • チャネル2(FSM2) ---- (CODE4_ON || CODE5_ON || CODE6_ON || CODE7_ON) && !empty
  • チャネル3(FSM3) ---- (CODE8_ON || CODE10_ON || CODE10_OFF) && !empty
  • 全チャネル(FSM0~3) --- コードがある場合はキューから取り出し先頭に戻ります。

このように、起動条件の例外はUFOフラグが立っている場合であり、終了(割り込み)条件の例外は自機が増加中に割り込まない場合です。


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

posted by sakurai on August 17, 2022 #496

設計方針

前回のBSVによるサウンドFSMの設計においては、ステートベース設計手法により設計しました。ステートベース設計とは、ここでは一連のフローを、クロックサイクルで定義されるステートに分解し、ステートのルールを一つずつ書いていく手法を指します。ただ、Verilogでも同じ手法で設計するため工数は同じであり、高級言語のご利益はありません。

前回の設計では、以前にVerilogでステートベースで設計した経験があるので、階層化ルールを用いて階層化FSMをステートベース設計しました。今回は比較のため、シーケンスベースで設計しようと思います。シーケンスベース設計とは、ここではシナリオを人手でステートに分解せずに、BSVに任せることを指します。

使用コンパイラ

ところが、2021年末にアップグレードのため、bscを再コンパイルしたところ(build 9a7d5e05)、Verilog出力が正しくできなくなりました。具体的にはスタックオーバーフローというエラーが出ます。フォーラムで聞いたところソースを送って欲しいとのことでした。

ソースを送って調べてもらいましたが、スタックを広げるコマンドを実行してもエラーが出るらしく、ソースを改善せよとのことです。ソースは巨大なFSMから構成されるため、FSMを分離したいのはやまやまなのですが、教えられた通り実施しても、bscが一個のFSMにまとめようとするためうまく行きませんでした。やむなく、以前のコンパイラ(build 38534dc)を使い続けることにします。

処理フロー

図54.2は以前の記事に示したもので、FSMはこれをデコードし、音声を出力するものです。

図54.2
図54.2 waveフォーマット例

従って、基本的なフローはこのフォーマットどおりのステートベース設計のものを踏襲します。

  • コード待ち ---- FSM0~3に応じたコードを受け付ける)
  • 例外フラグ ---- UFOの場合はONからOFFまでサウンドをならし続ける等の例外への対処
  • コードに応じてROMの先頭+16にポインタを移す
  • フォーマットサイズを取得し、ポインタをフォーマット長分だけ増加
  • "data"をスキップ
  • データサイズを取得しカウンタにセット
  • ポインタをデータの先頭に移す
  • 終了条件でなければループ
  •  音声データを取り出す
  •  データを出力
  •  ポインタを進める
  •  カウンタを1減らす
  • ループ終端

ここで前回は、上記ループ内部において、インターポレーションのため4クロックからなるサウンド出力を4回繰り返していました。が、出力はループ内では変わらないので、16サイクルに1回出力すれば良いことになります。よって上記のループは16サイクルになるように設計します。


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

posted by sakurai on February 1, 2022 #450

だいぶ前の記事ですが、テストベンチのBSVと、モジュールのBSVからVerilogを生成し、Verilogシミュレーションを実施しました。BSVではテストベンチにおいてクロックとリセットが自動生成され、暗黙的にモジュールに供給されます。一方、Verilogでは明示的にテストベンチにクロックとリセットを供給する必要があります。前の記事では、テストベンチ内にその処理を行う記述をインクルードする方法を用いました。

ここでは、最上位からそれらの信号を供給する手法をとります。これにより、よりスマートにVerilogシミュレーションが実行できます。まず、原始最上位ファイルを用意します。最上位からはテストベンチを呼び出しています。

最上位ファイル:mkTop.v

module mkTop () ;
   /*AUTOREGINPUT*/
   mkTb Tb_inst(/*AUTOINST*/);
  initial begin
    RST_N = 1'b0;
    #30;
    RST_N = 1'b1;
  end
  initial begin
    CLK = 1'b0;
    forever begin
       #5 CLK = ~CLK;
    end
  end
endmodule // mkTop  

これに対して、Verilog-modeのオートコネクションコマンドであるC-c C-aを用いてポートの追加を行えば、

最上位ファイル:mkTop.v

module mkTop () ;
   /*AUTOREGINPUT*/
   // Beginning of automatic reg inputs (for undeclared instantiated-module inputs)                
   reg                  CLK;                    // To Tb_inst of mkTb.v                            
   reg                  RST_N;                  // To Tb_inst of mkTb.v                            
   // End of automatics                                                                            
   mkTb Tb_inst(/*AUTOINST*/
                // Inputs                                                                          
                .CLK                    (CLK),
                .RST_N                  (RST_N));
  initial begin
    RST_N = 1'b0;
    #30;
    RST_N = 1'b1;
  end
  initial begin
    CLK = 1'b0;
    forever begin
       #5 CLK = ~CLK;
    end
  end
endmodule // mkTop              

のように、テストベンチに対してクロックとリセットが接続されます。

最上位、テストベンチ、モジュールを結合した実行ファイルを生成します。エラーが出ることもなく実行ファイルが生成され、実行ファイルによりVerilogシミュレーションを実行すれば、

\$ iverilog mkTop.v mkTb.v mkFibOne.v -o mkFibOne.exev
\$ ./mkFibOne.exev
1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181
6765
10946

このように、正しくVerilogシミュレーションが行われ、前記事と同じ結果となります。

結論としては、この記事のように自動結合を使用すれば、前記事のようにincludeを埋め込む必要はありませんでした。


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

posted by sakurai on July 24, 2021 #425

BSVによるSpaceInvaderのVerilogソースを公開」と題する記事をQiitaに投稿しました。

図%%.1
図425.1 Qiita投稿

ボードの説明、Verilogソースの入手法、VivadoによるFPGAの配置配線までをガイドしています。


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

敵弾の貫通度の修正 (3)

posted by sakurai on June 17, 2021 #423

実行結果

このように修正した結果、以下の図423.1に示すように、敵弾の種類により貫通度を変えることができました。

図%%.1
図423.1 貫通度の違い

図423.1の下部中央のバリケードの

  • 右側に当たったのは、"Rolling"ショットで貫通度=3
  • 左側に当たったのは、"Plunger"ショットで貫通度=7

と深さが異なります。

以下の図はFPGAボードの動画です。最初に右側にRollingショット、次に左側にPlungerショットが着弾しています。

図%%.2
図423.2 貫通度の違い(動画)

以下の図は前記事のオリジナル動画です。最初に左側にPlungerショット、次に右側にRollingショットが着弾しています。

図%%.3
図423.3 貫通度の違い(オリジナル動画)

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


ページ: