”
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.cpppart_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 の全体波形を示す。

done が出るまでの時間は約 17.03 us だった。
1つのトランザクションが見えるように拡大波形を示す。

それぞれ 1 回ずつReadが 2 回、Write が 1 回のアクセルがある。 1 回の転送の周期は、17 ns だった。これを 1000 回やっているので、17 us となり計算は合う。
part_2_masters.cpppart_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 の全体波形を示す。

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

この実装では、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.cpppart_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,
ihc::mm_master<int, ihc::aspace<1>, ihc::awidth<32>, ihc::dwidth<256>, ihc::latency<0>, ihc::maxburst<8>, ihc::waitrequest<true> >& b,
ihc::mm_master<int, ihc::aspace<2>, ihc::awidth<32>, ihc::dwidth<256>, ihc::latency<0>, ihc::maxburst<8>, ihc::waitrequest<true> >& c,
int N) {
for (int i = 0; i < N; ++i) {
c[i] = a[i] + b[i];
}
}
データバスの幅が 256 ビットになって、Read が 1 つのバス、Write も 1 つのバスの合計 2 つのバスになった。
ModelSim の全体波形を示す。

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

Write の幅を見ると 8 ns になっているのが分かる。これは、32ビット幅の演算器しか持っていないので、256 ビット幅のデータを満たすには、 256 / 32 = 8 クロック分(1クロックは 1 ns)かかるためだと思われる。
part_4_ddr_masters_coalesce.cpppart_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,
ihc::mm_master<int, ihc::aspace<1>, ihc::awidth<32>, ihc::dwidth<256>, ihc::latency<0>, ihc::maxburst<8>, ihc::waitrequest<true> >& b,
ihc::mm_master<int, ihc::aspace<2>, ihc::awidth<32>, ihc::dwidth<256>, ihc::latency<0>, ihc::maxburst<8>, ihc::waitrequest<true> >& c,
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 の全体波形を示す。

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

Read のアドレスが飛んでいるので、a[i] をRead しているアドレスと b[i] をRead しているアドレスがあると考えられる。よって、Write の転送は、半分のスループットになっているのが分かる。つまり、2 つデータを読んで 1 つデータを書いているわけだ。
part_5_ddr_masters_align.cpppart_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,
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,
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,
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 の全体波形を示す。

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

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> を追加している。

ModelSim の波形を示す。

これは、ihc::latency<0> を付加する前に比べてスループットが下がっている。以前は、done が出るまでに時間は約 1.04 ms だったが、今回は、約 1.33 ms になった。
拡大してみた。
- 2017年12月08日 05:13 |
- intel HLS
-
| トラックバック:0
-
| コメント:0