FC2カウンター FPGAの部屋 intel HLS
fc2ブログ

FPGAやCPLDの話題やFPGA用のツールの話題などです。 マニアックです。 日記も書きます。

FPGAの部屋

FPGAの部屋の有用と思われるコンテンツのまとめサイトを作りました。Xilinx ISEの初心者の方には、FPGAリテラシーおよびチュートリアルのページをお勧めいたします。

intel HLS の pointer_mm_master を試してみた2

intel HLS の pointer_mm_master を試してみた1”の続き。

前回は、intel HLS の F:\intelFPGA_lite\17.1\hls\examples\tutorials\interfaces\pointer_mm_master を試してみた。
ここには、part_1_pointers.cpp、part_2_masters.cpp、part_3_ddr_masters.cpp、part_4_ddr_masters_coalesce.cpp、part_5_ddr_masters_align.cpp の 5 つのAvalon-MM master のサンプルがあった。そして、それぞれの実装の違いをHDLのポート宣言を見ることで確認した。
今回は、それらの転送波形をModelSim で見てみよう。

part_1_pointers.cpp
part_1_pointers.cpp のハードウェア化関数、component void vector_add() コードを引用する。

component void vector_add(int* a,
                          int* b,
                          int* c,
                          int N) {
  for (int i = 0; i < N; ++i) {
    c[i] = a[i] + b[i];
  }
}


ModelSim の全体波形を示す。
Intel_HLS_135_171208.png

done が出るまでの時間は約 17.03 us だった。

1つのトランザクションが見えるように拡大波形を示す。
Intel_HLS_136_171208.png

それぞれ 1 回ずつReadが 2 回、Write が 1 回のアクセルがある。 1 回の転送の周期は、17 ns だった。これを 1000 回やっているので、17 us となり計算は合う。

part_2_masters.cpp
part_2_masters.cpp のハードウェア化関数、component void vector_add() コードを引用する。

component void vector_add(ihc::mm_master<int, ihc::aspace<1>, ihc::awidth<16>, ihc::dwidth<32> >& a,
                          ihc::mm_master<int, ihc::aspace<2>, ihc::awidth<16>, ihc::dwidth<32> >& b,
                          ihc::mm_master<int, ihc::aspace<3>, ihc::awidth<16>, ihc::dwidth<32> >& c,
                          int N) {
  for (int i = 0; i < N; ++i) {
    c[i] = a[i] + b[i];  
  }
}


part_1_pointers.cpp と比べると、32 ビット幅のバスを 3 つにした。つまりスループットが大きくなる要素がそろった。
ModelSim の全体波形を示す。
Intel_HLS_137_171208.png

3つのバスとも、アドレスとデータの幅は 32 ビットだ。
part_2_masters.cpp の done が出るまでに時間は約 1.04 ms となった。part_1_pointers.cpp との性能差は17 倍弱だ。

拡大してみよう。
Intel_HLS_138_171208.png

この実装では、Read と Write はパイプライン実行されている。クロックごとに 1 ns で Write されているので、1 回の転送の周期は 1 ns となる。

part_2_masters.cpp がなぜエラーになるかだが、演算の最初を拡大してみると、最初の演算の答えが avmm_3_rw_writedata に 0000464c と出ているが、まだ avmm_3_rw_write は 1 にアサートされていない。avmm_3_rw_write が 1 にアサートされるのはその 2 クロック後であるので、データが 2 個ずれているようだ。この、avmm_3_rw_write が 1 にアサートされるのを 2 クロック前にずらせれば良さそうだ。

part_3_ddr_masters.cpp
part_3_ddr_masters.cpp のハードウェア化関数、component void vector_add() コードを引用する。

component void vector_add(ihc::mm_master<int, ihc::aspace<1>, ihc::awidth<32>, ihc::dwidth<256>, ihc::latency<0>, ihc::maxburst<8>, ihc::waitrequest<true> >& a, // bank 1
                          ihc::mm_master<int, ihc::aspace<1>, ihc::awidth<32>, ihc::dwidth<256>, ihc::latency<0>, ihc::maxburst<8>, ihc::waitrequest<true> >& b, // bank 1
                          ihc::mm_master<int, ihc::aspace<2>, ihc::awidth<32>, ihc::dwidth<256>, ihc::latency<0>, ihc::maxburst<8>, ihc::waitrequest<true> >& c, // bank 2
                          int N) {
  for (int i = 0; i < N; ++i) {
    c[i] = a[i] + b[i];  
  }
}


データバスの幅が 256 ビットになって、Read が 1 つのバス、Write も 1 つのバスの合計 2 つのバスになった。
ModelSim の全体波形を示す。
Intel_HLS_139_171208.png

まばらにアクセスがあるが、ビット幅が 256 ビット幅なので、done が出るまでの時間は、約 1.63 us だった。part_1_pointers.cpp との性能差は約10.4 倍だった。

拡大してみてみよう。
Intel_HLS_140_171208.png

Write の幅を見ると 8 ns になっているのが分かる。これは、32ビット幅の演算器しか持っていないので、256 ビット幅のデータを満たすには、 256 / 32 = 8 クロック分(1クロックは 1 ns)かかるためだと思われる。

part_4_ddr_masters_coalesce.cpp
part_4_ddr_masters_coalesce.cpp のハードウェア化関数、component void vector_add() コードを引用する。

component void vector_add(ihc::mm_master<int, ihc::aspace<1>, ihc::awidth<32>, ihc::dwidth<256>, ihc::latency<0>, ihc::maxburst<8>, ihc::waitrequest<true> >& a, // bank 1
                          ihc::mm_master<int, ihc::aspace<1>, ihc::awidth<32>, ihc::dwidth<256>, ihc::latency<0>, ihc::maxburst<8>, ihc::waitrequest<true> >& b, // bank 1
                          ihc::mm_master<int, ihc::aspace<2>, ihc::awidth<32>, ihc::dwidth<256>, ihc::latency<0>, ihc::maxburst<8>, ihc::waitrequest<true> >& c, // bank 2
                          int N) {
  #pragma unroll 8
  for (int i = 0; i < N; ++i) {
    c[i] = a[i] + b[i];  
  }
}


part_4_ddr_masters_coalesce.cpp では、#pragma unroll 8 のプラグマが追加されているので、part_3_ddr_masters.cpp での 8 クロックに 1 回のデータ Write が改善されていると思われる。
ModelSim の全体波形を示す。
Intel_HLS_141_171208.png

done が出るまでの時間は、384 ns だった。アクセスがきちんと並んでいて、転送のスループットが大きいのが分かる。part_1_pointers.cpp との性能差は約 44.3 倍だった。

波形を拡大してみよう。
Intel_HLS_142_171208.png

Read のアドレスが飛んでいるので、a[i] をRead しているアドレスと b[i] をRead しているアドレスがあると考えられる。よって、Write の転送は、半分のスループットになっているのが分かる。つまり、2 つデータを読んで 1 つデータを書いているわけだ。

part_5_ddr_masters_align.cpp
part_5_ddr_masters_align.cpp のハードウェア化関数、component void vector_add() コードを引用する。

component void vector_add(ihc::mm_master<int, ihc::aspace<1>, ihc::awidth<32>, ihc::dwidth<256>, ihc::latency<0>, ihc::maxburst<8>, ihc::align<32>, ihc::waitrequest<true> >& a, // bank 1
                          ihc::mm_master<int, ihc::aspace<1>, ihc::awidth<32>, ihc::dwidth<256>, ihc::latency<0>, ihc::maxburst<8>, ihc::align<32>, ihc::waitrequest<true> >& b, // bank 1
                          ihc::mm_master<int, ihc::aspace<2>, ihc::awidth<32>, ihc::dwidth<256>, ihc::latency<0>, ihc::maxburst<8>, ihc::align<32>, ihc::waitrequest<true> >& c, // bank 2
                          int N) {
  #pragma unroll 8
  for (int i = 0; i < N; ++i) {
    c[i] = a[i] + b[i];  
  }
}


part_5_ddr_masters_align.cpp では、part_4_ddr_masters_coalesce.cpp に ihc::align<32> が追加されている。これでデータバスの 256 ビット幅にアラインされているということにしている。

ModelSim の全体波形を示す。
Intel_HLS_143_171208.png

done が出るまでの時間は、380 ns だった。part_4_ddr_masters_coalesce.cpp とほとんど同じようだ。part_1_pointers.cpp との性能差は約 44.8 倍だった。

波形を拡大してみた。
Intel_HLS_144_171208.png

part_4_ddr_masters_coalesce.cpp と同様だ。 ihc::align<32> を付けるとリソース使用量が減るのではないか?と思う。


最後に part_2_masters.cpp のエラーの対処方法を押しててもらったので、やってみよう。

component void vector_add(ihc::mm_master<int, ihc::aspace<1>, ihc::awidth<16>, ihc::dwidth<32>, ihc::latency<0> >& a,
                          ihc::mm_master<int, ihc::aspace<2>, ihc::awidth<16>, ihc::dwidth<32>, ihc::latency<0> >& b,
                          ihc::mm_master<int, ihc::aspace<3>, ihc::awidth<16>, ihc::dwidth<32>, ihc::latency<0> >& c,
                          int N) {
  for (int i = 0; i < N; ++i) {
    c[i] = a[i] + b[i];  
  }
}


このように、ihc::latency<0> を追加して build clean してから build default を行ったところ、PASSED になった。なお、mm_A, mm_B, mm_C の宣言の部分にも ihc::latency<0> を追加している。
Intel_HLS_145_171209.png

ModelSim の波形を示す。
Intel_HLS_146_171209.png

これは、ihc::latency<0> を付加する前に比べてスループットが下がっている。以前は、done が出るまでに時間は約 1.04 ms だったが、今回は、約 1.33 ms になった。

拡大してみた。
Intel_HLS_147_171209.png
  1. 2017年12月08日 05:13 |
  2. intel HLS
  3. | トラックバック:0
  4. | コメント:0

intel HLS の pointer_mm_master を試してみた1

intel HLS の F:\intelFPGA_lite\17.1\hls\examples\tutorials\interfaces\pointer_mm_master を試してみた。
ここには、part_1_pointers.cpp、part_2_masters.cpp、part_3_ddr_masters.cpp、part_4_ddr_masters_coalesce.cpp、part_5_ddr_masters_align.cpp の 5 つのAvalon-MM master のサンプルがある。

part_1_pointers.cpp のハードウェア化関数、component void vector_add() コードを引用する。

component void vector_add(int* a,
                          int* b,
                          int* c,
                          int N) {
  for (int i = 0; i < N; ++i) {
    c[i] = a[i] + b[i];
  }
}


通常のポインタ渡しのベクトル加算だ。こう記述すると単独のAvalon-MM Master インターフェースが実装され、a, b のRead と c のWrite はシリアライズされるそうだ。

part_2_masters.cpp のハードウェア化関数、component void vector_add() コードを引用する。

component void vector_add(ihc::mm_master<int, ihc::aspace<1>, ihc::awidth<16>, ihc::dwidth<32> >& a,
                          ihc::mm_master<int, ihc::aspace<2>, ihc::awidth<16>, ihc::dwidth<32> >& b,
                          ihc::mm_master<int, ihc::aspace<3>, ihc::awidth<16>, ihc::dwidth<32> >& c,
                          int N) {
  for (int i = 0; i < N; ++i) {
    c[i] = a[i] + b[i];  
  }
}


mm_master<> クラスを使用してcomponent のAvalon-MM インターフェースを属性を設定できるようです。
aspace<1>、aspace<2>、aspace<3> で、1, 2, 3 の 3 個のスペースを使用しているので、3つのAvalon-MM インターフェースが生成されるようです。
ihc::awidth<16> でアドレスのビット幅を 16 ビット幅にしている。
ihc::dwidth<32> でデータ幅を32 ビットにしている。

part_3_ddr_masters.cpp のハードウェア化関数、component void vector_add() コードを引用する。

component void vector_add(ihc::mm_master<int, ihc::aspace<1>, ihc::awidth<32>, ihc::dwidth<256>, ihc::latency<0>, ihc::maxburst<8>, ihc::waitrequest<true> >& a, // bank 1
                          ihc::mm_master<int, ihc::aspace<1>, ihc::awidth<32>, ihc::dwidth<256>, ihc::latency<0>, ihc::maxburst<8>, ihc::waitrequest<true> >& b, // bank 1
                          ihc::mm_master<int, ihc::aspace<2>, ihc::awidth<32>, ihc::dwidth<256>, ihc::latency<0>, ihc::maxburst<8>, ihc::waitrequest<true> >& c, // bank 2
                          int N) {
  for (int i = 0; i < N; ++i) {
    c[i] = a[i] + b[i];  
  }
}


ihc::aspace<1>、ihc::aspace<1>、ihc::aspace<2>
アドレス・バスが 32 ビット幅、データ・バスが 256 ビット幅となっている。しかし、次のpart_4 と比べるとスループットが小さいので、たぶん計算リソースは32ビットAdder 1 個なのだと思う。
ihc::latency<0> は外部メモリからのRead レイテンシを設定できるようだ。
ihc::maxburst<8> で最大バースト長を 8 に設定している。 ihc::maxburst<> が 2 以上の時に、ihc::waitrequest が必要のようだ。つまり、バーストの途中でデータが無くなったら、もしくはFIFO が一杯になったらデータのやり取りを休止できる。

part_4_ddr_masters_coalesce.cpp のハードウェア化関数、component void vector_add() コードを引用する。

component void vector_add(ihc::mm_master<int, ihc::aspace<1>, ihc::awidth<32>, ihc::dwidth<256>, ihc::latency<0>, ihc::maxburst<8>, ihc::waitrequest<true> >& a, // bank 1
                          ihc::mm_master<int, ihc::aspace<1>, ihc::awidth<32>, ihc::dwidth<256>, ihc::latency<0>, ihc::maxburst<8>, ihc::waitrequest<true> >& b, // bank 1
                          ihc::mm_master<int, ihc::aspace<2>, ihc::awidth<32>, ihc::dwidth<256>, ihc::latency<0>, ihc::maxburst<8>, ihc::waitrequest<true> >& c, // bank 2
                          int N) {
  #pragma unroll 8
  for (int i = 0; i < N; ++i) {
    c[i] = a[i] + b[i];  
  }
}


part_4 は part_3 に比べて「#pragma unroll 8」が追加されているだけだ。これで 8 並列 Adder が実装されるのだと思われる。
結果として、計算時間が 1/8 となり、スループットが向上するものと思われる。

part_5_ddr_masters_align.cpp のハードウェア化関数、component void vector_add() コードを引用する。

component void vector_add(ihc::mm_master<int, ihc::aspace<1>, ihc::awidth<32>, ihc::dwidth<256>, ihc::latency<0>, ihc::maxburst<8>, ihc::align<32>, ihc::waitrequest<true> >& a, // bank 1
                          ihc::mm_master<int, ihc::aspace<1>, ihc::awidth<32>, ihc::dwidth<256>, ihc::latency<0>, ihc::maxburst<8>, ihc::align<32>, ihc::waitrequest<true> >& b, // bank 1
                          ihc::mm_master<int, ihc::aspace<2>, ihc::awidth<32>, ihc::dwidth<256>, ihc::latency<0>, ihc::maxburst<8>, ihc::align<32>, ihc::waitrequest<true> >& c, // bank 2
                          int N) {
  #pragma unroll 8
  for (int i = 0; i < N; ++i) {
    c[i] = a[i] + b[i];  
  }
}


part_4 から part_5 では、ihc::align<32> が増えている。これは、32 バイトつまり、256 ビット幅で整列させることで、余計な回路を削減できるということだ。
例えば、4 バイト、32 バイトの整列がずれていると、8 個の int つまり、4 バイトの演算ができて、32 バイト分の書き込むデータが用意できていても、28 バイト分しか書き込むデータが無い事態に陥ることになる。もう 4 バイト分のデータは次の演算を待つ必要があって、その 4 バイトと一緒にメモリに書き込む必要がある。つまり、アラインがあっていないと面倒なことになる。アラインがあっていれば、8 並列Adder の出力をそのままデータバスに載せて書けば良いのである。

さて、build.bat のDEVICE を例によってCyclone V に変更した。
build default を実行した。
実行した結果 part_2_masters.exe の実行結果がすべてエラーになってしまった。(これは、解決策があるとのこと、後でやってみる)
Intel_HLS_133_171203.png
Intel_HLS_134_171203.png

さて、Verilog HDL のトップ・ファイルを見てみよう。

F:\intelFPGA_lite\17.1\hls\examples\tutorials\interfaces\pointer_mm_master\part_1_pointers.prj\components\vector_add フォルダの vector_add_inst.v のポート宣言部分を引用する。

vector_add vector_add_inst (
  // Interface: clock (clock end)
  .clock               ( ), // 1-bit clk input
  // Interface: reset (reset end)
  .resetn              ( ), // 1-bit reset_n input
  // Interface: call (conduit sink)
  .start               ( ), // 1-bit valid input
  .busy                ( ), // 1-bit stall output
  // Interface: return (conduit source)
  .done                ( ), // 1-bit valid output
  .stall               ( ), // 1-bit stall input
  // Interface: a (conduit sink)
  .a                   ( ), // 64-bit data input
  // Interface: b (conduit sink)
  .b                   ( ), // 64-bit data input
  // Interface: c (conduit sink)
  .c                   ( ), // 64-bit data input
  // Interface: N (conduit sink)
  .N                   ( ), // 32-bit data input
  // Interface: avmm_0_rw (avalon start)
  .avmm_0_rw_address   ( ), // 64-bit address output
  .avmm_0_rw_byteenable( ), // 8-bit byteenable output
  .avmm_0_rw_read      ( ), // 1-bit read output
  .avmm_0_rw_readdata  ( ), // 64-bit readdata input
  .avmm_0_rw_write     ( ), // 1-bit write output
  .avmm_0_rw_writedata ( )  // 64-bit writedata output
);


start, busy, done, stall 信号がある。
a, b, c 入力があるけど、下のAvalon-MM Master でRead/Write するから要らない気がする。avm_0_rw_ のAvalon-MM Master ですべてできるしシミュレーションではやっている。

F:\intelFPGA_lite\17.1\hls\examples\tutorials\interfaces\pointer_mm_master\part_2_masters.prj\components\vector_add フォルダの vector_add_inst.v のポート宣言部分を引用する。

vector_add vector_add_inst (
  // Interface: clock (clock end)
  .clock               ( ), // 1-bit clk input
  // Interface: reset (reset end)
  .resetn              ( ), // 1-bit reset_n input
  // Interface: call (conduit sink)
  .start               ( ), // 1-bit valid input
  .busy                ( ), // 1-bit stall output
  // Interface: return (conduit source)
  .done                ( ), // 1-bit valid output
  .stall               ( ), // 1-bit stall input
  // Interface: a (conduit sink)
  .a                   ( ), // 64-bit data input
  // Interface: b (conduit sink)
  .b                   ( ), // 64-bit data input
  // Interface: c (conduit sink)
  .c                   ( ), // 64-bit data input
  // Interface: N (conduit sink)
  .N                   ( ), // 32-bit data input
  // Interface: avmm_1_rw (avalon start)
  .avmm_1_rw_address   ( ), // 16-bit address output
  .avmm_1_rw_byteenable( ), // 4-bit byteenable output
  .avmm_1_rw_read      ( ), // 1-bit read output
  .avmm_1_rw_readdata  ( ), // 32-bit readdata input
  .avmm_1_rw_write     ( ), // 1-bit write output
  .avmm_1_rw_writedata ( ), // 32-bit writedata output
  // Interface: avmm_2_rw (avalon start)
  .avmm_2_rw_address   ( ), // 16-bit address output
  .avmm_2_rw_byteenable( ), // 4-bit byteenable output
  .avmm_2_rw_read      ( ), // 1-bit read output
  .avmm_2_rw_readdata  ( ), // 32-bit readdata input
  .avmm_2_rw_write     ( ), // 1-bit write output
  .avmm_2_rw_writedata ( ), // 32-bit writedata output
  // Interface: avmm_3_rw (avalon start)
  .avmm_3_rw_address   ( ), // 16-bit address output
  .avmm_3_rw_byteenable( ), // 4-bit byteenable output
  .avmm_3_rw_read      ( ), // 1-bit read output
  .avmm_3_rw_readdata  ( ), // 32-bit readdata input
  .avmm_3_rw_write     ( ), // 1-bit write output
  .avmm_3_rw_writedata ( )  // 32-bit writedata output
);


avmm_1_rw、avmm_2_rw、avmm_3_rw の 3 つのAvalon-MM インターフェースが生成されている。

F:\intelFPGA_lite\17.1\hls\examples\tutorials\interfaces\pointer_mm_master\part_3_ddr_masters.prj\components\vector_add フォルダの vector_add_inst.v のポート宣言部分を引用する。

vector_add vector_add_inst (
  // Interface: clock (clock end)
  .clock                  ( ), // 1-bit clk input
  // Interface: reset (reset end)
  .resetn                 ( ), // 1-bit reset_n input
  // Interface: call (conduit sink)
  .start                  ( ), // 1-bit valid input
  .busy                   ( ), // 1-bit stall output
  // Interface: return (conduit source)
  .done                   ( ), // 1-bit valid output
  .stall                  ( ), // 1-bit stall input
  // Interface: a (conduit sink)
  .a                      ( ), // 64-bit data input
  // Interface: b (conduit sink)
  .b                      ( ), // 64-bit data input
  // Interface: c (conduit sink)
  .c                      ( ), // 64-bit data input
  // Interface: N (conduit sink)
  .N                      ( ), // 32-bit data input
  // Interface: avmm_1_rw (avalon start)
  .avmm_1_rw_address      ( ), // 32-bit address output
  .avmm_1_rw_byteenable   ( ), // 32-bit byteenable output
  .avmm_1_rw_readdatavalid( ), // 1-bit readdatavalid input
  .avmm_1_rw_read         ( ), // 1-bit read output
  .avmm_1_rw_readdata     ( ), // 256-bit readdata input
  .avmm_1_rw_write        ( ), // 1-bit write output
  .avmm_1_rw_writedata    ( ), // 256-bit writedata output
  .avmm_1_rw_waitrequest  ( ), // 1-bit waitrequest input
  .avmm_1_rw_burstcount   ( ), // 4-bit burstcount output
  // Interface: avmm_2_rw (avalon start)
  .avmm_2_rw_address      ( ), // 32-bit address output
  .avmm_2_rw_byteenable   ( ), // 32-bit byteenable output
  .avmm_2_rw_readdatavalid( ), // 1-bit readdatavalid input
  .avmm_2_rw_read         ( ), // 1-bit read output
  .avmm_2_rw_readdata     ( ), // 256-bit readdata input
  .avmm_2_rw_write        ( ), // 1-bit write output
  .avmm_2_rw_writedata    ( ), // 256-bit writedata output
  .avmm_2_rw_waitrequest  ( ), // 1-bit waitrequest input
  .avmm_2_rw_burstcount   ( )  // 4-bit burstcount output
);


part_2 からでは、waitrequest と burstcount 信号が増えている。それに、avmm_3_rw が削除された。avmm_1_rw を a と b のRead で共有する。

F:\intelFPGA_lite\17.1\hls\examples\tutorials\interfaces\pointer_mm_master\part_4_ddr_masters_coalesce.prj\components\vector_add フォルダの vector_add_inst.v のポート宣言部分は、part_3 との変化はない。

F:\intelFPGA_lite\17.1\hls\examples\tutorials\interfaces\pointer_mm_master\part_5_ddr_masters_align.prj\components\vector_add フォルダの vector_add_inst.v のポート宣言部分は、part_3 、 part_4 との変化はない。
  1. 2017年12月03日 17:06 |
  2. intel HLS
  3. | トラックバック:0
  4. | コメント:0

intel HLS コンパイラを試してみる14(explicit_streams_buffer その1)

intel HLS コンパイラを試してみる13(mm_slave その3)”の続き。

今回は、examples\tutorials\interfaces\explicit_streams_buffer を試してみよう。
これは、”intel HLS コンパイラを試してみる7(image_downsampleを試す1)”でもやったAvalon Streaming インターフェースだが、ihc::buffer<>でストリーム入力が宣言されている。

image_inversion.cpp だがハードウェア化関数は、img_invert1() と img_invert_buff() がある。
二つの関数の宣言部分を引用する。

component void img_invert(int W,
              int H,
              ihc::stream_in<unsigned char>  &a,
              ihc::stream_out<unsigned char> &b) {


component void img_invert_buff(int W,
                           int H,
                           ihc::stream_in<unsigned char, ihc::buffer<64> > &a,
                           ihc::stream_out<unsigned char> &b) {


上に示すように、 img_invert_buff() の方の入力が、ihc::stream_in で定義されている。それでも同様に a.read() でストリームのデータを読むことができる。これは、ストリームのバッファの容量を指定する方法のようだ。

build.bat のDEVICE を CycloneV に変更した。
Intel_HLS_127_171127.png

build default を実行した。
Intel_HLS_128_171127.png

tutorial-msvc.exe、tutorial-x86.exe、tutorial-fpga.exe すべて PASSED だった。

F:\intelFPGA_lite\17.1\hls\examples\tutorials\interfaces\explicit_streams_buffer フォルダを示す。
tutorial-fpga.prj フォルダができていた。
Intel_HLS_129_171127.png

さて、ModelSimで波形を見てみよう。
全体波形を示す。
Intel_HLS_130_171127.png

上の背景が白の信号名が、img_invert1() の信号で、その下の信号が img_invert_buff() 用の信号だ。

img_invert1() でデータ転送している部分を拡大する。
Intel_HLS_131_171127.png

入力のa_ready, a_valid、出力の b_ready, b_valid が 1 で転送が連続していることを示している。

次に、 img_invert_buff() で転送している部分を示す。
Intel_HLS_132_171127.png

こちらも同様に、入力のa_ready, a_valid、出力の b_ready, b_valid が 1 で転送が連続している。
  1. 2017年11月27日 05:12 |
  2. intel HLS
  3. | トラックバック:0
  4. | コメント:0

intel HLS コンパイラを試してみる13(mm_slave その3)

intel HLS コンパイラを試してみる12(mm_slave その2)”の続き。

前回は、シミュレーション波形を比較したが、今回はレポートでリソース使用量などを比較してみよう。

まずは、part_1_basic_component のSummary を示す。
Intel_HLS_119_171125.png

ALUs が 2026 個、FFs が 2223 個、RAMs が 12 個使用している。

次に、part_2_slave_component のSummary を示す。
Intel_HLS_120_171125.png

ALUs が 2026 個、FFs が 2223 個、RAMs が 12 個使用していて、part_1_basic_component と全く同じだ。

part_3_slave_register_arguments のSummary を示す。
Intel_HLS_121_171125.png

ALUs が 2026 個、FFs が 2223 個、RAMs が 12 個使用していて、part_1_basic_component 、part_2_slave_component と全く一緒だ。なぜ、回路が違うので、リソース使用量が同じなんだろう?

part_4_slave_mem_args のSummary を示す。
Intel_HLS_122_171125.png

ALUs が 511 個、FFs が 377 個、RAMs が 16 個使用している。さすがにスレーブばかりのインターフェースではリソース使用量が少ない。ただし、RAMs は 16 個で増えている。

part_1_basic_component のComponent viewer 画面を示す。
Intel_HLS_123_171125.png

do, N, x がすべて外に出ている。

part_2_slave_component のComponent viewer 画面を示す。
Intel_HLS_124_171125.png

do だけ CSR として component swp_int_end の中に入っている。

part_3_slave_register_arguments のComponent viewer 画面を示す。
Intel_HLS_125_171125.png

do, N, x ともに CSR として component swp_int_end の中に入っている。

part_4_slave_mem_args のComponent viewer 画面を示す。
Intel_HLS_126_171125.png

x が無くなっていて、N と do がCSR として、 component swp_int_end の中に入っている。やはり、スレーブなので、全く違うな。。。
  1. 2017年11月25日 04:34 |
  2. intel HLS
  3. | トラックバック:0
  4. | コメント:0

intel HLS コンパイラを試してみる12(mm_slave その2)

intel HLS コンパイラを試してみる11(mm_slave その1)”の続き。

前回は、4つのAvalon Slave インターフェース(最初の1つはAvalon Master だが)を i++ でコンパイルした。今回は、ModelSimでシミュレーション波形を見ていこう。
今回も intel HLS のチュートリアルの F:\intelFPGA_lite\17.1\hls\examples\tutorials\interfaces\mm_slave を題材にする。

part_1_basic_component
最初に、part_1_basic_component.cpp のシミュレーション波形を見ていく。
シミュレーション波形全体を下に貼る。
Intel_HLS_101_171123.png

1つのRead 区間とWrite 区間は 260000 ps (260 ns) でクロックが 1000 ps なので、260 クロック分あるが、Read で見ると 2 クロックで1転送で、転送数は 130 個、ビットレーンごとに 4 回繰り返しているので、130/2 = 32.5 個だが、中途半端な数なので、たぶん32個ずつ処理していて、4クロック分は余計に必要なんだと思う。

次にStart 信号を見てみよう。
Intel_HLS_102_171123.png

Start 信号が 1 クロック分出て、その後に、Read 転送が入っているのが見える。Avalon-MM の Read 転送は”Avalon® Interface Specifications MNL-AVABUSREF 2017.05.08”の 23 ページの”Figure 7. Read and Write Transfers with Waitrequest”を見ると、read が 1 の時の最初のクロックでアドレスを認識して、次のクロックでRead データを受け取るようだ。
Read は同じアドレスを4 回 Read しているようだ。

Write は 1 クロックでWrite できるようだ。バイト・レーンの変換で、1バイトごとに書いているが、最初にバイト・レーン・イネーブルが 08 と 80 でアドレスを大きくしてデータを書いていって、次は、04 と 40 で、その次は01 と 10 で、最後に 02 と 20 でデータを書いていく。
Intel_HLS_103_171123.png

part_1_basic_component.cpp では、同じアドレスの int 型の変数から 4 バイト・レーン読んで、4 バイト・レーン書いているので、C の記述とハードウェア化された動作が違っているが、最適化のためだろう?と思う。

最後に done の波形を貼っておく。
Intel_HLS_104_171123.png

part_2_slave_component
次に、part_2_slave_component.cpp のシミュレーション波形を見てみよう。
最初に全体の波形を示す。
Intel_HLS_105_171123.png

Start 信号や busy, done, stall などの信号は無くなっている。その代わりに、avs_xra_... の信号が追加されていて、スタートを支持したり、エンドを知ることができる。

アドレスマップは、F:\intelFPGA_lite\17.1\hls\examples\tutorials\interfaces\mm_slaves\part_2_slave_component.prj\components\swp_int_end フォルダのswp_int_end_csr.h ファイルにあった。swp_int_end_csr.h の一部を引用する。
Intel_HLS_108_171123.png

最初のスタートを見てみよう。
Intel_HLS_106_171123.png

最初に、avs_xra_... の信号で、アドレスの2番地、3番地、1番地の最下位バイトに 1 を書いている。
2番地が 0x10 で、interrupt_enable[0:0] に 1 を書いて、Enable interrupt として、3番地が 0x18 で、interrupt_status is write 1 to clear なので、interrupt_status をクリアしているようだ。そして 1 番地が 0x8 で、 start[0:0] を 1 にしているようだ。

最後に、avs_xra_... の信号で、アドレスの3番地から 3 を読みだしている。これは、0x18 で、done[0:0] と interrupt_status[0:0] が共に 1 になっている。
Intel_HLS_107_171123.png

part_3_slave_register_arguments
part_3_slave_register_arguments.cpp のシミュレーション波形を見ていく。
シミュレーション波形全体を下に貼る。 x と N が無くなっている。これはレジスタにマップされている。swp_int_end_csr.h の一部を引用する。
Intel_HLS_110_171123.png

最初の設定部分を拡大する。レジスタへの設定が表示されている。
Intel_HLS_111_171123.png

レジスタマップを示す。 x と N がレジスタにマップされているのが分かる。swp_int_end_csr.h の一部を引用する。
Intel_HLS_109_171123.png

Avalon Slave へのWrite が見えるが、
最初は2番地で、0x10 の interrupt_enable[0:0] を 1 にしているようだ。
次の 3 番地は 0x18 で、interrupt_status[0:0] を 1 にしている。
4 番地は 0x20 で x[63:0] に 0x71b810 を書いている。
5 番地は 0x28 で、N[31:0] に 0x1000 を書いている。
1番地は 0x8 で、start[0:0] に 1 を書いて、スタートしている。

波形の最後は、、avs_xra_... の信号で、アドレスの3番地から 3 を読みだしている。これは、0x18 で、done[0:0] と interrupt_status[0:0] が共に 1 になっている。
Intel_HLS_112_171123.png

part_4_slave_mem_args
最後のpart_4_slave_mem_args.cpp のシミュレーション波形を見ていこう。
シミュレーション波形全体を示す。他のと違っている。すべてスレーブだからか?
Intel_HLS_113_171123.png

最初にスタート信号は無い。まずはスレーブなので、swp_int_end のメモリにデータをバイトごとに書いているのだろう?すべて受け身になるので、こうする必要がありそうだ。
Intel_HLS_114_171123.png

データを書いた後でスタートするようだ。スレーブ・インターフェースしかないため、自分で読むことができないので、書かれてからスタートするしかないのだと思う。
Intel_HLS_115_171123.png

アドレスマップを示す。 x はスレーブで書かれるので必要ないようだ。N だけレジスタマップされている。swp_int_end_csr.h の一部を引用する。
Intel_HLS_117_171123.png

最初は2番地で、0x10 の interrupt_enable[0:0] を 1 にしているようだ。
次の 3 番地は 0x18 で、interrupt_status[0:0] を 1 にしている。
4 番地は 0x20 で、N[31:0] に 0x1000 を書いている。
1番地は 0x8 で、start[0:0] に 1 を書いて、スタートしている。

スタートは良いのだが、結果もマスタが読んでくる必要があるようだ。つまり、何もアクセスが無い時間はスタートして処理を行っている時間ということだ。処理時間は、4.111 us だった。
Intel_HLS_118_171123.png

スタートしてから、4.111 us 後には 3 番地、0x18 をRead すると 3 が出て、、done[0:0] と interrupt_status[0:0] が共に 1 になっているのが分かる。
Intel_HLS_116_171123.png

Read もバイトごとにRead している。バイト・イネーブルが1バイトごとにイネーブルになっていく。

これで、4 つのパターンの特徴が分かった。自分で一番使うとしたら、part_3_slave_register_arguments だろうかな?
  1. 2017年11月23日 08:15 |
  2. intel HLS
  3. | トラックバック:0
  4. | コメント:0

intel HLS コンパイラを試してみる11(mm_slave その1)

intel HLS コンパイラを試してみる10(examples\tutorials\interfaces\mm_master_testbench_operators その2)”の続き。

前回は、examples\tutorials\interfaces\mm_master_testbench_operators をやってみたが、今回は、intel HLS のチュートリアルの F:\intelFPGA_lite\17.1\hls\examples\tutorials\interfaces\mm_slave をやってみることにする。

mm_slave は、intel HLS によるAvalon-MM Slave インターフェース・モジュールのいろいろな実装をテストすることができるサンプルだ。これを見れば、intel HLS によるAvalon-MM Slave インターフェースの実装の仕方が分かるのではないか?と思っている。

mm_slave にはpart_1_basic_component.cpp、part_2_slave_component.cpp、part_3_slave_register_arguments.cpp、part_4_slave_mem_args.cpp の4つのC++ ソースファイルがある。
READMEに説明が書いてある。README の説明に従ってやってみることにする。

part_1_basic_component.cpp はintel HLS の通常の Start , busy, done などがある intel HLS のデフォルトのインターフェースになる気がする。
component void swp_int_end() の動作としては、32ビット幅のデータの8ビットごとの入れ替えで、エンディアンを変換する関数のようだ。

component void swp_int_end (int* x,
                            int N ) {


Intel_HLS_94_171121.png
intelFPGA_lite\17.1\hls\examples\tutorials\interfaces\mm_slave のpart_1_basic_component.cpp を引用する。

part_2_slave_component.cpp は、 Avalon-MMスレーブインタフェースとして実装されるようだ。(hls_avalon_slave_component)

hls_avalon_slave_component
component void swp_int_end (int* x, 
                            int N ) {


Intel_HLS_95_171121.png
intelFPGA_lite\17.1\hls\examples\tutorials\interfaces\mm_slave のpart_2_slave_component.cppp を引用する。

part_3_slave_register_arguments.cpp は、コンポーネントの引数をコンポーネントのAvalon-MMスレーブインタフェースのメモリマップに追加しているそうだ。コンポーネントの引数のそれぞれがスレーブレジスタとして指定されているということだ。(hls_avalon_slave_register_argument)

hls_avalon_slave_component
component void swp_int_end (hls_avalon_slave_register_argument int* x,
                            hls_avalon_slave_register_argument int N ) {


Intel_HLS_96_171121.png
intelFPGA_lite\17.1\hls\examples\tutorials\interfaces\mm_slave の part_3_slave_register_arguments.cpp を引用する。

part_4_slave_mem_args.cpp は、ポインタ(Avalon MM-Master)インタフェースを、M20Kを使用した整数配列を格納するスレーブ・メモリに置き換えるそうだ。x のみ hls_avalon_slave_memory_argument() が使用されている。x はAvalon-MM マスタ・インターフェースになるのかもしれない?

hls_avalon_slave_component
component void swp_int_end (hls_avalon_slave_memory_argument(BUFFER_SIZE*sizeof(int)) int* x,
                            hls_avalon_slave_register_argument int N) {


Intel_HLS_97_171121.png
intelFPGA_lite\17.1\hls\examples\tutorials\interfaces\mm_slave の part_4_slave_mem_args.cpp を引用する。

build.bat を引用する。14行目をArria10 からCyclone Vに変更した。
Intel_HLS_98_171121.png

build.bat を起動した。
Intel_HLS_99_171121.png

part_1_basic_component.cpp、part_2_slave_component.cpp、part_3_slave_register_arguments.cpp、part_4_slave_mem_args.cpp の4つともPASSED になった。

F:\intelFPGA_lite\17.1\hls\examples\tutorials\interfaces\mm_slaves フォルダの内容を示す。
Intel_HLS_100_171121.png

F:\intelFPGA_lite\17.1\hls\examples\tutorials\interfaces\mm_slaves\part_1_basic_component.prj\components\swp_int_end フォルダのswp_int_end_inst.v の内容の一部を示す。

swp_int_end swp_int_end_inst (
  // Interface: clock (clock end)
  .clock               ( ), // 1-bit clk input
  // Interface: reset (reset end)
  .resetn              ( ), // 1-bit reset_n input
  // Interface: call (conduit sink)
  .start               ( ), // 1-bit valid input
  .busy                ( ), // 1-bit stall output
  // Interface: return (conduit source)
  .done                ( ), // 1-bit valid output
  .stall               ( ), // 1-bit stall input
  // Interface: x (conduit sink)
  .x                   ( ), // 64-bit data input
  // Interface: N (conduit sink)
  .N                   ( ), // 32-bit data input
  // Interface: avmm_0_rw (avalon start)
  .avmm_0_rw_address   ( ), // 64-bit address output
  .avmm_0_rw_byteenable( ), // 8-bit byteenable output
  .avmm_0_rw_read      ( ), // 1-bit read output
  .avmm_0_rw_readdata  ( ), // 64-bit readdata input
  .avmm_0_rw_write     ( ), // 1-bit write output
  .avmm_0_rw_writedata ( )  // 64-bit writedata output
);


Start などのブロックレベルの信号とメモリ・インターフェースがある。

F:\intelFPGA_lite\17.1\hls\examples\tutorials\interfaces\mm_slaves\part_2_slave_component.prj\components\swp_int_end フォルダのswp_int_end_inst.v の内容の一部を示す。

swp_int_end swp_int_end_inst (
  // Interface: clock (clock end)
  .clock               ( ), // 1-bit clk input
  // Interface: reset (reset end)
  .resetn              ( ), // 1-bit reset_n input
  // Interface: irq (interrupt end)
  .done_irq            ( ), // 1-bit irq output
  // Interface: x (conduit sink)
  .x                   ( ), // 64-bit data input
  // Interface: N (conduit sink)
  .N                   ( ), // 32-bit data input
  // Interface: avmm_0_rw (avalon start)
  .avmm_0_rw_address   ( ), // 64-bit address output
  .avmm_0_rw_byteenable( ), // 8-bit byteenable output
  .avmm_0_rw_read      ( ), // 1-bit read output
  .avmm_0_rw_readdata  ( ), // 64-bit readdata input
  .avmm_0_rw_write     ( ), // 1-bit write output
  .avmm_0_rw_writedata ( ), // 64-bit writedata output
  // Interface: avs_cra (avalon end)
  .avs_cra_read        ( ), // 1-bit read input
  .avs_cra_write       ( ), // 1-bit write input
  .avs_cra_address     ( ), // 2-bit address input
  .avs_cra_writedata   ( ), // 64-bit writedata input
  .avs_cra_byteenable  ( ), // 8-bit byteenable input
  .avs_cra_readdata    ( )  // 64-bit readdata output
);


start, busy, done, stall などの信号が消えて、avs_cra_.... のAvalon Slave の信号が増えた。

F:\intelFPGA_lite\17.1\hls\examples\tutorials\interfaces\mm_slaves\part_3_slave_register_arguments.prj\components\swp_int_end フォルダのswp_int_end_inst.v の内容の一部を示す。

swp_int_end swp_int_end_inst (
  // Interface: clock (clock end)
  .clock               ( ), // 1-bit clk input
  // Interface: reset (reset end)
  .resetn              ( ), // 1-bit reset_n input
  // Interface: irq (interrupt end)
  .done_irq            ( ), // 1-bit irq output
  // Interface: avmm_0_rw (avalon start)
  .avmm_0_rw_address   ( ), // 64-bit address output
  .avmm_0_rw_byteenable( ), // 8-bit byteenable output
  .avmm_0_rw_read      ( ), // 1-bit read output
  .avmm_0_rw_readdata  ( ), // 64-bit readdata input
  .avmm_0_rw_write     ( ), // 1-bit write output
  .avmm_0_rw_writedata ( ), // 64-bit writedata output
  // Interface: avs_cra (avalon end)
  .avs_cra_read        ( ), // 1-bit read input
  .avs_cra_write       ( ), // 1-bit write input
  .avs_cra_address     ( ), // 3-bit address input
  .avs_cra_writedata   ( ), // 64-bit writedata input
  .avs_cra_byteenable  ( ), // 8-bit byteenable input
  .avs_cra_readdata    ( )  // 64-bit readdata output
);


part_2_slave_component.prj とひかくすると、x と N が消えて、.avs_cra_address が 2 bit から 3 bit になった。つまり、x と N もレジスタにマップされたようだ。

F:\intelFPGA_lite\17.1\hls\examples\tutorials\interfaces\mm_slaves\part_4_slave_mem_args.prj\components\swp_int_end フォルダのswp_int_end_inst.v の内容の一部を示す。

swp_int_end swp_int_end_inst (
  // Interface: clock (clock end)
  .clock             ( ), // 1-bit clk input
  // Interface: reset (reset end)
  .resetn            ( ), // 1-bit reset_n input
  // Interface: irq (interrupt end)
  .done_irq          ( ), // 1-bit irq output
  // Interface: avs_cra (avalon end)
  .avs_cra_read      ( ), // 1-bit read input
  .avs_cra_write     ( ), // 1-bit write input
  .avs_cra_address   ( ), // 3-bit address input
  .avs_cra_writedata ( ), // 64-bit writedata input
  .avs_cra_byteenable( ), // 8-bit byteenable input
  .avs_cra_readdata  ( ), // 64-bit readdata output
  // Interface: avs_x (avalon end)
  .avs_x_read        ( ), // 1-bit read input
  .avs_x_write       ( ), // 1-bit write input
  .avs_x_address     ( ), // 12-bit address input
  .avs_x_writedata   ( ), // 32-bit writedata input
  .avs_x_byteenable  ( ), // 4-bit byteenable input
  .avs_x_readdata    ( )  // 32-bit readdata output
);


part_3_slave_register_arguments.prj と比較すると、avmm_0_rw_.... のAvalon MM-Master インターフェースが avs_x_..... のAvalon-MM slave インターフェースに置き換えられている。
  1. 2017年11月21日 05:25 |
  2. intel HLS
  3. | トラックバック:0
  4. | コメント:0

intel HLS コンパイラを試してみる10(examples\tutorials\interfaces\mm_master_testbench_operators その2)

intel HLS コンパイラを試してみる9(examples\tutorials\interfaces\mm_master_testbench_operators)”の続き。

前回は、Avalon MM インターフェースのチュートリアルをやってみて、ModelSim で波形を観測した。今回は、レポートとQuartus Prime を動かしてコンパイルしてみた。

まずは、レポートを貼っておく。

Summary
Intel_HLS_85_171119.png

Loops analysis
Intel_HLS_86_171119.png

Area analysis of system
Intel_HLS_87_171119.png

Area analysis of source
Intel_HLS_88_171119.png

Component Viewer
Intel_HLS_89_171119.png

Component memory viewer
Intel_HLS_90_171119.png

Verification statistics Latency は min = 24, max = 89, avg = 68 だった。
Intel_HLS_91_171119.png

Quartus Prime 17.1 を立ち上げた。
F:\intelFPGA_lite\17.1\hls\examples\tutorials\interfaces\mm_master_testbench_operators\operators.prj\quartus の quartus_compile.qpf を読み込んだ。
コンパイルを実行した。
Intel_HLS_92_171119.png

Entity の quartus_compile の下の、add_x をダブルクリックして開くと、IP のシンボルを見ることができた。
Intel_HLS_93_171119.png
  1. 2017年11月19日 04:51 |
  2. intel HLS
  3. | トラックバック:0
  4. | コメント:0
»