Posts Tagged with "BSV"

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

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

posted by sakurai on April 14, 2020 #239

サウンドFSMの完成

ミキサーは4chある音源のミキシングを行います。ミキシングは符号無し8bit整数を16bitに符号拡張し、加算することで行います。サウンド平均化を試しましたが、かえって違和感が出るため、シンプルな加算としました。念のためにクリッピング処理を入れています。

完成したsound階層を図239.1に示します。

図%%.1
図239.1 Vivado sound階層ブロック図

従来回路図234.2と比べてサウンドチャネルが4倍になっただけでなく、ミキサー回路を新設したため、かなり大きくなっています。表239.1に各モジュールの物量(LUT数)を示します。LUTは使用個数は4,216個であり、全容量70,560個中の5.98%でした。またBRAMは使用個数は39.5であり、全容量216中の18.3%でした。

ゲーム全体の各モジュールの物量を表239.1に示します。色付け部分が今回BSVで開発したモジュールです。

表239.1 各モジュールの物量
モジュール 説明 物量 BSV行数 Verilog行数
LUT数 LUT割合[%] BRAM数
invader_move 自機、インベーダ、UFO、自弾、敵弾、スコア等を処理するFSM 3,239 76.7 0.0 2,125
mkSoundFSM3 UFO音を演奏するFSM 197 4.7 0.0 448 939
mkSoundFSM2 インベーダ歩行音を演奏するFSM 192 4.6 0.0 950
mkSoundFSM1 インベーダ爆発音を演奏するFSM 190 4.4 0.0 922
mkSoundFSM0 自弾発射音、自機爆発音、自機増加音を演奏するFSM 184 4.7 0.0 957
mixer 4チャネルのサウンドミキサー 44 1.0 0.0 30
graphic_control VGA映像信号生成回路 38 0.9 0.0 92
para_seri サウンドパラシリ変換、タイミング生成回路 22 0.5 0.0 101
sound_rom0 自弾発射音、自機爆発音、自機増加音を格納するROM 20 0.5 7.0 IP
sound_rom1 インベーダ爆発音を格納するROM 20 0.5 7.0 IP
sound_rom2 インベーダ歩行音を格納するROM 20 0.5 7.0 IP
sound_rom3 UFO音を格納するROM 20 0.5 7.0 IP
pattern_rom 自機、インベーダ、UFO、自弾、敵弾、スコア等の図形を格納するROM 7 0.2 3.5 IP
buttons スイッチとボタンをORする回路 4 0.1 0.0 21
display_out ブランキング期間に表示を抑止する回路 3 0.1 0.0 21
vram ビデオRAM 2 0.0 8.0 IP
onestage ゲームFSMとサウンドFSMの間のバッファ 2 0.0 0.0 43
clk_wiz クロック制御回路 1 0.0 0.0 IP

図239.2に全体のセルの配置図を示します。

図%%.2
図239.2 Vivadoセル配置図

例外動作

基本的に、サウンドエンジンはサウンド番号を受け、ROMに示されるサウンド長だけサウンドを演奏し、サウンド中に割込みが入る場合にはその割込みサウンドを演奏しますが、例外が2つあります。

  • UFO飛行音 --- UFO飛行音ONでサウンドを演奏し、終了してもUFO飛行音OFFが来るまでは演奏し続ける。
  • 自機増加音 --- 自機増加音演奏中に割込みが入ると聞こえなくなる場合がある。このため、自機増加音は優先サウンドとして扱い、他の割込みを受け付けない。

自機増加音は自弾発射音、自機爆発音と独立であるため、別チャネルとするほうが良いのですが、頻度が低いのと別チャネルにするとハードウエアが無駄のため、このような仕様としました。実験の結果、優先サウンドとしてもゲーム上特に問題はありませんでした。


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

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

posted by sakurai on April 13, 2020 #238

サウンドFSMの作成(3)

FORMAT機能、DATA機能からコールされるREADCOUNT共通シーケンス及び、そこからコールされるREADMEM共通シーケンスを解説します。

   //  input:  romaddr
   //  output: (romaddr,...,romaddr+3) => dcount;
   //          romaddr + 4 => romaddr;
   //
   rule ruleREADCOUNT (state.func == READCOUNT);

      rule ruleS0 (state.step == S0);
         state.step <= S1;
         ret2 <= ret; // push to stack
         i <= 3;
         workd <= 0;
      endrule

      rule ruleS1 (state.step == S1);
         state <= State_t {cat:state.cat, func:READMEM, step:S0};
         ret <= State_t {cat:state.cat, func:READCOUNT, step:S2};
         worka <= romaddr + i;
      endrule

      rule ruleS2 (state.step == S2);
         if (i == 0) begin
            state <= ret2;
            dcount <= workd<<8 | extend(romdata);
            romaddr <= romaddr + 4;
         end else begin
            state.step <= S1;
            workd <= workd<<8 | extend(romdata);
            i <= i - 1;
         end
      endrule

  endrule // READCOUNT

図示すると、図238.1のようなステート遷移となり、リトルエンディアンで格納されている4バイトの数値を1バイトずつ4回取り出し、dcountにまとめる機能を持ちます。

図%%.1
図238.1 READCOUNT共通シーケンスステート遷移図

次にREADMEMはROMから1バイト読み出す共通シーケンスです。当初はwireを用いても階層の上り下りでレイテンシがかかり、7サイクルとなりましたが、後述のmkConnectionを用いたことにより、RTLと同様の3サイクルの設計とすることができました。

上記のREADCOUNTから呼ばれる際はサイクル数は無関係ですが、サウンド再生中は正しいスループットで読み出す必要があるため、過去記事にもあるように、コール元が1サイクルとコール先(READMEM)が3サイクルの4サイクルで1バイトを読み出す前提で、FSMのクロック周波数=176.4KHzを決めています。

   // READ MEM
   //  input:  worka
   //  output: romdata;
   //
   rule ruleREADMEM (state.func == READMEM);

      rule ruleS0 (state.step == S0);
         addr <= worka;
         state.step <= S1;
      endrule

      rule ruleS1 (state.step == S1); // ROM address phase
         state.step <= S2;
      endrule

      rule ruleS2 (state.step == S2); // ROM data phase
         data <= romdata;
         state <= ret;
      endrule

    endrule // READMEM

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

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

posted by sakurai on April 10, 2020 #237

サウンドFSMの作成(2)

階層化ステートマシンを解説します。基本的にBluespecにおいてはrule文のガードによりステートに入る条件を定義し、rule文の内部でステートの処理を定義します。そこで、ruleを構造化することで、ステートの構造化を実現します。具体的には以下のような構造となります。

rule ruleSOUND (state.cat == SOUND);

   rule ruleFORMAT (state.func == FORMAT);

      rule ruleS0 (state.step == S0); 
         // call readcount();
         state <= State_t {cat:SOUND, func:READCOUNT, step:S0}; 
         ret <= State_t {cat:SOUND, func:FORMAT, step:S1};
         case (code)
            'h1: romaddr <=     0 + 16;
            'h2: romaddr <=  4318 + 16;
            'h9: romaddr <= 13094 + 16;
         endcase
      endrule

      rule ruleS1 (state.step == S1);
         state <= State_t {cat:SOUND, func:DATA, step:S0};
         romaddr <= romaddr + extend(dcount);
      endrule

   endrule // FORMAT

ruleS0ではcodeによりROMアドレス先頭を決定します。wave formatの16バイト目からフォーマット長取得を行います。readcount()をコールしており、そのリターンはruleS1ステートです。readcount()ではdcount変数に4バイトの数値が得られます。

図%%.1
図237.1 FORMAT機能ステート遷移図
dcountにフォーマット長が得られたので、次にデータ長を取得します。
 rule ruleDATA (state.func == DATA);

     rule ruleS0 (state.step == S0);
         // call readcount();
         state <= State_t {cat:SOUND, func:READCOUNT, step:S0}; 
         ret <= State_t {cat:SOUND, func:DATA, step:S1};
         romaddr <= romaddr + 4; // skip "data"
     endrule

     rule ruleS1 (state.step == S1);
         state <= State_t {cat:SOUND, func:PLAY, step:S0}; 
         romaddr <= romaddr - 1;
     endrule

  endrule // DATA

dcountにデータ長が得られたので、実際のサウンドデータを出力するため、PLAYに移行します。

図%%.2
図237.2 DATA機能ステート遷移図
PLAYにおいては4倍のインターポレーションを行うため、4回同じデータを出力します。

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

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

posted by sakurai on April 9, 2020 #236

サウンドFSMの作成

waveフォーマット解析とデータ供給を実行する、メインのFSMの設計を行います。このような小さいFSMであっても階層設計を行います。その理由はソフトウェアと同じで、構造化のためです。最上位ステートは図236.1のようにINIT, SOUNDの2ステートとします。基本的にINITでは外部からサウンドコードが送信されてくるのを待ち、自分のコードであればSOUNDに遷移します。サウンド出力が終了すると、またINITに戻ります。

図%%.1
図236.1 最上位ステート

フラットなFSMではステート変数を整数で持ちますが、階層FSMでは構造体で持つことにします。階層は3階層で、上位2階層が意味のある機能を示し、最下層はステップとします。次に構造体の記述を示します。

typedef struct {
       Category_t cat;
       Function_t func;
       Step_t step;
} State_t deriving(Bits,Eq);

上位からカテゴリー、ファンクション、ステップと名付けました。最上位のカテゴリーは先の図のように、 INIT, SOUNDの2ステートからなります。

typedef enum { INIT, SOUND } Category_t deriving(Bits,Eq);

INITでは中間階層である機能は特になく、2ステートを持ち、LRCLKと同期させます。これはFSMクロックがLRCLKよりも速いため、LRCLKと確率的に位相が合わなくなるためです。

SOUNDでは機能としてFORMAT, DATA, PLAY, READCOUNT, READMEMの5種を持ちます。それぞれの機能は、フォーマット解析、データ長取得、演奏です。さらにそこから呼ばれる共通シーケンス(サブルーチン)として、データ長読み出し、データ1バイト読み出しが存在します。

図%%.2
図236.2 SOUND階層内ステート
第2階層であるファンクション変数の定義です。
typedef enum { FORMAT, DATA, PLAY, READCOUNT, READMEM } Function_t deriving(Bits,Eq);

同階層内にコール関係があり、DATAからREADCOUNTをコールします。さらにREADCOUNTからREADMEMをコールするので、リターンスタックは2段必要です。

最下層のステップの名前には特に意味が無く、シーケンシャルにステートを進めるだけです。シーケンシャルになる理由は、フォーマット解析やROMのデータ待ちなど様々です。

typedef enum { S0, S1, S2, S3, S4, S5, S6 } Step_t deriving(Bits,Eq);

以上は宣言であり、実際のインスタンシエーションは以下のようにモジュール内部で行います。

Reg#(State_t) state <- mkReg(State_t{cat:INIT,func:?,step:S0}),
     ret <- mkRegU,
     ret2 <- mkRegU;

ステート構造体stateと、さらに同じ構造を持つ2本のリターンレジスタret, ret2をインスタンスしています。


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

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

posted by sakurai on April 8, 2020 #235

ROMの作成

図234.2に示したように、サウンドROMが存在するので、BSVで作成します。ROMは合成には使用しませんが、bsimシミュレーションのために、 ROMは書き込まないRAMとして実装します。RAMはBSVではRegFileライブラリを使用します。RegFileは実際には1W5RのマルチポートRAMですが、他のポートは使用しません。

import RegFile::*;

最初にこの行によりライブラリRegFileを導入します。インスタンスは次の行で行います。同時にROMファイルの内容をロードします。

typedef Bit#(15) Addr_t;
typedef Bit#(8) Data_t;
RegFile#(Addr_t, Data_t) rom <- mkRegFileLoad("sound_data/ch00.hex", 0, 18593);

以下にROMファイルの内容を示します。チャネル0のファイルは3つのサウンドを持つ、18,594バイトのファイルです。

52
49
46
46
d6
10
:

サウンドの難しさ

映像とサウンドを一致させるには難しさがあります。

  • ワンショット --- 映像のほうがサウンドよりも長いため、ひとつの映像事象において複数のサウンドが鳴ってしまう。例えば自弾がどこにも当たらずに飛行して壁で爆発するまでに、発射音が複数回鳴ってしまう。
    図%%.1
    図235.1 ワンショットNG
  • プリエンプション --- 映像よりもサウンドのほうが長いため、2度目の映像事象にサウンドが鳴らない。例えばインベーダが非常に短い間隔で消滅した場合、2度目の消滅音が無視されてしまう。
    図%%.2
    図235.2 プリエンプションNG
    これらは一見矛盾するため、解決法を考える必要があります。状態ではなくエッジでサウンドを起動すれば解決できそうです。ただし、サウンド再生中に中断及び再実行可能にする必要があります。
    図%%.3
    図235.3 ワンショット
    図%%.4
    図235.4 プリエンプション
    さらに、ゲーム制御とサウンドのFSMでは動作周波数が30倍も異なり、ゲーム制御FSMからのサウンド指示間隔が短いと取りこぼす可能性があります。しかしながら、実験の結果取りこぼしを対策するためのサウンドキューよりも、リアルタイム性を優先したほうが良いことが分かりました。従って、当初FIFOジェネレータで作成したサウンドキューを廃止して1段のバッファとします。
    図%%.5

図235.5 ゲーム制御FSMとサウンドFSMの相互作用

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

BSVの設計トライアル

posted by sakurai on April 7, 2020 #234

スペースインベーダーのモジュールの入れ替え

過去にスペースインベーダーをRTLで開発しました。今回はBSVのトライアルとして既存のverilogモジュールの入れ替えを実施します。小さめのFSMであるサウンドFSMを入れ替えますが、合わせて仕様も高級なものにしようと思います。RTLで記述したときは、サウンドチャネルは1個で、優先度判定を行いサウンド割込みを行いました。今回は、サウンドチャネルを複数持って、サウンド重畳を行うことを考えます。そのため、複数の音を出し音量を平均化するような、サウンドミックス回路を新設します。

サウンドチャネルの決定

さて、音は以下の10種類ですが、同時発生を考えて、自機音、インベーダ音1, 2、UFO音の4チャネルとします。また、サウンド演奏中に割込みが入るため、プリエンプティブに設計する必要があります。

表234.1 音データとサウンドチャネルの関係
サウンドの説明 CODE番号 サウンドチャネル バイト数 先頭オフセット+16
ON OFF
自機弾発射音 1 自機音チャネル(#0) 4,318 0 + 16
自機爆発音 2 8,776 4,318 + 16
自機増加音 9 5,500 13,094 + 16
インベーダ爆発音 3 インベーダ音チャネル(#1) 4,622 0 + 16
インベーダ歩行音1 4 インベーダ音チャネル(#2) 1,266 0 + 16
インベーダ歩行音2 5 1,570 1,266 + 16
インベーダ歩行音3 6 1,570 2,836 + 16
インベーダ歩行音4 7 2,180 4,406 + 16
UFO爆発音 8 UFO音チャネル(#3) 25,968 0 + 16
UFO飛行音 10 16 + 10 1,846 25,968 + 16

変更前後のブロック図

現状のRTL設計でのブロック図をVivadoで表示したものを図234.1に示します。

図%%.1
図234.1 現サウンドシステムブロック図

新仕様のブロック図は図234.2のようになります。色付けの部分が新規設計モジュールです。

図%%.2
図234.2 新サウンドシステムブロック図

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

Emacs Verilog Mode

posted by sakurai on April 6, 2020 #233

ルール

大変便利なemacs verilog modeですが、最上位でうまく行かなかったので調査したところ、以下のようなことが分かりました。

中間階層で、下位モジュールインタフェースをパススルーして上位に上げる場合のルールは、

  • /*AUTOINPUT*/及び/*AUTOOUTPUT*/

最上位(テストベンチのように)で、信号がその階層内で留まる場合のルールは、

  • /*AUTOREGINPUT*/及び/*AUTOWIRE*/

と記述します。

ちなみに、 /*AUTOINPUT*//*AUTOREGINPUT*/が同時に存在する場合は /*AUTOINPUT*/が勝ち、/*AUTOOUTPUT*//*AUTOWIRE*/が同時に存在する場合は、/*AUTOWIRE*/が勝つようです。

実施結果

まず中間階層に相当する、原始のverilog fileを示します。下位モジュールの信号を上位に上げる場合なので、/*AUTOINPUT*/及び/*AUTOOUTPUT*/と記述し、さらにインターフェース部分には/*AUTOARG*/と記述します。

図%%.1
図233.1 中間階層原始ファイル

これに対してC-c C-aを実行すると、以下のように補完されます。

図%%.2
図233.2 中間階層補完後ファイル

次にテストベンチ相当の、原始のverilog fileを示します。下位モジュールからの信号はこの階層内で留まるため、/*AUTOREGINPUT*/及び/*AUTOWIRE*/と記述します。

図%%.3
図233.3 最上位原始ファイル

これに対してC-c C-aを実行すると、以下のように補完されます。

図%%.4
図233.4 最上位補完後ファイル

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

BSV(Bluespec SystemVerilog) (5)

posted by sakurai on April 3, 2020 #232

BSVによるテストベンチの記述

続いてテストベンチもBSVで記述していきます。シーケンシャルな記述が必要なので、自動FSMを利用します。以下にBSV(ファイル名Tb.bsv)を示します。テストベンチにクロックとリセットの記述が無いことに注意してください。

seqブロックの内部では、ソフトウェアと似ており、一行1クロックで順番に実行されます。内部的にはbscにより自動ステートマシンが生成され、一行ずつ順番に実行されます。

テストベンチファイル:Tb.bsv

import FibOne::*;
import StmtFSM::*;

(* synthesize *)
module mkTb();

   Fib_ifc fibo <- mkFibOne();
   int x = fibo.read();

   Stmt test = seq
      while (x < 10000)
         $display("%0d", x);
      $display("%0d", x);
      $finish;
   endseq;

   mkAutoFSM(test);

endmodule

コンパイルとBluesimシミュレーションの実行

このテストベンチをコンパイルし、シミュレーションを実行します。以下は下位モジュールが変更されていた場合ですが、自動的に下位モジュールもコンパイルされます。

\$ bsc -sim -u Tb.bsv
checking package dependencies
compiling ./FibOne.bsv
code generation for mkFibOne starts
Elaborated module file created: mkFibOne.ba
compiling Tb.bsv
code generation for mkTb starts
Elaborated module file created: mkTb.ba
All packages are up to date.

bluesimシミュレーションを実行します。

\$ bsc -sim -e mkTb -o mkTb.exec
Bluesim object created: mkTb.{h,o}
Bluesim object created: mkFibOne.{h,o}
Bluesim object created: model_mkTb.{h,o}
Simulation shared library created: mkTb.exec.so
Simulation executable created: mkTb.exec
\$ ./mkTb.exec
1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181
6765
10946

Verilogテストベンチと同様の結果が得られました。

Verilogシミュレーションの実行

確認のため、verilogシミュレーションを実施してみます。BSVではクロックとリセットは書かなくてもシミュレーション時に補完されるのですが、verilogシミュレーション時には無いため、マニュアルで追加する必要があります。それらの記述は前稿のテストベンチを参考にします。

以下にテストベンチからverilogコードを生成し(ファイル名mkTb.v)ます。

\$ bsc -verilog Tb.bsv
Verilog file created: mkTb.v

そのファイルにinclude文を追加します。場所は、initial begein endの間です。includeするファイルにはクロックとリセットの動作をverilogで記述します。その理由は、Bluesimの場合は外部から自動的にクロックとリセットが印加されるのに比べて、verilog simでは陽に与える必要があるからです。

テストベンチファイル:mkTb.v(部分)

initial
begin
  running = 1'h0;
  start_reg = 1'h0;
  start_reg_1 = 1'h0;
  state_can_overlap = 1'h0;
  state_fired = 1'h0;
  state_mkFSMstate = 3'h2;
  `include "initial.v"    <----ここ
end

initial.vの中身を示します。クロックとリセットの記述がされています。

インクルードファイル:initial.v

   force RST_N = 1'b0;
   #30;
   force RST_N = 1'b1;
end

initial begin
   force CLK = 1'b0;
   forever begin
   #5 force CLK = ~CLK;
   end

修正したテストベンチmkTb.vとモジュールを合わせてverilogシミュレーションを行います。

\$ bsc -verilog FibOne.bsv
Verilog file created: mkFibOne.v
\$ iverilog mkTb.v mkFibOne.v -o mkFibOne.exev
mkTb.v:220: tgt-vvp sorry: procedural continuous assignments are not yet fully supported. The RHS of this assignment will only be evaluated once, at the time the assignment statement is executed.
\$ ./mkFibOne.exev
1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181
6765
10946


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

BSV(Bluespec SystemVerilog) (4)

posted by sakurai on April 1, 2020 #231

モジュールインタフェースの引出し

Vivadoにより合成をかける場合に、前稿のモジュールmkFibOneのように出力の信号が無いと、何もしないものとみなされ最適化され、全てのロジックが削除されてしまいます。従って、mkFibOneモジュールに、計算した値を出力するインタフェースを設ける修正を行います。合わせて、テストベンチ記述がモジュール内に混入されていたものを、テストベンチに移します。

BSVではモジュールとインタフェースは明確に分離されています。修正したファイル(ファイル名FibOne.bsv)を示します。前述のように\$displayや\$finishはテストベンチへ移しました。

モジュールファイル:FibOne.bsv

interface Fib_ifc;
   method int read();
endinterface

(* synthesize, always_ready, always_enabled *)
module mkFibOne(Fib_ifc);
   Reg#(int) this_fib <- mkReg(0);
   Reg#(int) next_fib <- mkReg(1);

   rule fib;
      this_fib <= next_fib;
      next_fib <= this_fib + next_fib;  // note that this uses stale this_fib
   endrule: fib

   method int read();
      return this_fib;
   endmethod

endmodule: mkFibOne

ここで、

   Reg#(int) this_fib <- mkReg(0);

これは前稿で2行になっていたレジスタのインスタンシエーションの省略記法で、

Interface_type identifier <- module_name;

という文法を持ちます。この意味は、「Interface_typeの型を持つインタフェースを提供するモジュールmodule_nameをインスタンス化し、インタフェースのハンドルidentifierを取得する」ということです。

Verilogの生成

BSVプログラムに対して、bscによりverilog生成を実行すれば、

\$ bsc -verilog FibOne.bsv
Verilog file created: mkFibOne.v

合成されたverilog(ファイル名mkFibOne.v)は以下のようになります。

モジュールファイル:mkFibOne.v

//
// Generated by Bluespec Compiler (build 38534dc)
//
// On Wed Mar 25 09:12:14 JST 2020
//
//
// Ports:
// Name                         I/O  size props
// read                           O    32 reg
// CLK                            I     1 clock
// RST_N                          I     1 reset
//
// No combinational paths from inputs to outputs
//
//

`ifdef BSV_ASSIGNMENT_DELAY
`else
  `define BSV_ASSIGNMENT_DELAY
`endif

`ifdef BSV_POSITIVE_RESET
  `define BSV_RESET_VALUE 1'b1
  `define BSV_RESET_EDGE posedge
`else
  `define BSV_RESET_VALUE 1'b0
  `define BSV_RESET_EDGE negedge
`endif

module mkFibOne(CLK,
        RST_N,

        read);
  input  CLK;
  input  RST_N;

  // value method read
  output [31 : 0] read;

  // signals for module outputs
  wire [31 : 0] read;

  // register next_fib
  reg [31 : 0] next_fib;
  wire [31 : 0] next_fib$D_IN;
  wire next_fib$EN;

  // register this_fib
  reg [31 : 0] this_fib;
  wire [31 : 0] this_fib$D_IN;
  wire this_fib$EN;

  // value method read
  assign read = this_fib ;

  // register next_fib
  assign next_fib$D_IN = this_fib + next_fib ;
  assign next_fib$EN = 1'd1 ;

  // register this_fib
  assign this_fib$D_IN = next_fib ;
  assign this_fib$EN = 1'd1 ;

  // handling of inlined registers

  always@(posedge CLK)
  begin
    if (RST_N == `BSV_RESET_VALUE)
      begin
        next_fib <= `BSV_ASSIGNMENT_DELAY 32'd1;
    this_fib <= `BSV_ASSIGNMENT_DELAY 32'd0;
      end
    else
      begin
        if (next_fib$EN) next_fib <= `BSV_ASSIGNMENT_DELAY next_fib$D_IN;
    if (this_fib$EN) this_fib <= `BSV_ASSIGNMENT_DELAY this_fib$D_IN;
      end
  end

  // synopsys translate_off
  `ifdef BSV_NO_INITIAL_BLOCKS
  `else // not BSV_NO_INITIAL_BLOCKS
  initial
  begin
    next_fib = 32'hAAAAAAAA;
    this_fib = 32'hAAAAAAAA;
  end
  `endif // BSV_NO_INITIAL_BLOCKS
  // synopsys translate_on
endmodule  // mkFibOne

テストベンチ

修正したVerilogテストベンチtbmkFibOne.vを以下に示します。前稿ではクロックとリセットを下位モジュールに供給するだけだったのを、データ表示とシミュレーションの停止機能をモジュールから移動しています。

テストベンチファイル:tbmkFibOne.v

`timescale 1ns/1ps

module tb_mkFibOne;
   /*AUTOREGINPUT*/
   // Beginning of automatic reg inputs (for undeclared instantiated-module inputs)
   reg          CLK;            // To mkFibOne of mkFibOne.v
   reg          RST_N;          // To mkFibOne of mkFibOne.v
   // End of automatics
   /*AUTOWIRE*/
   // Beginning of automatic wires (for undeclared instantiated-module outputs)
   wire [31:0]      read;           // From mkFibOne of mkFibOne.v
   // End of automatics
   mkFibOne mkFibOne (/*AUTOINST*/
              // Outputs
              .read     (read[31:0]),
              // Inputs
              .CLK      (CLK),
              .RST_N        (RST_N));

   initial begin
      RST_N = 1'b0;
      #30;
      RST_N = 1'b1;
      forever begin
       if (CLK) $display("%0d", read);
       if (read > 10000) $finish;
       #10;
      end
   end

   initial begin
      CLK = 1'b0;
      forever begin
     #5 CLK = ~CLK;
      end
   end

   initial begin
      $dumpfile("tbmkFibOne.vcd");
      $dumpvars(0,mkFibOne);
   end

endmodule 

Verilogシミュレーション

準備ができたので、iverilogによるverilogシミュレーションを実行します。

\$ iverilog tbmkFibOne.v mkFibOne.v -o mkFibOne.exev
\$ ./mkFibOne.exev
VCD info: dumpfile tbmkFibOne.vcd opened for output.
0
1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181
6765
10946

前稿と同様の結果になりました。

Vivadoによるシミュレーション

VivadoのIPインテグレータにより、生成されたverilogを取り込み、IPインテグレータ上で最上位のテストベンチを作成します。先に作成したテストベンチ(ファイル名tbmkFibOne.v)はシミュレーション用で合成できないため、IPインテグレータで上位を作成します。図の左からZynqのPS部、クロックリセット生成、対象回路となっています。

図%%.1
図231.1 IP Integratorの回路図

vivado上でbehaviorシミュレーションを実施した結果です。iverilogの結果と同一になっています。

図%%.2
図231.2 Vivadoによる波形

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

BSV(Bluespec SystemVerilog) (3)

posted by sakurai on March 31, 2020 #230

BSV(Bluespec SystemVerilog)によるシミュレーション

bscとは、Bluespecが最近オープンソース化したコンパイラです。詳しくは、ここを見てください。

いろいろと調整した結果、bscがインストールできました。早速、以下のようなフィボナッチモジュール(ファイル名FibOne.bsv)をコンパイルしてみます。これは、フィボナッチ数列を発生するモジュールで、前の値に次の値を加算することを繰り返すものです。

モジュールファイル:FibOne.bsv

(* synthesize *)
module mkFibOne();
   // register containing the current Fibonacci value
   Reg#(int) this_fib();              // interface instantiation
   mkReg#(0) this_fib_inst(this_fib); // module instantiation
   // register containing the next Fibonacci value
   Reg#(int) next_fib();
   mkReg#(1) next_fib_inst(next_fib);

   rule fib;  // predicate condition always true, so omitted
      this_fib <= next_fib;
      next_fib <= this_fib + next_fib;  // note that this uses stale this_fib
      $display("%0d", this_fib);
      if ( this_fib > 10000 ) $finish(0) ;
  endrule: fib
endmodule: mkFibOne

モジュール内にテストベンチのような\$displayや\$finishの記述があるので、これだけでテストが可能です(が、モジュール設計としては良くないので、後で外します)。

BSVプログラムの説明

以下の部分は、現在の値this_fibと次の値next_fibを保持するレジスタのインスタンスです。

   // register containing the current Fibonacci value
   Reg#(int) this_fib();              // interface instantiation
   mkReg#(0) this_fib_inst(this_fib); // module instantiation
   // register containing the next Fibonacci value
   Reg#(int) next_fib();
   mkReg#(1) next_fib_inst(next_fib);

次のように、ruleブロックにアルゴリズム計算ルールを記述します。

      this_fib <= next_fib;
      next_fib <= this_fib + next_fib;  // note that this uses stale this_fib

コメントにも書いているように、this_fibとnext_fibは同時に変更されるので、それぞれ、直前の値を読み込み、同時に値を更新します。

Bluesimによるシミュレーション

コンパイル及びシミュレーションモデル生成(リンク)の2段階で行います。太字が入力部分です。

\$ bsc -sim FibOne.bsv
Elaborated module file created: mkFibOne.ba
\$ bsc -sim -e mkFibOne -o mkFibOne.exec
Bluesim object created: mkFibOne.{h,o}
Bluesim object created: model_mkFibOne.{h,o}
Simulation shared library created: mkFibOne.exec.so
Simulation executable created: mkFibOne.exec

mkFibOne.execというbluesimの実行ファイルが生成されたので起動します。

\$ ./mkFibOne.exec
0
1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181
6765
10946

Verlogの生成

次に、確認のためにverilogシミュレーションを実行します。まずbscにより、モジュールを合成可能なVerilogコードにコンパイルします。

\$ bsc -verilog FibOne.bsv
Verilog file created: mkFibOne.v

生成されたファイル名はモジュール名+".v"(mkFibOne.v)となります。

モジュールファイル:mkFibOne.v

//
// Generated by Bluespec Compiler (build 38534dc)
//
// On Mon Mar 23 06:33:47 JST 2020
//
//
// Ports:
// Name                         I/O  size props
// CLK                            I     1 clock
// RST_N                          I     1 reset
//
// No combinational paths from inputs to outputs
//
//

`ifdef BSV_ASSIGNMENT_DELAY
`else
  `define BSV_ASSIGNMENT_DELAY
`endif

`ifdef BSV_POSITIVE_RESET
  `define BSV_RESET_VALUE 1'b1
  `define BSV_RESET_EDGE posedge
`else
  `define BSV_RESET_VALUE 1'b0
  `define BSV_RESET_EDGE negedge
`endif

module mkFibOne(CLK,
        RST_N);
  input  CLK;
  input  RST_N;

  // register next_fib_inst
  reg [31 : 0] next_fib_inst;
  wire [31 : 0] next_fib_inst$D_IN;
  wire next_fib_inst$EN;

  // register this_fib_inst
  reg [31 : 0] this_fib_inst;
  wire [31 : 0] this_fib_inst$D_IN;
  wire this_fib_inst$EN;

  // register next_fib_inst
  assign next_fib_inst$D_IN = this_fib_inst + next_fib_inst ;
  assign next_fib_inst$EN = 1'd1 ;

  // register this_fib_inst
  assign this_fib_inst$D_IN = next_fib_inst ;
  assign this_fib_inst$EN = 1'd1 ;

  // handling of inlined registers

  always@(posedge CLK)
  begin
    if (RST_N == `BSV_RESET_VALUE)
      begin
        next_fib_inst <= `BSV_ASSIGNMENT_DELAY 32'd1;
    this_fib_inst <= `BSV_ASSIGNMENT_DELAY 32'd0;
      end
    else
      begin
        if (next_fib_inst$EN)
      next_fib_inst <= `BSV_ASSIGNMENT_DELAY next_fib_inst$D_IN;
    if (this_fib_inst$EN)
      this_fib_inst <= `BSV_ASSIGNMENT_DELAY this_fib_inst$D_IN;
      end
  end

  // synopsys translate_off
  `ifdef BSV_NO_INITIAL_BLOCKS
  `else // not BSV_NO_INITIAL_BLOCKS
  initial
  begin
    next_fib_inst = 32'hAAAAAAAA;
    this_fib_inst = 32'hAAAAAAAA;
  end
  `endif // BSV_NO_INITIAL_BLOCKS
  // synopsys translate_on

  // handling of system tasks

  // synopsys translate_off
  always@(negedge CLK)
  begin
    #0;
    if (RST_N != `BSV_RESET_VALUE) $display("%0d", $signed(this_fib_inst));
    if (RST_N != `BSV_RESET_VALUE)
      if ((this_fib_inst ^ 32'h80000000) > 32'h80002710) $finish(32'd0);
  end
  // synopsys translate_on
endmodule  // mkFibOne

テストベンチの作成

bluesimは暗黙のクロックやリセットが動作するため、テストベンチ無しでもシミュレーションが実行できました。一方verilogではそのような機能は無いので以下のようなテストベンチ(ファイル名tbmkFibOne.v)を用意します。

テストベンチ中の/*AUTO〇〇*/という記述は、emacsのverilog modeによるインスタンスやポートの自動生成の機能です。C-c C-aにより、面倒なポートリストやインスタンス部分が自動生成できます。テストベンチでは、モジュールへの入力用に/*AUTOREGINPUT*/と、モジュールからの出力用に/*AUTOWIRE*/を指定しておきます。

テストベンチファイル:tbmkFibOne.v

`timescale 1ns/1ps

module tb_mkFibOne;
   /*AUTOREGINPUT*/
   // Beginning of automatic reg inputs (for undeclared instantiated-module inputs)
   reg          CLK;            // To mkFibOne of mkFibOne.v
   reg          RST_N;          // To mkFibOne of mkFibOne.v
   // End of automatics
   /*AUTOWIRE*/
   mkFibOne mkFibOne (/*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

   initial begin
      $dumpfile("tbmkFibOne.vcd");
      $dumpvars(0,mkFibOne);
   end

endmodule 

Verilogシミュレーションの実行

iverilogにより実行ファイルmkFibOne.exevを生成し、シミュレーションを実行すると、同じ結果となりました。

\$ iverilog tbmkFibOne.v mkFibOne.v -o mkFibOne.exev
\$ ./mkFibOne.exev
VCD info: dumpfile tbmkFibOne.vcd opened for output.
0
1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181
6765
10946

波形ビュワーであるgtkwaveを起動します。verilogシミュレーションで生成したVCDファイルを指定します。

\$ gtkwave -f tbmkFibOne.vcd
GTKWave Analyzer v3.3.111 (w)1999-2020 BSI

[0] start time.
[520000] end time.

verilogシミュレーションの波形をgtkwaveで表示します。

図%%.1
図230.1 GTKWaveによる波形

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


ページ: