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