”
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 の全体波形を示す。
done が出るまでの時間は約 17.03 us だった。
1つのトランザクションが見えるように拡大波形を示す。
それぞれ 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 の全体波形を示す。
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.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, 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.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, 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.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, 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
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, 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]; } }
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, 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 は 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, 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_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 の実行結果がすべてエラーになってしまった。(これは、解決策があるとのこと、後でやってみる) さて、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 との変化はない。
2017年12月03日 17:06 |
intel HLS
| トラックバック:0
| コメント:0
”
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 に変更した。build default を実行した。 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 フォルダができていた。 さて、ModelSimで波形を見てみよう。 全体波形を示す。 上の背景が白の信号名が、img_invert1() の信号で、その下の信号が img_invert_buff() 用の信号だ。 img_invert1() でデータ転送している部分を拡大する。 入力のa_ready, a_valid、出力の b_ready, b_valid が 1 で転送が連続していることを示している。 次に、 img_invert_buff() で転送している部分を示す。 こちらも同様に、入力のa_ready, a_valid、出力の b_ready, b_valid が 1 で転送が連続している。
2017年11月27日 05:12 |
intel HLS
| トラックバック:0
| コメント:0
”
intel HLS コンパイラを試してみる12(mm_slave その2) ”の続き。
前回は、シミュレーション波形を比較したが、今回はレポートでリソース使用量などを比較してみよう。
まずは、part_1_basic_component のSummary を示す。
ALUs が 2026 個、FFs が 2223 個、RAMs が 12 個使用している。
次に、part_2_slave_component のSummary を示す。
ALUs が 2026 個、FFs が 2223 個、RAMs が 12 個使用していて、part_1_basic_component と全く同じだ。
part_3_slave_register_arguments のSummary を示す。
ALUs が 2026 個、FFs が 2223 個、RAMs が 12 個使用していて、part_1_basic_component 、part_2_slave_component と全く一緒だ。なぜ、回路が違うので、リソース使用量が同じなんだろう?
part_4_slave_mem_args のSummary を示す。
ALUs が 511 個、FFs が 377 個、RAMs が 16 個使用している。さすがにスレーブばかりのインターフェースではリソース使用量が少ない。ただし、RAMs は 16 個で増えている。
part_1_basic_component のComponent viewer 画面を示す。
do, N, x がすべて外に出ている。
part_2_slave_component のComponent viewer 画面を示す。
do だけ CSR として component swp_int_end の中に入っている。
part_3_slave_register_arguments のComponent viewer 画面を示す。
do, N, x ともに CSR として component swp_int_end の中に入っている。
part_4_slave_mem_args のComponent viewer 画面を示す。
x が無くなっていて、N と do がCSR として、 component swp_int_end の中に入っている。やはり、スレーブなので、全く違うな。。。
2017年11月25日 04:34 |
intel HLS
| トラックバック:0
| コメント:0
”
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 のシミュレーション波形を見ていく。
シミュレーション波形全体を下に貼る。
1つのRead 区間とWrite 区間は 260000 ps (260 ns) でクロックが 1000 ps なので、260 クロック分あるが、Read で見ると 2 クロックで1転送で、転送数は 130 個、ビットレーンごとに 4 回繰り返しているので、130/2 = 32.5 個だが、中途半端な数なので、たぶん32個ずつ処理していて、4クロック分は余計に必要なんだと思う。
次にStart 信号を見てみよう。
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 でデータを書いていく。
part_1_basic_component.cpp では、同じアドレスの int 型の変数から 4 バイト・レーン読んで、4 バイト・レーン書いているので、C の記述とハードウェア化された動作が違っているが、最適化のためだろう?と思う。
最後に done の波形を貼っておく。
part_2_slave_component 次に、part_2_slave_component.cpp のシミュレーション波形を見てみよう。
最初に全体の波形を示す。
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 の一部を引用する。
最初のスタートを見てみよう。
最初に、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 になっている。
part_3_slave_register_arguments part_3_slave_register_arguments.cpp のシミュレーション波形を見ていく。
シミュレーション波形全体を下に貼る。 x と N が無くなっている。これはレジスタにマップされている。swp_int_end_csr.h の一部を引用する。
最初の設定部分を拡大する。レジスタへの設定が表示されている。
レジスタマップを示す。 x と N がレジスタにマップされているのが分かる。swp_int_end_csr.h の一部を引用する。
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 になっている。
part_4_slave_mem_args 最後のpart_4_slave_mem_args.cpp のシミュレーション波形を見ていこう。
シミュレーション波形全体を示す。他のと違っている。すべてスレーブだからか?
最初にスタート信号は無い。まずはスレーブなので、swp_int_end のメモリにデータをバイトごとに書いているのだろう?すべて受け身になるので、こうする必要がありそうだ。
データを書いた後でスタートするようだ。スレーブ・インターフェースしかないため、自分で読むことができないので、書かれてからスタートするしかないのだと思う。
アドレスマップを示す。 x はスレーブで書かれるので必要ないようだ。N だけレジスタマップされている。swp_int_end_csr.h の一部を引用する。
最初は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 だった。
スタートしてから、4.111 us 後には 3 番地、0x18 をRead すると 3 が出て、、done[0:0] と interrupt_status[0:0] が共に 1 になっているのが分かる。
Read もバイトごとにRead している。バイト・イネーブルが1バイトごとにイネーブルになっていく。
これで、4 つのパターンの特徴が分かった。自分で一番使うとしたら、part_3_slave_register_arguments だろうかな?
2017年11月23日 08:15 |
intel HLS
| トラックバック:0
| コメント:0
”
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 ) {
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 ) {
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 ) {
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) {
intelFPGA_lite\17.1\hls\examples\tutorials\interfaces\mm_slave の part_4_slave_mem_args.cpp を引用する。
build.bat を引用する。14行目をArria10 からCyclone Vに変更した。
build.bat を起動した。
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 フォルダの内容を示す。
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 ( .clock ( ), .resetn ( ), .done_irq ( ), .avs_cra_read ( ), .avs_cra_write ( ), .avs_cra_address ( ), .avs_cra_writedata ( ), .avs_cra_byteenable( ), .avs_cra_readdata ( ), .avs_x_read ( ), .avs_x_write ( ), .avs_x_address ( ), .avs_x_writedata ( ), .avs_x_byteenable ( ), .avs_x_readdata ( ) );
part_3_slave_register_arguments.prj と比較すると、avmm_0_rw_.... のAvalon MM-Master インターフェースが avs_x_..... のAvalon-MM slave インターフェースに置き換えられている。
2017年11月21日 05:25 |
intel HLS
| トラックバック:0
| コメント:0
”
intel HLS コンパイラを試してみる9(examples\tutorials\interfaces\mm_master_testbench_operators) ”の続き。
前回は、Avalon MM インターフェースのチュートリアルをやってみて、ModelSim で波形を観測した。今回は、レポートとQuartus Prime を動かしてコンパイルしてみた。
まずは、レポートを貼っておく。
Summary
Loops analysis
Area analysis of system
Area analysis of source
Component Viewer
Component memory viewer
Verification statistics Latency は min = 24, max = 89, avg = 68 だった。
Quartus Prime 17.1 を立ち上げた。
F:\intelFPGA_lite\17.1\hls\examples\tutorials\interfaces\mm_master_testbench_operators\operators.prj\quartus の quartus_compile.qpf を読み込んだ。
コンパイルを実行した。
Entity の quartus_compile の下の、add_x をダブルクリックして開くと、IP のシンボルを見ることができた。
2017年11月19日 04:51 |
intel HLS
| トラックバック:0
| コメント:0