Posts Tagged with "Design"

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

BSVによるUARTの設計(5)

posted by sakurai on May 4, 2021 #395

Verilogシミュレーション

bscにより階層的にverilogファイルを生成し、iverilogによりverilogシミュレーションを行います。さらに波形を観測します。太字は入力文字を示します。

$ bsc -u -verilog mkTb.bsv
checking package dependencies
compiling ./mkUart.bsv
code generation for mkUart starts
Verilog file created: mkUart.v
compiling mkTb.bsv
code generation for mkTb starts
Verilog file created: mkTb.v
All packages are up to date.
$ iverilog top.v mkTb.v mkUart.v -o mkTb
$ ./mkTb
VCD info: dumpfile mkTb.vcd opened for output.
$ gtkwave -A mkTb.vcd

GTKWave Analyzer v3.3.107 (w)1999-2020 BSI

[0] start time.
[630] end time.

前記事に示すように、データ55H, AAH, C3H, 3CHを順に送信する場合の、モジュールの内部の波形です。

図%%.1
図395.1 mkUartの波形

  • 最初にデータがAAHになっていますが、BSVでは不定値をAAHで表しています。最初の送信データは55Hです。
  • モジュールからRDY_load()がアサートされているので、テストベンチからデータが出力されると同時にEN_load()がアサートされます。
  • 次に内部レジスタdata()が55Hに変化します。同時にdoneがネゲートされます。同時にFSMの開始レジスタfsm_start_reg()がアサートされます。
  • 次にfsm_start_reg_1()がアサートされ、FSMが開始します。そのタイミングでodata()のスタートビット(=L)を出力します。
  • 次にodata()の8bitをLSBから順に10101010と出力します。これはデータが55Hであるためです。
  • 最後にストップビット(=H)を2bit出力します。
  • 次にdone()がアサートされ、同時にRDY_load()がアサートされます。

図%%.2
図395.2 mkUartの波形

  • 次の送信データはAAHです。
  • 次にモジュールからRDY_load()がアサートされているので、テストベンチからデータが出力されると同時にEN_load()がアサートされます。
  • 次に内部レジスタdata()がAAHに変化します。同時にdoneがネゲートされます。
  • 次にFSMの開始レジスタfsm_start_reg()がアサートされます。
  • 次にfsm_start_reg_1()がアサートされ、FSMが開始します。そのタイミングでodata()のスタートビット(=L)を出力します。
  • 次にodata()の8bitをLSBから順に01010101と出力します。これはデータがAAHであるためです。
  • 最後にストップビット(=H)を2bit出力します。
  • 次にdone()がアサートされ、同時にRDY_load()がアサートされます。

図%%.3
図395.3 mkUartの波形

  • 次の送信データはC3Hです。
  • 次にモジュールからRDY_load()がアサートされているので、テストベンチからデータが出力されると同時にEN_load()がアサートされます。
  • 内部レジスタdata()がC3Hに変化します。同時にdoneがネゲートされます。同時にFSMの開始レジスタfsm_start_reg()がアサートされます。
  • 次にfsm_start_reg_1()がアサートされ、FSMが開始します。そのタイミングでodata()のスタートビット(=L)を出力します。
  • 次にodata()の8bitをLSBから順に11000011と出力します。これはデータがC3Hであるためです。
  • 最後にストップビット(=H)を2bit出力します。
  • 次にdone()がアサートされ、同時にRDY_load()がアサートされます。

図%%.4
図395.4 mkUartの波形

  • 次の送信データは3CHです。
  • 次にモジュールからRDY_load()がアサートされているので、テストベンチからデータが出力されると同時にEN_load()がアサートされます。
  • 次に内部レジスタdata()が3CHに変化します。同時にdoneがネゲートされます。同時にFSMの開始レジスタfsm_start_reg()がアサートされます。
  • 次にfsm_start_reg_1()がアサートされ、FSMが開始します。そのタイミングでodata()のスタートビット(=L)を出力します。
  • 次にodata()の8bitをLSBから順に00111100と出力します。これはデータが3CHであるためです。
  • 最後にストップビット(=H)を2bit出力します。
  • 次にdone()がアサートされ、同時にRDY_load()がアサートされます。
  • テストベンチはdoneを監視しているので、doneがアサートされると終了です。

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

BSVによるUARTの設計(4)

posted by sakurai on May 3, 2021 #394

トップモジュールの修正

同様にC-c C-aを実行することにより、トップモジュールを修正します。クロックとリセットのレジスタが生成され、呼び出すモジュールのクロックとリセットにそれぞれ接続されます。以下にソースの変化点だけを示します。

top.v

`timescale 1ns/1ns
module top();
   /*AUTOREGINPUT*/
   // Beginning of automatic reg inputs (for undeclared instantiated-module inputs)
   reg                      CLK;                    // To mkTb_inst of mkTb.v
   reg                      RST_N;                  // To mkTb_inst of mkTb.v
   // End of automatics
   /*AUTOWIRE*/
   mkTb mkTb_inst(/*AUTOINST*/
              // Inputs
              .CLK                  (CLK),
              .RST_N                (RST_N));

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

BSVによるUARTの設計(3)

posted by sakurai on April 30, 2021 #393

トップモジュール

例によって、テストベンチにクロックとリセットを供給する最上位を設計します。

top.v

`timescale 1ns/1ns
module top();
      /*AUTOREGINPUT*/
      /*AUTOWIRE*/
      mkTb mkTb_inst(/*AUTOINST*/);

      initial begin
            RST_N = 1'b0;
            #10;
            RST_N = 1'b1;
      end

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

      initial begin
            $dumpfile("mkTb.vcd");
            $dumpvars;
      end
endmodule

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

BSVによるUARTの設計(2)

posted by sakurai on April 29, 2021 #392

テストベンチ

前稿で設計したUARTをドライブするテストベンチを設計します。

ハンドシェイク信号がBSVにより自動的に生成されるため、タイミングを取ってデータをロードする必要はありません。データ待ちは自動的に行われます。このへんもBSVの素晴らしい点です。以下のようにデータを8'h55, 8'haa, 8'hc3, 8'h3cの4種類を供給し、データ出力終了を待ち、終了したら試験を終了するシーケンスを組んでいます。

mkTb.bsv

import StmtFSM::*;
import mkUart::*;

(* synthesize *)
module mkTb();
      Uart_ifc uart <- mkUart();

      Stmt test = seq
            repeat(8) noAction;
            uart.load(8'h55);
            uart.load(8'haa);
            uart.load(8'hc3);
            uart.load(8'h3c);
            await (uart.done());
            $finish;
      endseq;

      mkAutoFSM(test);
endmodule

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

BSVによるUARTの設計

posted by sakurai on April 28, 2021 #391

UARTの仕様

UARTは以下のようにシリアルでデータを出力するためのモジュールです。これをBSVで設計します。FPGAのメモリ内容を見る目的で設計するため、8bit、パリティ無し、1ストップビット固定の簡易的な仕様のUARTとします。

図%%.1
図391.1 UARTの波形

例えば19,200 bpsで通信する場合は、ステートマシンを19.2 KHzのクロックで駆動します。

mkUart.bsv

import StmtFSM::*;

interface Uart_ifc;
      method Bit#(1) read();
      method Action load(Bit#(8) newdata);
      method Bool done();
endinterface

(* synthesize *)
module mkUart(Uart_ifc);
      Reg#(Bit#(8)) data <- mkRegU;
      Reg#(Bit#(1)) odata <- mkReg(1'h1); // stop bit
      Reg#(Bool) doneFlag <- mkReg(True);

      Stmt test = seq
            odata <= 1'h0; // start bit
            repeat (8) action
                  odata <= data[0];
                  data <= (data >> 1);
            endaction
            odata <= 1'h1; // stop bit
            doneFlag <= True;
      endseq;

      FSM fsm <- mkFSM(test);

      method Bit#(1) read();
            return odata;
      endmethod

      method Bool done();
            return doneFlag;
      endmethod

      method Action load(Bit#(8) newdata);
            action
                  data <= newdata;
                  doneFlag <= False;
                  fsm.start();
            endaction
      endmethod
endmodule

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

posted by sakurai on April 27, 2021 #390

Verilogシミュレーション

これにより、モジュールにクロックとリセットが最上位に自動的に生成され、モジュールとのI/Fが正しく接続されたので、iverilgにより全体のverilogシミュレーションモデルを作成し、verilogシミュレーションを実行します。

$ iverilog top.v testFSM.v -o testFSM.exe

これを実行すると以下の表示が得られ、正しくFSMが動作したことが分かります。

$ ./testFSM.exe
VCD info: dumpfile testFSM.vcd opened for output.
Counter = 0, State: IDLE
Counter = 1, State: STEP1
Counter = 2, State: STEP1
Counter = 3, State: STEP1
Counter = 4, State: STEP1
 (中略)
Counter = 96, State: STEP1
Counter = 97, State: STEP2
Counter = 98, State: STOP
Counter = 99, State: IDLE
Counter = 100, State: IDLE
Done

同時にダンプファイルtestFSM.vcdが得られるので、GTKWave波形ビュワーにより以下のようにVCDを開き、波形を確認します。

$ gtkwave -A testFSM.vcd

GTKWave Analyzer v3.3.107 (w)1999-2020 BSI

[0] start time.
[1010] end time.

図%%.1
図390.1 testFSMの波形

設定を同名のtestFSM.gtkwに入れておくと、上記"-A"フラグにより起動と同時に波形まで開くことができます。


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

posted by sakurai on April 26, 2021 #389

Verilog階層生成

まずソースファイルをコンパイルしてverilogコードを生成します。

$ bsc -verilog testFSM.bsv
Verilog file created: testFSM.v

このようにtestFSM.vが生成されます。

次に最上位ファイルから正しくモジュールが呼び出されるように、最上位ファイルtop.vを修正します。emacsでtop.vを開き、C-c C-aをするだけで(過去記事参照)、自動的にインタフェースが生成されます。最上位には以下の2行を記述します。

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

以下に最上位top.vにおいて、C-c C-a実行で変化する前とした後のコードを示します。

top.v(実行前)

module top();
   /*AUTOREGINPUT*/
   /*AUTOWIRE*/
   testFSM testFSM_inst(/*AUTOINST*/);
(以下略)

top.v(実行後)

module top();
   /*AUTOREGINPUT*/
   // Beginning of automatic reg inputs (for undeclared instantiated-module inputs)
   reg CLK; // To testFSM_inst of testFSM.v
   reg RST_N; // To testFSM_inst of testFSM.v
   // End of automatics
   /*AUTOWIRE*/
   // Beginning of automatic wires (for undeclared instantiated-module outputs)
   wire [2:0] read; // From testFSM_inst of testFSM.v
   // End of automatics
   testFSM testFSM_inst(/*AUTOINST*/
         // Outputs
         .read (read[2:0]),
         // Inputs
         .CLK (CLK),
         .RST_N (RST_N));
(以下略)

上記はコメントも含め全て、emacs verilog modeにより自動生成されたものです。


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

posted by sakurai on April 23, 2021 #388

Bluesimシミュレーション

Bluesimを実行する場合には上位モジュールは必要ありません。BSVソース中にはクロックもリセットもありませんが、シミュレーション環境により自動的にクロックとリセットが与えられ、シミュレーションが実行されます。

Bluesim実行コマンドは以下のとおりです。

$ bsc -sim testFSM.bsv
Elaborated module file created: testFSM.ba
$ bsc -sim -e testFSM -o testFSM Bluesim object created: testFSM.{h,o}
Bluesim object created: model_testFSM.{h,o}
Simulation shared library created: testFSM.so
Simulation executable created: testFSM

これを実行すると、

\$ ./testFSM
Counter = 0, State: IDLE
Counter = 1, State: STEP1
Counter = 2, State: STEP1
Counter = 3, State: STEP1
Counter = 4, State: STEP1
 (中略)
Counter = 96, State: STEP1
Counter = 97, State: STEP2
Counter = 98, State: STOP
Counter = 99, State: IDLE
Counter = 100, State: IDLE
Done

のように表示され、FSMが動作したことが分かります。


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

posted by sakurai on April 22, 2021 #387

ソースファイル

以下にサンプルのステートマシンのソースを示します。

testFSM.bsv

import StmtFSM::*;
interface FSM_ifc;
   method State read();
endinterface

typedef enum { IDLE, STEP1, STEP2, STEP3, STOP } State deriving(Bits,Eq);

(* synthesize, always_ready, always_enabled *)
module testFSM(FSM_ifc);
   Reg#(State) state <- mkReg(IDLE);
   Reg#(int) counter <- mkReg(0);

   rule runCounter;
      if (counter == 100) begin
         \$display("Done");
         \$finish;
      end
      counter <= counter + 1;
   endrule

   rule stateIdle ( state == IDLE ); //default state
      \$display("Counter = %3d, State: IDLE", counter);
      if (counter % 4 == 0)
         state <= STEP1;
   endrule

   rule stateStep1 ( state == STEP1 );
      \$display("Counter = %3d, State: STEP1", counter);
      if (counter % 8 == 0)
         state <= STEP2;
   endrule

   rule stateStep2 ( state == STEP2 );
      \$display("Counter = %3d, State: STEP2", counter);
      state <= STOP;
   endrule

   rule stateSTOP ( state == STOP );
      \$display("Counter = %3d, State: STOP", counter);
      state <= IDLE;
   endrule

   method State read();
      return state;
   endmethod
endmodule: testFSM

これをドライブする上位モジュールの原始ファイルを以下に示します。

top.v

`timescale 1ns/1ns

module top();
   /*AUTOREGINPUT*/
   /*AUTOWIRE*/
   testFSM testFSM_inst(/*AUTOINST*/);

   initial begin
      RST_N = 1'b0;
      #10;
      RST_N = 1'b1;
   end

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

   initial begin
      \$dumpfile("testFSM.vcd");
      \$dumpvars;
   end
endmodule


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

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点は使用されません。

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


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


ページ: