Vivado HLS を使ってみた。第一回まとめ [FPGA]
今回のRGB→YCbCr変換、最適化はもっとかけられます。変換に3x3のマトリクス演算を行っていますが、ホントは必要ありません。Yの係数を全部足すと1.0になるという条件と、(B-Y)、(R-Y)の形に式を変えると、乗算器は4つで済みます。実際XilinxのIPなどはそうやって作られてます。IPの資料を見ると詳しく載ってます。C側の計算を変える事になりますが、HDLでがんばるより楽でしょう。
実際に使えるようなIPを作ることを目的にしていないので、一番シンプルな計算式を持ってきています。
ということで、簡単なプログラムを対象に、C to HDLをやってみました。
…やべぇ…前からかかってた「HDLなんかもう一行も書きたくないよ~」病に拍車をかける結果となりました…
実はAlteraのC2Hを使った時も同じ事を思ったんですが(笑)
最近(Qsysになってから)C2Hのアップデートが聞こえてきませんね?どうしたのかな?
今回の例は簡単なのでHDLで書いてもたかがしれてますが、ちょっと複雑な処理が入ったらもうHDLなんかやりたくないです。
本当はもっと色々なディレクティブが必要になります、I/Oにも設定が必要です、使えるCの構文にも制限があります、純粋にソフトウェア的な書き方だけでは最適化は難しいかも、思った通りの回路にはなかなかならない場合もある、などなど考慮する点はたくさんあります。
でもCで記述して、誤差とか精度とか詰めて、ソースの最適化かけたときもCのシミュレーションで確認、シミュレーションでOKだったらディレクティブとかいじって最適化、HDLを全く見ずにハードウェア完成。コンパイルも実行時間もHDLより遙かに速いので効率も良いです。これは癖になります。
HLSの新しいバージョンではAXI関連が有効になってます。今回のもAXI Streamにするとほかとの接続が良くなります。XPSへのエクスポートもあるので、Cで作ったモジュールを自動でZynqのAXIチャンネルに接続することもできます。ここでもHDLを排除できます。
AlteraもQsys、SOPC Builderで同様なことが可能です。というかこの辺昔からAlteraの方が強いです。AXIではなくAvalonという独自の接続形態でしたが、非常に便利です。やっとXilinxも追いついてきたという感じがします。
AXI対応はXilinxの方が先行ですかね。AXIは一種の流行なので、特にこだわる理由は無いと思いますが。
なにも書かずにものができあがる、AlteraもXilinxも、多分ほかのベンダーも、みんな目指してるところでしょう。
今はその中間解として、システム自動作成ツール(Qsys、XPS、+IP群)+C系合成ツール(C2H、HLS)+CPU(ARM、Nios、MB)+モデル設計ツール(DSP Builder、SystGen)というところに落ち着いてる感じでしょうか。バラバラなのが難点ですが適材適所で行くしか無いのが現状でしょう。もちろんHDLで書いた方が効率よい部分もあるので、しばらくはごちゃ混ぜ状態が続きそうです。
それにしてもやばい。結構できが良いので本気でHDL書きたくない気分になってます…ライセンス約50万でしたっけ…?う~む…
まぁそんなこと言いながらHDLは書くんですけどね(笑)
※DigiKeyとかで見てみたら、HLS単体だとFloatingでも25万ぐらいでした…うぅう~むぅ…
実際に使えるようなIPを作ることを目的にしていないので、一番シンプルな計算式を持ってきています。
ということで、簡単なプログラムを対象に、C to HDLをやってみました。
…やべぇ…前からかかってた「HDLなんかもう一行も書きたくないよ~」病に拍車をかける結果となりました…
実はAlteraのC2Hを使った時も同じ事を思ったんですが(笑)
最近(Qsysになってから)C2Hのアップデートが聞こえてきませんね?どうしたのかな?
今回の例は簡単なのでHDLで書いてもたかがしれてますが、ちょっと複雑な処理が入ったらもうHDLなんかやりたくないです。
本当はもっと色々なディレクティブが必要になります、I/Oにも設定が必要です、使えるCの構文にも制限があります、純粋にソフトウェア的な書き方だけでは最適化は難しいかも、思った通りの回路にはなかなかならない場合もある、などなど考慮する点はたくさんあります。
でもCで記述して、誤差とか精度とか詰めて、ソースの最適化かけたときもCのシミュレーションで確認、シミュレーションでOKだったらディレクティブとかいじって最適化、HDLを全く見ずにハードウェア完成。コンパイルも実行時間もHDLより遙かに速いので効率も良いです。これは癖になります。
HLSの新しいバージョンではAXI関連が有効になってます。今回のもAXI Streamにするとほかとの接続が良くなります。XPSへのエクスポートもあるので、Cで作ったモジュールを自動でZynqのAXIチャンネルに接続することもできます。ここでもHDLを排除できます。
AlteraもQsys、SOPC Builderで同様なことが可能です。というかこの辺昔からAlteraの方が強いです。AXIではなくAvalonという独自の接続形態でしたが、非常に便利です。やっとXilinxも追いついてきたという感じがします。
AXI対応はXilinxの方が先行ですかね。AXIは一種の流行なので、特にこだわる理由は無いと思いますが。
なにも書かずにものができあがる、AlteraもXilinxも、多分ほかのベンダーも、みんな目指してるところでしょう。
今はその中間解として、システム自動作成ツール(Qsys、XPS、+IP群)+C系合成ツール(C2H、HLS)+CPU(ARM、Nios、MB)+モデル設計ツール(DSP Builder、SystGen)というところに落ち着いてる感じでしょうか。バラバラなのが難点ですが適材適所で行くしか無いのが現状でしょう。もちろんHDLで書いた方が効率よい部分もあるので、しばらくはごちゃ混ぜ状態が続きそうです。
それにしてもやばい。結構できが良いので本気でHDL書きたくない気分になってます…ライセンス約50万でしたっけ…?う~む…
まぁそんなこと言いながらHDLは書くんですけどね(笑)
※DigiKeyとかで見てみたら、HLS単体だとFloatingでも25万ぐらいでした…うぅう~むぅ…
Vivado HLS を使ってみた(その6) [FPGA]
リアルタイムでFullHD 1080p60ぐらいは処理して欲しいですよね。
ソースは(その5)のintバージョン。
1080pで148.5MHzなので、ソリューションの周波数を150MHzにしてみます。
MHzを付けると周波数と認識されるようです。
これで合成すると…(レポートファイル)
1クロックに1つ答えが得られない困るので、ディレクティブ(指示子)を与えます。おぉ、やっと機種依存するところが出てきた(笑)
「Directive」タブを開き、関数(rgb2ycbcr)を右クリックします。
ディレクティブの選択画面が出るので「PIPELINE」を選択します。「Source File」を選ぶと「#pragma HLS PIPELINE」がcppソースに埋め込まれます。「Directive File」を選ぶとディレクティブの別のファイルに保存されます。どちらでも効能は同じ。好みの方にします。
「Directive File」を選んだ場合。ディレクティブのタブに新しく「HLS_PIPELINE」が追加されます。
これで合成開始。
データの流れとリソース。
パイプライン化され、1クロックで1個結果が出てくるようになりました。予想周波数は5.47[ns](約182MHz)。
この辺でいい加減一度ぐらいHDLシミュレーションしておかないと…いままでCのシミュレーションが楽で楽で…改めて思う…HDLのテストベンチってめんどくさい…
中身はほぼ同じで、BMPファイルを流し込んで結果を保存します。ただ、色々書くのが面倒なので、BGRをCbYCrに置き換えたBMPファイルだけ作成します。
ちなみにI/Oピンの情報はレポートファイルの一番下に書かれています。細かい制御方法はXilinxの資料にゆずりますが、今回はI/Oになにも指定していないので、関数の返値(YCbCr)に「ap_ctrl_hs」が、引数(RGB)に「ap_none」が設定されています。Y,Cb,Crそれぞれに「_ap_vld」という信号が付いてますが、この場合「ap_done」と同じ信号になるので見ません。
・「ap_rst」を解除する。
・「ap_start」を'1'にすると動作開始。
・クロック立ち上がり&「ap_ready」が'1'にだったら次のRGBデータをBMPファイルから与える。
・クロック立ち上がり&「ap_done」が'1'だったらYCbCrデータをBMPファイルに書き込む。
という制御だけになります。
シミュレーションの最初と最後だけ出してみます。
カラーコードにしたので、シミュレーションの最後の値が動いていませんが、これで正常です。下の方のsとdがそれぞれのピクセルカウンタです。
HDLシミュレーションで生成されたBMPファイルと、Cの整数シミュレーションで生成されたBMPファイルのバイナリ比較は完全一致でした。Cの浮動小数点モデルで生成したBMPファイルとの比較では所々1bitの差が出ます。
PlanAheadでインプリしてみます。
ソースは(その5)のintバージョン。
1080pで148.5MHzなので、ソリューションの周波数を150MHzにしてみます。
MHzを付けると周波数と認識されるようです。
これで合成すると…(レポートファイル)
================================================================ == Vivado HLS Report for 'rgb2ycbcr' ================================================================ * Date: * Version: 2013.1 * Project: rgb2ycbcr * Solution: solution1 * Product family: zynq zynq_fpv6 * Target device: xc7z020clg484-1 ================================================================ == Performance Estimates ================================================================ + Timing (ns): * Summary: +---------+-------+----------+------------+ | Clock | Target| Estimated| Uncertainty| +---------+-------+----------+------------+ |default | 6.67| 5.47| 0.83| +---------+-------+----------+------------+ + Latency (clock cycles): * Summary: +-----+-----+-----+-----+---------+ | Latency | Interval | Pipeline| | min | max | min | max | Type | +-----+-----+-----+-----+---------+ | 6| 6| 7| 7| none | +-----+-----+-----+-----+---------+ + Detail: * Instance: N/A * Loop: N/A ================================================================ == Utilization Estimates ================================================================ * Summary: +-----------------+---------+-------+--------+-------+ | Name | BRAM_18K| DSP48E| FF | LUT | +-----------------+---------+-------+--------+-------+ |Expression | -| -| 0| 235| |FIFO | -| -| -| -| |Instance | -| 7| 72| 20| |Memory | -| -| -| -| |Multiplexer | -| -| -| -| |Register | -| -| 231| -| |ShiftMemory | -| -| -| -| +-----------------+---------+-------+--------+-------+ |Total | 0| 7| 303| 255| +-----------------+---------+-------+--------+-------+ |Available | 280| 220| 106400| 53200| +-----------------+---------+-------+--------+-------+ |Utilization (%) | 0| 3| ~0 | ~0 | +-----------------+---------+-------+--------+-------+ + Detail: * Instance: +--------------------------------+-----------------------------+---------+-------+----+----+ | Instance | Module | BRAM_18K| DSP48E| FF | LUT| +--------------------------------+-----------------------------+---------+-------+----+----+ |rgb2ycbcr_mul_8ns_10ns_18_3_U1 |rgb2ycbcr_mul_8ns_10ns_18_3 | 0| 1| 18| 5| |rgb2ycbcr_mul_8ns_10s_19_3_U5 |rgb2ycbcr_mul_8ns_10s_19_3 | 0| 1| 0| 0| |rgb2ycbcr_mul_8ns_12ns_20_3_U7 |rgb2ycbcr_mul_8ns_12ns_20_3 | 0| 1| 0| 0| |rgb2ycbcr_mul_8ns_12s_20_3_U3 |rgb2ycbcr_mul_8ns_12s_20_3 | 0| 1| 18| 5| |rgb2ycbcr_mul_8ns_12s_20_3_U4 |rgb2ycbcr_mul_8ns_12s_20_3 | 0| 1| 18| 5| |rgb2ycbcr_mul_8ns_9ns_17_3_U2 |rgb2ycbcr_mul_8ns_9ns_17_3 | 0| 1| 18| 5| |rgb2ycbcr_mul_8ns_9s_18_3_U6 |rgb2ycbcr_mul_8ns_9s_18_3 | 0| 1| 0| 0| +--------------------------------+-----------------------------+---------+-------+----+----+ |Total | | 0| 7| 72| 20| +--------------------------------+-----------------------------+---------+-------+----+----+ * Memory: N/A * FIFO: N/A * Shift register: N/A * Expression: +--------------------+----------+-------+---+----+------------+------------+ | Variable Name | Operation| DSP48E| FF| LUT| Bitwidth P0| Bitwidth P1| +--------------------+----------+-------+---+----+------------+------------+ |tmp1_fu_171_p2 | + | 0| 0| 19| 19| 19| |tmp_10_fu_272_p2 | + | 0| 0| 21| 21| 21| |tmp_5_fu_236_p2 | + | 0| 0| 21| 21| 21| |p_neg1_fu_320_p2 | - | 0| 0| 21| 1| 21| |p_neg5_fu_295_p2 | - | 0| 0| 21| 1| 21| |p_neg_t1_fu_339_p2 | - | 0| 0| 10| 1| 10| |p_neg_t7_fu_314_p2 | - | 0| 0| 10| 1| 10| |Cb_fu_373_p3 | Select | 0| 0| 10| 1| 1| |Cr_fu_396_p3 | Select | 0| 0| 10| 1| 1| |agg_result_Cb_V | Select | 0| 0| 8| 1| 2| |agg_result_Cr_V | Select | 0| 0| 8| 1| 2| |tmp_13_fu_367_p3 | Select | 0| 0| 10| 1| 10| |tmp_s_fu_358_p3 | Select | 0| 0| 10| 1| 10| |icmp1_fu_413_p2 | icmp | 0| 0| 2| 2| 1| |icmp_fu_390_p2 | icmp | 0| 0| 2| 2| 1| |tmp_14_fu_345_p2 | icmp | 0| 0| 26| 21| 13| |tmp_15_fu_350_p2 | icmp | 0| 0| 26| 21| 13| +--------------------+----------+-------+---+----+------------+------------+ |Total | | 0| 0| 235| 117| 177| +--------------------+----------+-------+---+----+------------+------------+ * Multiplexer: N/A * Register: +---------------------+----+-----+-----------+ | Name | FF | Bits| Const Bits| +---------------------+----+-----+-----------+ |G_cast_reg_462 | 8| 20| 12| |ap_CS_fsm | 3| 3| 0| |p_neg_t1_reg_556 | 10| 10| 0| |p_neg_t7_reg_551 | 10| 10| 0| |tmp1_reg_504 | 19| 19| 0| |tmp3_cast_reg_509 | 21| 21| 0| |tmp5_cast_reg_514 | 21| 21| 0| |tmp_10_reg_535 | 21| 21| 0| |tmp_11_cast_reg_499 | 20| 20| 0| |tmp_11_reg_530 | 9| 9| 0| |tmp_12_reg_541 | 1| 1| 0| |tmp_14_reg_561 | 1| 1| 0| |tmp_15_reg_566 | 1| 1| 0| |tmp_17_reg_546 | 9| 9| 0| |tmp_2_reg_489 | 17| 17| 0| |tmp_4_reg_525 | 1| 1| 0| |tmp_5_reg_519 | 21| 21| 0| |tmp_7_cast_reg_494 | 20| 20| 0| |tmp_reg_484 | 18| 18| 0| +---------------------+----+-----+-----------+ |Total | 231| 243| 12| +---------------------+----+-----+-----------+ ================================================================ == Interface ================================================================ * Summary: +------------------------+-----+-----+------------+-----------------+--------------+ | RTL Ports | Dir | Bits| Protocol | Source Object | C Type | +------------------------+-----+-----+------------+-----------------+--------------+ |ap_clk | in | 1| ap_ctrl_hs | rgb2ycbcr | return value | |ap_rst | in | 1| ap_ctrl_hs | rgb2ycbcr | return value | |ap_start | in | 1| ap_ctrl_hs | rgb2ycbcr | return value | |ap_done | out | 1| ap_ctrl_hs | rgb2ycbcr | return value | |ap_idle | out | 1| ap_ctrl_hs | rgb2ycbcr | return value | |ap_ready | out | 1| ap_ctrl_hs | rgb2ycbcr | return value | |agg_result_Y_V | out | 8| ap_vld | agg_result_Y_V | pointer | |agg_result_Y_V_ap_vld | out | 1| ap_vld | agg_result_Y_V | pointer | |agg_result_Cb_V | out | 8| ap_vld | agg_result_Cb_V | pointer | |agg_result_Cb_V_ap_vld | out | 1| ap_vld | agg_result_Cb_V | pointer | |agg_result_Cr_V | out | 8| ap_vld | agg_result_Cr_V | pointer | |agg_result_Cr_V_ap_vld | out | 1| ap_vld | agg_result_Cr_V | pointer | |s_R_V | in | 8| ap_none | s_R_V | scalar | |s_G_V | in | 8| ap_none | s_G_V | scalar | |s_B_V | in | 8| ap_none | s_B_V | scalar | +------------------------+-----+-----+------------+-----------------+--------------+7クロックに1演算という回路になりました。今回はap_start,ap_doneなどが制御信号として働きます。ap_startで演算開始、ap_doneで計算結果が得られる、という制御になります。
1クロックに1つ答えが得られない困るので、ディレクティブ(指示子)を与えます。おぉ、やっと機種依存するところが出てきた(笑)
「Directive」タブを開き、関数(rgb2ycbcr)を右クリックします。
ディレクティブの選択画面が出るので「PIPELINE」を選択します。「Source File」を選ぶと「#pragma HLS PIPELINE」がcppソースに埋め込まれます。「Directive File」を選ぶとディレクティブの別のファイルに保存されます。どちらでも効能は同じ。好みの方にします。
「Directive File」を選んだ場合。ディレクティブのタブに新しく「HLS_PIPELINE」が追加されます。
これで合成開始。
================================================================ == Vivado HLS Report for 'rgb2ycbcr' ================================================================ * Date: * Version: 2013.1 * Project: rgb2ycbcr * Solution: solution1 * Product family: zynq zynq_fpv6 * Target device: xc7z020clg484-1 ================================================================ == Performance Estimates ================================================================ + Timing (ns): * Summary: +---------+-------+----------+------------+ | Clock | Target| Estimated| Uncertainty| +---------+-------+----------+------------+ |default | 6.67| 5.47| 0.83| +---------+-------+----------+------------+ + Latency (clock cycles): * Summary: +-----+-----+-----+-----+----------+ | Latency | Interval | Pipeline | | min | max | min | max | Type | +-----+-----+-----+-----+----------+ | 6| 6| 1| 1| function | +-----+-----+-----+-----+----------+ + Detail: * Instance: N/A * Loop: N/A ================================================================ == Utilization Estimates ================================================================ * Summary: +-----------------+---------+-------+--------+-------+ | Name | BRAM_18K| DSP48E| FF | LUT | +-----------------+---------+-------+--------+-------+ |Expression | -| -| 0| 235| |FIFO | -| -| -| -| |Instance | -| 7| 72| 20| |Memory | -| -| -| -| |Multiplexer | -| -| -| -| |Register | -| -| 287| -| |ShiftMemory | -| -| 0| 24| +-----------------+---------+-------+--------+-------+ |Total | 0| 7| 359| 279| +-----------------+---------+-------+--------+-------+ |Available | 280| 220| 106400| 53200| +-----------------+---------+-------+--------+-------+ |Utilization (%) | 0| 3| ~0 | ~0 | +-----------------+---------+-------+--------+-------+ + Detail: * Instance: +--------------------------------+-----------------------------+---------+-------+----+----+ | Instance | Module | BRAM_18K| DSP48E| FF | LUT| +--------------------------------+-----------------------------+---------+-------+----+----+ |rgb2ycbcr_mul_8ns_10ns_18_3_U1 |rgb2ycbcr_mul_8ns_10ns_18_3 | 0| 1| 18| 5| |rgb2ycbcr_mul_8ns_10s_19_3_U5 |rgb2ycbcr_mul_8ns_10s_19_3 | 0| 1| 0| 0| |rgb2ycbcr_mul_8ns_12ns_20_3_U7 |rgb2ycbcr_mul_8ns_12ns_20_3 | 0| 1| 0| 0| |rgb2ycbcr_mul_8ns_12s_20_3_U3 |rgb2ycbcr_mul_8ns_12s_20_3 | 0| 1| 18| 5| |rgb2ycbcr_mul_8ns_12s_20_3_U4 |rgb2ycbcr_mul_8ns_12s_20_3 | 0| 1| 18| 5| |rgb2ycbcr_mul_8ns_9ns_17_3_U2 |rgb2ycbcr_mul_8ns_9ns_17_3 | 0| 1| 18| 5| |rgb2ycbcr_mul_8ns_9s_18_3_U6 |rgb2ycbcr_mul_8ns_9s_18_3 | 0| 1| 0| 0| +--------------------------------+-----------------------------+---------+-------+----+----+ |Total | | 0| 7| 72| 20| +--------------------------------+-----------------------------+---------+-------+----+----+ * Memory: N/A * FIFO: N/A * Shift register: +--------------------+---+----+-----+-----------+ | Name | FF| LUT| Bits| Const Bits| +--------------------+---+----+-----+-----------+ |G_cast_reg_467 | 0| 8| 20| 12| |s_B_V_read_reg_450 | 0| 8| 8| 0| |s_R_V_read_reg_456 | 0| 8| 8| 0| +--------------------+---+----+-----+-----------+ |Total | 0| 24| 36| 12| +--------------------+---+----+-----+-----------+ * Expression: +--------------------+----------+-------+---+----+------------+------------+ | Variable Name | Operation| DSP48E| FF| LUT| Bitwidth P0| Bitwidth P1| +--------------------+----------+-------+---+----+------------+------------+ |tmp1_fu_177_p2 | + | 0| 0| 19| 19| 19| |tmp_10_fu_277_p2 | + | 0| 0| 21| 21| 21| |tmp_5_fu_241_p2 | + | 0| 0| 21| 21| 21| |p_neg1_fu_325_p2 | - | 0| 0| 21| 1| 21| |p_neg5_fu_300_p2 | - | 0| 0| 21| 1| 21| |p_neg_t1_fu_344_p2 | - | 0| 0| 10| 1| 10| |p_neg_t7_fu_319_p2 | - | 0| 0| 10| 1| 10| |Cb_fu_378_p3 | Select | 0| 0| 10| 1| 1| |Cr_fu_401_p3 | Select | 0| 0| 10| 1| 1| |agg_result_Cb_V | Select | 0| 0| 8| 1| 2| |agg_result_Cr_V | Select | 0| 0| 8| 1| 2| |tmp_13_fu_372_p3 | Select | 0| 0| 10| 1| 10| |tmp_s_fu_363_p3 | Select | 0| 0| 10| 1| 10| |icmp1_fu_418_p2 | icmp | 0| 0| 2| 2| 1| |icmp_fu_395_p2 | icmp | 0| 0| 2| 2| 1| |tmp_14_fu_350_p2 | icmp | 0| 0| 26| 21| 13| |tmp_15_fu_355_p2 | icmp | 0| 0| 26| 21| 13| +--------------------+----------+-------+---+----+------------+------------+ |Total | | 0| 0| 235| 117| 177| +--------------------+----------+-------+---+----+------------+------------+ * Multiplexer: N/A * Register: +-------------------------------------+----+-----+-----------+ | Name | FF | Bits| Const Bits| +-------------------------------------+----+-----+-----------+ |G_cast_reg_467 | 8| 20| 12| |Y_reg_524 | 8| 8| 0| |ap_CS_fsm | 1| 1| 0| |ap_reg_ppiten_pp0_it1 | 1| 1| 0| |ap_reg_ppiten_pp0_it2 | 1| 1| 0| |ap_reg_ppiten_pp0_it3 | 1| 1| 0| |ap_reg_ppiten_pp0_it4 | 1| 1| 0| |ap_reg_ppiten_pp0_it5 | 1| 1| 0| |ap_reg_ppiten_pp0_it6 | 1| 1| 0| |ap_reg_ppstg_Y_reg_524_pp0_it5 | 8| 8| 0| |ap_reg_ppstg_tmp_11_reg_540_pp0_it5 | 9| 9| 0| |ap_reg_ppstg_tmp_12_reg_551_pp0_it5 | 1| 1| 0| |ap_reg_ppstg_tmp_17_reg_556_pp0_it5 | 9| 9| 0| |ap_reg_ppstg_tmp_4_reg_535_pp0_it5 | 1| 1| 0| |p_neg_t1_reg_566 | 10| 10| 0| |p_neg_t7_reg_561 | 10| 10| 0| |s_B_V_read_reg_450 | 8| 8| 0| |s_R_V_read_reg_456 | 8| 8| 0| |tmp1_reg_509 | 19| 19| 0| |tmp3_cast_reg_514 | 21| 21| 0| |tmp5_cast_reg_519 | 21| 21| 0| |tmp_10_reg_545 | 21| 21| 0| |tmp_11_cast_reg_504 | 20| 20| 0| |tmp_11_reg_540 | 9| 9| 0| |tmp_12_reg_551 | 1| 1| 0| |tmp_14_reg_571 | 1| 1| 0| |tmp_15_reg_576 | 1| 1| 0| |tmp_17_reg_556 | 9| 9| 0| |tmp_2_reg_494 | 17| 17| 0| |tmp_4_reg_535 | 1| 1| 0| |tmp_5_reg_529 | 21| 21| 0| |tmp_7_cast_reg_499 | 20| 20| 0| |tmp_reg_489 | 18| 18| 0| +-------------------------------------+----+-----+-----------+ |Total | 287| 299| 12| +-------------------------------------+----+-----+-----------+ ================================================================ == Interface ================================================================ * Summary: +------------------------+-----+-----+------------+-----------------+--------------+ | RTL Ports | Dir | Bits| Protocol | Source Object | C Type | +------------------------+-----+-----+------------+-----------------+--------------+ |ap_clk | in | 1| ap_ctrl_hs | rgb2ycbcr | return value | |ap_rst | in | 1| ap_ctrl_hs | rgb2ycbcr | return value | |ap_start | in | 1| ap_ctrl_hs | rgb2ycbcr | return value | |ap_done | out | 1| ap_ctrl_hs | rgb2ycbcr | return value | |ap_idle | out | 1| ap_ctrl_hs | rgb2ycbcr | return value | |ap_ready | out | 1| ap_ctrl_hs | rgb2ycbcr | return value | |agg_result_Y_V | out | 8| ap_vld | agg_result_Y_V | pointer | |agg_result_Y_V_ap_vld | out | 1| ap_vld | agg_result_Y_V | pointer | |agg_result_Cb_V | out | 8| ap_vld | agg_result_Cb_V | pointer | |agg_result_Cb_V_ap_vld | out | 1| ap_vld | agg_result_Cb_V | pointer | |agg_result_Cr_V | out | 8| ap_vld | agg_result_Cr_V | pointer | |agg_result_Cr_V_ap_vld | out | 1| ap_vld | agg_result_Cr_V | pointer | |s_R_V | in | 8| ap_none | s_R_V | scalar | |s_G_V | in | 8| ap_none | s_G_V | scalar | |s_B_V | in | 8| ap_none | s_B_V | scalar | +------------------------+-----+-----+------------+-----------------+--------------+
データの流れとリソース。
パイプライン化され、1クロックで1個結果が出てくるようになりました。予想周波数は5.47[ns](約182MHz)。
この辺でいい加減一度ぐらいHDLシミュレーションしておかないと…いままでCのシミュレーションが楽で楽で…改めて思う…HDLのテストベンチってめんどくさい…
中身はほぼ同じで、BMPファイルを流し込んで結果を保存します。ただ、色々書くのが面倒なので、BGRをCbYCrに置き換えたBMPファイルだけ作成します。
ちなみにI/Oピンの情報はレポートファイルの一番下に書かれています。細かい制御方法はXilinxの資料にゆずりますが、今回はI/Oになにも指定していないので、関数の返値(YCbCr)に「ap_ctrl_hs」が、引数(RGB)に「ap_none」が設定されています。Y,Cb,Crそれぞれに「_ap_vld」という信号が付いてますが、この場合「ap_done」と同じ信号になるので見ません。
・「ap_rst」を解除する。
・「ap_start」を'1'にすると動作開始。
・クロック立ち上がり&「ap_ready」が'1'にだったら次のRGBデータをBMPファイルから与える。
・クロック立ち上がり&「ap_done」が'1'だったらYCbCrデータをBMPファイルに書き込む。
という制御だけになります。
`timescale 1ns / 1ps `default_nettype none //****************************************** // module bmp_file; integer bfSize; // File Size integer bfOffBits; // Image Offset integer biWidth; // Width integer biHeight; // Height integer biBitCount; // bit/pixel integer imgsize; // Size localparam headersize = 14+40; reg [7:0] tch; reg [7:0] header [0:headersize-1]; integer ifp, ofp, tx; task init; input [8*20:0] ifname; input [8*20:0] ofname; integer i; begin ifp = $fopen(ifname, "rb"); ofp = $fopen(ofname, "wb"); $display("open %s", ifname); $display("%s created", ofname); tx = $fread(header, ifp); //, 0, headersize); $display("header = %d", tx); bfSize = {header[ 5],header[ 4],header[ 3],header[ 2]}; bfOffBits = {header[13],header[12],header[11],header[10]}; biWidth = {header[21],header[20],header[19],header[18]}; biHeight = {header[25],header[24],header[23],header[22]}; biBitCount = {header[29],header[28]}; imgsize = bfSize - bfOffBits; $display(" = %d,%x :(%d,%d,%d)",bfSize,bfOffBits,biWidth,biHeight,biBitCount); for (i = 0; i < headersize; i = i+1) $fwrite(ofp,"%c",header[i]); // copy header info end endtask task done; begin $fclose(ifp); $fclose(ofp); end endtask task sread; output [7:0] B; output [7:0] G; output [7:0] R; begin $fread(B, ifp); $fread(G, ifp); $fread(R, ifp); end endtask task swrite; input [7:0] B; input [7:0] G; input [7:0] R; begin $fwrite(ofp,"%c", B); $fwrite(ofp,"%c", G); $fwrite(ofp,"%c", R); end endtask endmodule // //****************************************** //****************************************** // module tb; localparam ifname = "../H-IIA-F13.bmp"; localparam ofname = "../out_hdl.bmp"; integer bfSize; // File Size integer bfOffBits; // Image Offset integer biWidth; // Width integer biHeight; // Height integer biBitCount; // bit/pixel wire ap_start; wire ap_done; wire ap_idle; wire ap_ready; wire [7:0] agg_result_Y_V; wire agg_result_Y_V_ap_vld; wire [7:0] agg_result_Cb_V; wire agg_result_Cb_V_ap_vld; wire [7:0] agg_result_Cr_V; wire agg_result_Cr_V_ap_vld; wire [7:0] s_R_V; wire [7:0] s_G_V; wire [7:0] s_B_V; reg clock; reg reset; reg start; reg [7:0] R,G,B; wire [7:0] Y,Cb,Cr; rgb2ycbcr tgt ( .ap_clk (clock ), // ap_clk, .ap_rst (reset ), // ap_rst, .ap_start (ap_start ), // ap_start, .ap_done (ap_done ), // ap_done, .ap_idle (ap_idle ), // ap_idle, .ap_ready (ap_ready ), // ap_ready, .agg_result_Y_V (agg_result_Y_V ), // agg_result_Y_V, .agg_result_Y_V_ap_vld (agg_result_Y_V_ap_vld ), // agg_result_Y_V_ap_vld, .agg_result_Cb_V (agg_result_Cb_V ), // agg_result_Cb_V, .agg_result_Cb_V_ap_vld (agg_result_Cb_V_ap_vld ), // agg_result_Cb_V_ap_vld, .agg_result_Cr_V (agg_result_Cr_V ), // agg_result_Cr_V, .agg_result_Cr_V_ap_vld (agg_result_Cr_V_ap_vld ), // agg_result_Cr_V_ap_vld, .s_R_V (s_R_V ), // s_R_V, .s_G_V (s_G_V ), // s_G_V, .s_B_V (s_B_V ) // s_B_V, ); bmp_file bmp(); assign ap_start = start; assign s_R_V = R; assign s_G_V = G; assign s_B_V = B; assign Y = agg_result_Y_V; assign Cb = agg_result_Cb_V; assign Cr = agg_result_Cr_V; integer s,d; initial begin reset <= 1'b1; start <= 1'b0; # 100; @(posedge clock); reset <= 1'b0; @(posedge clock); start <= 1'b1; bmp.init(ifname,ofname); for (s = 0; s < bmp.imgsize; s = s+3) begin wait (ap_ready); bmp.sread(B,G,R); @(posedge clock); end start <= 1'b0; # 500; end initial begin wait (start); for (d = 0; d < bmp.imgsize; d = d+3) begin wait (ap_done); @(posedge clock); bmp.swrite(Cb,Y,Cr); end bmp.done; #300; $stop; end // Clock localparam period = (1000.0/148.5); initial clock <= 1'b0; always #(period/2) clock <= ~clock; endmodule // //****************************************** `default_nettype wire…行間から「HDLめんどくさい」が漏れてる…
シミュレーションの最初と最後だけ出してみます。
カラーコードにしたので、シミュレーションの最後の値が動いていませんが、これで正常です。下の方のsとdがそれぞれのピクセルカウンタです。
HDLシミュレーションで生成されたBMPファイルと、Cの整数シミュレーションで生成されたBMPファイルのバイナリ比較は完全一致でした。Cの浮動小数点モデルで生成したBMPファイルとの比較では所々1bitの差が出ます。
PlanAheadでインプリしてみます。
Number of BUFGs 1 out of 32 3% Number of DSP48E1s 7 out of 220 3% Number of External IOBs 57 out of 200 28% Number of LOCed IOBs 0 out of 57 0% Number of Slices 102 out of 13300 1% Number of Slice Registers 212 out of 106400 1% Number used as Flip Flops 212 Number used as Latches 0 Number used as LatchThrus 0 Number of Slice LUTS 245 out of 53200 1% Number of Slice LUT-Flip Flop pairs 307 out of 53200 1%HLSの予測値にかなり近い値になりました。レポートされた周波数は150MHz制約に対してSlack 1.63[ns]、約198MHzという結果になってます。
Vivado HLS を使ってみた(その5) [FPGA]
試しに精度の計算をコンパイラ任せにしてみます。ap_fixedではなくintで書き直します。
レポートファイル。
中間値のbit幅が自動計算されてます。ap_fixedの時は18bitだった変数が自動で必要な幅になってます。また、リソースも減ってます。丸めやサチュレーション関連の回路でしょうか。
PlanAheadでインプリしたところ。
精度をユーザがちゃんと制御したい、自動計算だと中間精度のbit数がふくれあがってしまう、などの場合はfixedなどで精度を制御する必要がありますが、普通はintでコンパイラ任せの方がなんか良さそうです。第一楽ですし。
#define fw 12 #define frc 4096 /* pow(2, fw) */ #define dat 256 /* pow(2, dw) */ YCbCr rgb2ycbcr(RGB s) { YCbCr d; const int cY [3] = { 0.2126f*frc, 0.7152f*frc, 0.0722f*frc }; const int cCb[3] = {-0.1146f*frc, -0.3854f*frc, 0.5000f*frc }; const int cCr[3] = { 0.5000f*frc, -0.4542f*frc, -0.0458f*frc }; const int op5 = 0.5f * dat * frc; const int llim = 0; const int ulim = dat-1; int R,G,B; int Y,Cb,Cr; R = s.R; G = s.G; B = s.B; Y = (cY [0] * R + cY [1] * G + cY [2] * B ) /frc; Cb = (cCb[0] * R + cCb[1] * G + cCb[2] * B + op5) /frc; Cr = (cCr[0] * R + cCr[1] * G + cCr[2] * B + op5) /frc; if (Y < llim) Y = llim; if (ulim < Y ) Y = ulim; if (Cb < llim) Cb = llim; if (ulim < Cb) Cb = ulim; if (Cr < llim) Cr = llim; if (ulim < Cr) Cr = ulim; d.Y = Y ; d.Cb = Cb; d.Cr = Cr; return d; }より簡単に。
レポートファイル。
================================================================ == Vivado HLS Report for 'rgb2ycbcr' ================================================================ * Date: * Version: 2013.1 * Project: rgb2ycbcr * Solution: solution1 * Product family: zynq zynq_fpv6 * Target device: xc7z020clg484-1 ================================================================ == Performance Estimates ================================================================ + Timing (ns): * Summary: +---------+--------+----------+------------+ | Clock | Target | Estimated| Uncertainty| +---------+--------+----------+------------+ |default | 100.00| 23.29| 12.50| +---------+--------+----------+------------+ + Latency (clock cycles): * Summary: +-----+-----+-----+-----+---------+ | Latency | Interval | Pipeline| | min | max | min | max | Type | +-----+-----+-----+-----+---------+ | 0| 0| 1| 1| none | +-----+-----+-----+-----+---------+ + Detail: * Instance: N/A * Loop: N/A ================================================================ == Utilization Estimates ================================================================ * Summary: +-----------------+---------+-------+--------+-------+ | Name | BRAM_18K| DSP48E| FF | LUT | +-----------------+---------+-------+--------+-------+ |Expression | -| 7| 0| 235| |FIFO | -| -| -| -| |Instance | -| -| -| -| |Memory | -| -| -| -| |Multiplexer | -| -| -| -| |Register | -| -| -| -| |ShiftMemory | -| -| -| -| +-----------------+---------+-------+--------+-------+ |Total | 0| 7| 0| 235| +-----------------+---------+-------+--------+-------+ |Available | 280| 220| 106400| 53200| +-----------------+---------+-------+--------+-------+ |Utilization (%) | 0| 3| 0| ~0 | +-----------------+---------+-------+--------+-------+ + Detail: * Instance: N/A * Memory: N/A * FIFO: N/A * Shift register: N/A * Expression: +-----------------------+----------+-------+---+----+------------+------------+ | Variable Name | Operation| DSP48E| FF| LUT| Bitwidth P0| Bitwidth P1| +-----------------------+----------+-------+---+----+------------+------------+ |tmp_11_cast_fu_281_p2 | * | 1| 0| 0| 8| 12| |tmp_1_fu_136_p2 | * | 1| 0| 0| 8| 12| |tmp_2_fu_142_p2 | * | 1| 0| 0| 8| 9| |tmp_6_fu_179_p2 | * | 1| 0| 0| 8| 10| |tmp_7_cast_fu_189_p2 | * | 1| 0| 0| 8| 12| |tmp_7_fu_287_p2 | * | 1| 0| 0| 8| 9| |tmp_fu_126_p2 | * | 1| 0| 0| 8| 10| |tmp1_fu_152_p2 | + | 0| 0| 19| 19| 19| |tmp_10_fu_321_p2 | + | 0| 0| 21| 21| 21| |tmp_5_fu_219_p2 | + | 0| 0| 21| 21| 21| |p_neg1_fu_335_p2 | - | 0| 0| 21| 1| 21| |p_neg5_fu_233_p2 | - | 0| 0| 21| 1| 21| |p_neg_t1_fu_355_p2 | - | 0| 0| 10| 1| 10| |p_neg_t7_fu_253_p2 | - | 0| 0| 10| 1| 10| |Cb_fu_389_p3 | Select | 0| 0| 10| 1| 1| |Cr_fu_419_p3 | Select | 0| 0| 10| 1| 1| |agg_result_Cb_V | Select | 0| 0| 8| 1| 2| |agg_result_Cr_V | Select | 0| 0| 8| 1| 2| |tmp_13_fu_375_p3 | Select | 0| 0| 10| 1| 10| |tmp_s_fu_273_p3 | Select | 0| 0| 10| 1| 10| |icmp1_fu_437_p2 | icmp | 0| 0| 2| 2| 1| |icmp_fu_407_p2 | icmp | 0| 0| 2| 2| 1| |tmp_14_fu_383_p2 | icmp | 0| 0| 26| 21| 13| |tmp_15_fu_413_p2 | icmp | 0| 0| 26| 21| 13| +-----------------------+----------+-------+---+----+------------+------------+ |Total | | 7| 0| 235| 173| 251| +-----------------------+----------+-------+---+----+------------+------------+ * Multiplexer: N/A * Register: N/A ================================================================ == Interface ================================================================ * Summary: +------------------------+-----+-----+------------+-----------------+--------------+ | RTL Ports | Dir | Bits| Protocol | Source Object | C Type | +------------------------+-----+-----+------------+-----------------+--------------+ |ap_start | in | 1| ap_ctrl_hs | rgb2ycbcr | return value | |ap_done | out | 1| ap_ctrl_hs | rgb2ycbcr | return value | |ap_idle | out | 1| ap_ctrl_hs | rgb2ycbcr | return value | |ap_ready | out | 1| ap_ctrl_hs | rgb2ycbcr | return value | |agg_result_Y_V | out | 8| ap_vld | agg_result_Y_V | pointer | |agg_result_Y_V_ap_vld | out | 1| ap_vld | agg_result_Y_V | pointer | |agg_result_Cb_V | out | 8| ap_vld | agg_result_Cb_V | pointer | |agg_result_Cb_V_ap_vld | out | 1| ap_vld | agg_result_Cb_V | pointer | |agg_result_Cr_V | out | 8| ap_vld | agg_result_Cr_V | pointer | |agg_result_Cr_V_ap_vld | out | 1| ap_vld | agg_result_Cr_V | pointer | |s_R_V | in | 8| ap_none | s_R_V | scalar | |s_G_V | in | 8| ap_none | s_G_V | scalar | |s_B_V | in | 8| ap_none | s_B_V | scalar | +------------------------+-----+-----+------------+-----------------+--------------+こちらの方が若干小さいです。
中間値のbit幅が自動計算されてます。ap_fixedの時は18bitだった変数が自動で必要な幅になってます。また、リソースも減ってます。丸めやサチュレーション関連の回路でしょうか。
PlanAheadでインプリしたところ。
Number of DSP48E1s 5 out of 220 2% Number of External IOBs 55 out of 200 27% Number of LOCed IOBs 0 out of 55 0% Number of Slices 60 out of 13300 1% Number of Slice Registers 1 out of 106400 1% Number used as Flip Flops 1 Number used as Latches 0 Number used as LatchThrus 0 Number of Slice LUTS 171 out of 53200 1% Number of Slice LUT-Flip Flop pairs 171 out of 53200 1%こちらだと乗算器が減ってます。詳しくは見てませんが、シフトなどでまかなえる部分は最適化されたんだと思います。速度は約23[ns]。だいたい同じです。
精度をユーザがちゃんと制御したい、自動計算だと中間精度のbit数がふくれあがってしまう、などの場合はfixedなどで精度を制御する必要がありますが、普通はintでコンパイラ任せの方がなんか良さそうです。第一楽ですし。
Vivado HLS を使ってみた(その4) [FPGA]
浮動小数点のままではさすがにでかいし、精度過剰なので固定小数点化します。
「ap_fixed<>」が固定小数点を扱う型です。bit数とか丸めモードとかサチュレーションモードとか設定できるようになってます。今は適当。floatの代わりに置き換えています。
まずはシミュレーション。fw(小数点以下の精度)が8bitぐらいだと明らかにおかしな画になりますが、12bitぐらいだと見た目は大丈夫そうです。出力されたBMPファイルをバイナリ比較すると1bitぐらい違います。
C Synthesisをかけてみます。
レポートファイル。
データフローとリソース。
なんとまあ、組み合わせ回路になってしまいました。そりゃそうか。できたHDLを見てみると、ap_startがap_done,ready、各_ap_vldに直結されてます。
実際このままPlanAheadに放り込んでZynqでインプリしたところ、
#define fw 12 YCbCr rgb2ycbcr(RGB s) { YCbCr d; const ap_fixed<2+dw+fw, fw, AP_RND_CONV, AP_SAT> cY [3] = { 0.2126f, 0.7152f, 0.0722f }; const ap_fixed<2+dw+fw, fw, AP_RND_CONV, AP_SAT> cCb[3] = {-0.1146f, -0.3854f, 0.5000f }; const ap_fixed<2+dw+fw, fw, AP_RND_CONV, AP_SAT> cCr[3] = { 0.5000f, -0.4542f, -0.0458f }; const ap_fixed<2+dw+fw, fw, AP_RND_CONV, AP_SAT> op5 = 0.5f * dat; const ap_fixed<2+dw+fw, fw, AP_RND_CONV, AP_SAT> llim = 0.0f; const ap_fixed<2+dw+fw, fw, AP_RND_CONV, AP_SAT> ulim = (float)(dat-1); ap_fixed<2+dw+fw, fw, AP_RND_CONV, AP_SAT> R,G,B; ap_fixed<2+dw+fw, fw, AP_RND_CONV, AP_SAT> Y,Cb,Cr; R = s.R; G = s.G; B = s.B; Y = cY [0] * R + cY [1] * G + cY [2] * B; Cb = cCb[0] * R + cCb[1] * G + cCb[2] * B + op5; Cr = cCr[0] * R + cCr[1] * G + cCr[2] * B + op5; if (Y < llim) Y = llim; if (ulim < Y ) Y = ulim; if (Cb < llim) Cb = llim; if (ulim < Cb) Cb = ulim; if (Cr < llim) Cr = llim; if (ulim < Cr) Cr = ulim; d.Y = Y; d.Cb = Cb; d.Cr = Cr; return d; }
「ap_fixed<>」が固定小数点を扱う型です。bit数とか丸めモードとかサチュレーションモードとか設定できるようになってます。今は適当。floatの代わりに置き換えています。
まずはシミュレーション。fw(小数点以下の精度)が8bitぐらいだと明らかにおかしな画になりますが、12bitぐらいだと見た目は大丈夫そうです。出力されたBMPファイルをバイナリ比較すると1bitぐらい違います。
C Synthesisをかけてみます。
レポートファイル。
=============================================================== == Vivado HLS Report for 'rgb2ycbcr' ================================================================ * Date: * Version: 2013.1 * Project: rgb2ycbcr * Solution: solution1 * Product family: zynq zynq_fpv6 * Target device: xc7z020clg484-1 ================================================================ == Performance Estimates ================================================================ + Timing (ns): * Summary: +---------+--------+----------+------------+ | Clock | Target | Estimated| Uncertainty| +---------+--------+----------+------------+ |default | 100.00| 24.80| 12.50| +---------+--------+----------+------------+ + Latency (clock cycles): * Summary: +-----+-----+-----+-----+---------+ | Latency | Interval | Pipeline| | min | max | min | max | Type | +-----+-----+-----+-----+---------+ | 0| 0| 1| 1| none | +-----+-----+-----+-----+---------+ + Detail: * Instance: N/A * Loop: N/A ================================================================ == Utilization Estimates ================================================================ * Summary: +-----------------+---------+-------+--------+-------+ | Name | BRAM_18K| DSP48E| FF | LUT | +-----------------+---------+-------+--------+-------+ |Expression | -| 7| 0| 372| |FIFO | -| -| -| -| |Instance | -| -| -| -| |Memory | -| -| -| -| |Multiplexer | -| -| -| -| |Register | -| -| -| -| |ShiftMemory | -| -| -| -| +-----------------+---------+-------+--------+-------+ |Total | 0| 7| 0| 372| +-----------------+---------+-------+--------+-------+ |Available | 280| 220| 106400| 53200| +-----------------+---------+-------+--------+-------+ |Utilization (%) | 0| 3| 0| ~0 | +-----------------+---------+-------+--------+-------+ + Detail: * Instance: N/A * Memory: N/A * FIFO: N/A * Shift register: N/A * Expression: +--------------------------+----------+-------+---+----+------------+------------+ | Variable Name | Operation| DSP48E| FF| LUT| Bitwidth P0| Bitwidth P1| +--------------------------+----------+-------+---+----+------------+------------+ |r_V_13_fu_246_p2 | * | 1| 0| 0| 18| 8| |r_V_17_fu_342_p2 | * | 1| 0| 0| 18| 7| |r_V_8_fu_140_p2 | * | 1| 0| 0| 18| 10| |r_V_9_fu_154_p2 | * | 1| 0| 0| 18| 8| |r_V_fu_130_p2 | * | 1| 0| 0| 18| 7| |rhs_V_2_cast_fu_256_p2 | * | 1| 0| 0| 18| 10| |rhs_V_4_cast_fu_360_p2 | * | 1| 0| 0| 18| 10| |p_Val2_11_fu_336_p2 | + | 0| 0| 18| 18| 18| |p_Val2_17_fu_440_p2 | + | 0| 0| 18| 18| 18| |p_Val2_5_fu_232_p2 | + | 0| 0| 18| 18| 18| |r_V_10_fu_164_p2 | + | 0| 0| 28| 28| 28| |r_V_11_fu_174_p2 | + | 0| 0| 28| 28| 28| |r_V_15_fu_272_p2 | + | 0| 0| 28| 28| 28| |Cb_V_fu_466_p3 | Select | 0| 0| 18| 1| 12| |Cr_V_fu_480_p3 | Select | 0| 0| 18| 1| 12| |Y_V_fu_452_p3 | Select | 0| 0| 18| 1| 12| |qb_assign_1_fu_222_p2 | and | 0| 0| 2| 1| 1| |qb_assign_3_fu_326_p2 | and | 0| 0| 2| 1| 1| |qb_assign_5_fu_430_p2 | and | 0| 0| 2| 1| 1| |r_1_fu_306_p2 | icmp | 0| 0| 10| 9| 1| |r_2_fu_410_p2 | icmp | 0| 0| 10| 9| 1| |r_fu_202_p2 | icmp | 0| 0| 2| 1| 1| |tmp_2_fu_460_p2 | icmp | 0| 0| 22| 18| 12| |tmp_4_fu_474_p2 | icmp | 0| 0| 22| 18| 12| |tmp_9_fu_446_p2 | icmp | 0| 0| 22| 18| 12| |brmerge_i62_i1_fu_320_p2 | or | 0| 0| 2| 1| 1| |brmerge_i62_i2_fu_424_p2 | or | 0| 0| 2| 1| 1| |brmerge_i62_i_fu_216_p2 | or | 0| 0| 2| 1| 1| |r_V_16_fu_278_p2 | xor | 0| 0| 40| 28| 29| |r_V_21_fu_382_p2 | xor | 0| 0| 40| 28| 29| +--------------------------+----------+-------+---+----+------------+------------+ |Total | | 7| 0| 372| 402| 337| +--------------------------+----------+-------+---+----+------------+------------+ * Multiplexer: N/A * Register: N/A ================================================================ == Interface ================================================================ * Summary: +------------------------+-----+-----+------------+-----------------+--------------+ | RTL Ports | Dir | Bits| Protocol | Source Object | C Type | +------------------------+-----+-----+------------+-----------------+--------------+ |ap_start | in | 1| ap_ctrl_hs | rgb2ycbcr | return value | |ap_done | out | 1| ap_ctrl_hs | rgb2ycbcr | return value | |ap_idle | out | 1| ap_ctrl_hs | rgb2ycbcr | return value | |ap_ready | out | 1| ap_ctrl_hs | rgb2ycbcr | return value | |agg_result_Y_V | out | 8| ap_vld | agg_result_Y_V | pointer | |agg_result_Y_V_ap_vld | out | 1| ap_vld | agg_result_Y_V | pointer | |agg_result_Cb_V | out | 8| ap_vld | agg_result_Cb_V | pointer | |agg_result_Cb_V_ap_vld | out | 1| ap_vld | agg_result_Cb_V | pointer | |agg_result_Cr_V | out | 8| ap_vld | agg_result_Cr_V | pointer | |agg_result_Cr_V_ap_vld | out | 1| ap_vld | agg_result_Cr_V | pointer | |s_R_V | in | 8| ap_none | s_R_V | scalar | |s_G_V | in | 8| ap_none | s_G_V | scalar | |s_B_V | in | 8| ap_none | s_B_V | scalar | +------------------------+-----+-----+------------+-----------------+--------------+
データフローとリソース。
なんとまあ、組み合わせ回路になってしまいました。そりゃそうか。できたHDLを見てみると、ap_startがap_done,ready、各_ap_vldに直結されてます。
実際このままPlanAheadに放り込んでZynqでインプリしたところ、
Number of DSP48E1s 9 out of 220 4% Number of External IOBs 55 out of 200 27% Number of LOCed IOBs 0 out of 55 0% Number of Slices 18 out of 13300 1% Number of Slice Registers 0 out of 106400 0% Number used as Flip Flops 0 Number used as Latches 0 Number used as LatchThrus 0 Number of Slice LUTS 60 out of 53200 1% Number of Slice LUT-Flip Flop pairs 60 out of 53200 1%DSPリソースがHLSの計算より多い。多分0.5倍が最適化されずに乗算器が実装されたんでしょう。速度は約21[ns]と若干速いですがほぼ予想値通りです。
Vivado HLS を使ってみた(その3) [FPGA]
ようやく対象となる関数本体を作ります。「Source」にある「rgb2ycbcr.cpp」を編集します。
まずはテストベンチの関数をそのまんまコピーして、関数名を「rgb2ycbcr」にします。浮動小数点使ってますが、気にしません。
main関数のコメントを切り替えて、この関数を使うように変更します。シミュレーション結果はもちろん変わりません。
試しにこのまま合成してみます。の前にソリューションの周波数を落とします。これは周波数が高いまま(デフォルト100MHz)だとレポート画面が横長になりすぎるから、というだけの理由です。
緑色の再生ボタン(C Synthesis)を押します。ものの10数秒で完了です。レポートファイルがこちら。
「Analysis」でデータの流れが見られます。
リソースの使われ方。
なにも指示を与えていないので、パイプライン化もされていません。レイテンシ16クロック、17クロックに1演算、となっています。浮動小数点のコアを使っているので結構大きいです、が、これで良いのだったらこのまま完成でもOK?実際にインプリを試してはいませんが、HDLは普通にできてるのでこのままでも大丈夫そうです。
とは言えさすがにナンなので、整数化してみます。
まずはテストベンチの関数をそのまんまコピーして、関数名を「rgb2ycbcr」にします。浮動小数点使ってますが、気にしません。
#include "rgb2ycbcr.h" #define dat 256 /* pow(2, dw) */ YCbCr rgb2ycbcr(RGB s) { YCbCr d; const float cY [3] = { 0.2126f, 0.7152f, 0.0722f }; const float cCb[3] = {-0.1146f, -0.3854f, 0.5000f }; const float cCr[3] = { 0.5000f, -0.4542f, -0.0458f }; const float op5 = 0.5f * dat; const float llim = 0.0f; const float ulim = (float)(dat-1); float R,G,B; float Y,Cb,Cr; R = (float)s.R; G = (float)s.G; B = (float)s.B; Y = cY [0] * R + cY [1] * G + cY [2] * B; Cb = cCb[0] * R + cCb[1] * G + cCb[2] * B + op5; Cr = cCr[0] * R + cCr[1] * G + cCr[2] * B + op5; if (Y < llim) Y = llim; if (ulim < Y ) Y = ulim; if (Cb < llim) Cb = llim; if (ulim < Cb) Cb = ulim; if (Cb < llim) Cr = llim; if (ulim < Cb) Cr = ulim; d.Y = Y; d.Cb = Cb; d.Cr = Cr; return d; }
main関数のコメントを切り替えて、この関数を使うように変更します。シミュレーション結果はもちろん変わりません。
試しにこのまま合成してみます。の前にソリューションの周波数を落とします。これは周波数が高いまま(デフォルト100MHz)だとレポート画面が横長になりすぎるから、というだけの理由です。
緑色の再生ボタン(C Synthesis)を押します。ものの10数秒で完了です。レポートファイルがこちら。
================================================================ == Vivado HLS Report for 'rgb2ycbcr' ================================================================ * Date: * Version: 2013.1 * Project: rgb2ycbcr * Solution: solution1 * Product family: zynq zynq_fpv6 * Target device: xc7z020clg484-1 ================================================================ == Performance Estimates ================================================================ + Timing (ns): * Summary: +---------+--------+----------+------------+ | Clock | Target | Estimated| Uncertainty| +---------+--------+----------+------------+ |default | 100.00| 37.63| 12.50| +---------+--------+----------+------------+ + Latency (clock cycles): * Summary: +-----+-----+-----+-----+---------+ | Latency | Interval | Pipeline| | min | max | min | max | Type | +-----+-----+-----+-----+---------+ | 16| 16| 17| 17| none | +-----+-----+-----+-----+---------+ + Detail: * Instance: N/A * Loop: N/A ================================================================ == Utilization Estimates ================================================================ * Summary: +-----------------+---------+-------+--------+-------+ | Name | BRAM_18K| DSP48E| FF | LUT | +-----------------+---------+-------+--------+-------+ |Expression | -| -| 0| 114| |FIFO | -| -| -| -| |Instance | -| 24| 2007| 5419| |Memory | -| -| -| -| |Multiplexer | -| -| -| 388| |Register | -| -| 77| -| |ShiftMemory | -| -| -| -| +-----------------+---------+-------+--------+-------+ |Total | 0| 24| 2084| 5921| +-----------------+---------+-------+--------+-------+ |Available | 280| 220| 106400| 53200| +-----------------+---------+-------+--------+-------+ |Utilization (%) | 0| 10| 1| 11| +-----------------+---------+-------+--------+-------+ + Detail: * Instance: +-------------------------------------------+----------------------------------------+---------+-------+-----+-----+ | Instance | Module | BRAM_18K| DSP48E| FF | LUT | +-------------------------------------------+----------------------------------------+---------+-------+-----+-----+ |rgb2ycbcr_fadd_32ns_32ns_32_3_full_dsp_U1 |rgb2ycbcr_fadd_32ns_32ns_32_3_full_dsp | 0| 2| 177| 385| |rgb2ycbcr_fadd_32ns_32ns_32_3_full_dsp_U2 |rgb2ycbcr_fadd_32ns_32ns_32_3_full_dsp | 0| 2| 177| 385| |rgb2ycbcr_fadd_32ns_32ns_32_3_full_dsp_U3 |rgb2ycbcr_fadd_32ns_32ns_32_3_full_dsp | 0| 2| 177| 385| |rgb2ycbcr_fcmp_32ns_32ns_1_3_U14 |rgb2ycbcr_fcmp_32ns_32ns_1_3 | 0| 0| 66| 239| |rgb2ycbcr_fcmp_32ns_32ns_1_3_U15 |rgb2ycbcr_fcmp_32ns_32ns_1_3 | 0| 0| 66| 239| |rgb2ycbcr_fmul_32ns_32ns_32_3_max_dsp_U4 |rgb2ycbcr_fmul_32ns_32ns_32_3_max_dsp | 0| 3| 128| 320| |rgb2ycbcr_fmul_32ns_32ns_32_3_max_dsp_U5 |rgb2ycbcr_fmul_32ns_32ns_32_3_max_dsp | 0| 3| 128| 320| |rgb2ycbcr_fmul_32ns_32ns_32_3_max_dsp_U6 |rgb2ycbcr_fmul_32ns_32ns_32_3_max_dsp | 0| 3| 128| 320| |rgb2ycbcr_fmul_32ns_32ns_32_3_max_dsp_U7 |rgb2ycbcr_fmul_32ns_32ns_32_3_max_dsp | 0| 3| 128| 320| |rgb2ycbcr_fmul_32ns_32ns_32_3_max_dsp_U8 |rgb2ycbcr_fmul_32ns_32ns_32_3_max_dsp | 0| 3| 128| 320| |rgb2ycbcr_fmul_32ns_32ns_32_3_max_dsp_U9 |rgb2ycbcr_fmul_32ns_32ns_32_3_max_dsp | 0| 3| 128| 320| |rgb2ycbcr_fptoui_32ns_64_3_U10 |rgb2ycbcr_fptoui_32ns_64_3 | 0| 0| 160| 414| |rgb2ycbcr_fptoui_32ns_64_3_U11 |rgb2ycbcr_fptoui_32ns_64_3 | 0| 0| 160| 414| |rgb2ycbcr_uitofp_64ns_32_3_U12 |rgb2ycbcr_uitofp_64ns_32_3 | 0| 0| 128| 519| |rgb2ycbcr_uitofp_64ns_32_3_U13 |rgb2ycbcr_uitofp_64ns_32_3 | 0| 0| 128| 519| +-------------------------------------------+----------------------------------------+---------+-------+-----+-----+ |Total | | 0| 24| 2007| 5419| +-------------------------------------------+----------------------------------------+---------+-------+-----+-----+ * Memory: N/A * FIFO: N/A * Shift register: N/A * Expression: +------------------+----------+-------+---+----+------------+------------+ | Variable Name | Operation| DSP48E| FF| LUT| Bitwidth P0| Bitwidth P1| +------------------+----------+-------+---+----+------------+------------+ |Cb_1_fu_246_p3 | Select | 0| 0| 32| 1| 1| |Cb_2_fu_272_p3 | Select | 0| 0| 32| 1| 31| |Y_1_fu_236_p3 | Select | 0| 0| 32| 1| 1| |agg_result_Cr_V | Select | 0| 0| 8| 1| 8| |agg_result_Y_V | Select | 0| 0| 8| 1| 2| |tmp_22_fu_291_p2 | or | 0| 0| 2| 1| 1| +------------------+----------+-------+---+----+------------+------------+ |Total | | 0| 0| 114| 6| 44| +------------------+----------+-------+---+----+------------+------------+ * Multiplexer: +-------------------+----+-----------+-----+-----------+ | Name | LUT| Input Size| Bits| Total Bits| +-------------------+----+-----------+-----+-----------+ |grp_fu_105_p0 | 32| 2| 32| 64| |grp_fu_105_p1 | 13| 2| 13| 26| |grp_fu_111_p1 | 10| 2| 10| 20| |grp_fu_141_p0 | 32| 2| 32| 64| |grp_fu_148_p0 | 8| 2| 8| 16| |grp_fu_161_opcode | 2| 2| 2| 4| |grp_fu_161_p0 | 32| 4| 32| 128| |grp_fu_161_p1 | 10| 2| 10| 20| |grp_fu_168_opcode | 2| 2| 2| 4| |grp_fu_168_p0 | 32| 2| 32| 64| |grp_fu_168_p1 | 10| 2| 10| 20| |grp_fu_80_p0 | 32| 3| 32| 96| |grp_fu_80_p1 | 32| 3| 32| 96| |grp_fu_84_p0 | 32| 3| 32| 96| |grp_fu_84_p1 | 32| 3| 32| 96| |grp_fu_88_p0 | 32| 2| 32| 64| |grp_fu_88_p1 | 32| 2| 32| 64| |grp_fu_99_p1 | 13| 2| 13| 26| +-------------------+----+-----------+-----+-----------+ |Total | 388| 42| 388| 968| +-------------------+----+-----------+-----+-----------+ * Register: +----------------+----+-----+-----------+ | Name | FF | Bits| Const Bits| +----------------+----+-----+-----------+ |Cb_1_reg_348 | 32| 32| 0| |ap_CS_fsm | 5| 5| 0| |reg_201 | 32| 32| 0| |tmp_25_reg_354 | 8| 8| 0| +----------------+----+-----+-----------+ |Total | 77| 77| 0| +----------------+----+-----+-----------+ ================================================================ == Interface ================================================================ * Summary: +------------------------+-----+-----+------------+-----------------+--------------+ | RTL Ports | Dir | Bits| Protocol | Source Object | C Type | +------------------------+-----+-----+------------+-----------------+--------------+ |ap_clk | in | 1| ap_ctrl_hs | rgb2ycbcr | return value | |ap_rst | in | 1| ap_ctrl_hs | rgb2ycbcr | return value | |ap_start | in | 1| ap_ctrl_hs | rgb2ycbcr | return value | |ap_done | out | 1| ap_ctrl_hs | rgb2ycbcr | return value | |ap_idle | out | 1| ap_ctrl_hs | rgb2ycbcr | return value | |ap_ready | out | 1| ap_ctrl_hs | rgb2ycbcr | return value | |agg_result_Y_V | out | 8| ap_vld | agg_result_Y_V | pointer | |agg_result_Y_V_ap_vld | out | 1| ap_vld | agg_result_Y_V | pointer | |agg_result_Cb_V | out | 8| ap_vld | agg_result_Cb_V | pointer | |agg_result_Cb_V_ap_vld | out | 1| ap_vld | agg_result_Cb_V | pointer | |agg_result_Cr_V | out | 8| ap_vld | agg_result_Cr_V | pointer | |agg_result_Cr_V_ap_vld | out | 1| ap_vld | agg_result_Cr_V | pointer | |s_R_V | in | 8| ap_none | s_R_V | scalar | |s_G_V | in | 8| ap_none | s_G_V | scalar | |s_B_V | in | 8| ap_none | s_B_V | scalar | +------------------------+-----+-----+------------+-----------------+--------------+
「Analysis」でデータの流れが見られます。
リソースの使われ方。
なにも指示を与えていないので、パイプライン化もされていません。レイテンシ16クロック、17クロックに1演算、となっています。浮動小数点のコアを使っているので結構大きいです、が、これで良いのだったらこのまま完成でもOK?実際にインプリを試してはいませんが、HDLは普通にできてるのでこのままでも大丈夫そうです。
とは言えさすがにナンなので、整数化してみます。
Vivado HLS を使ってみた(その2) [FPGA]
まずテストプログラムを作ってしまいます。ごく普通にC/C++で書いていきます。
(tab=4をスペースに置換してるので崩れてます)
I/Oポートのデータ幅を決めておきたいので、そこだけ特別な記述が入ります。
前の記事の通り、題材はRGB→YCbCr変換。実はVivado HLSには画像のためのパッケージも入っていて、RGBとかYCbCrなどの型がすでに宣言されていますが、今回は使わずに全部自分で書いてみます。
ヘッダ。「Source」を右クリックして「rgb2ycbcr.h」というファイル名で追加。
※これは後から気づいたことですが、dwはマクロじゃ無くてテンプレートで与えるべきでしょうね。
テストベンチ。「TestBench」を右クリックして「test01.cpp」というファイル名で追加。
BMPファイルを読み込んで、変換して、BMPに保存します。実行パスが「.\rgb2ycbcr\solution1\csim\build\csim.exe」という深いところにできるので画像へのパスが長めに…(笑)
RGB→YCbCr変換はWikiに書いてある方法そのままです。ついでに画像を元に戻すYCbCr→RGBの逆変換関数も用意します。けど、なにも工夫してません。ちゃんと精度のこと考えてないので、正確でもありません。エラーチェックもろくしていない全くもっていい加減なプログラム。まぁだいたい見た目が似てればよいや、的な作りです。
後でmainでコメントアウトしているrgb2ycbcrとref_rgb2ycbcrを入れ替えてテストします。
とりあえずこのまま実行してみます。「Run C Simulation」をポチ。
デフォルトのまま実行開始。
こんな画像が出てきます。(※So-netブログはBMPダメ)。
左から、元画像、Y、Cb、Cr、BGR=CbYCrに置き換え、逆変換した画像
実行環境ができたので、ハードウェアになる対象の関数を記述していきます。
(tab=4をスペースに置換してるので崩れてます)
I/Oポートのデータ幅を決めておきたいので、そこだけ特別な記述が入ります。
前の記事の通り、題材はRGB→YCbCr変換。実はVivado HLSには画像のためのパッケージも入っていて、RGBとかYCbCrなどの型がすでに宣言されていますが、今回は使わずに全部自分で書いてみます。
ヘッダ。「Source」を右クリックして「rgb2ycbcr.h」というファイル名で追加。
#define dw 8 #include <ap_int.h> struct RGB { ap_uint<dw> R,G,B; }; struct YCbCr { ap_uint<dw> Y,Cb,Cr; }; YCbCr rgb2ycbcr(RGB s);「ap_uint<n>」が「nビットの符号無し整数」という型で、これがI/Oのデータ幅になります。SystemCの「sc_uint<>」とだいたい同じでしょう。「rgb2ycbcr」がハードウェアになる関数です。
※これは後から気づいたことですが、dwはマクロじゃ無くてテンプレートで与えるべきでしょうね。
テストベンチ。「TestBench」を右クリックして「test01.cpp」というファイル名で追加。
#include <fstream> #include <iostream> #include <iomanip> #include <cstdlib> using namespace std; #include "rgb2ycbcr.h" #define dat 256 /* pow(2, dw) */ //////////////////////////// // // Y = ( 0.2126R + 0.7152G + 0.0722B) // Cb = (-0.1146R - 0.3854G + 0.5000B) + 0.5 // Cr = ( 0.5000R - 0.4542G - 0.0458B) + 0.5 // YCbCr ref_rgb2ycbcr(RGB s) { YCbCr d; const float cY [3] = { 0.2126f, 0.7152f, 0.0722f }; const float cCb[3] = {-0.1146f, -0.3854f, 0.5000f }; const float cCr[3] = { 0.5000f, -0.4542f, -0.0458f }; const float op5 = 0.5f * dat; const float llim = 0.0f; const float ulim = (float)(dat-1); float R,G,B; float Y,Cb,Cr; R = (float)s.R; G = (float)s.G; B = (float)s.B; Y = cY [0] * R + cY [1] * G + cY [2] * B; Cb = cCb[0] * R + cCb[1] * G + cCb[2] * B + op5; Cr = cCr[0] * R + cCr[1] * G + cCr[2] * B + op5; if (Y < llim) Y = llim; if (ulim < Y ) Y = ulim; if (Cb < llim) Cb = llim; if (ulim < Cb) Cb = ulim; if (Cb < llim) Cr = llim; if (ulim < Cb) Cr = ulim; d.Y = Y; d.Cb = Cb; d.Cr = Cr; return d; } //////////////////////////// // // R = Y + 1.5748(Cr-0.5) // G = Y - 0.1873(Cb-0.5) - 0.4681(Cr-0.5) // B = Y + 1.8556(Cb-0.5) // // R = Y + 1.5748Cr - 0.7874 // G = Y - 0.1873Cb - 0.4681Cr + 0.3277 // B = Y + 1.8556Cb - 0.9278 // RGB ref_ycbcr2rgb(YCbCr s) { RGB d; const float cR[3] = { 0.0000f, 1.5748f, -0.7874f * dat }; const float cG[3] = {-0.1873f, -0.4681f, 0.3277f * dat }; const float cB[3] = { 1.8556f, 0.0000f, -0.9278f * dat }; const float llim = 0.0f; const float ulim = (float)(dat-1); float R,G,B; float Y,Cb,Cr; Y = (float)s.Y; Cb = (float)s.Cb; Cr = (float)s.Cr; R = Y + cR[1] * Cr + cR[2]; G = Y + cG[0] * Cb + cG[1] * Cr + cG[2]; B = Y + cB[0] * Cb + cB[2]; if (R < llim) R = llim; if (ulim < R) R = ulim; if (G < llim) G = llim; if (ulim < G) G = ulim; if (B < llim) B = llim; if (ulim < B) B = ulim; d.R = R; d.G = G; d.B = B; return d; } #define dpath "../../../../" const char ifname[]= dpath "H-IIA-F13.bmp"; const char ofname[]= dpath "out.bmp"; const char ofnameRGB[]= dpath "outRGB.bmp"; const char ofnameY[]= dpath "outY.bmp"; const char ofnameCb[]= dpath "outCb.bmp"; const char ofnameCr[]= dpath "outCr.bmp"; #pragma pack(1) struct BITMAPFILEHEADER { char bfType[2]; // "BM" unsigned int bfSize; // File Size (Byte) unsigned short bfReserved1; // unsigned short bfReserved2; // unsigned int bfOffBits; // Image Offset }; #pragma pack(0) #pragma pack(1) struct BGR { unsigned char B,G,R; }; #pragma pack(0) int main() { ifstream ifile; ofstream ofile; ofstream ofileRGB; ofstream ofileY; ofstream ofileCb; ofstream ofileCr; BITMAPFILEHEADER bf; int imgsize; RGB src; YCbCr des; ifile.open(ifname, ios::binary); ofile.open(ofname, ios::binary); ofileRGB.open(ofnameRGB, ios::binary); ofileY.open(ofnameY, ios::binary); ofileCb.open(ofnameCb, ios::binary); ofileCr.open(ofnameCr, ios::binary); // bf ifile.read((char*)&bf, sizeof(bf)); cout << "bfType : " << bf.bfType[0] << bf.bfType[1] << endl; cout << "bfSize : " << bf.bfSize << endl; cout << "bfOffBits : " << bf.bfOffBits << endl; // copy header ofile.write((char*)&bf, sizeof(bf)); ofileRGB.write((char*)&bf, sizeof(bf)); ofileY.write((char*)&bf, sizeof(bf)); ofileCb.write((char*)&bf, sizeof(bf)); ofileCr.write((char*)&bf, sizeof(bf)); for (int i = sizeof(bf); i < bf.bfOffBits; i++) { char tch; ifile.read(&tch, sizeof(tch)); ofile.write(&tch, sizeof(tch)); ofileRGB.write(&tch, sizeof(tch)); ofileY.write(&tch, sizeof(tch)); ofileCb.write(&tch, sizeof(tch)); ofileCr.write(&tch, sizeof(tch)); } // copy imgsize = bf.bfSize - bf.bfOffBits; for (int i = 0; i < imgsize; i+=3) { BGR ipx, opx; ifile.read((char*)&ipx, sizeof(ipx)); src.R = ipx.R; src.G = ipx.G; src.B = ipx.B; des = ref_rgb2ycbcr(src); // des = rgb2ycbcr(src); opx.R = des.Y; opx.B = des.Y; opx.G = des.Y; ofileY.write((char*)&opx, sizeof(opx)); opx.R = des.Cb; opx.B = des.Cb; opx.G = des.Cb; ofileCb.write((char*)&opx, sizeof(opx)); opx.R = des.Cr; opx.B = des.Cr; opx.G = des.Cr; ofileCr.write((char*)&opx, sizeof(opx)); opx.R = des.Cr; opx.B = des.Cb; opx.G = des.Y; ofile.write((char*)&opx, sizeof(opx)); src = ref_ycbcr2rgb(des); opx.R = src.R; opx.G = src.G; opx.B = src.B; ofileRGB.write((char*)&opx, sizeof(opx)); } ifile.close(); ofile.close(); ofileRGB.close(); ofileY.close(); ofileCb.close(); ofileCr.close(); cout << "done\n"; }
BMPファイルを読み込んで、変換して、BMPに保存します。実行パスが「.\rgb2ycbcr\solution1\csim\build\csim.exe」という深いところにできるので画像へのパスが長めに…(笑)
RGB→YCbCr変換はWikiに書いてある方法そのままです。ついでに画像を元に戻すYCbCr→RGBの逆変換関数も用意します。けど、なにも工夫してません。ちゃんと精度のこと考えてないので、正確でもありません。エラーチェックもろくしていない全くもっていい加減なプログラム。まぁだいたい見た目が似てればよいや、的な作りです。
後でmainでコメントアウトしているrgb2ycbcrとref_rgb2ycbcrを入れ替えてテストします。
とりあえずこのまま実行してみます。「Run C Simulation」をポチ。
デフォルトのまま実行開始。
こんな画像が出てきます。(※So-netブログはBMPダメ)。
左から、元画像、Y、Cb、Cr、BGR=CbYCrに置き換え、逆変換した画像
実行環境ができたので、ハードウェアになる対象の関数を記述していきます。
Vivado HLS を使ってみた(その1) [FPGA]
またご無沙汰です。
最近、ZedBoardとこれ
http://avnet.co.jp/design/kits/xilinx/AES-FMC-IMAGEON-V2000C-G.aspx
入手しました。OnSemiのこのセンサーはFullHD 100fpsというセンサーですが、256x256 1700fpsで使ってみたいです。ちょっとした中速度撮影カメラですね。BayerパターンのRAW出力なので後処理は必要です。
また、Allowから低価格CycloneV SOCのSoCKitが発表されました。
http://www.arrownac.com/solutions/sockit/
DDR3がARMコアとFPGAハードマクロに1GBずつ2系統ってことでしょうか。DS5の扱いがどうなるかによりますが使ってみたいです。HSMCに上記のイメージセンサーつなぎたいな~
まぁ、この辺はおいおい。
さて、これまた最近、Xilinx VivadoのHLSに触れる機会がありました。その昔AutoESLと呼ばれてたC/C++→HDLツールです。もっと効率よい方法があるかもしれませんが、試行錯誤しながら使ってみます。参考資料はこれ。
http://www.xilinx.com/support/documentation/sw_manuals/xilinx2012_2/ug902-vivado-high-level-synthesis.pdf (PDF)
あと、サンプル記述の例が埋め込まれているので、それも参考になります。
起動画面。
「Create New Project」で新しくプロジェクト作成します。
題材としてRGB→YCbCr変換。IPはどこにでもあるので自分で作る必要は無いんですが、結果がわかっているので試すにはよろしかろうと。
ソースファイルの追加。「Top Function」で指定した関数から下がハードウェアに変換されます。
ここでちょっと違和感。ファイルを追加しないと次にいけないんですが、まだプロジェクトフォルダが作成されていないので、ファイルの場所が?今回の場合
c:\work\hls_test\rgb2ycbcr
フォルダにファイルを入れるような気がするんですが、この時点で「rgb2ycbcr」は無いため、「hls_test」に作ることになります。後でファイルの場所を変えれば良いだけですが…想定されてる使い方が違うのかな?
テストベンチ。あとで。
「ソリューション」と呼ばれる、回路生成の設定画面。
コンパイラオプションセットみたいな感じです。速度とかデバイスを指定します。これは後で変更可能ですし、複数のソリューションを用意することもできます。
今回はZedBoardを想定してZynq7020を設定。速度はデフォルト。
設定後。あ、デバイス間違えてるw
設定完了後の画面。ここにテストベンチとか追加して行きます。
使い方はほぼEclipseのままです。
上のメニューとかボタン。
緑の再生ボタンがHDL生成。その左がC/C++シミュレーション。この二つをよく使います。
Debug、Synthesis、Analysisはパースペクティブの切り替え。Debugは普通のCデバッガです。Analysisがハードウェアのデータの流れを解析するツールになります。
ここからソースを記述していきます。順番として
・テストベンチ作成。リファレンスとなるデータの読み書きを行うプログラムを書きます。
・ハードウェアになる対象の関数を作成。
・対象の関数を最適化。しなくても良いかも。
・ディレクティブ(指示子?)追加。I/Oの種類とかパイプラインとか。
てな感じで行きます。
最近、ZedBoardとこれ
http://avnet.co.jp/design/kits/xilinx/AES-FMC-IMAGEON-V2000C-G.aspx
入手しました。OnSemiのこのセンサーはFullHD 100fpsというセンサーですが、256x256 1700fpsで使ってみたいです。ちょっとした中速度撮影カメラですね。BayerパターンのRAW出力なので後処理は必要です。
また、Allowから低価格CycloneV SOCのSoCKitが発表されました。
http://www.arrownac.com/solutions/sockit/
DDR3がARMコアとFPGAハードマクロに1GBずつ2系統ってことでしょうか。DS5の扱いがどうなるかによりますが使ってみたいです。HSMCに上記のイメージセンサーつなぎたいな~
まぁ、この辺はおいおい。
さて、これまた最近、Xilinx VivadoのHLSに触れる機会がありました。その昔AutoESLと呼ばれてたC/C++→HDLツールです。もっと効率よい方法があるかもしれませんが、試行錯誤しながら使ってみます。参考資料はこれ。
http://www.xilinx.com/support/documentation/sw_manuals/xilinx2012_2/ug902-vivado-high-level-synthesis.pdf (PDF)
あと、サンプル記述の例が埋め込まれているので、それも参考になります。
起動画面。
「Create New Project」で新しくプロジェクト作成します。
題材としてRGB→YCbCr変換。IPはどこにでもあるので自分で作る必要は無いんですが、結果がわかっているので試すにはよろしかろうと。
ソースファイルの追加。「Top Function」で指定した関数から下がハードウェアに変換されます。
ここでちょっと違和感。ファイルを追加しないと次にいけないんですが、まだプロジェクトフォルダが作成されていないので、ファイルの場所が?今回の場合
c:\work\hls_test\rgb2ycbcr
フォルダにファイルを入れるような気がするんですが、この時点で「rgb2ycbcr」は無いため、「hls_test」に作ることになります。後でファイルの場所を変えれば良いだけですが…想定されてる使い方が違うのかな?
テストベンチ。あとで。
「ソリューション」と呼ばれる、回路生成の設定画面。
コンパイラオプションセットみたいな感じです。速度とかデバイスを指定します。これは後で変更可能ですし、複数のソリューションを用意することもできます。
今回はZedBoardを想定してZynq7020を設定。速度はデフォルト。
設定後。あ、デバイス間違えてるw
設定完了後の画面。ここにテストベンチとか追加して行きます。
使い方はほぼEclipseのままです。
上のメニューとかボタン。
緑の再生ボタンがHDL生成。その左がC/C++シミュレーション。この二つをよく使います。
Debug、Synthesis、Analysisはパースペクティブの切り替え。Debugは普通のCデバッガです。Analysisがハードウェアのデータの流れを解析するツールになります。
ここからソースを記述していきます。順番として
・テストベンチ作成。リファレンスとなるデータの読み書きを行うプログラムを書きます。
・ハードウェアになる対象の関数を作成。
・対象の関数を最適化。しなくても良いかも。
・ディレクティブ(指示子?)追加。I/Oの種類とかパイプラインとか。
てな感じで行きます。