Posts Tagged with "Vivado HLx"

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

インベーダーゲームのソースの研究を読んで理解したことを記します。

UFOスコア表

参考にしたUFOスコア表です。

図%%.1
図319.1 UFOスコア表

BSV実装

この表には重なりがあるので、実は、表は1つで問題ありません。 弊社のBSV実装では以下の表を用いています。

UInt#(5) ufo_score[15] = {10,10,10,5,15,10,10,5,5,10,15,10,10,5,30};

ただし、インデックスの初期値を6としており、7番目から使用することで上記のアルゴリズムを再現しています。まず、インデックスの初期化です。

ufo_score_idx <= 6;

次にインクリメント部です。この表は15エントリしかないので、15でラップアラウンドします。

ufo_score_idx <= ufo_score_idx + 1; if (ufo_score_idx == 15) ufo_score_idx <= 0;

また、インベーダーゲームは得点の1の位は常に0なので、得点は1/10で記憶しています。

オリジナル

さて、ソースコードの研究によると、少々ズレています。

図%%.2
図319.2 UFOスコア表

コメントでも書かれているように、UFOスコア表は16エントリにも関わらず、バグにより15個までしか使用されず、15番目の次は0番目に戻っています。従って、最後の50点は使用されません。

Javascript実装

最後に、Javascriptによる実装でも同様なアルゴリズムにより、自弾ショット数の15の剰余を取った数に6のオフセットをかけています。

const // UFOスコアテーブル us = [100,100,100,50,150,100,100,50,50,100,150,100,100,50,300];

const plus = us[(6 + shotCount) % 15];

結果として、上記4種類は実装は微妙に異なるものの、最終的には同一の結果となります。


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

posted by sakurai on September 17, 2020 #318

インベーダーゲームのソースの研究を読んで理解したことを記します。

敵弾速度

参考にしたcellvaderにおいて、敵弾の移動速度は1pixcel/tickでした(tick=1/60sec)。それで特に違和感がなかったのですが、上記資料を見ると、33%も速い4pixcel/3tickとのこと。そのため、3tick毎に4pixcel動かすように修正しますが、注意として衝突判定は1pixcel毎に行う必要があります。そうでないとすり抜けが起きる可能性があります。従って、

フレーム0: 何もしない
フレーム1: 何もしない
フレーム2: y座標を-1して衝突判定、衝突の場合は衝突処理し、中断
      y座標を-2して衝突判定、衝突の場合は衝突処理し、中断
      y座標を-3して衝突判定、衝突の場合は衝突処理し、中断
      y座標を-4して衝突判定、衝突の場合は衝突処理し、中断
      y座標を-4して敵弾移動

この3フレームを繰り返すことになります。さらに、インベーダ―数が8未満の場合は敵弾速度が高速化するとのことです。具体的には5pixcel/3tickとなります。従って、上記のフレーム2においてさらに「y座標を-5して衝突判定、衝突の場合は衝突処理し、中断」の処理を加え、敵弾はy-5の場所に移動します。

歩行音

フリートトーン(fleet tone)、もしくはステップサウンド(step sounds)と書かれていますが、インベーダーの歩行音のことです。同じく参考にしたcellvaderでは、歩行音の発生間隔は1tone/1fleetでした。つまり隊の動作と歩行音が同期しています。ただし、歩行音の発生毎に4種類の歩行音を切り替えます。

ところが、インベーダーゲームのソースの研究では、隊の移動と歩行音は同期しておらず、歩行音間隔の最小は5だとのことです。具体的な表を示せば、オリジナルのアルゴリズムは以下の表318.1のようになります。確かに、現状の実装ではインベーダー数が1になる場合は歩行音が速すぎる気がします。原文には5未満になると不愉快な音になると書かれています。

表318.1 オリジナルの歩行音間隔表
インベーダー数[匹] 歩行音間隔[tick]
55 52
54 52
53 52
52 52
51 52
50 52
49 46
48 46
47 46
46 46
45 46
44 46
43 46
42 39
41 39
40 39
39 39
38 39
37 39
36 39
35 34
34 34
33 34
32 34
31 34
30 34
29 34
28 34
27 28
26 28
25 28
24 28
23 28
22 28
21 24
20 24
19 24
18 24
17 24
16 21
15 21
14 21
13 21
12 19
11 19
10 19
9 16
8 16
7 14
6 13
5 12
4 11
3 9
2 7
1 5

そのため、現状での隊(rack)の先頭での歩行音処理を廃止し、次の処理を追加します。

  • 歩行音間隔タイマーを設置します。
  • インベーダー1匹の処理につき、歩行音間隔タイマーを1だけデクリメントし、タイマーがゼロになったら歩行音を鳴らします。
  • タイマーがゼロになったら、その時点のインベーダー数で上表を引き、歩行音間隔値をロードします。

インベーダーが減るたびに上表を引いて歩行音間隔値を変更するのではありません。それだとタイマーがゼロになる寸前でインベーダーが死ぬと新たなタイマー値がロードされ、歩行音間隔が約2倍の長さとなる不具合が起きる可能性があります。

歩行音が隊と同期はしていても不快な音にならないように、最小を5にするだけで良いような気もしますが。

Javascript実装

Javascriptによる実装では、インベーダ数=55の時に52、インベーダ数=1の時に5と、上表と同様ですが、その間は線形補完しているようです。


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

ゲームFSMとサウンドFSMの連携

posted by sakurai on June 5, 2020 #271

Ultra96においてBSVで開発

元々Verilog版では、コマンドバッファに書き込むだけで特に何もしなくても動作していました。今回BSVで再設計する際に、サウンドを4chとし、取りこぼしを避けるために考えたのがサウンドキュー(FIFO)でした。

図%%.1
図271.1 サウンドキュー

これはVivadoのFIFOジェネレータで作成したため、最小段数でもかなり深く1024段程度となっています。 実験したところ、確かに取りこぼしは無いのですが、一方、サウンドがゲームとズレて行き、まるでサウンドレコーダのような動作になってしまいました。そのため、FIFOを1段に修正しました。FIFOジェネレータでは1段のFIFOは作成できないのでVerilogで記述しました。1段のためFIFOと呼ぶのはおかしいのでコマンドバッファと呼ぶことにします。

コマンドバッファには、ゲームFSMからコマンドが来たことを示すフラグemptyを設け、書き込むと!emptyとなるようにします。サウンドFSMからは!emptyの時に新たにコマンドが来たと判断し、コマンドを読んだ後にemptyに変更します。

図%%.2
図271.2 1段バッファに変更

Artyボード移植後

Ultra96ではこれで動作していたのですが、Artyボードに移植後に自機増加音が無視されることに気づきました。サウンドFSMが取りこぼしているのだと推測し、再考すると、ゲームFSMが不必要に速いことに気づきました。DSOを接続して調べたところ、96.4%がウェイトだと判明したので、これを1MHzに落としたところ、動作するようでした。

ところが実験すると、依然として自機増加音(コマンドNo.9)が無視されるようです。そこで、ILAを接続して、

  • サウンドコマンド
  • サウンドFSMステート
  • コマンドバッファemtpy
  • サウンドFSM内部フラグ(fNO9)

を観測しました。最後の内部フラグfNO9は自機増加音がプリエンプトされないように割込みを禁止するためのフラグで、コマンドNo.9を受け付けた際にTrueになる信号です。

図%%.3
図271.3 ILA波形(NG)

図271.3はゲームFSMクロックを2MHzとして取得したものですが、フラグfNO9がTrueにならず、コマンドNo.9を無視しています。その原因は、サウンドFSMが受け取る前に次のコマンドNo.4を上書きしているためです。

従って、コマンドの書き込みの際にemptyである場合のみ書き込み、!emptyの場合は捨てる処理を行います(図271.4のマゼンタ矢印の処理を追加)。

図%%.4
図271.4 両側でemptyを確認するように修正

このように修正したところ、No.9の次のコマンドが!empty(=buffer full)のため捨てられることにより、図271.5のように受け付けられるようになりました。
図%%.5
図271.5 ILA波形(OK)

FIFOではないので、原理上取りこぼしは防げないものの、実用上これで動作するようです。

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

Qiitaに投稿しました

posted by sakurai on May 12, 2020 #255

過去ブログの、BSVによるスペースインベーダーの再設計の記事#234~#239, #254をまとめてQiitaに投稿しました。さらに考察を加えています。

BSV (Bluespec SystemVerilog)によるスペースインベーダーの再設計

過去ブログ記事でUltra96ボードを用いた、VerilogHDLによるSpace Invadersゲームの作成を投稿しましたが、その続きです。

図%%.1
図255.1 Qiitaの投稿記事


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

BSVの設計トライアル(21)

posted by sakurai on May 11, 2020 #254

ゲームFSMのアルゴリズム

トライアルの結果、BSVによるゲームFSMが完成しました。過去記事のステートベースのサウンドステートマシンと異なり、ステート分解をしていないため、rule文を一切使用していません。全てbsc(Bluespec Compiler)の、StmtFSMライブラリにステート管理を任せました。

基本的にはCで記述するようにゲームが記述できることが分かりました。例えば、弾の移動及び衝突判定、衝突処理(爆発マーク)、爆発マーク消去等のアルゴリズムを考えると、自弾、敵弾共にアルゴリズムは共通で、疑似コードで書けば、

if (弾爆発タイマ >= 1) {   // 弾爆発中
    弾爆発タイマ++;
    if (弾爆発タイマ == MAX) {
        弾削除;            // 論理的な消去
        弾爆発マーク消去;   // 物理的な消去
        弾爆発タイマ停止;
    }
} else {
    if (弾が出ていない and 弾生成条件) {
        弾生成処理;
        弾発射音;     // 自弾のみ
    }
    if (弾存在) {
        衝突判定;
        if (対象物) {  // 自弾の場合はインベーダ及びUFO、敵弾の場合は自機
            弾削除;          // 論理的な消去
            対象物ステート <= 爆発;
            対象物爆発タイマ <= 0;
        } else if (上下ハズレ || ベース || 弾) { // 弾:自弾の場合は敵弾、敵弾の場合は自弾
            弾マーク消去;
            弾爆発マーク;
            弾爆発タイマ <= 1;
        } else {        // 衝突していない場合
            弾を進める;
        }
    }
}

一方、対象物は、

if (対象物ステート == 爆発) {
    if (対象物爆発タイマ==0) {
        対象物爆発タイマ <= 1;
        対象物爆発音;
        対象物爆発マーク;
    } else {
        対象物爆発タイマ++;
        if (対象物爆発タイマ == MAX) {
           対象物削除;          // 論理的な消去
           対象物爆発マーク消去; // 物理的な消去
        }
    }
}

のようになりますが、StmtFSMを使うと、このようなシーケンスをクロック毎のステートに分解しなくて記述できます。

インベーダのタイミング

某所で質問があったので、タイミングについて解説します。基本の1 tickは1/60秒で、その中で、インベーダ1匹、敵弾全弾、自機、自弾、UFO、スコア等の処理を行います。以下は実際のBSVのメインループのコードです。

 while (game_flag) seq // メインループ
    for (noy <= 0; noy < `Inv_TateS; noy <= noy + 1) seq  // インベーダの行処理
       for (nox <= 0; nox < `Inv_YokoS; nox <= nox + 1) seq // インベーダの列処理
          if (inv_s[nox][noy]) seq // インベーダが生きてれば
             ivader;      // インベーダ処理
             gun;         // 自機処理
             bullet;      // 自弾処理
             for (idx <= 0; idx < extend(max); idx <= idx + 1) seq
                invBullet(idx);  // 敵弾全弾処理
             endseq
             ufo;         // UFO処理
             scores;      // スコア表示
             endJudge;    // 終了判定
             counter <= counter + 1;  // tickカウンタ++
             wait_timer;  // インナーループを1/60secにするウエイト
          endseq
       endseq
    endseq
 endseq
 gameOver;  // ゲームオーバー表示

1tick=1/60secの間に、インベーダ1匹(2ピクセル移動)の処理に対して、自機(1ピクセル移動)、敵弾(1ピクセル移動)、自弾(4ピクセル移動)の処理が行われます。インベーダは初期に55匹存在するので、1/55倍のスピードで始まりますが、最終的に1倍のスピードになります。従って、インベーダを倒すたびにインベーダ全体は速くなり、一方その他の速度は変わらないわけです。

FPGAでの実装では1 tick内にインベーダ全体を移動することは可能ですし、そのような実装も見ますが、ゲーム性が変わってしまいます。具体的には、インベーダ全体の速度が次第に速くならなかったり、後ろのインベーダを撃つことができなくなります。

例えば、インベーダゲームのレインボーは、後ろのインベーダを撃つことにより出現します。インベーダは残りが一匹になると左へは2ピクセルずつ右には3ピクセルずつ移動します。下2段のインベーダは、左右2ピクセルまでの移動では跡が残らない図形になっていますが、3ピクセルだと跡が消えずに残ります。もちろん今回の実装でもレインボーを体験できます。

ゲームFSMの完成

図254.1は、BSVで再設計したゲームFSMにより動作する、インベーダーゲームの動画です。過去記事に書いたように、サウンドが4ch同時発声と高品質になりました。

図%%.1
図254.1 ゲームFSMの完成


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

BSVの設計トライアル(20)

posted by sakurai on May 4, 2020 #253

実行結果

次は、ファンクションの中にシーケンスを組み込み、ゲームFSMの設計トライアルを行います。 シーケンスを人手で分解することは、なるべくしたくありません。ファンクションでシーケンスが定義できれば、インベーダ動作、自機動作等のファンクションを作成し、順番にそれらを呼び出せば良いはずです。

import StmtFSM::*;

interface TestFSM_ifc;
   method Action inp(UInt#(8) inx);
endinterface

(* synthesize *)
module mkTestFSM(TestFSM_ifc);

Reg#(UInt#(8)) i <- mkRegU;
Reg#(UInt#(8)) x <- mkRegU;

function Stmt test1;
   return (seq
      $display("%3d 1-1", $time);
  delay(5);
      $display("%3d 1-2", $time);
   endseq);
endfunction

function Stmt test2(UInt#(8) xx);
   return (seq
      $display("%3d 2-1", $time);
      for (i <= 0; i < xx; i <= i + 1)
         $display("%3d 2-loop-%1d", $time, i);
      $display("%3d 2-2", $time);
   endseq);
endfunction

   Stmt main =
   seq
      $display("%3d fsm1.start", $time);
      test1;
      $display("%3d fsm2.start", $time);
      test2(x);
   endseq;

   mkAutoFSM(main);

   method Action inp(UInt#(8) inx);
     x <= inx;
   endmethod

endmodule: mkTestFSM

このためのテストベンチを示します。あえてモジュール外部からループ回数を入れているのは、ループ回数がダイナミックに(実行時に)決定できるかを確認するためです。ファンクションのループを8回呼び出してみます。

import StmtFSM::*;
import TestFSM::*;
import Connectable::*;

(* synthesize, always_ready, always_enabled *)
module mkTb (Empty);
  TestFSM_ifc test <- mkTestFSM();
  Reg#(UInt#(8)) count <- mkReg(8);

   Stmt main =
      seq
         test.inp(count);
         repeat(40) noAction;
      endseq;

      mkAutoFSM(main);

endmodule

実行結果を示します。test1の次にtest2が呼び出され、ループが8回回ったことを示しています。

 20 fsm1.start
 30 1-1
 90 1-2
100 fsm2.start
110 2-1
130 2-loop-0
150 2-loop-1
170 2-loop-2
190 2-loop-3
210 2-loop-4
230 2-loop-5
250 2-loop-6
270 2-loop-7
290 2-2

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

BSVの設計トライアル(19)

posted by sakurai on May 1, 2020 #252

実行結果

以下に実行結果を示します。

senderFSM 20 FSM started
receiverFSM 20 receiver FSM started
senderFSM 30 Enq 10
senderFSM 40 Enq 20
receiverFSM 40 FIFO popped data 10
senderFSM 50 Enq 30
receiverFSM 70 FIFO popped data 20
receiverFSM 100 FIFO popped data 30

10nsずつ見ていきます。最初の10nsはリセット期間なので、20nsからFSM動作を開始します。

senderFSM 20 FSM started
receiverFSM 20 receiver FSM started

同時にセンダーFSMとレシーバーFSMが動作を開始しました。

senderFSM 30 Enq 10

センダーFSMがデータ10をエンキューしました。FIFOには1段のデータがあるはずです。

senderFSM 40 Enq 20
receiverFSM 40 FIFO popped data 10

レシーバーFSMが10をデキューすると同時にセンダーFSMがデータ20をエンキューしました。FIFOには差し引き1段のデータがあるはずです。

senderFSM 50 Enq 30

センダーFSMのレイテンシは1サイクル10nsなので次々に(FIFO FULLにならない限り)エンキューします。これが最後のデータです。FIFOには2段のデータがあるはずです。

receiverFSM 70 FIFO popped data 20

レシーバーFSMのレイテンシは3サイクル30nsなので、70nsにならないと次データの20がデキューできません。FIFOには1段のデータがあるはずです。

receiverFSM 100 FIFO popped data 30

レイテンシである3サイクル後にレシーバーFSMがデキューしました。FIFOには0段のデータがあるはずです。つまりFIFOは空になったはずです。

波形で見たほうが判りやすいです。エンキュー動作(オレンジの信号)はFIFOがフルでない限り1サイクルで行われるのに対してデキュー動作(ブルーの信号)は3サイクル毎に実行されています。

図%%.1
図252.1 センダーFSMとレシーバーFSM

以上はデータを3つエンキューした場合ですが、ここで4つ目をエンキューすると動作が異なります。図252.2に示すように、3つまではレイテンシ1でエンキューできていましたが、3つめでFIFO FULLとなり、その後はレシーバーFSMのレイテンシが見えてきます。つまりエンキュー動作もレイテンシが3となります。

図%%.2
図252.2 センダーFSMとレシーバーFSM

図252.1でもFIFOがFULLになっているのですが、その時にエンキューが無いのでレシーバー側のレイテンシが見えませんでした。図252.2ではFIFOがFULL状態でエンキューしようとして待たされています。FIFOは2段だということが分かります。

実は、FIFO段数もコントロール可能であり、任意の段数のFIFOを作成するにはmkSizedFIFOFを使用します。

  FIFOF#(Bit#(8)) fifo <- mkSizedFIFOF(2);

mkSizedFIFOFの引数サイズを2とすると、上記と同じ動作を行います。サイズを1にすると、デキューするまでエンキューが待たされる、図252.3のような動作となります。

図%%.3
図252.3  FIFOが1段の場合の動作

このFIFOライブラリは上記のように良くできていて、FIFOフルの場合等、FIFO がデータを受け取れない場合には自動的にエンキュー動作が抑止されます。普通ならFULLで無い条件でエンキュー動作を行う記述を書かなければいけませんが、その必要がありません。

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

BSVの設計トライアル(18)

posted by sakurai on April 30, 2020 #251

StmtFSM

前稿ではBSVにより、ステートベースのFSMを設計しました。ステートベースとは、シーケンスを人手でステートに分解し、一つ一つのステートに対してルールを書くもので、基本的にはverilogと同程度の工数がかかります。一方、BSVにはステートマシンを効率的に設計できる、StmtFSMというライブラリが存在します。

検証用FSM

このライブラリの検証用のために検証用FSMを作成します。検証用FSMは2つのコンカレントなFSMを持ち、一方のSenderFSMがFIFOにデータを詰め(エンキュー)、他方のReceiverFSMがFIFOからデータを取り出す(デキュー)ものとします。コンカレントであり、エンキューとデキューは同時に起こります。

図%%.1
図251.1 センダーFSMとレシーバーFSM

FSMはそれぞれ別に書き、その内部ではステートメントはシーケンシャルに実行されます。このシーケンシャルの動きはBSCにより管理され、隠れた(設計者から見えない)ステートが割り付けられます。以下にBSVのコードを示します。
package Tb;
import StmtFSM::*;
import FIFOF::*;

(* synthesize *)
module mkTb (Empty);
   FIFOF#(Bit#(8)) fifo <- mkFIFOF;

   Stmt sender =
      seq
         $display("senderFSM  %4d FSM started", $time);
         action
            $display("senderFSM  %4d Enq 10", $time);
            fifo.enq(10);
         endaction
         action
            $display("senderFSM  %4d Enq 20", $time);
            fifo.enq(20);
         endaction
         action
            $display("senderFSM  %4d Enq 30", $time);
            fifo.enq(30);
         endaction
         repeat (8) noAction;
      endseq;

      FSM senderFSM <- mkFSM(sender );

      Stmt receiver =
         seq
            $display("receiverFSM %4d receiver FSM started", $time);
            while(True) seq
               action
                  $display("receiverFSM %4d FIFO popped data", $time, fifo.first());
                  fifo.deq();
               endaction
               repeat (2) noAction;
            endseq
         endseq;

         FSM receiverFSM <- mkFSM(receiver);

      rule startit;
         senderFSM.start();
         receiverFSM.start();
      endrule

      rule finish (senderFSM.done() && !receiverFSM.done());
         $finish;
      endrule
   endmodule
endpackage

コードに示すように、エンキューはレイテンシ1で実行され、デキューはレイテンシ3で実行されます。各々のFSMは、エンキューデータ、デキューデータをそれぞれ表示します。


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

BSVの設計トライアル(17)

posted by sakurai on April 29, 2020 #250

レジスタ配列によるリターンスタック

前稿のリターンスタックrsはベクター配列で構成しましたが、最初に戻りレジスタ配列を試してみます。当初から配列で試したところ、うまく行っていませんでしたが、成功したのでその方法を記述します。まずリターンスタックrsを配列宣言します。

   // return stack
   Reg#(State_t) rs[3];

次に、リターンスタックrsを3段レジスタによりインスタンシエートします。これが無かったため、今まで動作しませんでした。ちなみにverilogでは宣言しただけで使用できますが、BSVでは宣言し、次にインスタンシエートが必要です。

   for (int i = 0; i < 3; i = i + 1)
      rs[i] <- mkRegU;

マクロ命令定義はベクター配列と同じ配列によるアクセス法を用います。

`define call(SUB)                    `_pushNext; state <= State_t {func:SUB, step:S0}
`define _pushNext                   rs[sp] <= State_t {func:state.func, step:nextStep()}; sp <= sp + 1
`define return                          state <= rs[sp-1]; sp <= sp - 1
`define next                             state.step <= nextStep()

説明は表248.1と同一です。これを用いて、前稿の検証FSMを実行してみます。

\$ bsc -sim -u TestFSM4.bsv
checking package dependencies
compiling TestFSM3.bsv
code generation for mkTestFSM starts
Elaborated module file created: mkTestFSM.ba
All packages are up to date.
\$ bsc -sim -e mkTestFSM -o mkTestFSM
Bluesim object created: mkTestFSM.{h,o}
Bluesim object created: model_mkTestFSM.{h,o}
Simulation shared library created: mkTestFSM.so
Simulation executable created: mkTestFSM
\$ ./mkTestFSM -m 15 -V dump.vcd | tee result
L1 S0
L1 S1
 L2 S0
 L2 S1
 L2 S2
  L3 S0
  L3 S1
   L4 S0
   L4 S1
  L3 S2
 L2 S3
 L2 S4
L1 S2
L1 S2
\$

正しく実行することが検証できました。波形を図250.1に示します。内部信号を見ると、ベクター配列と同様、rs_0, rs_1, rs_2という3個のレジスタインスタンスが生成されています。

図%%.1
図250.1 検証用FSMのBsim波形(レジスタ配列使用)

合成結果

Vivadoによる合成結果は21 LUTのサイズでした。レジスタ配列とベクター配列は同様のサイズであったため、今後は最も素直なこの方式を採用します。

ソフトウェアの場合は高速なレジスタと低速なメモリがあるため、リターンレジスタ等の実装が必要ですが、FSMではよほどのことが無い限り全てレジスタなので、リターンレジスタを使用しない、この実装を基本的に使用していきます。


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

BSVの設計トライアル(16)

posted by sakurai on April 28, 2020 #249

ベクター配列によるリターンスタック

前稿までのリターンスタックrsはレジスタファイルで構成しましたが、別法のベクター配列を試してみます。 まずベクターをインポートします。

import Vector::*;

次に、リターンスタックrsを3段インスタンシエートします。

   // return stack
   Vector#(3, Reg#(State_t)) rs <- replicateM(mkRegU);

さらにマクロ命令定義を書き換えます。ベクター配列はrs[sp]として扱えるので、直感的に分かりやすいです。

`define call(SUB)                    `_pushNext; state <= State_t {func:SUB, step:S0}
`define _pushNext                   rs[sp] <= State_t {func:state.func, step:nextStep()}; sp <= sp + 1
`define return                          state <= rs[sp-1]; sp <= sp - 1
`define next                             state.step <= nextStep()

説明は表248.1と同一です。これを用いて、前稿の検証FSMを実行してみます。

\$ bsc -sim -u TestFSM3.bsv
checking package dependencies
compiling TestFSM3.bsv
code generation for mkTestFSM starts
Elaborated module file created: mkTestFSM.ba
All packages are up to date.
\$ bsc -sim -e mkTestFSM -o mkTestFSM
Bluesim object created: mkTestFSM.{h,o}
Bluesim object created: model_mkTestFSM.{h,o}
Simulation shared library created: mkTestFSM.so
Simulation executable created: mkTestFSM
\$ ./mkTestFSM -m 15 -V dump.vcd | tee result
L1 S0
L1 S1
 L2 S0
 L2 S1
 L2 S2
  L3 S0
  L3 S1
   L4 S0
   L4 S1
  L3 S2
 L2 S3
 L2 S4
L1 S2
L1 S2
\$

正しく実行することが検証できました。波形を図249.1に示します。内部信号を見ると、rs_0, rs_1, rs_2という3個のレジスタインスタンスが生成されています。これが3段のリターンスタックを構成しています。

図%%.1
図249.1 検証用FSMのBsim波形(ベクター配列使用)

合成結果

Vivadoによる合成結果は21 LUTのサイズでした。レジスタファイルよりもベクター配列のほうが、わずかに小さくなることが分かりました。レジスタファイルは下位モジュールのレジスタファイルをインスタンスする必要がありますが、ベクター配列は上記のようにレジスタが展開されるだけです。従って、インタフェースが簡略化されるため、小さくなると考えられます。


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


ページ: