BSVを用いたサウンドFSMのシーケンスベースによる再設計


![ezgif.com-effects.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223298/5366c527-a1cb-38f7-96e2-9d3906a93eac.gif)

# 1. はじめに
勉強がてら**BSV**(Bluespec SystemVerilog)による高位合成を用いてフルハードウェアでスペースインベーダーを[設計](https://qiita.com/mocapapa/items/3928c7f727b97f64d036)しましたが、その中にはゲームシナリオを司る**GameFSM**と、GameFSMからサウンドコードを受けて様々なサウンドを鳴らす**SoundFSM**の2種のFSM (Finite State Machine)が存在します。その2つのFSMをバッファする1段キューOneStageが入っています。

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223298/6cdb7d80-6b32-966d-6813-d20a3c1efe84.png)|
|:-:|
|図1 GameFSMとSoundFSM|

それぞれの設計手法は、たまたま以下のようにしました。

- SoundFSM ---- **ステートベース設計**
- GameFSM ---- **シーケンスベース設計**

ここで、**ステートベース設計**とは、ステートを一つ一つBSVで書いていくことを意味し、一方**シーケンスベース設計**とはステート分解をせずに、高位合成を用いて設計し、ステートマシンの実装は高位合成コンパイラに任せることを意味します。

設計手法がこのように異なっていた理由は、[verilog版](https://qiita.com/mocapapa/items/f92dd112cceba7c229b3)を設計したときは、全てステートベース設計としたため、SoundFSMはその比較的簡単なステート遷移を忠実にBSVで設計したためです。一方でGameFSMはステート数が非常に多く、ステート遷移はコンパイラに任せたいと思ったためです。

今回はSoundFSMもシーケンスベース設計に変更しました。その理由はもちろん、ステートベース設計ではステート分解を人力で行うため、高級言語のメリットがあまり出なく、また同じ機能をステートベース設計とシーケンスベース設計とで設計してみて、結果の違いを見たかったためです。

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223298/df607502-089a-886d-6cfe-5a278e945ac6.png)|
|:-:|
|図2 作成したPMODインタフェースボード|

図2はベンダーである[JLCPCB](https://jlcpcb.com/)から実装が完了して送付された[Ultra96toPMODボード](https://qiita.com/mocapapa/items/f92dd112cceba7c229b3#ultra96topmod%E3%83%9C%E3%83%BC%E3%83%89)です。最新版のUltra96toPMODV10ボードデータの場所等は[この記事](https://qiita.com/mocapapa/items/a2faa710503e4affa88b#143-fs%E3%83%9E%E3%82%A4%E3%82%AF%E3%83%AD%E8%A3%BDultra96topmod%E3%83%9C%E3%83%BC%E3%83%89)で示しています。

# 2. システム構成 #
SoundFSMのソフトブロック階層を図3に示します。ソフトブロックとは中身が見える階層で、これはSoundFSMのインスタンスであるmkSoundFSM及び8bit×32KwordのシングルポートROMで構成されます。上位のsound階層では従来通り、この階層を4チャネル使用し、ミキサーで合成した後パラシリ変換によりシリアルDAC用のデータとしています。

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223298/8a7c2694-fb08-18be-7a0e-9e32a3eb2eff.png)|
|:-:|
|図3 SoundFSMと専用ROMを含む階層|

## 2.1 SoundFSM ##
基本的には[前記事](https://qiita.com/mocapapa/items/3928c7f727b97f64d036)のとおりですが、ステートマシンを構成していたrule分を取り去り、

```verilog
import StmtFSM::*;
```

としてステートマシンを自動設計するライブラリを呼び出しています。後は
```verilog
	     while(True) seq
```
という無限ループの中に処理を書き、それらの処理を高位合成コンパイラがステートマシンに変換します。

### 2.1.1 ハンドシェーク

図4に、図1と同一ですがGameFSMとSoundFSMの間のハンドシェークを示します。

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223298/6cdb7d80-6b32-966d-6813-d20a3c1efe84.png)|
|:-:|
|図4 GameFSMとSoundFSMの間のハンドシェーク|

いわゆる2線式のハンドシェークであり、以下のようなアルゴリズムにより動作します。

1. GameFSM側はemtpyを待ち、emptyの場合にはコードとwr_enを出力します。
1. OneStage側はwr_enによりemptyフラグが!emptyとなります。
1. GameFSM側はコードの出力後は次の処理に移ります。
1. SoundFSM側は自コードかつ!emptyを待ち、!emptyの場合はrd_enを返します。
1. OneStage側はrd_enによりemptyとなります。
1. SoundFSM側はemptyを待ち、emptyの場合には!rd_enを出力します。
1. SoundFSM側はUFOオフコマンドならUFOフラグをOFFして終了します。
1. SoundFSM側はUFOオフコマンドでなければフォーマットをデコードします。
1. SoundFSM側はサウンドをカウント分演奏します。

ただし、UFOの飛行音は一旦ONになると、OFFが来るまで鳴り続ける仕様となっているため、上記ハンドシェークに依らない演奏が必要です。そのため、UFO飛行音が来るとUFOフラグを内部的に立て、起動時に上記ハンドシェークによるコマンド入力またはUFOフラグのONにより、FSMを起動します。

1. GameFSM側はなにもしません。
1. SoundFSM側はUFOフラグ==Trueの場合に実行はしますが、emptyを見ず、かつrd_enは返しません。
1. SoundFSM側は内部的にはUFO飛行音と扱います。
1. SoundFSM側はフォーマットをデコードします。
1. SoundFSM側はサウンドをカウント分演奏します。

## 2.2 ROM ##

基本的には[前記事](https://qiita.com/mocapapa/items/3928c7f727b97f64d036)のとおりですが、音量バランスの再調整を実施したことと、自機破壊音が短かめだったので、図5のように約40%延長し、さらにフェードアウトをかけました。


|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223298/b9327427-c162-662c-5a70-4a1b09ea536c.png)|
|:-:|
|図5 自機破壊音の延長|

ROM構成は旧版と同一で、8bit×32KwordのROMをFSMのチャネル数分だけ4個使用します。

表1.　ROM構成表

<table>
  <tr>
     <th ">Channel</th>
     <th ">Code</th>
     <th ">Sound</th>
   <th ">Start</td>
    <th ">Size [bytes]</th>
    <th ">Entry</th>
  </tr>
  <tr>
     <td rowspan=4 ">自機音<br />チャネル(#0)</td>
    <td style="text-align:right;"">1</td>
    <td ">自機弾発射音</td>
    <td style="text-align:right;">0</td>
    <td style="text-align:right;">3,422</td>
    <td style="text-align:right;">0+16</td>
  </tr>
  <tr>
    <td style="text-align:right;"">2</td>
    <td ">自機爆発音</td>
    <td style="text-align:right;">3,422</td>
    <td style="text-align:right;">16,150</td>
    <td style="text-align:right;">3,422+16</td>
  </tr>
  <tr>
    <td style="text-align:right;">9</td>
    <td ">自機増加音</td>
    <td style="text-align:right;">16,150</td>
    <td style="text-align:right;">5,500</td>
    <td style="text-align:right;">16,150+16</td>
  </tr>
  <tr>
    <td colspan=3 ">合計 [bytes] (32KB ROM使用率)</td>
    <td colspan=2 style="text-align:right;">21,650 (66%)</td>
  </tr>
  <tr>
     <td rowspan=2 ">インベーダ音<br />チャネル(#1)</td>
    <td style="text-align:right;">3</td>
    <td ">インベーダ爆発音</td>
    <td style="text-align:right;">0</td>
    <td style="text-align:right;">4,622</td>
    <td style="text-align:right;">0+16</td>
  </tr>
  <tr>
    <td colspan=3 ">合計 [bytes] (32KB ROM使用率)</td>
    <td colspan=2 style="text-align:right;">4,622 (14%)</td>
  </tr>
  <tr>
     <td rowspan=5 ">インベーダ音<br />チャネル(#2)</td>
    <td style="text-align:right;">4</td>
    <td ">インベーダ歩行音 1</td>
    <td style="text-align:right;">0</td>
    <td style="text-align:right;">1,266</td>
    <td style="text-align:right;">0+16</td>
  </tr>
  <tr>
    <td style="text-align:right;">5</td>
    <td ">インベーダ歩行音 2</td>
    <td style="text-align:right;">1,266</td>
    <td style="text-align:right;">1,570</td>
    <td style="text-align:right;">1,266+16</td>
  </tr>
  <tr>
    <td style="text-align:right;">6</td>
    <td ">インベーダ歩行音 3</td>
    <td style="text-align:right;">2,836</td>
    <td style="text-align:right;">1,570</td>
    <td style="text-align:right;">2,836+16</td>
  </tr>
  <tr>
    <td style="text-align:right;">7</td>
    <td ">インベーダ歩行音 4</td>
    <td style="text-align:right;">4,406</td>
    <td style="text-align:right;">2,180</td>
    <td style="text-align:right;">4,406+16</td>
  </tr>
  <tr>
    <td colspan=3 ">合計 [bytes] (32KB ROM使用率)</td>
    <td colspan=2 style="text-align:right;">6,586 (20%)</td>
  </tr>
  <tr>
     <td rowspan=3 ">UFO音<br />チャネル(#3)</td>
    <td style="text-align:right;">8</td>
    <td ">UFO爆発音</td>
    <td style="text-align:right;">0</td>
    <td style="text-align:right;">25,968</td>
    <td style="text-align:right;">0+16</td>
  </tr>
  <tr>
    <td style="text-align:right;">10</td>
    <td ">UFO飛行音</td>
    <td style="text-align:right;">25,968</td>
    <td style="text-align:right;">1,846</td>
    <td style="text-align:right;">25,968+16</td>
  </tr>
  <tr>
    <td colspan=3 ">合計 [bytes] (32KB ROM使用率)</td>
    <td colspan=2 style="text-align:right;">27,814 (85%)</td>
  </tr>
</table></center>

# 3. テストケース
コードを開発する際にはテストケースが重要です。バグを発見してアルゴリズムを修正した場合、バグを発見したケースだけを確認すれば良いのではなく、全てのケースにおいてdegradeしていないかの確認が必要です。そのため、一か所修正したら、基本的には全てのテストケースを流す必要があります。人力ではとてもできないため、テストケースを流すための自動化をせざるをえません。

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

表中のV1はステートベース設計のコード、V2はシーケンスベース設計のコードを意味します。

このテストケースによりバグを発見し、修正した後に再度全て流すループを回し、ソースコードを開発しました。実際にはテストケースには漏れがあり、テストケース上では動作してもFPGAに焼いて実行させてバグを発見した場合もあります。その際はそのバグを再現するようなテストケースをまず作成し、バグを修正し、テストケース及びFPGAで確認しました。

# 4. ソースコード
完成したBSVのソースコードを添付します。define疑似命令によりFSM0, 1, 2, 3との違いを吸収してソースの共通化を図っています。

```verilog: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
    `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);
       method Action sync(Bool lrclk);
       method Action empty(Bool flag);
       method Addr_t rom_address();
       method Data_t sdout();
       method Bool soundon();
       method Bool fifo_ren();
    endinterface
    
     (* 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,
                       data <- mkRegU,
                       dout <- mkReg(`NULL);
       Reg#(UInt#(32)) workd <- mkRegU;
       Reg#(UInt#(15)) dcount <- mkRegU;
       Reg#(Addr_t) worka <- mkRegU,
                       romaddr <- mkRegU,
                       addr <- mkRegU;
       Reg#(UInt#(8)) ii <- mkReg(0);
       Reg#(Bool) son <- mkReg(False),
                       sonEarly <- mkReg(False),
                       ren <- mkReg(False),
                       emptyf <- mkReg(True);
    `ifdef FSM3
        Reg#(Bool) fUFO <- mkReg(False);
    `endif

       // subfunctions
       //   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);
             if (`COND_FSM3) action
                fUFO <= (code == `SOUND10_ON);
                ren <= True;
                current <= code;
             endaction else if (fUFO) action
                current <= `SOUND10_ON;
            endaction
    `endif
             await(emptyf);
             ren <= False;
    `ifdef FSM3
             if (code == `SOUND10_OFF) continue;
    `endif
             await(lrclk);
             await(!lrclk);
             delay(4);

             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
             readcount;
             romaddr <= romaddr + extend(dcount) + 4;
          
             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;
                   action
                      sonEarly <= True;
                      son <= False;
                      dout <= `NULL;
                   endaction
                endseq else seq
                   readmem;
                   action
                      son <= True;
                      dout <= romdata;
                   endaction
                endseq

                delay(11);
                action
                   romaddr <= romaddr + 1;
                   worka <= romaddr + 1;
                   dcount <= dcount - 1;
                endaction
             endseq
    `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
```

# 5. 結果比較

表3に**ステートベース設計**及び**シーケンスベース設計**という、2種類の設計手法で設計した同じ仕様のSoundFSMの1チャネル分の結果を示します。行数やFPGA LUT数に幅があるのはチャネル0～3までで仕様が若干異なるためです。

表3 旧BSV版(ステートベース)SoundFSM結果
|BSV行数|生成Verilog行数|FPGA LUT数|
|--:|--:|--:|
|437|890～936|184~197|

表4 新BSV版(シーケンスベース)SoundFSM結果
|BSV行数|生成Verilog行数|FPGA LUT数|
|--:|--:|--:|
|288|1,807～1,977|220～231|

- BSV行数は437行から288行へ66%と減少しました。
- 生成されたVerilog行数は平均913行から1,892行へとほぼ200%と大幅に増加しました。ただし、中を見ると、論理合成に関係の無いエラー表示が約1,000行と多く、論理合成対象は約800行であり、人手設計とあまり変わりませんでした。
- FPGAのLUTで比較すれば、平均191個から226個へと118%と若干増加しました。Verilog行数が増えたほどは物量は増えていません。その理由は前述のとおりです。
- 開発工数は行数が増えると線形では増えず、組み合わせのため指数的に増加すると思われます。そのため、BSV行数が減ったことは開発工数が1/3程度になったと考えられます。　

今回は同じサウンドFSMとして、前回が**ステート(ルール)ベース**による設計、今回が**シーケンスベース**の設計という、異なった2つの設計手法によりFSMを設計しました。表2と表3に関してBSVとVerilogの行数の比を見ると、ルールベース(表3)では2.09倍であるのに対して、シーケンスベース(表4)では6.57倍と3.14倍程度増加しています。ただし、増加する原因は、ステートマシンの自動生成のエラー表示のためのようです。最終的なFPGAのLUTはあまり変わらないので、工数削減とのトレードオフに関して、十分自動ステートマシン設計(シーケンスベース設計)が優れていると評価できます。