FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

AWS EC2 F1インスタンスを使ったハードウェア開発の勉強 (8. cl_dram_dmaにAXIマスタを追加する)

AWS F1インスタンス HDK の勉強を続けている。 遅いながらにどうにか進めている。cl_dram_dma についてなんとなく分かってきた。 前回はアサーションを追加してAXIバスが動作していることを確認した。

次はAXIをどうにか動かすことはできないだろうか?目標としてはCLモジュール内に新しいAXIマスタを作成して内部からDRAMにアクセスできるパスを作りたい。

目標としては、以下の部分にAXIマスタを接続してDRAMにアクセスし、データをフェッチする。

  1. DMAでホストからデータをDDR4メモリに格納する。
  2. AXIマスタデータをフェッチする
  3. 演算し、結果を格納する。

として、例えば行列積のアクセラレータをF1インスタンス上で動作させてみたい。

f:id:msyksphinz:20180516232156p:plain

まずはテストベクタを作成して、DDR4メモリに対して自由にデータを読み書きしてみよう。

test_dram_matrix テストベクタの作成

サンプルプロジェクトとしてtest_dram_dmaをコピーしてtest_dram_matrix を作成した。これは、

  1. $readmemh で整数行列(16×16)を2つ分、DRAMにロードする
  2. CLに通知し、同じ場所からデータをフェッチする(という回路を作成する)
  3. フェッチしたデータを使って行列積を計算し、計算結果をDRAMに格納する

というシナリオだ。

f:id:msyksphinz:20180517000121p:plain
図. test_dram_matrix の考えるシナリオ

とりあえず書きかけのコードだが、githubに開発中のものをアップロードしている。

github.com

readmemhをしているのは以下で、16×16のデータをまずはローカルメモリに読み込む。

initial begin
  $readmemh ("datasets1.txt", datasets1);
  $readmemh ("datasets2.txt", datasets2);
end

次に、データをDMAでDDR4に転送する。tb.que_buffer_to_cltb.hm_put_bytetb.start_que_to_cl を使って転送する。 行列1はchannel0、行列1はchannel1を使って転送する。

f:id:msyksphinz:20180517000330p:plain
図. 行列積のデータをホストから転送するデータパス
  //Queue data to be transfered to CL DDR
  tb.que_buffer_to_cl(.chan(0), .src_addr(host_memory_buffer_address), .cl_addr(64'h0000_0004_0000_0000), .len(matrix_size) ); // move buffer to DDR 0

  // Put test pattern in host memory
  for (int i = 0 ; i < matrix_size / 4 ; i++) begin
    tb.hm_put_byte(.addr(host_memory_buffer_address+0), .d(datasets1[i][ 7: 0]));
    tb.hm_put_byte(.addr(host_memory_buffer_address+1), .d(datasets1[i][15: 8]));
    tb.hm_put_byte(.addr(host_memory_buffer_address+2), .d(datasets1[i][23:16]));
    tb.hm_put_byte(.addr(host_memory_buffer_address+3), .d(datasets1[i][31:24]));
    host_memory_buffer_address+=4;
  end

  host_memory_buffer_address = 64'h0_0001_0000;

  tb.que_buffer_to_cl(.chan(1), .src_addr(host_memory_buffer_address), .cl_addr(64'h0000_0004_0001_0000), .len(matrix_size) );  // move buffer to DDR 1

  for (int i = 0 ; i < matrix_size / 4 ; i++) begin
    tb.hm_put_byte(.addr(host_memory_buffer_address+0), .d(datasets2[i][ 7: 0]));
    tb.hm_put_byte(.addr(host_memory_buffer_address+1), .d(datasets2[i][15: 8]));
    tb.hm_put_byte(.addr(host_memory_buffer_address+2), .d(datasets2[i][23:16]));
    tb.hm_put_byte(.addr(host_memory_buffer_address+3), .d(datasets2[i][31:24]));
    host_memory_buffer_address+=4;
  end

...
  //Start transfers of data to CL DDR
  tb.start_que_to_cl(.chan(0));
  tb.start_que_to_cl(.chan(1));

  do begin
    status[0] = tb.is_dma_to_cl_done(.chan(0));
    status[1] = tb.is_dma_to_cl_done(.chan(1));
    #10ns;
    timeout_count++;
  end while ((status != 4'hf) && (timeout_count < 4000));

次に、CLに対してデータフェッチをトリガするのは以下だ。CLバス経由で0x500のアドレスに対して1を書き込む。これをCL側のデータフェッチのトリガ信号としよう。

  tb.poke(.addr(32'h0500), .data(32'h1), .size(DataSize::UINT16), .intf(AxiPort::PORT_OCL)); // write register

CL側の記述

CL側のハードウェア構成はファブリックのS1はデフォルトでTie offされており、自由に使用できる、のように書いてあるが実際にはATGのバスが接続されている。 これは取り外して良いのだろうか?とりあえず使わないのであれば外してしまってもよい気がしている。

f:id:msyksphinz:20180517000702p:plain
図. 謎。sh_cl_oclから分岐したバスがなぜかS01ポートに接続されている?これは取り外してよいのだろうか?
///////////////////////////////////////////////////////////////////////
///////////////// Secondary AXI Master module /////////////////////////
///////////////////////////////////////////////////////////////////////
// cl_dram_dma_axi_mstr  CL_DRAM_DMA_AXI_MSTR (
//     .aclk(clk),
//     .aresetn(dma_pcis_slv_sync_rst_n),
//     .cl_axi_mstr_bus(cl_axi_mstr_bus),
//     .axi_mstr_cfg_bus(axi_mstr_cfg_bus)
//   );

その代わりに、空いたcl_axi_mstr_busに対してAXIマスタを接続した。とりあえず、DRAMのデータを書き込んだところに対してデータフェッチを行う記述だ。

f:id:msyksphinz:20180517000843p:plain
図. 謎。sh_cl_oclから分岐したバスがなぜかS01ポートに接続されている?これは取り外してよいのだろうか?
    case (state)
      state_init: begin
        if (axi_mstr_cfg_bus.wr && axi_mstr_cfg_bus.addr[ 7: 0] == 8'h00 &&
            !cl_axi_mstr_bus.arvalid) begin
          cl_axi_mstr_bus.arvalid <= 1'b1;
          cl_axi_mstr_bus.araddr  <= 64'h0000_0004_0000_0000;
          cl_axi_mstr_bus.arid    <= 16'b0;                     // Only 1 outstanding command
          cl_axi_mstr_bus.arlen   <= 8'h00;                     // Always 1 burst
          cl_axi_mstr_bus.arsize  <= 3'b111;                    // Always 128 bytes
        end
        if (cl_axi_mstr_bus.arvalid && cl_axi_mstr_bus.arready) begin
          cl_axi_mstr_bus.arvalid <= 1'b0;
          state <= state_row;
        end
      end
      state_row: begin
        cl_axi_mstr_bus.arvalid <= 1'b1;
        cl_axi_mstr_bus.araddr  <= 64'h0000_0004_0001_0004;
        cl_axi_mstr_bus.arid    <= 16'b0;                     // Only 1 outstanding command
        cl_axi_mstr_bus.arlen   <= 8'h00;                     // Always 1 burst
        cl_axi_mstr_bus.arsize  <= 3'b010;                    // Always 4 bytes
        state <= state_col;
      end
      state_col: begin
        if (cl_axi_mstr_bus.arready) begin
          if (col_counter <= 15) begin
            cl_axi_mstr_bus.arvalid <= 1'b1;
            cl_axi_mstr_bus.araddr  <= cl_axi_mstr_bus.araddr + 64;  // Proceed 64-byte
            state <= state_col;
            col_counter <= col_counter + 6'h1;
          end else begin
            cl_axi_mstr_bus.arvalid <= 1'b0;
            state <= state_init;
          end
          cl_axi_mstr_bus.arid    <= 16'b0;                     // Only 1 outstanding command
          cl_axi_mstr_bus.arlen   <= 8'h00;                     // Always 1 burst
          cl_axi_mstr_bus.arsize  <= 3'b010;                    // Always 4 bytes
        end // if (cl_axi_mstr_bus.arready)
      end // case: state_col
    endcase // case (state)

cl_axi_mstr_busに対するデータロガーも接続して動作を観察しておく。

   always @ (negedge clk) begin
      if (cl_axi_mstr_bus.awvalid & cl_axi_mstr_bus.awready) begin
         $display ("%t : [cl_axi_mstr_bus AW] LEN=%d SIZE=%d ADDR=%x", $time,
                   cl_axi_mstr_bus.awlen, cl_axi_mstr_bus.awsize, cl_axi_mstr_bus.awaddr);
      end
      if (cl_axi_mstr_bus.arvalid & cl_axi_mstr_bus.arready) begin
         $display ("%t : [cl_axi_mstr_bus AR] LEN=%d SIZE=%d ADDR=%x", $time,
                   cl_axi_mstr_bus.arlen, cl_axi_mstr_bus.arsize, cl_axi_mstr_bus.araddr);
      end
      if (cl_axi_mstr_bus.wvalid & cl_axi_mstr_bus.wready) begin
         $display ("%t : [cl_axi_mstr_bus  W] STB=%x DATA=%x", $time, cl_axi_mstr_bus.wstrb, cl_axi_mstr_bus.wdata);
      end
      if (cl_axi_mstr_bus.rvalid & cl_axi_mstr_bus.rready) begin
         $display ("%t : [cl_axi_mstr_bus  R] DATA=%x", $time, cl_axi_mstr_bus.rdata);
      end
   end // always @ (negedge clk)

これで動作を見てみた。 めっちゃ時間はかかるが、どうやらちゃんとDRAMにアクセスしてデータをフェッチできたみたいだ。

[            33462000] : Initializing buffers
[            33462000] : starting H2C DMA channels
            33474000 : [sh_cl_dma AW] LEN= 15 SIZE=6 ADDR=0000000400000000
            33474000 : [sh_cl_dma  W] STB=ffffffffffffffff DATA=efe3be658f3f3b25123b5fef95d0dbf2eeee6b4b49aa9135f25b09fb79da57bd9ea0db5f4ea6d904e1ef3abb5e3cf5d888d272fbb6f7bafe70ef716ea7feef1a
            33482000 : [sh_cl_dma  W] STB=ffffffffffffffff DATA=cdeb2996cebedacc06c3d8fe51fdf3420225cefba9cf9fddee6ffa0fa70bfa0febbc9e3dfb1ebaee5b0d21dee4a10ee92989facf78cd9b379abb719bcf2a0eea
            33490000 : [sh_cl_dma AW] LEN= 15 SIZE=6 ADDR=0000000400010000
            33494000 : [sh_cl_dma  W] STB=ffffffffffffffff DATA=2404db84ad6199a6c37abe9c7cf482e9be6d67ccd40f64cdf2e9e4c8267a7f297bd88dbca72faa04fbbed32f24e1eac3ad3cbfcc9d2ed1a050df8a68b92d4b99
            33498000 : [sh_cl_dma  W] STB=ffffffffffffffff DATA=acb3f8a6a6de5ddc950d8cade9d6b9daafb96ebae1a1a4bcc8a2a24ffc9eb0f4bd38e6d75ce4703f7b0ce090d25c7e52be7d3ab7f3a5d0a93eaafe1c2db7c5cf
            33506000 : [sh_cl_dma  W] STB=ffffffffffffffff DATA=43bdcf1cba9bccd0d2b8d5e6cbb3dd51fed2553af9541b01230b7fb12bacb2b4c8fcea96b23ea5b1dd4086c9ffdecb8fbfcab8ba2e6b5d99591a5c93aad1e4be
            33514000 : [sh_cl_dma  W] STB=ffffffffffffffff DATA=908a71bc8e2dbc6dc818d823af24f74cb42dedb33e8cccfe42e84bde682cb56a76aa5c78a7e1b20cdb263f68a76f2ac8576cedf6dbde1ff9ccd61b8d2d2a29a3
            33522000 : [sh_cl_dma  W] STB=ffffffffffffffff DATA=3db619aceddb35fddeefcb6ea8b1a6ae36ff7cb5b55fb2230eba5eea09f559b119319c8fe28ff36feb437cc792fc6498b1dbad5ae492cce7bef8aea48ff4f4ba
            33530000 : [sh_cl_dma  W] STB=ffffffffffffffff DATA=59716bea2d1e4dbfc6fbb8abfaea0e16aabdc9cdfedac4eb9f3bef10a9ad4fd48b98eadd8d00eefbda36ddbddfd1cbafde8674437efc2b354fe87ac7c1083fc9
            33538000 : [sh_cl_dma  W] STB=ffffffffffffffff DATA=fa8c09af8ccb49e6fb7c8e54d30be8e2a23cfe69ccbd9ccb40debf5acddbd0bafcfaae244eeca080fc4deb95d44cd9723b7fe462f7feb21cebf54e9b3c6f6beb
            33546000 : [sh_cl_dma  W] STB=ffffffffffffffff DATA=a8df1175cc16bae62bec11411a14e41e532fb6f433f32d3be0cadc320942237d72afdcb1a1c33c0f6f2fa8c40000dd7bfafcf3b0aaec9cbbbaafe33c6f5e31bb
            33554000 : [sh_cl_dma  W] STB=ffffffffffffffff DATA=ddd631659ffe89ea783a31c9a4bf1cdcd89ee92f5cf3acd7fae2dc1eb2abdb5d3ffdec13b8380fadefbf7eea9a67bf1bdcdefd275ae5ba8c29aaa545eda4eb4f
            33562000 : [sh_cl_dma  W] STB=ffffffffffffffff DATA=26a3c6cd3fa9d0bdfbb2c08ad72de5c0ca0fd8d00bfa9bb4cef194abe7c28fc4e07029fedd9c22acfe3fa16b01cb140ec8d1e5a8caecbbdfeb9c0c0dd0faf13d
            33570000 : [sh_cl_dma  W] STB=ffffffffffffffff DATA=ceddd41b08ab61bded11af8bb27654fbaa4b105dc8f61b16eeaff71c19cb6b7be8ee39273ca94dcf650fdd76287ef63bdb6dd0fad249cf8c9a689401c3d5bbbe
...
[            33732000] : starting C2H DMA channels
Writing 0xDEAD_BEEF to address 0x00000500
            34762000 : [axi_mstr_cfg_bus W] ADDR=00000500
            34766000 : [cl_axi_mstr_bus AR] LEN=  0 SIZE=7 ADDR=0000000400000000
            34766000 : [axi_mstr_cfg_bus W] ADDR=00000500
            34782000 : [cl_axi_mstr_bus AR] LEN=  0 SIZE=2 ADDR=0000000400010004
            34798000 : [cl_axi_mstr_bus AR] LEN=  0 SIZE=2 ADDR=0000000400010044
            34814000 : [cl_axi_mstr_bus AR] LEN=  0 SIZE=2 ADDR=0000000400010084
            34830000 : [cl_axi_mstr_bus AR] LEN=  0 SIZE=2 ADDR=00000004000100c4
            34846000 : [cl_axi_mstr_bus AR] LEN=  0 SIZE=2 ADDR=0000000400010104
            34862000 : [cl_axi_mstr_bus AR] LEN=  0 SIZE=2 ADDR=0000000400010144
            34878000 : [cl_axi_mstr_bus AR] LEN=  0 SIZE=2 ADDR=0000000400010184
            34894000 : [cl_axi_mstr_bus AR] LEN=  0 SIZE=2 ADDR=00000004000101c4
            34910000 : [cl_axi_mstr_bus AR] LEN=  0 SIZE=2 ADDR=0000000400010204
            34926000 : [cl_axi_mstr_bus AR] LEN=  0 SIZE=2 ADDR=0000000400010244
            34942000 : [cl_axi_mstr_bus AR] LEN=  0 SIZE=2 ADDR=0000000400010284
            34958000 : [cl_axi_mstr_bus AR] LEN=  0 SIZE=2 ADDR=00000004000102c4
            34974000 : [cl_axi_mstr_bus AR] LEN=  0 SIZE=2 ADDR=0000000400010304
            34986000 : [cl_axi_mstr_bus  R] DATA=efe3be658f3f3b25123b5fef95d0dbf2eeee6b4b49aa9135f25b09fb79da57bd9ea0db5f4ea6d904e1ef3abb5e3cf5d888d272fbb6f7bafe70ef716ea7feef1a
            34990000 : [cl_axi_mstr_bus AR] LEN=  0 SIZE=2 ADDR=0000000400010344
            34994000 : [cl_axi_mstr_bus  R] DATA=a622eeadfbdeae582fc8a39e30bacd0ac9daba51caadfdff7dab21b8740b152c2493f4fabeaa3bef8eaeeb3bcd7941a978ffa3fec942a985b0ce9b1de998efbf
            35006000 : [cl_axi_mstr_bus AR] LEN=  0 SIZE=2 ADDR=0000000400010384
            35014000 : [cl_axi_mstr_bus  R] DATA=28eacbe767f7f34ff42e9a5c4de1cec44fbe9bcecc064db7fcf1a0f3b234c6d1f26cb0fdf3524d8bcdb917fbb91b4efcea83dfa8a7e5c1ebb12ce8ccdaaf6c4f
            35022000 : [cl_axi_mstr_bus AR] LEN=  0 SIZE=2 ADDR=00000004000103c4
            35026000 : [cl_axi_mstr_bus  R] DATA=68f25ebf12cc8f5daea31cd4bd2bc9c64cbb656cc918a8bf2afdb3e8b25cedc4c65584db1fecd041fc5a124f93babe1ecd89fcba9edd802918bd676a0ac560cc
            35038000 : [cl_axi_mstr_bus AR] LEN=  0 SIZE=2 ADDR=0000000400010404
            35046000 : [cl_axi_mstr_bus  R] DATA=1cadddac5ca664b3740e3fd2f5caf9ae1e5fb3e99ca3d0c3b2b8a2ff100e75febb65b131e40daef7b95ab6c8f6a5d4e5fff319b6cb208f4aeaeeb108b3ff1fb4
            35050000 : [cl_axi_mstr_bus  R] DATA=1efd6a41ceaf3ff86fdfdbf6b5ab2b1d9258660912ebf13944cd0387bdf9eee5c67a911a7ed6887c48edde0ec87faebae3bd46dc31636a7d5b06e9af5be4787f
            35058000 : [cl_axi_mstr_bus  R] DATA=7cadeaa3470d2f7e3fb1dcbf0fb5ca57d492d6c238b8ffe2a2eefc8cc2dbabdfb66568e5fa704caafc31d0dc1e9ebbb327c5b21fcdf3193d5ab2b53243b1ebfc
            35074000 : [cl_axi_mstr_bus  R] DATA=b301cea0c9bbde7c2594f2f3b7ad0b61ac9b2bd19ca2c9c081d0ce61e6c8cc2cd2d624785a9ea4d49dd3dd5266cd1b1c3d3bc3265cd941fc361aafc26ac2edbd
            35090000 : [cl_axi_mstr_bus  R] DATA=0def5d1ebb3f453e61ff7ea314e1abaafb6ffcbcae48681feb742569fe267bf18b2abbb1bf1b42dbb059cdb0d8b9bd05a531e78dd7af7d31bc2e08af5ff1f365

C++で記述された軽量CNN実装 mojo-cnn 試行 (3. RISC-V on FPGAで動かしてみる)

f:id:msyksphinz:20170925003926p:plain

前回はmojo-cnnをRISC-V向けに移植したので、今度は実機で動くかどうか試してみよう。

mojo-cnnはモデルデータをファイルとして保持しており、真面目に動かすならば1つのバイナリにすべきなのかもしれないが、とりあえずRocket-ChipにあるProxy Kernelがホスト上にあるファイルを読み込む役割を持っていると想定して、バイナリファイルのそばにデータを置いておく。

久しぶりにZedBoardを起動して、RISC-Vターゲットにコンパイルしたmojo-cnnのバイナリとCIFAR-10データセットをコピーしてきて、起動した。

root@zynq:~# ./fesvr-zynq  ~/mojo-cnn-msyksphinz/examples/test

とりあえずしばらく流しているけれども、全然動く気配がない。もう少し真面目にデバッグしないとダメかな。。。

f:id:msyksphinz:20180516005220p:plain
図. ZedBoard 上でRISC-V Mojo-CNNを動かしてみるが、どうも動かない。。。動作周波数が単純に低い?

C++で記述された軽量CNN実装 mojo-cnn 試行 (2. RISC-V 向けにリコンパイルとシミュレーション)

f:id:msyksphinz:20180515001039j:plain
※ 画像はイメージです。

前回はmojo-cnnをx86上で動作させてみたのだが、最終的にはRISC-Vプロセッサ上で動作させてみたいので、RISC-V向けにリコンパイルしてみよう。

やることはMakefileのg++の部分を書き換えるだけだ。

diff --git a/examples/makefile b/examples/makefile
index 75ca26c..d8485f5 100644
--- a/examples/makefile
+++ b/examples/makefile
@@ -1,4 +1,4 @@
-CC=g++
+CC=riscv64-unknown-elf-g++
 # CFLAGS_OMP= -I../mojo/ -std=c++11 -fopenmp -O3 -DMOJO_OPM -DMOJO_AVX -msse4 -mavx

さらに、usleepが定義されていないのでその部分はとりあえず削除した。

diff --git a/mojo/network.h b/mojo/network.h
index 413400a..481a282 100644
--- a/mojo/network.h
+++ b/mojo/network.h
@@ -94,8 +94,9 @@ mojo::matrix transform(const mojo::matrix in, const int x_center, const int y_ce
 #include <windows.h>
        void mojo_sleep(unsigned milliseconds) { Sleep(milliseconds); }
 #else
-#include <unistd.h>
-       void mojo_sleep(unsigned milliseconds) { usleep(milliseconds * 1000); }
+//#include <unistd.h>
+//     void mojo_sleep(unsigned milliseconds) { usleep(milliseconds * 1000); }
+       void mojo_sleep(unsigned milliseconds) { return; }
 #endif

 #ifdef MOJO_PROFILE_LAYERS

これでリコンパイルした。

$ make
riscv64-unknown-elf-g++ -I../mojo/ -std=c++11 test.cpp -o test
riscv64-unknown-elf-g++ -I../mojo/ -std=c++11 train_mnist.cpp -o train_mnist
riscv64-unknown-elf-g++ -I../mojo/ -std=c++11 train_cifar.cpp -o train_cifar

まずはspikeを使って実行してみよう。QEMUを使えばもっと早いのかもしれないが使い方がまだよく分かっていない。

$ spike pk test
Mojo CNN Configuration:
  0 : I1 : input 32 32 3 identity
  1 : Lc1 : convolution 3 50 1 elu
  2 : Lp1 : max_pool 2 2
  3 : Lc2 : deepcnet 100 elu
  4 : Lc3 : deepcnet 150 elu
  5 : R3 : concatenate 7 zero
  6 : Lc4 : deepcnet 200 elu
  7 : Lc5 : deepcnet 250 elu
  8 : FC1 : fully_connected 10 tanh

  I1-Lc1, Lc1-Lp1, Lp1-Lc2
  Lc2-Lc3, Lc3-R3, R3-Lc4
  Lc4-Lc5, Lc5-FC1

Testing CIFAR-10:
  testing : 10% (2035sec remaining)

とりあえず最後まで完走した!(キャプチャ取り忘れた...)とりあえず最後まで動くRISC-Vバイナリが作れたようだ。

Zynq FPGAで動かしてみようかな。

C++で記述された軽量CNN実装 mojo-cnn 試行 (1. x86上での動作試行)

f:id:msyksphinz:20180515001039j:plain
※ 画像はイメージです。

ディープラーニングと言えばTensorFlowだのKerasだのChainerだの、高級なインタフェースを持ったツールを使うケースが多いが、例えばマイコンや組み込みプロセッサなどでディープラーニングを動かしたいときは、そこまで高級な機能はいらず、C++などで簡単に記述されたCNNの実装のほうが実行しやすかったりする。

というわけで、RISC-V上で(というかFPGA上などで動いている非力なプロセッサ)でCNNを動かすことができれば面白そうだ。 「ゼロから作るディープラーニング」を見ながら位置からC++で実装してもよいけど大変そうなので、とりあえず簡単なフレームワークは無いかと探して、mojo-cnnというC++のCNN実装を見つけた。

github.com

mojo-cnn を使ってCIFAR-10を動かしたい

さっそくgit cloneして動作させてみる。環境はUbuntu 18.04 LTS上にダウンロードして試行した。 MNISTはもう飽きてしまったのでCIFAR-10を使ってみたい。ちょうとmojo-cnnにもCIFAR-10を動かすための環境がある。

単純にcloneしてビルドすると以下のようにエラーが出る。

$ git clone https://github.com/gnawice/mojo-cnn.git
$ cd mojo-cnn/example
$ make
g++ -I../mojo/ -std=c++11 -fopenmp -O3 -DMOJO_OPM -DMOJO_AVX -msse4 -mavx test.cpp -o test
In file included from test.cpp:44:0:
../mojo/mojo.h:31:0: warning: "MOJO_AVX" redefined
 #define MOJO_AVX // turn on AVX / SSE3 / SIMD optimizations

<command-line>:0:0: note: this is the location of the previous definition
In file included from ../mojo/network.h:38:0,
                 from ../mojo/mojo.h:78,
                 from test.cpp:44:
../mojo/layer.h:41:10: fatal error: windows.h: No such file or directory
 #include <windows.h>
          ^~~~~~~~~~~
compilation terminated.
makefile:7: recipe for target 'test' failed
make: *** [test] Error 1

#include <windows.h> を除去しても以下のようなエラーで進まない。

diff --git a/mojo/layer.h b/mojo/layer.h
index 4abef77..1e2e773 100644
--- a/mojo/layer.h
+++ b/mojo/layer.h
@@ -38,7 +38,7 @@
 namespace mojo
 {

-#include <windows.h>
+// #include <windows.h>
        /*
        double PCFreq = 0.0;
        __int64 CounterStart = 0;
g++ -I../mojo/ -std=c++11 -fopenmp -O3 -DMOJO_OPM -DMOJO_AVX -msse4 -mavx test.cpp -o test
In file included from test.cpp:44:0:
../mojo/mojo.h:31:0: warning: "MOJO_AVX" redefined
 #define MOJO_AVX // turn on AVX / SSE3 / SIMD optimizations

<command-line>:0:0: note: this is the location of the previous definition
In file included from ../mojo/mojo.h:76:0,
                 from test.cpp:44:
../mojo/core_math.h: In function ‘void mojo::dotsum_unwrapped_2x2(const float*, const float*, float*, int)’:
../mojo/core_math.h:178:2: error: ‘_mm256_zeroupper’ was not declared in this scope
  _mm256_zeroupper();
  ^~~~~~~~~~~~~~~~
../mojo/core_math.h:178:2: note: suggested alternative: ‘_mm_setzero_pd’
  _mm256_zeroupper();
...

どうもLinux環境だとAVXのIntrinsicが使えないようなので、これを使わないようにしよう。

  • AVX / SSE を使うオプションを削除する。またVGGはOpenCVを使うので除去した。
diff --git a/examples/makefile b/examples/makefile
index ee79729..70267b4 100644
--- a/examples/makefile
+++ b/examples/makefile
@@ -1,7 +1,7 @@
 CC=g++
-CFLAGS_OMP= -I../mojo/ -std=c++11 -fopenmp -O3 -DMOJO_OPM -DMOJO_AVX -msse4 -mavx
+CFLAGS_OMP= -I../mojo/ -std=c++11 -O3

-all: test train_mnist train_cifar vgg
+all: test train_mnist train_cifar

 test: test.cpp
        $(CC) $(CFLAGS_OMP) test.cpp -o test
  • MOJO_AVX / MOJO_OMP をundefinedにする。
--- a/mojo/mojo.h
+++ b/mojo/mojo.h
@@ -28,8 +28,8 @@

 #pragma once

-#define MOJO_AVX       // turn on AVX / SSE3 / SIMD optimizations
-#define MOJO_OMP       // allow multi-threading through openmp
+// #define MOJO_AVX    // turn on AVX / SSE3 / SIMD optimizations
+// #define MOJO_OMP    // allow multi-threading through openmp
 //#define MOJO_LUTS    // use look up tables, uses more memory
 //#define MOJO_CV3     // use OpenCV 3.x utilities
 //#define MOJO_CV2     // use OpenCV 2.x utilities

これでコンパイルできるようになったのでだが、次にトレーニングデータとしてCIFAR-10のデータセットをダウンロードしてこなければならない。

$ cd ../data/
$ curl -L http://www.cs.toronto.edu/~kriz/cifar-10-binary.tar.gz | tar xvz

これでシミュレーションを開始しよう。

$ ./train_cifar
==  Network Configuration  ====================================================
  0 : I1 : input 32 32 3 identity
  1 : C1 : convolution 3 16 1 elu
  2 : P1 : semi_stochastic_pool 3 3
  3 : C2 : convolution 3 64 1 elu
  4 : P2 : semi_stochastic_pool 4 4
  5 : FC2 : fully_connected 10 softmax

  I1-C1, C1-P1, P1-C2
  C2-P2, P2-FC2
==  CIFAR-10  Epoch  1  =============================================== 0:00:00
  mini batch:           16
  training time:        236.644 seconds on 1 threads
  model updates:        1805 (37% of records)
  estimated accuracy:   43.39%
  testing:              20% (13sec remaining)

...

==  CIFAR-10  Epoch  129  ============================================= 7:00:58
  mini batch:           16
  training time:        175.415 seconds on 1 threads
  model updates:        1061 (26% of records)
  estimated accuracy:   68.424%
  test accuracy:        67.69% (32.31% error)
  saved model:          ../models/snapshots/tmp_129.txt

==  CIFAR-10  Epoch  130  ============================================= 7:04:10
  mini batch:           16
  training time:        175.767 seconds on 1 threads
  model updates:        1061 (26% of records)
  estimated accuracy:   68.426%
  test accuracy:        67.69% (32.31% error)
  saved model:          ../models/snapshots/tmp_130.txt

==  CIFAR-10  Epoch  131  ============================================= 7:07:23
  mini batch:           16
  training time:        175.754 seconds on 1 threads
  model updates:        1062 (26% of records)
  estimated accuracy:   68.436%
  test accuracy:        67.66% (32.34% error)
  saved model:          ../models/snapshots/tmp_131.txt

Elvis just left the building. No further improvement in training found.
Stopping..

EPOCH 131までかかってる... しかも7時間も。さすがにAVXもなし、GPUもなしというのでかなりきつい。

でもとりあえず純粋C++で動いているので、RISC-Vとかほかのプラットフォームに移植しやすそうだし改造も簡単そうだ。

AWS EC2 F1インスタンスを使ったハードウェア開発の勉強 (7. cl_dram_dmaプロジェクトの解析)

AWS F1インスタンス HDK の勉強を続けている。 前回ModelSimを使ってcl_dram_dma の波形パタンを取得しようとしたが、動作が非常に遅くなってしまい失敗した。

どうにかして波形以外の情報を取得するためには、アサーションのようなものを組み込んでテキストファイルとしてログを取得するしかない。

という訳で、cl_dram_dmaのAXIバスの位置にロガーを挿入して動作を確認してみた。 GitHubに変更箇所をアップロードしてある。

f:id:msyksphinz:20180512230652p:plain
図. cl_dram_dma のデザインでアサーションを挿入した場所

github.com

ちなみに、テストパタンはTBで記述されている。ソフトウェアで書いてあるのかと思ったら違うのね。

  • aws-fpga/hdk/cl/examples/cl_dram_dma/verif/tests/test_dram_dma.sv

  • 0x0000_0000_0002 から 128byte ほど 0xAA を転送する。

      //Queue data to be transfered to CL DDR
      tb.que_buffer_to_cl(.chan(0), .src_addr(host_memory_buffer_address), .cl_addr(64'h0000_0000_0002), .len(len0) ); // move buffer to DDR 0

      // Put test pattern in host memory
      for (int i = 0 ; i < len0 ; i++) begin
         tb.hm_put_byte(.addr(host_memory_buffer_address), .d(8'hAA));
         host_memory_buffer_address++;
      end
  1. 0x0004_0000_0000 から 128byte ほど 0xBB を転送する。
      tb.que_buffer_to_cl(.chan(1), .src_addr(host_memory_buffer_address), .cl_addr(64'h0004_0000_0000), .len(len1) ); // move buffer to DDR 1

      for (int i = 0 ; i < len1 ; i++) begin
         tb.hm_put_byte(.addr(host_memory_buffer_address), .d(8'hBB));
         host_memory_buffer_address++;
      end
  1. 0x0008_0000_0005 から 6000byte ほど 0xCC を転送する。
      tb.que_buffer_to_cl(.chan(2), .src_addr(host_memory_buffer_address), .cl_addr(64'h0008_0000_0005), .len(len2) ); // move buffer to DDR 2

      for (int i = 0 ; i < len2 ; i++) begin
         tb.hm_put_byte(.addr(host_memory_buffer_address), .d(8'hCC));
         host_memory_buffer_address++;
      end
  1. 0x000C_0000_0000 から 300byte ほど 0xDD を転送する。
      tb.que_buffer_to_cl(.chan(3), .src_addr(host_memory_buffer_address), .cl_addr(64'h000C_0000_0000), .len(len3) ); // move buffer to DDR 3

      for (int i = 0 ; i < len3 ; i++) begin
         tb.hm_put_byte(.addr(host_memory_buffer_address), .d(8'hDD));
         host_memory_buffer_address++;
      end

一応、正しくログが取れているようだ。

tb.card.u_ddr4_rdimm_D.rcd_enabled.genblk1.u_ddr4_dimm.rank_instances[0].even_ranks.u_ddr4_rank.Micron_model.instance_of_sdram_devices[15].micron_mem_model.u_ddr4_model.always_diff_ck.if_diff_ck:Initialization complete @27185552
tb.card.u_ddr4_rdimm_D.rcd_enabled.genblk1.u_ddr4_dimm.rank_instances[0].even_ranks.u_ddr4_rank.Micron_model.instance_of_sdram_devices[16].micron_mem_model.u_ddr4_model.always_diff_ck.if_diff_ck:Initialization complete @27185552
tb.card.u_ddr4_rdimm_D.rcd_enabled.genblk1.u_ddr4_dimm.rank_instances[0].even_ranks.u_ddr4_rank.Micron_model.instance_of_sdram_devices[17].micron_mem_model.u_ddr4_model.always_diff_ck.if_diff_ck:Initialization complete @27185552
[            33462000] : Initializing buffers
[            33462000] : starting H2C DMA channels
            33474000 : [sh_cl_dma AW] LEN=  0 SIZE=6 ADDR=0000000000000002
            33474000 : [sh_cl_dma  W] STB=fffffffffffffffc DATA=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0000
            33482000 : [sh_cl_dma  W] STB=ffffffffffffffff DATA=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
            33490000 : [sh_cl_dma AW] LEN=  0 SIZE=6 ADDR=0000000000000040
            33494000 : [sh_cl_dma  W] STB=0000000000000003 DATA=0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000aaaa
            33506000 : [sh_cl_dma AW] LEN=  0 SIZE=6 ADDR=0000000000000080
            33510000 : [sh_cl_dma  W] STB=ffffffffffffffff DATA=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
            33522000 : [sh_cl_dma AW] LEN=  1 SIZE=6 ADDR=0000000400000000
            33526000 : [sh_cl_dma  W] STB=ffffffffffffffff DATA=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
            33642000 : [sh_cl_dma AW] LEN=  0 SIZE=6 ADDR=0000000800000005
            33646000 : [sh_cl_dma  W] STB=ffffffffffffffe0 DATA=cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc0000000000
            33650000 : [sh_cl_dma  W] STB=ffffffffffffffff DATA=cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
            33766000 : [sh_cl_dma AW] LEN= 62 SIZE=6 ADDR=0000000800000040
...

RISC-V仕様のFormal表現いろいろ

8th RISC-V Workshop in Barcelona で非常に気になったワーキンググループの一つ、RISC-V Formal Specification について調べた。

Formal Group は、 RISC-Vの仕様を、英語で書かれた仕様書としてだけでなく、マシンでもチェック可能なプログラムのような形で表現すること。 実際、RISC-Vの仕様書はすべての命令が英語の文章として記述されており、正式なDescriptionとして記述されてないので非常に気になっていた。

f:id:msyksphinz:20180512000426p:plain
MITによるFormal Semanticsの発表。発表資料は[リンク先](https://content.riscv.org/wp-content/uploads/2018/05/slidesThomasBourgeat.pdf)を参照。

今回の 8th RISC-V Workshop でのMITの発表は、このRISC-Vの仕様をHaskellとして表現する、ということ。

ちなみにこれ以外にもFormal表現はたくさんあるらしい (RISC-V Organizationは統一する気は無いのか...)

このriscv-semanticでいうと、さらに、CLASHというコンパイラを使うと、Haskellのコードを回路に変換することができる、らしい。

試しにソースコードを見てみる。 確かに、すべての命令がHaskellで記述されている。

  • src/ExecuteM.hs
execute (Mulh rd rs1 rs2) = do
  x <- getRegister rs1
  y <- getRegister rs2
  setRegister rd (highBits ((regToZ_signed x) * (regToZ_signed y)) :: t)

MMUの部分とかは結構分かりやすいのではないだろうか?

  • src/VirtualMemory.hs
calculateAddress :: (RiscvProgram p t) => AccessType -> MachineInt -> p MachineInt
calculateAddress accessType va = do
  mode <- fmap getMode (getCSRField Field.MODE)
  privMode <- getPrivMode
  mprv <- getCSRField Field.MPRV
  mpp <- getCSRField Field.MPP
  let effectPriv = if mprv == 1 then decodePrivMode mpp else privMode
  if mode == None || (privMode == Machine && accessType == Instruction) || (effectPriv == Machine)
    then return va
    else -- First the translation may be in a cache, possibly stalled, cacheAccess use the typeclass defined "TLB"
      cacheAccess va $
       do
    ppn <- getCSRField Field.PPN
    maybePTE <- findLeafEntry (mode, accessType, va, (shift ppn 12)) (pageTableLevels mode - 1)
    case maybePTE of
      Nothing -> pageFault accessType va
      Just (level, pte, addr) -> do
        let r = testBit pte 1
        let w = testBit pte 2
        let x = testBit pte 3
        let u = testBit pte 4

RISC-V Formal Semantics を使えるようにする

まずは、Haskellコンパイラ環境であるStackをインストールする。

curl -sSL https://get.haskellstack.org/ | sh
git clone https://github.com/mit-plv/riscv-semantics.git
cd riscv-semantics
./install.sh  # これで何かしら大量にインストールされる

で、make を実行してみるのだが、なんか失敗するじゃないか。

$ make
riscv-none-embed-gcc -march=rv32im -mabi=ilp32 -static -nostdlib -nostartfiles -mcmodel=medany -c init.S -o init32.o
make[1]: riscv-none-embed-gcc: コマンドが見つかりませんでした
Makefile:25: ターゲット 'init32.o' のレシピで失敗しました

そもそも riscv-none-embed-gcc が必要とは書いていないのでこれはおかしいのでは?Makefileを書き直してriscv32-unknown-elf-gcc で書き直してみる。

diff --git a/Makefile b/Makefile
index 4878218..689bf7d 100644
--- a/Makefile
+++ b/Makefile
@@ -9,7 +9,9 @@ test:
        $(MAKE) -C test

 riscv-tests:
-       RISCV_PREFIX=riscv-none-embed- $(MAKE) -C riscv-tests/isa rv64mi rv64si rv64ui
+       $(MAKE) -C riscv-tests/isa rv64mi rv64si rv64ui
+
+#      RISCV_PREFIX=riscv-none-embed- $(MAKE) -C riscv-tests/isa rv64mi rv64si rv64ui

 clean:
        rm -rf .stack-work/
diff --git a/test/Makefile b/test/Makefile
index e325f2f..289857a 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -12,8 +12,8 @@ HEX32=$(addsuffix .hex,$(ELF32))
 HEX64=$(addsuffix .hex,$(ELF64))
 ELF2HEX=../elf2hex

-RISCVCC64=riscv-none-embed-gcc -march=rv64im -mabi=lp64 -static -nostdlib -nostartfiles -mcmodel=medany
-RISCVCC32=riscv-none-embed-gcc -march=rv32im -mabi=ilp32 -static -nostdlib -nostartfiles -mcmodel=medany
+RISCVCC64=riscv32-unknown-elf-gcc -march=rv64im -mabi=lp64 -static -nostdlib -nostartfiles -mcmodel=medany
+RISCVCC32=riscv32-unknown-elf-gcc -march=rv32im -mabi=ilp32 -static -nostdlib -nostartfiles -mcmodel=medany

 all: $(HEX32) $(HEX64)

これで通るようになった。makeを実行するとテストパタンセットがすべてコンパイルされた?

ただし、ここから先のHaskellの実行がうまく行かない。Haskellについてはあまりに無知なのだが、何か間違っているのだろうか?

stack exec riscv-semantics test/build/thuemorse32.hex
Executable named riscv-semantics not found on path: ["/home/msyksphinz/work/formal/riscv-semantics/.stack-work/install/x86_64-linux/nightly-2017-10-26/8.2.1/bin","/home/msyksphinz/.stack/snapshots/x86_64-linux/nightly-2017-10-26/8.2.1/bin","/home/msyksphinz/.stack/compiler-tools/x86_64-linux/ghc-8.2.1/bin","/home/msyksphinz/.stack/programs/x86_64-linux/ghc-8.2.1/bin"...

AWS EC2 F1インスタンスを使ったハードウェア開発の勉強 (6. cl_dram_dmaの波形取得の試行)

AWS F1インスタンス HDK の勉強を続けている。なかなか時間が取れなくて遅々として進まないが...

何か一つF1インスタンス向けに一つデザインを作って動かしてみたい。そのためには、DRAMのメモリアクセスを理解して実装できるようにならなければならない。

そのために、cl_dram_dmaデザインサンプルの波形を取得したいのだが、、うまく行かない。

まず、これまでのようにVivadoシミュレータを使ったシミュレーションで、GUIを立ち上げて波形を取得しようとしたのだが、Vivado Simulatorが異常終了してしまった。

git diff Makefile.vivado waves.tcl
diff --git a/hdk/cl/examples/cl_dram_dma/verif/scripts/Makefile.vivado b/hdk/cl/examples/cl_dram_dma/verif/scripts/Makefile.vivado
index 972820f..2e50fb7 100644
--- a/hdk/cl/examples/cl_dram_dma/verif/scripts/Makefile.vivado
+++ b/hdk/cl/examples/cl_dram_dma/verif/scripts/Makefile.vivado
@@ -29,5 +29,5 @@ run:
 ifeq ($(TEST),test_null)
        cd $(SIM_DIR) && xsim -R -log $(C_TEST).log -tclbatch $(SCRIPTS_DIR)/waves.tcl tb
 else
-       cd $(SIM_DIR) && xsim -R -log $(TEST).log -tclbatch $(SCRIPTS_DIR)/waves.tcl tb
+       cd $(SIM_DIR) && xsim -R -log $(TEST).log -g -tclbatch $(SCRIPTS_DIR)/waves.tcl tb
 endif
diff --git a/hdk/cl/examples/cl_dram_dma/verif/scripts/waves.tcl b/hdk/cl/examples/cl_dram_dma/verif/scripts/waves.tcl
index 4b14d0d..706639e 100644
--- a/hdk/cl/examples/cl_dram_dma/verif/scripts/waves.tcl
+++ b/hdk/cl/examples/cl_dram_dma/verif/scripts/waves.tcl
@@ -24,4 +24,4 @@ if { [string length $curr_wave] == 0 } {
 }

 run 200 us
-quit
+# quit
make TEST=test_dram_dma
f:id:msyksphinz:20180511003343p:plain
図. Vivado Simulator で波形を表示させようとすると落ちる。

しかし諦めることはない。aws-fpgaリポジトリはそれ以外のシミュレータも対応している。 例えば、VCS / QuestaSim / IES に対応している。 私はVCSやIESは持っていないが、Quaestaならば Intel FPGA Starter Editionが無料で使えるではないか!どうにかこれで代用できないだろうか?

www.altera.co.jp

というわけ、Xilinxの環境にも関わらず、QuestaSim Intel FPGA Starter Editionをダウンロードしてインストールした。

インストール自体は普通に完了した。ただしいくつか正常に動作させるために足りていないといわれるパッケージをインストールしたり、QuestaSimのディレクトリでリンクを追加したりした。

$ cd ${HOME}/intelFPGA_lite/18.0/modelsim_ase
$ ln -s linuxaloem linuxpe
$ ln -s linuxaloem linux_x86_64pe

さらに、QuestaSimを動作させるために Makefile を以下のように変更した。 これでシミュレーションを実行する。

diff --git a/hdk/cl/examples/cl_dram_dma/verif/scripts/Makefile.questa b/hdk/cl/examples/cl_dram_dma/verif/scripts/Makefile.questa
index 994d639..3480a42 100644
--- a/hdk/cl/examples/cl_dram_dma/verif/scripts/Makefile.questa
+++ b/hdk/cl/examples/cl_dram_dma/verif/scripts/Makefile.questa
@@ -26,13 +26,13 @@ compile: $(COMPLIB_DIR)
        mkdir -p $(SIM_DIR)
        cd ${SIM_DIR} && ln -s -f ../questa_complib/modelsim.ini
        cd $(SIM_DIR) && vlog $(C_FILES) -ccflags "-I$(C_SDK_USR_INC_DIR)" -ccflags "-I$(C_SDK_USR_UTILS_DIR)" -ccflags "-I$(C_COMMON_DIR)" -ccflags "-DSV_TEST" -ccflags "-DSCOPE" -ccflags "-I$(C_INC_DIR)"
-       cd $(SIM_DIR) && vlog -mfcu -sv -64 -timescale 1ps/1ps -93 -L $(COMPLIB_DIR)/unisims_ver -L $(COMPLIB_DIR)/unimacro_ver -L $(COMPLIB_DIR)/secureip -f $(SCRIPTS_DIR)/top.$(SIMULATOR).f
+       cd $(SIM_DIR) && vlog -mfcu -sv -timescale 1ps/1ps -93 -L $(COMPLIB_DIR)/unisims_ver -L $(COMPLIB_DIR)/unimacro_ver -L $(COMPLIB_DIR)/secureip -f $(SCRIPTS_DIR)/top.$(SIMULATOR).f

 run:
 ifeq ($(TEST),test_null)
-       cd $(SIM_DIR) &&  vsim -c -voptargs="+acc" -64 -t ps -sv_seed random -L $(COMPLIB_DIR)/unisims_ver -L $(COMPLIB_DIR)/unisim -L $(COMPLIB_DIR)/unifast_ver -L $(COMPLIB_DIR)/unifast -L $(COMPLIB_DIR)/unimacro_ver -L $(COMPLIB_DIR)/unimacro -L $(COMPLIB_DIR)/secureip -L $(COMPLIB_DIR)/axi_register_slice_v2_1_12 -L $(COMPLIB_DIR)/axi_infrastructure_v1_1_0 -L $(COMPLIB_DIR)/axi_crossbar_v2_1_13 -L $(COMPLIB_DIR)/xpm -L $(COMPLIB_DIR)/axi_clock_converter_v2_1_11 -L $(COMPLIB_DIR)/fifo_generator_v13_1_4 -L $(COMPLIB_DIR)/axi_data_fifo_v2_1_11 -L $(COMPLIB_DIR)/generic_baseblocks_v2_1_0 -l $(C_TEST).log -do "run -all; quit -f" tb glbl $(TEST)
+       cd $(SIM_DIR) &&  vsim -c -voptargs="+acc" -t ps -sv_seed random -L $(COMPLIB_DIR)/unisims_ver -L $(COMPLIB_DIR)/unisim -L $(COMPLIB_DIR)/unifast_ver -L $(COMPLIB_DIR)/unifast -L $(COMPLIB_DIR)/unimacro_ver -L $(COMPLIB_DIR)/unimacro -L $(COMPLIB_DIR)/secureip -L $(COMPLIB_DIR)/axi_register_slice_v2_1_12 -L $(COMPLIB_DIR)/axi_infrastructure_v1_1_0 -L $(COMPLIB_DIR)/axi_crossbar_v2_1_13 -L $(COMPLIB_DIR)/xpm -L $(COMPLIB_DIR)/axi_clock_converter_v2_1_11 -L $(COMPLIB_DIR)/fifo_generator_v13_1_4 -L $(COMPLIB_DIR)/axi_data_fifo_v2_1_11 -L $(COMPLIB_DIR)/generic_baseblocks_v2_1_0 -l $(C_TEST).log -do "run -all; quit -f" tb glbl $(TEST)
 else
-       cd $(SIM_DIR) &&  vsim -c -voptargs="+acc" -64 -t ps -sv_seed random -L $(COMPLIB_DIR)/unisims_ver -L $(COMPLIB_DIR)/unisim -L $(COMPLIB_DIR)/unifast_ver -L $(COMPLIB_DIR)/unifast -L $(COMPLIB_DIR)/unimacro_ver -L $(COMPLIB_DIR)/unimacro -L $(COMPLIB_DIR)/secureip -L $(COMPLIB_DIR)/axi_register_slice_v2_1_12 -L $(COMPLIB_DIR)/axi_infrastructure_v1_1_0 -L $(COMPLIB_DIR)/axi_crossbar_v2_1_13 -L $(COMPLIB_DIR)/xpm -L $(COMPLIB_DIR)/axi_clock_converter_v2_1_11 -L $(COMPLIB_DIR)/fifo_generator_v13_1_4 -L $(COMPLIB_DIR)/axi_data_fifo_v2_1_11 -L $(COMPLIB_DIR)/generic_baseblocks_v2_1_0 -l $(TEST).log -do "run -all; quit -f" tb glbl $(TEST)
+       cd $(SIM_DIR) &&  vsim -c -voptargs="+acc" -t ps -sv_seed random -L $(COMPLIB_DIR)/unisims_ver -L $(COMPLIB_DIR)/unisim -L $(COMPLIB_DIR)/unifast_ver -L $(COMPLIB_DIR)/unifast -L $(COMPLIB_DIR)/unimacro_ver -L $(COMPLIB_DIR)/unimacro -L $(COMPLIB_DIR)/secureip -L $(COMPLIB_DIR)/axi_register_slice_v2_1_12 -L $(COMPLIB_DIR)/axi_infrastructure_v1_1_0 -L $(COMPLIB_DIR)/axi_crossbar_v2_1_13 -L $(COMPLIB_DIR)/xpm -L $(COMPLIB_DIR)/axi_clock_converter_v2_1_11 -L $(COMPLIB_DIR)/fifo_generator_v13_1_4 -L $(COMPLIB_DIR)/axi_data_fifo_v2_1_11 -L $(COMPLIB_DIR)/generic_baseblocks_v2_1_0 -l $(TEST).log -do "add log -r sim:/tb/card/fpga/CL/*; run -all" tb glbl $(TEST)
 endif

 $(COMPLIB_DIR):

シミュレーションを実行すると、以下で固まってしまった。Vivado Simulator (のGUIなし)だと、ちゃんと進むのに...

make TEST=test_dram_dma QUESTA=1
# Info: [Unisim HPIO_VREF-1] Fabric Tune Value changed to 0011101. Instance: tb.card.fpga.CL.SH_DDR.<protected>.<protected>.<protected>.inst.u_ddr4_mem_intfc.u_mig_ddr4_phy.inst.u_ddr_iob.genByte[8].u_ddr_iob_byte.genblk1.genVref.u_hpio_vref
# Info: [Unisim HPIO_VREF-1] Fabric Tune Value changed to 0011101. Instance: tb.card.fpga.CL.SH_DDR.<protected>.<protected>.<protected>.inst.u_ddr4_mem_intfc.u_mig_ddr4_phy.inst.u_ddr_iob.genByte[7].u_ddr_iob_byte.genblk1.genVref.u_hpio_vref
# Info: [Unisim HPIO_VREF-1] Fabric Tune Value changed to 0011101. Instance: tb.card.fpga.CL.SH_DDR.<protected>.<protected>.<protected>.inst.u_ddr4_mem_intfc.u_mig_ddr4_phy.inst.u_ddr_iob.genByte[6].u_ddr_iob_byte.genblk1.genVref.u_hpio_vref
# Info: [Unisim HPIO_VREF-1] Fabric Tune Value changed to 0011101. Instance: tb.card.fpga.CL.SH_DDR.<protected>.<protected>.<protected>.inst.u_ddr4_mem_intfc.u_mig_ddr4_phy.inst.u_ddr_iob.genByte[5].u_ddr_iob_byte.genblk1.genVref.u_hpio_vref
# Info: [Unisim HPIO_VREF-1] Fabric Tune Value changed to 0011101. Instance: tb.card.fpga.CL.SH_DDR.<protected>.<protected>.<protected>.inst.u_ddr4_mem_intfc.u_mig_ddr4_phy.inst.u_ddr_iob.genByte[4].u_ddr_iob_byte.genblk1.genVref.u_hpio_vref
# Info: [Unisim HPIO_VREF-1] Fabric Tune Value changed to 0011101. Instance: tb.card.fpga.CL.SH_DDR.<protected>.<protected>.<protected>.inst.u_ddr4_mem_intfc.u_mig_ddr4_phy.inst.u_ddr_iob.genByte[3].u_ddr_iob_byte.genblk1.genVref.u_hpio_vref

GUIを立ち上げてみた。とりあえずシミュレーションは進んでいるようだが、まったく終わらない... 一応波形は取れているようだが、Vivado Simulationに比べて非常に遅い。何故だ?

f:id:msyksphinz:20180511010814p:plain
図. test_dram_dma の波形を取得する。なんとなく波形がとれているが...