Posts Issued in May, 2021

posted by sakurai on May 13, 2021 #401

以下のスクリプトでVerilogシミュレーションを実行します。

bsc -verilog -u mkTb.bsv
cp top-original.v top.v
emacs -nw top.v
// emacsでautomodeにより、top.vを生成
iverilog -y /usr/local/lib/Bluespec/Verilog/ top.v mkTb.v mkUart.v -o mkTb.exe
./mkTb.exe
gtkwave -A mkTb.vcd

図%%.1
図401.1 シミュレーション波形

図はちょうど1行を送信したところで、横方向のxが256(xは255までだがチェックサム出力の際に256となる)から0に戻り、縦方向のyが0から1になった時点の波形です。データを0x33, 0x66, 0x0a, 0x30, 0x64と送信しています。

ストップビットを1ビットに削ったところ、1,582,090サイクルとなりました。1アスキーバイトあたり12サイクルなので、8bitの他、スタートが1bit、ストップが3bit相当となっています。送信時間は

  • 19,200bpsでは1.4分
  • 38,400bpsでは41秒
  • 57,600bpsでは28秒
  • 115,200bpsでは14秒
  • 230,400bpsでは6.9秒

となります。バイナリだとこれの半分の時間となるはずですが、デバッグの関係で、当面アスキーコードの転送とします。


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

posted by sakurai on May 12, 2021 #400

前稿の疑似コードをBSVに変換します。作成したBSVプログラムを以下に示します。

mkTb.bsv

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

typedef Bit#(16) Addr;
typedef Bit#(8) Data;

(* synthesize *)
module mkTb();
      RegFile#(Addr, Data) rom <- mkRegFileLoad("data.hex", 0, 65535);
      Reg#(Addr) address <- mkReg(0);
      Reg#(Data) data <- mkReg(0);
      Reg#(Data) byteData <- mkReg(0);
      Reg#(int) x <- mkReg(0);
      Reg#(int) y <- mkReg(0);
      Reg#(Data) checksum <- mkReg(0);
      Uart_ifc uart <- mkUart();

      function Stmt nibbleOut(Data nibble);
            return (seq
                  byteData <= nibble + (nibble >= 10) ? (- 10 + 8'h61) : 8'h30;
                  uart.load(byteData);
                  // $write("%c", byteData);
            endseq);
      endfunction: nibbleOut

      function Stmt byteOut(Data inData);
            return (seq
                  nibbleOut((inData >> 4) & 8'h0f);
                  nibbleOut(inData & 8'h0f);
            endseq);
      endfunction: byteOut

      Stmt test = seq
            for (y <= 0; y <= 255; y <= y + 1) seq
                  checksum <= 0;
                  for (x <= 0; x <= 255; x <= x + 1) seq
                        data <= rom.sub(address);
                        address <= address + 1;
                        checksum <= checksum + data;
                        byteOut(data);
                  endseq
                  byteOut(checksum);
                  uart.load(8'h0a); // LF
                  // $display("");
            endseq
            await (uart.done());
            $finish;
      endseq;

      mkAutoFSM(test);
endmodule

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

posted by sakurai on May 11, 2021 #399

上位モジュールの最適化

メモリダンプアルゴリズムが疑似コードにより記述できたので、関数により同じような処理をまとめる最適化を実施します。

疑似コード

int x, y, address;
byte data, checksum;

address = 0;
for (y=0 to 255) {
      checksum = 0;
      for (x = 0 to 255) {
            data = memory[address++];
            checksum += data;
            byteOut(data);
      }
      byteOut(checksum);
      uart.load(LF);
}

function byteOut(byte) {
      nibbleOut((byte >> 4) & 0x0f);
      nibbleOut(byte & 0x0f);
}

function nibbleOut(nibble) {
      byte = nibble + (nibble >= 10) ? (- 10 + "a") : "0";
      uart.load(byte);
}

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

posted by sakurai on May 7, 2021 #398

上位モジュールの設計

1バイトの送信はうまく行ったようなので、上位モジュールmkMemoryDumpを設計します。概略仕様を以下に示します。

  • メモリのアドレス0からの内容を65536バイト送信します。
  • 256行×256列で送信し、1行毎にチェックサムを送信します。
  • 1バイトのバイナリは表示可能な2バイトのアスキーコードに変換します。
  • 行末は改行します。

メモリ空間は64Kバイトあります。以下にアルゴリズムをC likeの疑似コードにより示します。

疑似コード

int x, y, address;
byte data, checksum, upper, lower;

address = 0;
for (y=0 to 255) {
      checksum = 0;
      for (x = 0 to 255) {
            data = memory[address++];
            checksum += data;
            upper = (data >> 4) & 8'h0f;
            upper += (upper >= 10) ? (-10 + "a") : "0";
            uart.load(upper);
            lower = data &  8'h0f;
            lower += (lower >= 10) ? (-10 + "a") : "0";
            uart.load(lower);
      }
      upper = (checksum >> 4) &  8'h0f;
      upper += (upper >= 10) ? (-10 + "a") : "0";
      uart.load(upper);
      lower = checksum &  8'h0f;
      lower += (lower >= 10) ? (-10 + "a") : "0";
      uart.load(lower);
      uart.load(LF);
}

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

BSVによるUARTの設計(7)

posted by sakurai on May 6, 2021 #397

UARTのインタフェース

UARTのポート定義は次の図のようになります。

図%%.1
図397.1 mkUartのport定義

  • データ: load_newdata (8bit, 入力)
  • Ready: RDY_load (1bit, 出力)
  • Enable: EN_load (1bit, 入力)

の3種があり、ReadyとEnableは自動生成されています。ここでReadyはモジュールの受信可能なタイミングを表し、一方EnableはテストベンチからのデータがValidであることを表します。

テストベンチでのモジュール呼び出しは次の図のようになります。

図%%.2
図397.2 mkTbのmkUart呼び出し

シミュレーション波形は次の図のようになります。

図%%.3
図397.3 インタフェースの波形

まずRDY_load(オレンジ)がアサートされて受信可能状態になっているとき、55H()というデータが準備できた際にEN_load()がアサートされます。すると次のサイクルで55H()がUART内部に受け付けられ、同時にRDY_load(オレンジ)がネゲートされ、EN_load()もネゲートされます。

テストベンチはすぐにRDY_load(オレンジ)のアサートを待っていますが通信時にはRDY_load(オレンジ)はネゲートされています。次にRDY_load(オレンジ)がアサートされた時点でEN_load()を出力し同時にデータAAH()を出力します。次のサイクルでデータAAH()が受けつけられます。

このようなハンドシェークが自動的に、つまり誤りなく生成されることもBSVの魅力です。


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

BSVによるUARTの設計(6)

posted by sakurai on May 5, 2021 #396

BSVによるインタフェース生成

BSVによるハンドシェークインタフェース信号の生成を細かく見ていきます。 注目するのは、テストベンチからUARTに与える8ビットデータとそのハンドシェーク信号です。記述はデータ(data)だけですが、ハンドシェーク信号(RDY_data, EN_data)が自動的に生成されます。その様子を図396.1に示します。左側がテストベンチで、右側がUARTモジュールです。

図%%.1
図396.1 ハンドシェーク図

右側のモジュールから見た信号は以下のようになります。

  • データ: data (8bit, 入力)
  • Ready: RDY_data (1bit, 出力)
  • Enable: EN_data (1bit, 入力)

ここでRDY_dataはモジュールの受信可能なタイミングを表し、一方EN_dataはテストベンチからのデータがValidであることを表します。

図%%.2
図396.2 ハンドシェークタイミング図

RDYの確認とENのアサートが同サイクルであることに注意します。通常のFF受けの設計だと、RDYを確認した次のサイクルからENのアサートになりそうですが、ENは次のようにRDYを含む組み合わせ回路により構成されています。そのため、ENはRDYと同タイミングでアサートされます。

図%%.3
図396.3 EN生成回路(部分)

ENはFF受けされるので、組み合わせ回路出力でヒゲが有っても良いという考えなのでしょうか。このほうがレイテンシが短縮されます。


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

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));

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