FC2カウンター FPGAの部屋 2021年01月
fc2ブログ

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

FPGAの部屋

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

”IMX219 MIPI sensor to Ultra96-V2 FPGA DisplayPort”をやってみる9

”IMX219 MIPI sensor to Ultra96-V2 FPGA DisplayPort”をやってみる8”の続き。

Raspberry Pi のソニー製Ver. 2 カメラを Ultra96V2 に接続して、 DisplayPort で表示するプロジェクトをやってみることにしたということで、前回は、正常に動作した Vivado 2019.2 のプロジェクトを Vivado 2020.1 に変換して、カメラ画像がディスプレイに表示できるかどうか?確かめてみたところ、正常に画像を表示できた。今回は、更に Vivado 2020.2 に変換してカメラ画像がディスプレイに表示できるか?確かめてみよう。

結論から言うと Vivado 2020.1 プロジェクトを Vivado 2020.2 に変換しても、正常にカメラ画像を表示することができた。
ultra96_mipi_dp_107_210131.png

ultra96_mipi_dp_108_210131.png

これで、無料になった MIPI CSI-2 Rx Subsystem を使うことができるようなった。

次に自分で新規の Vivado 2020.2 プロジェクトを作成して IP を追加して、ブロックデザインを作ってみた(ultra96v2_picam2_dp_202_2)が、こちらはカメラ画像をディスプレイに表示することができなかった。何かが足りないようだ。

File メニューから Export -> Export Block Design... を選択して、 design_1.tcl ファイルを作成した。

新規の Vivado 2020.2 プロジェクトをもう一度作成して、 design_1.tcl ファイルでブロックデザインを再生して、やってみたところカメラ画像をディスプレイに表示することができた。
ultra96_mipi_dp_109_210131.png

ultra96_mipi_dp_110_210131.png

gtkterm の表示内容を示す。
ultra96_mipi_dp_111_210131.png

ディスプレイのカメラ画像を示す。(使いまわしだ)
ultra96_mipi_dp_101_210128.jpg
  1. 2021年01月31日 04:32 |
  2. Ultra96
  3. | トラックバック:0
  4. | コメント:0

”IMX219 MIPI sensor to Ultra96-V2 FPGA DisplayPort”をやってみる8

”IMX219 MIPI sensor to Ultra96-V2 FPGA DisplayPort”をやってみる7”の続き。

Raspberry Pi のソニー製Ver. 2 カメラを Ultra96V2 に接続して、 DisplayPort で表示するプロジェクトをやってみることにしたということで、前回は、Vivado の他のバージョンではどうなんだろう?ということで、Vivado 2019.2 を使ってやってたところ、正常にカメラ画像がディスプレイに表示できた。今回は、正常に動作した Vivado 2019.2 のプロジェクトを Vivado 2020.1 に変換して、カメラ画像がディスプレイに表示できるかどうか?確かめてみよう。

Vivado 2019.2 の前回のプロジェクトを Vivado 2020.1 で読み込むと IP をアップグレードするかを聞いてくるので、アップグレードを選択して、IP をアップグレードした。
プロジェクトを示す。
ultra96_mipi_dp_102_210128.png

ブロックデザインを示す。
ultra96_mipi_dp_103_210128.png

論理合成、インプリメンテーション、ビデオストリームの生成を行った。結果を示す。
ultra96_mipi_dp_104_210128.png

Vitis 2020.1 の画面を示す。Vitis プロジェクトは作り直した。
ultra96_mipi_dp_105_210128.png

Ultra96V2 で試したところ、正常にカメラ画像をディスプレイに表示することができた。
ultra96_mipi_dp_101_210128.jpg

gtkterm の表示を示す。
ultra96_mipi_dp_106_210128.png

ただし、Vitis 2019.2 のプロジェクトもそうだけど、画面がほんの少し揺れるときがある。昨日の晩は少し揺れたけど、今朝はピッタリ止まって表示されている。何らかの原因があるのだろうか?
  1. 2021年01月30日 04:00 |
  2. Ultra96
  3. | トラックバック:0
  4. | コメント:0

”IMX219 MIPI sensor to Ultra96-V2 FPGA DisplayPort”をやってみる7

”IMX219 MIPI sensor to Ultra96-V2 FPGA DisplayPort”をやってみる6”の続き。

Raspberry Pi のソニー製Ver. 2 カメラを Ultra96V2 に接続して、 DisplayPort で表示するプロジェクトをやってみることにしたということで、前回は、Mini DisplayPort のポートがあるディスプレイを購入したので、カメラ画像が映るかどうか?確かめた。カメラ画像は映ったのだが、チラチラしていて正常に表示されない。今回は、Vivado の他のバージョンではどうなんだろう?ということで、Vivado 2019.2 を使ってやってみることにした。

まずは、 source tools/Xilinx/Vitis/2019.2/settings64.sh を実行して、 Vivado 2019.2 の環境を整える。

ultra96v2_imx219_to_displayport/fpga ディレクトリに行って、 make clean を行った。
ultra96_mipi_dp_76_210126.png

ultra96v2_imx219_to_displayport/fpga ディレクトリの Makefile を下のように修正した。
ultra96_mipi_dp_77_210126.png

make build/design_1/design_1.bd を実行した。
ultra96_mipi_dp_78_210126.png
ultra96_mipi_dp_79_210126.png

design_1 ブロックデザインが生成された。
ultra96_mipi_dp_80_210126.png

Vivado 2019.2 を立ち上げて、 ultra96v2_picam2_dp_192 プロジェクトを作成した。
design_1 ブロックデザインをインポートして、Create HDL Wrapper で Verilog HDL のラッパーファイルを生成した。
Ulra96_V2_constraints_190430.xdc をインポートした。
ultra96_mipi_dp_81_210126.png

ブロックデザインを示す。
ultra96_mipi_dp_82_210126.png

Address Editor を示す。
ultra96_mipi_dp_83_210126.png

論理合成、インプリメンテーション、ビデオストリームの生成を行った。
Project Summary を示す。
ultra96_mipi_dp_84_210126.png

Export Hardware を行った。
ultra96_mipi_dp_98_210128.png

Vivado 2020.2 の Tools メニューから Launch Vitis IDE を選択して Vitis 2020.2 を立ち上げる。

Vitis IDE Launcher ダイアログが表示される。
Workspace を選択するのに Brows... ボタンをクリックする。

ultra96v2_picam2_dp_192 ディレクトリで vitis_work 新規フォルダーを作成する。

ltra96v2_picam2_dp_192/vitis_work ディレクトリに入る。”開く”ボタンをクリックする。

Vitis IDE Launcher ダイアログに戻って、ultra96v2_picam2_dp_192/vitis_work ディレクトリが選択されているので、Launch ボタンをクリックする。

Vitis IDE が開いた。
PROJECT から Create Application Project をクリックする。

New Application Project ダイアログが開く。

Platform 画面で Create a new platform from hardware (XSA) タブをクリックする。

XSA File の Brows... ボタンをクリックする。

ultra96v2_picam2_dp_192/design_1_wrapper.xsa ファイルを選択し、”開く”ボタンをクリックする。

XSA File が指定され、Platfom name には design_1_wrapper が表示された。

Application Project Details 画面では、Application project name に ultra96v2_picam2_dp を指定した。

Domain 画面。デフォルトのまま。

Templetes 画面では、Empty Application を選択する。

プラットフォームとアプリケーション・プロジェクトが作成された。

ultra96v2_picam2_dp_system -> ultra96v2_picam2_dp -> src を選択して、右クリックし右クリックメニューから Import Sources... を選択する。

Import Sources ダイアログが開く。
lscript.ld 以外のすべてのファイルにチェックを入れて、Finish ボタンをクリックする。

ultra96v2_picam2_dp_system -> ultra96v2_picam2_dp -> src に選択したファイルがインポートされた。

design_1_wrapper プラットフォームを展開して、 platform.spr をクリックする。
design_1_wrapper -> psu_cortexa53_0 -> zynqmp_fsbl -> Board Support Package をクリックする。
Modify BSP Settings ボタンをクリックする。

Board Support Package Settings ダイアログで standalone をクリックすると、stdin, stdout が psu_uart_0 に設定されているのが分かる。
それを psu_uart_1 に変更する。

同様に、standalone _psu_cortex53_0 -> Board Support Package の Modify BSP Settings ボタンをクリックする。
Board Support Package Settings ダイアログで standalone をクリックし、stdin, stdout を psu_uart_1 に変更する。

psu_pmu_0 -> zynqmp_pmufw -> Board Support Package の Modify BSP Settings ボタンをクリックする。
Board Support Package Settings ダイアログで standalone をクリックし、stdin, stdout を psu_uart_1 に変更する。

ultra96v2_picam2_dp_system を右クリックし右クリックメニューから Build Project を選んだらビルドが始まって、成功した。
ultra96_mipi_dp_99_210128.png

Ultra96V2 + Picam2 と MIPI Adapter Mezzanine を用意し、Ultra96V2 を JTAG モードにして、電源をON した。

Vitis 2020.2 IDE の Assistant ウインドウで ultra96v2_picam2_dp_system -> ultra96v2_picam2_dp -> Debug を右クリックし右クリックメニューから Launch on Hardware (Single Application Debug) を選択した。
ZynqMP がコンフィギュレーションされて、カメラ画像が正常に表示された。成功だ。。。
ultra96_mipi_dp_101_210128.jpg

gtkterm の画面を示す。
ultra96_mipi_dp_100_210128.png
  1. 2021年01月29日 04:54 |
  2. Ultra96
  3. | トラックバック:0
  4. | コメント:0

”IMX219 MIPI sensor to Ultra96-V2 FPGA DisplayPort”をやってみる6

”IMX219 MIPI sensor to Ultra96-V2 FPGA DisplayPort”をやってみる5”の続き。

Raspberry Pi のソニー製Ver. 2 カメラを Ultra96V2 に接続して、 DisplayPort で表示するプロジェクトをやってみることにしたということで、前回は、Vivado プロジェクトにデバック用の ILA IP が実装されているので、Vivado Analyzer で実機の波形を確認したが波形が出ていて動作していそうだった。今回は、Mini DisplayPort のポートがあるディスプレイを購入したので、カメラ画像が映るかどうか?確かめてみる。

購入したディスプレイはいっとうさんと同じ13.3インチのモバイル・ディスプレイだ。
このディスプレイにはネイティブに Mini DisplayPort の入力端子がある。

Vitis から Ultra96V2 をコンフィギュレーションして、アプリケーション・ソフトウェアを起動した。
ターミナルの表示を示す。
ultra96_mipi_dp_95_210128.png

今度は

、Lane count = 2
Link rate = 540Gbps

になっている。

表示はカメラ画像は映っているようなのだが、チラチラして、画像位置もおかしいようだ。
ultra96_mipi_dp_96_210128.jpg

う〜ん。何処が悪いのだろうか?
148.5 MHz の HD 解像度のビデオストリーム出力を 100 MHz で出力しているということで、AXI4-Stream to Video Out IP が underflow しているんじゃないだろうか?ということで、Vivado Analyzer で AXI4-Stream to Video Out IP の underflow の Rise をトリガにしてみたが、トリガかからなかった。つまり underflow していない。。。
ultra96_mipi_dp_94_210128.png

あれ?もしかして、Zynq UltraScale MPSoC の DisplayPort って dp_video_out の hsync と vsync 、 de_out に同期させる必要があったんじゃなかったかな?
(追記)もしかすると、ベアメタルだとDPDMAとか設定されていないから、こちらから供給してもOKとかあるのかな?
(更に追記)PLから入れるクロックと切り替えができると教えてもらった。調べてみると、displayport.c の中で、

XAVBuf_SetAudioVideoClkSrc(&avbuf, XAVBUF_PL_CLK, XAVBUF_PS_CLK);

を実行しているので、PLからのクロックに切り替わっているようだ。

Ultra96のDisplayPortを使用するためのテスト3(XGA解像度のdisplay_cont)
Ultra96のDisplayPortを使用するためのプロジェクトを作成する1(DisplayPort_test_XGA1_sync)

MX219 MIPI sensor to Ultra96-V2 FPGA DisplayPort”では、 dp_video_out_hsync と dp_video_out_vsync が使われていないので、画像が流れるのは、もしかして当たり前なんじゃないだろうか?
ultra96_mipi_dp_97_210128.png
  1. 2021年01月28日 05:26 |
  2. Ultra96
  3. | トラックバック:0
  4. | コメント:0

MIPI CS-2 Receiver Subsystem IP を調べる1

IMX219 MIPI sensor to Ultra96-V2 FPGA DisplayPort”については、いっとうさんがカメラ画像が写ったということなので、同じディスプレイを購入した。今日届くので、試してみようと思う。

MIPI について調べたいと思っていたので、MIPI CS-2 Receiver Subsystem IP を見てみよう。
IMX219 MIPI sensor to Ultra96-V2 FPGA DisplayPort”のブロックデザインでの MIPI CS-2 Receiver Subsystem IP を示す。
ultra96_mipi_dp_85_210127.png

MIPI CS-2 Receiver Subsystem IP をダブルクリックして設定を確認する。
現在のカメラは Raspberry Pi Camera Ver.2.1 のソニー製のカメラだ。

MIPI CS-2 Receiver Subsystem IP の設定ダイアログの Configuration タブを示す。
ultra96_mipi_dp_86_210127.png

Pixel Format は RAW10 Serial Data Lanes は 2 レーンだった。
Include Video Format Bridge にチェックが入っている。
DPHY の Line Rate (Mbps) は 912 Mbps だった。
CSI-2 Option は CSI2 Conroller Registor Interface にチェックが入っている。
Line Buffer Depth は 4096 だった。(2021/01/30:追記)動作した Vivado 2019.2 では 512 だった。
VFB Options の Allowed VC は All 、 Pixels Per Clock は 1 、 TUSER Width は 1 だった。

Shared Login タブを示す。
ultra96_mipi_dp_87_210127.png

Select whether the MMCM and PLL are included in the core itself or in the example design は Include Shared Logic in core のラジオボタンにチェックが入っている。

Pin Assignment タブを示す。
ultra96_mipi_dp_92_210127.png

Clock Lane や Data Lane のピン配置の設定がある。
このピン配置は、特定の位置のピンに決まっているようで、例えば、 Clock Lane の選択肢を示すが、これらから選択となるようだ。
ultra96_mipi_dp_88_210127.png

Application Example Design タブを示す。
ultra96_mipi_dp_89_210127.png

Target Board は ZCU102 、 FCM Model は LI-IMX274MIPI-FMC V1.0 Single Sensor 、 Design Topology は MIPI Video Pipe Camera to Display だった。

ここで Target Board の選択肢を見てみよう。
ultra96_mipi_dp_90_210127.png

他に SP701 と VCK190 がある。
SP701 は Spartan-7 SP701 FPGA 評価キット で 7 シリーズ用の MIPI CS-2 Receiver Subsystem IP のようだ。
VCK190 は Versal AI コア シリーズ VCK190 評価キット で、Versal 用の MIPI CS-2 Receiver Subsystem IP のようだ。

(2021/01/29:追記)
すべてのバスを展開した MIPI CS-2 Receiver Subsystem IP を示す。
video_out_tdata[15:0] と 16 ビット幅となっている。
ultra96_mipi_dp_93_210127.png
  1. 2021年01月27日 05:14 |
  2. IP
  3. | トラックバック:0
  4. | コメント:0

”IMX219 MIPI sensor to Ultra96-V2 FPGA DisplayPort”をやってみる5

”IMX219 MIPI sensor to Ultra96-V2 FPGA DisplayPort”をやってみる4”の続き。

Raspberry Pi のソニー製Ver. 2 カメラを Ultra96V2 に接続して、 DisplayPort で表示するプロジェクトをやってみることにしたということで、前回は、 Ultra96V2 + Picam2 と MIPI Adapter Mezzanine を用意し、アプリケーション・ソフトウェアを起動してカメラをディスプレイに表示しようとしたが黒い画像が表示されただけだった。今回は、Vivado プロジェクトにデバック用の ILA IP が実装されているので、Vivado Analyzer で実機の波形を確認してみよう。

Vitis 2020.2 でアプリケーション・ソフトウェアを起動してから、Vivado 2020.2 で Flow Navigator の PRGRAM AND DEBUG -> Open Hardware Manager -> Open Target -> Auto Connect を選択した。

Vivado Analyzer の画面が表示された。
最初に ila_0 の波形を観察する。
ila_0 は、 mipi_csi2_rx_subsyst_0 の video_out に入れてある。
ultra96_mipi_dp_65_210125.png

hw_ila_1 が ila_0 の波形のようだ。
ultra96_mipi_dp_68_210125.png

拡大してみよう。
TDATA[15:0] を見ると、上位バイトは 00 だが、下位バイトに値が入っているようだ。
ultra96_mipi_dp_69_210125.png

hw_ila_2 には、ビデオ関係の各種制御信号が入っている。
hsync などが動いているのが分かる。
ultra96_mipi_dp_70_210125.png

vsync にトリガを掛けてみたが、動いていた。
ultra96_mipi_dp_71_210125.png

hw_ila_3 は、 AXI_VDMA の M_AXIS_MM2S の AXI4-Stream 出力を受けた Video Test Pattern Generator の vtg_0 の m_axis_video 出力に接続されている。
ultra96_mipi_dp_66_210125.png

拡大すると TDATA にデータが出力されているのが分かる。
ultra96_mipi_dp_67_210125.png

hw_ila_4 は、 v_gamma_lut_0 の出力 m_axi_video に接続されている。これは、 AXI VDMA の S_AXIS_S2MM に接続されている。つまりカメラ側の最終ステージとなる。
ultra96_mipi_dp_72_210125.png

拡大すると、やはりデータが流れているのが分かる。
ultra96_mipi_dp_73_210125.png

hw_ila_5 は AXI_VDMA の M_AXIS_MM2S の AXI4-Stream 出力に接続されている。 Video Test Pattern Generator の vtg_0 の s_axis_video に接続されている。
ultra96_mipi_dp_74_210125.png

拡大すると、やはり TDATA にデータが流れているのが分かる。
ultra96_mipi_dp_75_210125.png

今まで見てきた Vivado Analyzer の波形はすべて問題無さそうなのだが、何処が悪いのだろうか?
ソフトウェアを詳しく見ていこう。
  1. 2021年01月26日 04:27 |
  2. Ultra96
  3. | トラックバック:0
  4. | コメント:0

”IMX219 MIPI sensor to Ultra96-V2 FPGA DisplayPort”をやってみる4

”IMX219 MIPI sensor to Ultra96-V2 FPGA DisplayPort”をやってみる3”の続き。

Raspberry Pi のソニー製Ver. 2 カメラを Ultra96V2 に接続して、 DisplayPort で表示するプロジェクトをやってみることにしたということで、前回は、Vitis 2020.2 を起動してプラットフォームとアプリケーション・プロジェクトを作成して、ビルドを行って成功した。今回は、 Ultra96V2 + Picam2 と MIPI Adapter Mezzanine を用意し、アプリケーション・ソフトウェアを起動してカメラをディスプレイに表示してみよう。

最初に、UART を ps_uart_1 にするのを忘れていた。これでは、ターミナルに表示できないので、変更しよう。
design_1_wrapper プラットフォームを展開して、 platform.spr をクリックする。
design_1_wrapper -> psu_cortexa53_0 -> zynqmp_fsbl -> Board Support Package をクリックする。
Modify BSP Settings ボタンをクリックする。
ultra96_mipi_dp_54_210124.png

Board Support Package Settings ダイアログで standalone をクリックすると、stdin, stdout が psu_uart_0 に設定されているのが分かる。
ultra96_mipi_dp_55_210124.png

それを psu_uart_1 に変更する。
ultra96_mipi_dp_56_210124.png

同様に、standalone _psu_cortex53_0 -> Board Support Package の Modify BSP Settings ボタンをクリックする。
ultra96_mipi_dp_57_210124.png

Board Support Package Settings ダイアログで standalone をクリックし、stdin, stdout を psu_uart_1 に変更する。
ultra96_mipi_dp_58_210124.png

psu_pmu_0 -> zynqmp_pmufw -> Board Support Package の Modify BSP Settings ボタンをクリックする。
ultra96_mipi_dp_59_210124.png

Board Support Package Settings ダイアログで standalone をクリックし、stdin, stdout を psu_uart_1 に変更する。
ultra96_mipi_dp_60_210124.png

Ultra96V2 + Picam2 と MIPI Adapter Mezzanine を用意し、Ultra96V2 を JTAG モードにして、電源をON した。

Vitis 2020.2 IDE の Assistant ウインドウで ultra96v2_picam2_dp_system -> ultra96v2_picam2_dp -> Debug を右クリックし右クリックメニューから Launch on Hardware (Single Application Debug) を選択した。
ZynqMP がコンフィギュレーションされて、ディスプレイでは黒い画像が表示されているのだろうか?画面は真っ暗だが、No Signal にはならない。。。
ultra96_mipi_dp_61_210124.png

gtkterm に表示はされている。(115200 bps, 8 bit, 1 Stop bit)
ultra96_mipi_dp_62_210124.png

現在のUltra96V2 + Picam2 と MIPI Adapter Mezzanine の接続の様子を示す。
ultra96_mipi_dp_63_210124.jpg

MIPI Adapter Mezzanine の上に PMOD 出力のドータボードが乗っている。そこの設定ピンは下のMIPI Adapter Mezzanine と同じ位置に設定ピンをすればOKだ。
ultra96_mipi_dp_64_210124.jpg
  1. 2021年01月24日 09:55 |
  2. Ultra96
  3. | トラックバック:0
  4. | コメント:0

”IMX219 MIPI sensor to Ultra96-V2 FPGA DisplayPort”をやってみる3

”IMX219 MIPI sensor to Ultra96-V2 FPGA DisplayPort”をやってみる2”の続き。

Raspberry Pi のソニー製Ver. 2 カメラを Ultra96V2 に接続して、 DisplayPort で表示するプロジェクトをやってみることにしたということで、前回は、Vivado 2020.2 を起動して、プロジェクトを作成し、 make で生成されたブロックデザイン・ファイルをインポートしたところ、不完全だったので、 tcl ファイルを起動してブロックデザインを作り直した。制約ファイルを追加して、論理合成、インプリメンテーション、ビットストリームの生成を行い、XSA ファイルを生成した。今回は、Vitis 2020.2 を起動してプラットフォームとアプリケーション・プロジェクトを作成して、ビルドを行う。

Vivado 2020.2 の Tools メニューから Launch Vitis IDE を選択して Vitis 2020.2 を立ち上げる。

Vitis IDE Launcher ダイアログが表示される。
Workspace を選択するのに Brows... ボタンをクリックする。
ultra96_mipi_dp_35_210123.png

ultra96v2_picam2_dp ディレクトリで vitis_work 新規フォルダーを作成する。
ultra96_mipi_dp_36_210123.png

ultra96v2_picam2_dp/vitis_work ディレクトリに入る。”開く”ボタンをクリックする。
ultra96_mipi_dp_37_210123.png

Vitis IDE Launcher ダイアログに戻って、ultra96v2_picam2_dp/vitis_work ディレクトリが選択されているので、Launch ボタンをクリックする。
ultra96_mipi_dp_38_210123.png

Vitis IDE が開いた。
PROJECT から Create Application Project をクリックする。
ultra96_mipi_dp_39_210123.png

New Application Project ダイアログが開く。
ultra96_mipi_dp_40_210123.png

Platform 画面で Create a new platform from hardware (XSA) タブをクリックする。
ultra96_mipi_dp_41_210123.png

XSA File の Brows... ボタンをクリックする。
ultra96_mipi_dp_42_210123.png

ultra96v2_picam2_dp/design_1_wrapper.xsa ファイルを選択し、”開く”ボタンをクリックする。
ultra96_mipi_dp_43_210123.png

XSA File が指定され、Platfom name には design_1_wrapper が表示された。
ultra96_mipi_dp_44_210123.png

Application Project Details 画面では、Application project name に ultra96v2_picam2_dp を指定した。
ultra96_mipi_dp_45_210123.png

Domain 画面。デフォルトのまま。
ultra96_mipi_dp_46_210123.png

Templetes 画面では、Empty Application を選択する。
ultra96_mipi_dp_47_210123.png

プラットフォームとアプリケーション・プロジェクトが作成された。
ultra96_mipi_dp_48_210123.png

ultra96v2_picam2_dp_system -> ultra96v2_picam2_dp -> src を選択して、右クリックし右クリックメニューから Import Sources... を選択する。

Import Sources ダイアログが開く。
lscript.ld 以外のすべてのファイルにチェックを入れて、Finish ボタンをクリックする。
ultra96_mipi_dp_49_210123.png

ultra96v2_picam2_dp_system -> ultra96v2_picam2_dp -> src に選択したファイルがインポートされた。
ultra96_mipi_dp_50_210123.png

design_1_wrapper プラットフォームを展開して、 platform.spr をクリックする。
design_1_wrapper -> psu_cortexa53_0 -> zynqmp_fsbl -> Board Support Package をクリックする。
Modify BSP Settings ボタンをクリックする。
ultra96_mipi_dp_51_210123.png

drivers をクリックして、 psu_dp を見ると Driver は dppsu になっている。これでツィッターの情報によると良いようなのだが、Ubuntu 20.04 LTS では、 avbuf になっていたのだが、この Ubuntu 18.04 だとデフォルト値が違うのだろか?
修正する手間がなくてよいが、何か納得行かない。。。
3 個すべての Board Support Package の psu_dp の Driver は dppsu になっていた。
ultra96_mipi_dp_52_210123.png

これで、 ultra96v2_picam2_dp_system を右クリックし右クリックメニューから Build Project を選んだらビルドが始まって、成功した。
ultra96_mipi_dp_53_210123.png
  1. 2021年01月23日 14:01 |
  2. Ultra96
  3. | トラックバック:0
  4. | コメント:0

”IMX219 MIPI sensor to Ultra96-V2 FPGA DisplayPort”をやってみる2

”IMX219 MIPI sensor to Ultra96-V2 FPGA DisplayPort”をやってみる1”の続き。

Raspberry Pi のソニー製Ver. 2 カメラを Ultra96V2 に接続して、 DisplayPort で表示するプロジェクトをやってみることにしたということで、前回は、”MX219 MIPI sensor to Ultra96-V2 FPGA DisplayPort”を git clone して、 make を行った。今回は、Vivado 2020.2 を起動して、プロジェクトを作成し、前回 make で生成されたブロックデザイン・ファイルをインポートしたところ、不完全だったので、 tcl ファイルを起動してブロックデザインを作り直した。制約ファイルを追加して、論理合成、インプリメンテーション、ビットストリームの生成を行い、XSA ファイルを生成する。

Vivado 2020.2 を立ち上げて、Create Project をクリックした。
ultra96_mipi_dp_8_210121.png

New Project ダイアログの Project Name 画面では、 Project name に ultra96v2_picam2_dp を入力した。
ultra96_mipi_dp_9_210121.png

Default Part 画面では、 Boards をクリックして、 Vendor に avnet.com を選択し、 Ultra96v2 Single Board Computer を選択した。
ultra96_mipi_dp_10_210121.png

ultra96v2_picam2_dp プロジェクトが生成された。
ultra96_mipi_dp_11_210121.png

make で生成されたブロックデザインをインポートしよう。
Sources ウインドウで右クリックし右クリックメニューから Add Sources... を選択する。

Add Sources ダイアログが立ち上がる。
Add or create design sources のラジオボタンをクリックする。
ultra96_mipi_dp_12_210121.png

Add or Create Design Soures 画面で Add Files ボタンをクリックする。
ultra96_mipi_dp_13_210121.png

ultra96v2_imx219_to_displayport/fpga/build/design_1 ディレクトリの design_1.bd を選択する。
ultra96_mipi_dp_14_210121.png

Add or Create Design Soures 画面に戻って、 Copy soures into project のチェックボックスにチェックを入れる。
ultra96_mipi_dp_15_210121.png

Sources ウインドウの Design Sources に design_1 が入った。
ultra96_mipi_dp_16_210121.png

ブロックデザインを見ると不完全とうことが分かる。
ultra96_mipi_dp_17_210121.png

ツィッターで知ったのだが、このブロックデザインを削除して、tcl ファイルを起動して、ブロックデザインを作り直すと良いそうだ。
ultra96v2_imx219_to_displayport/fpga/bd ディレクトリの imx219_to_mpsoc_displayport_vivado2020.2.tcl ファイルを実行する。
ultra96_mipi_dp_18_210121.png

design_1 ブロックデザインを削除した。

Vivado 2020.2 の TCL Console で次のコマンドを実行した。
cd /media/masaaki/Ubuntu_Disk/HDL/Ultra96V2/ultra96v2_imx219_to_displayport/fpga/bd
source ./imx219_to_mpsoc_displayport_vivado2020.2.tcl

ultra96_mipi_dp_19_210121.png

すると、ブロックデザインが生成された。
ultra96_mipi_dp_20_210121.png

ブロックデザインを拡大する。
ultra96_mipi_dp_21_210121.png

今度は大丈夫そうだ。
Address Editor 画面を示す。
ultra96_mipi_dp_22_210121.png

Address Map 画面を示す。
ultra96_mipi_dp_23_210121.png

Sources ウインドウで design_1 _i を右クリックし右クリックメニューから Create HDL Wrapper... をクリックして、 design_1_wrapper を生成した。
ultra96_mipi_dp_24_210121.png

次は制約ファイルだが、 ultra96v2_imx219_to_displayport/fpga/constraints ディレクトリの Ultra96_V2_constraints_190430.xdc をインポートしよう。
ultra96_mipi_dp_25_210121.png

Sources ウインドウで右クリックし右クリックメニューから Add Sources... を選択する。

Add Sources ダイアログが立ち上がる。
Add or create constraints のラジオボタンをクリックする。
ultra96_mipi_dp_26_210121.png

Add Files ボタンをクリックして、ultra96v2_imx219_to_displayport/fpga/constraints ディレクトリの Ultra96_V2_constraints_190430.xdc を選択した。
Copy soures into project のチェックボックスにチェックを入れて、Finish ボタンをクリックする。
ultra96_mipi_dp_27_210121.png

Ultra96_V2_constraints_190430.xdc がプロジェクトに入った。
Flow Navigator で Generate Bitstream をクリックして、論理合成、インプリメンテーション、ビットストリームの生成を行う。
ultra96_mipi_dp_28_210121.png

ビットストリームの生成が成功した。 Project Summary を示す。
ultra96_mipi_dp_29_210121.png

File メニューから Export -> Export Hardware... を選択する。
Export Hardware Platform ダイアログが表示される。
Export Hardware Platform 画面は Next > ボタンをクリックして進める。
ultra96_mipi_dp_30_210121.png

Output 画面では、 Include bitstream ラジオボタンをクリックする。
ultra96_mipi_dp_31_210121.png

Files 画面では、 XSA ファイル名と Export されるディレクトリが表示されている。
ultra96_mipi_dp_32_210121.png

Exporting Hardware Platform 画面では、 Finish ボタンをクリックする。
ultra96_mipi_dp_33_210121.png

design_1_wrapper.xsa ファイルが生成された。
ultra96_mipi_dp_34_210121.png
  1. 2021年01月22日 04:46 |
  2. Ultra96
  3. | トラックバック:0
  4. | コメント:0

”IMX219 MIPI sensor to Ultra96-V2 FPGA DisplayPort”をやってみる1

このところ、ツィッターで話題の”MX219 MIPI sensor to Ultra96-V2 FPGA DisplayPort”をやってみたいということでやってみることにした。
これは、Raspberry Pi のソニー製Ver. 2 カメラを Ultra96V2 に接続して、 DisplayPort で表示するプロジェクトだ。

Raspberry Pi のソニー製Ver. 2 カメラを Ultra96V2 に接続するには、 AISTARVISION MIPI Adapter V2.1 が必要になる。これはすでに販売中止だが、幸運なことに私はすでに持っている。

最初に Vivado 2020.2 の settings64.sh を走らせておく。

source /media/masaaki/Ubuntu_Disk/tools/Xilinx/Vitis/2020.2/settings64.sh

最初に ultra96v2_imx219_to_displayport を git clone する。
git clone https://github.com/gtaylormb/ultra96v2_imx219_to_displayport.git
ultra96_mipi_dp_1_210121.png

ultra96v2_imx219_to_displayport ディレクトリを示す。
ultra96_mipi_dp_2_210121.png

ultra96v2_imx219_to_displayport/fpga ディレクトリを見ると Makefile がある。
ultra96_mipi_dp_3_210121.png

Makefile を見ると build/design_1/design_1.bd を実行すれば良さそうだ。

BD_SRC = \
bd/imx219_to_mpsoc_displayport_vivado2020.2.tcl


なので Vivado 2020.2 で実行できそうだ。
ultra96_mipi_dp_4_210121.png

cd ultra96v2_imx219_to_displayport/fpga/ して、
make build/design_1/design_1.bd を実行した。
ultra96_mipi_dp_5_210121.png
ultra96_mipi_dp_6_210121.png

build ディレクトリが生成され、その下に design_1 ディレクトリができて、その下に ip ディレクトリと design_1.bd ファイルが生成されている。
ultra96_mipi_dp_7_210121.png

  1. 2021年01月21日 04:47 |
  2. Ultra96
  3. | トラックバック:0
  4. | コメント:0

Vitis Vision Library の medianblur を ZYBO Z7-20 で使ってみる4

Vitis Vision Library の medianblur を ZYBO Z7-20 で使ってみる3”の続き。

AXI4 Master 版のメディアン・フィルタと XF_8UC3 を XF_8UC4 に変換する IP を使用して、メディアン・フィルタ処理後の画像をディスプレイに出力するシステムを作成しようということで、前回は、 Vitis 2020.2 を立ち上げて、プラットフォームとアプリケーション・プロジェクトを作成し、実機検証を行い、フィルタ処理画像を見ることができた。今回は、複雑な構成になっているので、後先間違っているようだが、ブロック図を書いて、構成を説明する。

使用するボードは ZYBO Z7-20 で、使用ツールは Vivado 2020.2, Vitis 2020.2 だ。

まずは、今回の Vivado 2020.2 の median_vision プロジェクトの構成図を示す。
median_vision_21_210120.png

ソフトウェア側には、 bmp_data.h と median_vision.c がある。
bmp_data.h は 800 x 600 ピクセルの画像に、ノイズを拡散した画像を C のヘッダ・ファイルにしたコードとなっている。 median_vision.c は bmp_data.h を読み込んで、 CV_8UC3 の画像データ(ORG_CV_8UC3 とする)と CV_8UC4 の画像データ(ORG_CV_8UC4 とする)を DDR SDRAM のアドレスに書き込む。
median_vision.c は各 IP の初期化や、設定、ターミナルにプロンプトを表示して、ビットマップ・ディスプレイ・コントローラーの表示画像を切り替えている。

ORG_CV_8UC3 が CV::Mat のデータの部分で、このフォーマットは”Vitis Vision Library の AXI4 Master インターフェース版 medianblur をZYBO Z7-20 で使ってみる1(準備編)”で解析していて、下に示すようなフォーマットになっている。
axim_medianblur_4_210104.png

ORG_CV_8UC4 は 1 ピクセルが 32 ビット単位で、上のバイトレーンから順番にアルファ・チャネル 8 ビット、 Red 8 ビット、 Green 8 ビット、 Blue 8 ビットとなっていて、ビットマップ・ディスプレイ・コントローラーが直接表示できるフォーマットとなっている。

メディアン・フィルタ(median_blur_accel)は CV_8UC3 のデータ部分(ORG_CV_8UC3)を DMA Read して、CV_8UC3 のデータ部分のフィルタ結果(FILT_CV_8UC3 とする)を DMA Write する。 CV_8UC3 のデータではビットマップ・ディスプレイ・コントローラーが表示できないので、 CV_8UC4 に変換する必要がある。そこで、CV_8UC3 を画像フォーマット変換(Xf_8uc3_2rgb)で CV_8UC4 (FILT_CV_8UC4 とする)に変換している。その結果をビットマップ・ディスプレイ・コントローラーで表示する。

それで、ビットマップ・ディスプレイ・コントローラー は ORG_CV_8UC4 と FILT_CV_8UC4 を切り替えてディスプレイに表示する。切り替えの指示はターミナルに 0 を入力するか? 1 を入力するか?で切り替える。
0 の時は、 ORG_CV_8UC4 を表示して、 1 の時は FILT_CV_8UC4 をディスプレイに表示する。
  1. 2021年01月20日 04:43 |
  2. Vitis_Vision
  3. | トラックバック:0
  4. | コメント:0

Vitis Vision Library の medianblur を ZYBO Z7-20 で使ってみる3

AXI4 Master 版のメディアン・フィルタと XF_8UC3 を XF_8UC4 に変換する IP を使用して、メディアン・フィルタ処理後の画像をディスプレイに出力するシステムを作成しようということで、前回は、タイミングエラーを解消して、ビットストリームの生成が成功して、XSA ファイルを生成した。今回は、Vitis 2020.2 を立ち上げて、プラットフォームとアプリケーション・プロジェクトを作成し、実機検証を行う。

Vitis 2020.2 を立ち上げて、前回生成した median_v_bd_wrapper.xsa ファイルを元に、median_v_bd_wrapper プラットフォームを作成し、median_vision アプリケーション・プロジェクトを作成した。

画像をヘッダに変換した bmp_data.h を用意して、アプリケーション・ソフトウェアの median_vision.c を作成した。
median_vision_15_210119.png

median_vision.c のコードを貼っておく。

// median_vision.c
// 2021/01/16 by marsee
//

#include <stdio.h>
#include <stdint.h>
#include "xil_io.h"
#include "xparameters.h"

#include "xmedian_blur_accel.h"
#include "xxf_8uc3_2rgb.h"

#include "bmp_data.h"

#define ORG_PICT_XF_8UC4_ADDR   0x10000000
#define ORG_PICT_XF_8UC3_ADDR   0x10200000
#define FILTER_XF_8UC3_ADDR     0x10400000
#define FILTER_XF_8UC4_ADDR     0x10600000

#define HORIZONTAL_PIXELS   800
#define VERTICAL_LINES      600

int bmp_write_xf_8uc3(uint32_t xf_8uc4_addr, uint32_t xf_8uc3_addr);
void Xil_DCacheFlush(void);

int main(){
    XMedian_blur_accel XMedian_blur_accel_ap;
    XXf_8uc3_2rgb XXf_8uc3_2rgb_ap;
    int inbyte_in;

    XMedian_blur_accel_Initialize(&XMedian_blur_accel_ap, 0);
    XXf_8uc3_2rgb_Initialize(&XXf_8uc3_2rgb_ap, 0);

    XMedian_blur_accel_Set_rows(&XMedian_blur_accel_ap, (u32)VERTICAL_LINES);
    XMedian_blur_accel_Set_cols(&XMedian_blur_accel_ap, (u32)HORIZONTAL_PIXELS);
    XXf_8uc3_2rgb_Set_rows(&XXf_8uc3_2rgb_ap, (u32)VERTICAL_LINES);
    XXf_8uc3_2rgb_Set_cols(&XXf_8uc3_2rgb_ap, (u32)HORIZONTAL_PIXELS);

    XMedian_blur_accel_Set_img_in(&XMedian_blur_accel_ap, (u32)ORG_PICT_XF_8UC3_ADDR);
    XMedian_blur_accel_Set_img_out(&XMedian_blur_accel_ap, (u32)FILTER_XF_8UC3_ADDR);
    XXf_8uc3_2rgb_Set_p_src(&XXf_8uc3_2rgb_ap, (u32)FILTER_XF_8UC3_ADDR);
    XXf_8uc3_2rgb_Set_p_dst(&XXf_8uc3_2rgb_ap, (u32)FILTER_XF_8UC4_ADDR);

    bmp_write_xf_8uc3(ORG_PICT_XF_8UC4_ADDR, ORG_PICT_XF_8UC3_ADDR);
    Xil_DCacheFlush();

    XMedian_blur_accel_Start(&XMedian_blur_accel_ap);
    while(!XMedian_blur_accel_IsDone(&XMedian_blur_accel_ap));

    XXf_8uc3_2rgb_Start(&XXf_8uc3_2rgb_ap);
    while(!XXf_8uc3_2rgb_IsDone(&XXf_8uc3_2rgb_ap));

    Xil_Out32(XPAR_BITMAP_DISP_CNTRLER_0_BASEADDR, ORG_PICT_XF_8UC4_ADDR);

    while(1){
        printf("\nPlease input <0> or <1> (<q> : exit) = ");
        fflush(stdout);
        inbyte_in = inbyte();
        printf("%c", inbyte_in);
        fflush(stdout);
        switch(inbyte_in) {
            case '0': //bmp image
                Xil_Out32(XPAR_BITMAP_DISP_CNTRLER_0_BASEADDR, ORG_PICT_XF_8UC4_ADDR);
                break;
            case '1': // Laplacian filter
                Xil_Out32(XPAR_BITMAP_DISP_CNTRLER_0_BASEADDR, FILTER_XF_8UC4_ADDR);
                break;
            case 'q': // exit
                return(0);
        }
    }
}

int bmp_write_xf_8uc3(uint32_t xf_8uc4_addr, uint32_t xf_8uc3_addr){
    uint32_t pix[4];

    for(int y=0; y<VERTICAL_LINES; y++){
        for(int x=0; x<HORIZONTAL_PIXELS; x++){
            int xf_8uc4 = 0xff000000 + ((uint32_t)bmp_file_array[y][x][2]<<16)
                    +((uint32_t)bmp_file_array[y][x][1]<<8)+(uint32_t)bmp_file_array[y][x][0];
            Xil_Out32(xf_8uc4_addr+(y*HORIZONTAL_PIXELS+x)*sizeof(uint32_t), xf_8uc4);

            switch((x+y*HORIZONTAL_PIXELS)%4){
            case 0 :
                pix[0] = xf_8uc4;
                break;
            case 1 :
                pix[1] = xf_8uc4;
                Xil_Out32(xf_8uc3_addr, ((pix[1]&0xff)<<24)+(pix[0]&0xffffff));
                xf_8uc3_addr += sizeof(uint32_t);
                break;
            case 2 :
                pix[2] = xf_8uc4;
                Xil_Out32(xf_8uc3_addr, ((pix[2]&0xffff)<<16)+((pix[1]&0xffff00)>>8));
                xf_8uc3_addr += sizeof(uint32_t);
                break;
            default : // 3
                pix[3] = xf_8uc4;
                Xil_Out32(xf_8uc3_addr, ((pix[3]&0xffffff)<<8)+((pix[2]&0xff0000)>>16));
                xf_8uc3_addr += sizeof(uint32_t);
                break;
            }
        }
    }
    return(0);
}


ビルドすると median_vision.elf ができた。

ZYBO Z7-20 を接続して、電源ON した。

ターミナルで sudo gtkterm を入力して、 gtkterm を立ち上げて、 115200 bps, 8bit, 1 stop bit に設定した。

Assistant ウインドウの median_vision_system -> median_vision -> Debug を右クリックし、右クリックメニューから Run -> Launch on Hardware (Single Application Debug) を選択して、アプリケーションを起動した。

gtkterm に表示されている。
0 を入力すると元画像で、 1 を入力するとメディアン・フィルタ処理されているはずだ。
median_vision_16_210119.png

最初に表示された元画像は正常に表示されている。 gtkterm で 0 を入力しても同様だ。
ノイズが追加されているのが分かると思う。
median_vision_17_210119.jpg

gtkterm で 1 を入力したところ、メディアン・フィルタ処理画像が表示された。成功だ。
元画像のノイズが取り除かれている。
median_vision_19_210119.jpg
  1. 2021年01月19日 05:04 |
  2. Vitis_Vision
  3. | トラックバック:0
  4. | コメント:0

Vitis Vision Library の medianblur を ZYBO Z7-20 で使ってみる2

AXI4 Master 版のメディアン・フィルタと XF_8UC3 を XF_8UC4 に変換する IP を使用して、メディアン・フィルタ処理後の画像をディスプレイに出力するシステムを作成しようということで、前回は、IP を用意して、ブロックデザインを作成し、論理合成、インプリメンテーション、ビットストリームの生成を行ったところ、タイミング・エラーが発生した。今回は、タイミング・エラーの解消をめざそう。

Intra-Clock のタイミング・エラーが出ているので、 clk_fpga_0 の動作周波数を 100 MHz から 80 MHz に下げてみよう。
median_vision_8_210115.png

これで、再度、論理合成、インプリメンテーション、ビットストリームの生成を行ったが、やはり、タイミング・エラーで clk_fpga_0 の Intra-Clock エラーが出ている。
median_vision_9_210115.png

ここで、CDC は大丈夫と検証されている(ということにしているので)、clk_fpga_0 から pclk_buf へのパスと、 pclk_buf から clk_fpga_0 へのパスに False Path を設定した。
median_vision_10_210115.png

これで、再々度、論理合成、インプリメンテーション、ビットストリームの生成を行った。今度はタイミングがメットした。
median_vision_11_210117.png

ハードウェアをエクスポートして、median_v_bd_wrapper.xsa ファイルを作成した。
median_vision_14_210117.png
  1. 2021年01月18日 04:59 |
  2. Vitis_Vision
  3. | トラックバック:0
  4. | コメント:0

Vitis Vision Library の medianblur を ZYBO Z7-20 で使ってみる1

Vitis HLS 2020.2 で Vitis Vision Library を使用する4(medianblur 編)”で AXI4 Master による DMA 版のメディアン・フィルタを実装した。この実装は、CV_8UC3 のデータを DMA Read してメディアン・フィルタ処理をして、また CV_8UC3 で DMA Write する。そして、”XF_8UC3 を XF_8UC4 に変換する xf_8uc3_2rgb プロジェクトを作成する1”と”XF_8UC3 を XF_8UC4 に変換する xf_8uc3_2rgb プロジェクトを作成する2”で CV_8UC3 と同等の XF_8UC3 のデータを DMA Read して CV_8UC4 と同等の XF_8UC4 フォーマットで DMA Write する IP ができた。
これら 2 つの IP を組み合わせれば、 CV_8UC3 のデータを DMA Read して、 CV_8UC4 で DMA Write する実装が作れる。 CV_8UC4 のデータはアルファ・チャネル、Red、Green、Blue の 8 ビットずつの 32 ビット幅フォーマットになっているので、既存の自作ビットマップ・ディスプレイ・コントローラーを使用すれば、画像としてディスプレイに表示することができる。

そういうことで早速、Vivado 2020.2 を使用してプロジェクトを作っていこう。
Vivado 2020.2 で median_vision プロジェクトを作成した。
median_vision_1_210115.png

ビットマップ・ディスプレイ・コントローラー IP の BMDispCaL 、medianblur、xf_8uc3_2rgb のディレクトリを作成して、各 IP のファイルを中に入れた。
median_vision_12_210117.png

IP Catalog に各 IP を追加した。
median_vision_13_210117.png

median_v_bd ブロックデザインを作成し、各 IP を Add IP してブロックデザインを完成させた。
なお、 processing_system7_0 の FCLK_CLK0 は 100 MHz で、 FCLK_CLK1 は 25 MHz に設定した。
median_vision_2_210115.png

Address Editor 画面を示す。
median_vision_3_210115.png

Address Map 画面を示す。
median_blur_accel_0 は Vitis HLS で INTERFACE オプションの s_axilite で実装された s_axi_control と Vitis HLS の INTERFACE オプションの m_axi で実装された DMA のオフセットアドレスの s_axi_control_r の 2 つのアドレス領域がある。
median_vision_4_210115.png

ビットマップ・ディスプレイ・コントローラーの HDMI 出力ピンを指定する制約ファイルの median_filter_axis.xdc を作成した。
median_vision_5_210115.png

これで準備が整ったので、論理合成、インプリメンテーション、ビットストリームの生成を行った。
結果を示すと、タイミングエラーが発生している。
median_vision_6_210115.png

Flow Navigator から IMPLEMENTATION -> Open Implemented Design をクリックして、Timing Error を表示した。
median_vision_7_210115.png

clk_fpga_0 の内部のタイミング違反と pclk_buf -> clk_fpga_0 のクロック間のパスのタイミング違反、Other Path Groups の **async_default** で clk_fpga_0 -> pclk_buf のクロック間のパスのタイミング違反がでていた。
  1. 2021年01月17日 04:27 |
  2. Vitis_Vision
  3. | トラックバック:0
  4. | コメント:0

XF_8UC3 を XF_8UC4 に変換する xf_8uc3_2rgb プロジェクトを作成する2

XF_8UC3 を XF_8UC4 に変換する xf_8uc3_2rgb プロジェクトを作成する1”の続き。

XF_8UC3 を XF_8UC4 に変換する xf_8uc3_2rgb プロジェクトを作成してみようと思うということで、前回はソースコードとテストベンチを貼って、 Vitis HLS 2020.2 のプロジェクトを示した。今回は、 C シミュレーション、 C コードの合成、 C/RTL 協調シミュレーション、 Export RTL を行う。

Vitis HLS 2020.2 の xf_8uc3_2rgb プロジェクトの設定を示す。
Vitis HLS 2020.1 から Vitis HLS に同梱される OpenCV のインストールは無いということだ。よって、OpenCV を使用しているテストベンチのインクルード・パスとリンク・パスの設定が必要となる。
Project メニューから Project Settings... を選択して、ダイアログを表示させる。
Simulation をクリックして、 CFLAGS にインストール済みの OpenCV のインクルード・パスを入力する。

-I/usr/local/include


Linker Flagに

-L/usr/local/lib -lopencv_core -lopencv_imgcodecs -lopencv_imgproc

を入力する。
Input Argument に画像のファイル名

128x128_blue123.png

を入力する。
xf_8uc3_2rgb_2_210114.png

C シミュレーションから。ここでは、

#define DEBUG

のコメントアウトを外す。
結果を示す。
xf_8uc3_2rgb_3_210114.png

最初は CV_8UC3 のフォーマットの 10 個の入力値が表示され、その下の 10 個の出力値が CV_8UC4 で出力されていることが分かる。

次に、

#define DEBUG

のコメントアウトを行う。
xf_8uc3_2rgb_4_210114.png

これで C コードの合成を行った。結果を示す。
xf_8uc3_2rgb_5_210114.png
xf_8uc3_2rgb_6_210114.png

レイテンシは 480053 クロックだった。 LOOP_TRIPCOUNT が 480000 だったので、レイテンシは良い感じだ。

C/RTL 協調シミュレーションを行ったが、当初 INTERFACE 指示子の m_axi オプションの depth が画像ピッタリのサイズの指定ではなかったため当初はエラーになってしまった。
xf_8uc3_2rgb_8_210114.png

このようなエラーが起こった場合は、 INTERFACE 指示子の m_axi オプションの depth が画像ピッタリのサイズになっているか?をチェックすることをお勧めする。なお、Vivado HLS 2019.2 と Vitis HLS 2020.1 は Vitis HLS 2020.2 よりも進んで、C/RTL 協調シミュレーションの波形は出力されるが、最終的にエラーになる。

さて、C/RTL 協調シミュレーションを行うには、今回は、画像ファイルのファイル名を指定する必要がある。
C/RTL 協調シミュレーションを開始したら、 Co-Simulation Dialog が表示されるので、 Input Argument に画像ファイルの名前を入れる。波形を観察するために Dump Trace も all にしておく。
xf_8uc3_2rgb_7_210114.png

結果を示す。
xf_8uc3_2rgb_10_210114.png

レイテンシは 16446 クロックだった。画像の総ピクセル数は 16384 なので、優秀だ。

C/RTL 協調シミュレーションの波形を示す。
xf_8uc3_2rgb_11_210114.png

WVALID がほとんど 1 なのが分かる。

波形の最初の部分を拡大してみよう。
xf_8uc3_2rgb_12_210114.png

無事に CV_8UC4 に変換されていることが分かる。

Export RTL を行って、IP にした。結果を示す。
xf_8uc3_2rgb_9_210114.png

AXI4 Lite のレジスタマップを示す。

// ==============================================================
// Vitis HLS - High-Level Synthesis from C, C++ and OpenCL v2020.2 (64-bit)
// Copyright 1986-2020 Xilinx, Inc. All Rights Reserved.
// ==============================================================
// control
// 0x00 : Control signals
//        bit 0  - ap_start (Read/Write/COH)
//        bit 1  - ap_done (Read/COR)
//        bit 2  - ap_idle (Read)
//        bit 3  - ap_ready (Read)
//        bit 7  - auto_restart (Read/Write)
//        others - reserved
// 0x04 : Global Interrupt Enable Register
//        bit 0  - Global Interrupt Enable (Read/Write)
//        others - reserved
// 0x08 : IP Interrupt Enable Register (Read/Write)
//        bit 0  - enable ap_done interrupt (Read/Write)
//        bit 1  - enable ap_ready interrupt (Read/Write)
//        others - reserved
// 0x0c : IP Interrupt Status Register (Read/TOW)
//        bit 0  - ap_done (COR/TOW)
//        bit 1  - ap_ready (COR/TOW)
//        others - reserved
// 0x10 : Data signal of ap_return
//        bit 31~0 - ap_return[31:0] (Read)
// 0x18 : Data signal of p_src
//        bit 31~0 - p_src[31:0] (Read/Write)
// 0x1c : reserved
// 0x20 : Data signal of rows
//        bit 31~0 - rows[31:0] (Read/Write)
// 0x24 : reserved
// 0x28 : Data signal of cols
//        bit 31~0 - cols[31:0] (Read/Write)
// 0x2c : reserved
// 0x30 : Data signal of p_dst
//        bit 31~0 - p_dst[31:0] (Read/Write)
// 0x34 : reserved
// (SC = Self Clear, COR = Clear on Read, TOW = Toggle on Write, COH = Clear on Handshake)

  1. 2021年01月15日 04:36 |
  2. Vitis_Vision
  3. | トラックバック:0
  4. | コメント:0

XF_8UC3 を XF_8UC4 に変換する xf_8uc3_2rgb プロジェクトを作成する1

もうだいぶ、Vitis Vision Library には嫌気が差してきたのだが、最後に XF_8UC3 を XF_8UC4 に変換する xf_8uc3_2rgb プロジェクトを作成してみようと思う。
xf_8uc3_2rgb プロジェクトを何で作るのか?だが、とりあえずは Vitis HLS 2020.2 でやってみよう。

まずは、 ソースコードの xf_8uc3_2rgb.cpp を示す。

// xf_8uc3_2rgb.cpp
// 2021/01/12 by marsee
// 2021/01/12: for Vitis HLS 2020.2
//

#include "ap_int.h"
#include "hls_stream.h"

int dmar2axis(ap_uint<32>* _src, int rows, int cols, hls::stream<ap_uint<32> >& axis_out);
int xf_8uc3_2axis(hls::stream<ap_uint<32> >& axis_in, int rows, int cols, hls::stream<ap_uint<32> >& axis_out);
int axis2dmaw(hls::stream<ap_uint<32> >& axis_in, int rows, int cols, ap_uint<32>* _dst);

#define DEBUG

int xf_8uc3_2rgb(ap_uint<32>* _src, int rows, int cols, ap_uint<32>* _dst){
#pragma HLS DATAFLOW
#pragma HLS INTERFACE m_axi depth=16384 bundle=gmem port=_dst offset=slave
#pragma HLS INTERFACE s_axilite port=cols
#pragma HLS INTERFACE s_axilite port=rows
#pragma HLS INTERFACE m_axi depth=12288 bundle=gmem port=_src offset=slave
#pragma HLS INTERFACE s_axilite port=return

    hls::stream<ap_uint<32> > axis0, axis1;

    dmar2axis(_src, rows, cols, axis0);
    xf_8uc3_2axis(axis0, rows, cols, axis1);
    axis2dmaw(axis1, rows, cols, _dst);

    return(0);
}

int dmar2axis(ap_uint<32>* _src, int rows, int cols, hls::stream<ap_uint<32> >& axis_out){
    const int rows_cols_limit = (int)((float)rows * (float)cols * 3.0/4.0 + 0.7);
    ap_uint<32> pix;
    //printf("rows_cols_limit = %d\n",rows_cols_limit);

    LOOP_dr2a: for(int xy=0; xy<rows_cols_limit; xy++){
#pragma HLS PIPELINE II=1
#pragma HLS LOOP_TRIPCOUNT avg=360000 max=360000 min=360000
        pix = _src[xy];
        axis_out << pix;
#ifdef DEBUG
        if(xy < 10)
            printf("%x\n", (unsigned int)pix);
#endif
    }
    return(0);
}

int xf_8uc3_2axis(hls::stream<ap_uint<32> >& axis_in, int rows, int cols, hls::stream<ap_uint<32> >& axis_out){
    ap_uint<32> rgb[3];
    ap_uint<32> pix;
    const int rows_cols_limit = rows * cols;

    LOOP_x2s:for(int xy=0; xy<rows_cols_limit; xy++){
#pragma HLS PIPELINE II=1
#pragma HLS LOOP_TRIPCOUNT avg=480000 max=480000 min=480000
        switch(xy%4){
        case 0 :
            axis_in >> rgb[0];
            pix = (rgb[0] & 0xffffff) + 0xff000000;
            axis_out << pix;
            break;
        case 1 :
            axis_in >> rgb[1];
            pix = ((rgb[1] & 0xffff)<<8) + ((rgb[0] & 0xff000000)>>24) + 0xff000000;
            axis_out << pix;
            break;
        case 2 :
            axis_in >> rgb[2];
            pix = ((rgb[2] & 0xff)<<16) + ((rgb[1] & 0xffff0000)>>16) + 0xff000000;
            axis_out << pix;
            break;
        default : // 3
            pix = ((rgb[2] & 0xffffff00)>>8) + 0xff000000;
            axis_out << pix;
            break;
        }
    }
    return(0);
}

int axis2dmaw(hls::stream<ap_uint<32> >& axis_in, int rows, int cols, ap_uint<32>* _dst){
    const int rows_cols_limit = rows * cols;
    ap_uint<32> pix;

#ifdef DEBUG
    printf("\n");
#endif

    LOOP_a2dw:for(int xy=0; xy<rows_cols_limit; xy++){
#pragma HLS PIPELINE II=1
#pragma HLS LOOP_TRIPCOUNT avg=480000 max=480000 min=480000
        axis_in >> pix;
        _dst[xy] = pix;
#ifdef DEBUG
        if(xy < 10)
            printf("%x\n", (unsigned int)pix);
#endif
    }
    return(0);
}


テストベンチの xf_8uc3_2rgb_tb.cpp を示す。

// xf_8uc3_2rgb_tb.cpp
// 2021/01/12 by marsee
//

#include "ap_int.h"
#include "hls_stream.h"
#include "opencv2/opencv.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgcodecs/imgcodecs.hpp"

int xf_8uc3_2rgb(ap_uint<32>* _src, int rows, int cols, ap_uint<32>* _dst);

int main(int argc, char **argv) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <INPUT IMAGE>", argv[0]);
        exit(1);
    }

    cv::Mat in_img, out_img, conv_img;

    in_img = cv::imread(argv[1], 1); // reading in the color image

    if (in_img.data == NULL) {
        fprintf(stderr, "ERROR: Cannot open image %s\n ", argv[1]);
        exit(1);
    }

    out_img.create(in_img.rows, in_img.cols, CV_8UC4);
    conv_img.create(in_img.rows, in_img.cols, CV_8UC3);

    xf_8uc3_2rgb((ap_uint<32> *)in_img.data, in_img.rows, in_img.cols, (ap_uint<32> *)out_img.data);
    cv::cvtColor(out_img, conv_img, cv::COLOR_BGRA2BGR);

    cv::imwrite("output.png", conv_img);

    return(0);
}


Vitis HLS 2020.2 の xf_8uc3_2rgb プロジェクトを示す。
xf_8uc3_2rgb_1_210114.png

xf_8uc3_2rgb.cpp の

#define DEBUG

を生かすと XF_8UC3 と XF_8UC4 の最初の 10 個のデータを表示する。
C コードの合成の時は、コメントにする。
  1. 2021年01月14日 04:42 |
  2. Vitis_Vision
  3. | トラックバック:0
  4. | コメント:0

Vitis HLS 2020.2 で hls::stream の ap_axiu , ap_axis データタイプが AXI4-Stream ポートでしか使用できない

ちょっとタイトルが長くなってしまったが、今まで、Vivado HLS で複数の関数を並列に実行させるのに、 hls::stream<ap_axiu<32,1,1,1> >& axis_out と言うように、hls::stream の ap_axis, ap_axiu データイプのポートを使用してきたが、これは、Vitis HLS 2020.2 では使用できないようだ。当然ながら、外部への AXI4-Stream 入出力としては使用できるが、内部のポートとしてはエラーになってしまう。

xf_8uc3_2rgb.cpp を示す。このソースコードは、XF_8UC3 を RGB のバイト・レーンにに変換して、hls ストリームとして出力する xf_8uc3_2axis() 関数と、hls ストリームを DMA Write する関数 axis2dmaw() を xf_8uc3_2rgb() 関数で連結してある。

// xf_8uc3_2rgb.cpp
// 2021/01/12 by marsee
//

#include <ap_int.h>
#include <hls_stream.h>
#include <ap_axi_sdata.h>

int xf_8uc3_2axis(ap_uint<32>* _src, int rows, int cols, hls::stream<ap_axiu<32,1,1,1> >& axis_out){
    ap_uint<32> rgb[3];
    const int rows_cols_limit = rows * cols;
    ap_axiu<32,1,1,1> pix;
    int po = 0;

    pix.last = 0;
    LOOP_x2s:for(int xy=0; xy<rows_cols_limit; xy++){
#pragma HLS PIPELINE II=1
#pragma HLS LOOP_TRIPCOUNT avg=480000 max=480000 min=480000
        switch(xy%4){
        case 0 :
            rgb[0] = _src[po++];
            pix.data = (rgb[0] & 0xffffff) + 0xff000000;
            if(xy == 0)
                pix.user = 1;
            else
                pix.user = 0;
            axis_out << pix;
            break;
        case 1 :
            rgb[1] = _src[po++];
            pix.data = ((rgb[1] & 0xffff)<<8) + ((rgb[0] & 0xff000000)>>24) + 0xff000000;
            axis_out << pix;
            break;
        case 2 :
            rgb[2] = _src[po++];
            pix.data = ((rgb[2] & 0xff)<<16) + ((rgb[1] & 0xffff0000)>>16) + 0xff000000;
            axis_out << pix;
            break;
        default: // 3
            pix.data = ((rgb[2] & 0xffffff00)>>8) + 0xff000000;
            axis_out << pix;
            break;
        }
    }
    return(0);
}

int axis2dmaw(hls::stream<ap_axiu<32,1,1,1> >& axis_in, int rows, int cols, ap_uint<32>* _dst){
    const int rows_cols_limit = rows * cols;
    ap_axiu<32,1,1,1> pix;

    LOOP_a2dw:for(int xy=0; xy<rows_cols_limit; xy++){
#pragma HLS PIPELINE II=1
#pragma HLS LOOP_TRIPCOUNT avg=480000 max=480000 min=480000
        axis_in >> pix;
        _dst[xy] = pix.data;
    }
    return(0);
}

int xf_8uc3_2rgb(ap_uint<32>* _src, int rows, int cols, ap_uint<32>* _dst){
#pragma HLS DATAFLOW
#pragma HLS INTERFACE m_axi depth=480000 port=_dst offset=slave
#pragma HLS INTERFACE s_axilite port=cols
#pragma HLS INTERFACE s_axilite port=rows
#pragma HLS INTERFACE s_axilite port=_src
#pragma HLS INTERFACE m_axi depth=4800000 port=_src offset=slave
#pragma HLS INTERFACE s_axilite port=return

    hls::stream<ap_axiu<32,1,1,1> > axis;

    xf_8uc3_2axis(_src, rows, cols, axis);
    axis2dmaw(axis, rows, cols, _dst);

    return(0);
}


Vitis HLS 2020.2 で xf_8uc3_2rgb プロジェクトを作成して、ソースコードとして xf_8uc3_2rgb.cpp を登録して、C コードの合成を行うとエラーが発生した。
axim_medianblur_10_210113.png

エラー内容を示す。

ERROR: [HLS 214-208] The ap_axis|ap_axiu|qdma_axis|hls::axis data types must only be used for AXI-Stream ports in the interface. 'axis_out' is not an AXI-Stream and/or is not a port in the interface (xf_8uc3_2rgb/xf_8uc3_2rgb.cpp:9:0)
ERROR: [HLS 214-208] The ap_axis|ap_axiu|qdma_axis|hls::axis data types must only be used for AXI-Stream ports in the interface. 'axis_in' is not an AXI-Stream and/or is not a port in the interface (xf_8uc3_2rgb/xf_8uc3_2rgb.cpp:48:0)



Vivado HLS 2019.2 で xf_8uc3_2rgb プロジェクトを作成して、同じソースコードの C コードの合成をしたところ、問題なく合成できた。
axim_medianblur_11_210113.png

やはり、Vitis HLS 2020.2 では、関数間の内部のポートとしては、 hls::stream<ap_axiu<32,1,1,1> > が使えなくなってしまったようだ。

それでは、Vitis HLS 2020.1 はどうなんだろう?ということで、同様にやってみたが、 C コードの合成は成功した。
axim_medianblur_12_210113.png

ということで、 hls::stream<ap_axiu<32,1,1,1> > が関数内部のポートとして使えなくなったのは、 Vitis HLS 2020.2 からのようだ。
  1. 2021年01月13日 04:13 |
  2. Vitis HLS
  3. | トラックバック:0
  4. | コメント:2

XF_8UC4 を使用して median_blur_accel() を実装する

OpenCV Mat 形式での .data のデータフォーマットを検証する”では画像ファイルを読み込んだデータを一旦 CV_8UC4 (BGRA)に変換してから、 array2axis() を呼び出すと 32 ビット幅のバイト・レーンに アルファ・チャネル、Red , Green 、Blue が並ぶことが確認できた。こうなると、自分の IP から使用しやすくなる。 medianblur も XF_8UC4 を使って実装してみよう。

まずは、 xf_median_blur_config.h の TYPE の定義を XF_8UC4 に変更した。
axim_medianblur_8_210112.png

xf_median_blur_tb.cpp を変更した。これは全部のソースコードを貼っておく。

/*
 * Copyright 2019 Xilinx, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "common/xf_headers.hpp"
#include "xf_median_blur_config.h"

int main(int argc, char** argv) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <INPUT IMAGE PATH 1>", argv[0]);
        return EXIT_FAILURE;
    }

    cv::Mat in_img, out_img, ocv_ref, diff, cnv_img, cnv_out_img;

//  Reading in the image:
#if GRAY
    in_img = cv::imread(argv[1], 0); // reading in the gray image
#else
    in_img = cv::imread(argv[1], 1); // reading in the color image
    cnv_img.create(in_img.rows, in_img.cols, CV_8UC4);
    cv::cvtColor(in_img, cnv_img, cv::COLOR_BGR2BGRA);
#endif

    if (in_img.data == NULL) {
        fprintf(stderr, "ERROR: Cannot open image %s\n ", argv[1]);
        return EXIT_FAILURE;
    }

// create memory for output image
#if GRAY
    ocv_ref.create(in_img.rows, in_img.cols, CV_8UC1);
    out_img.create(in_img.rows, in_img.cols, CV_8UC1); // create memory for output image
    diff.create(in_img.rows, in_img.cols, CV_8UC1);
#else
    ocv_ref.create(in_img.rows, in_img.cols, CV_8UC3);
    out_img.create(in_img.rows, in_img.cols, CV_8UC4); // create memory for output image
    cnv_out_img.create(in_img.rows, in_img.cols, CV_8UC3);
    diff.create(in_img.rows, in_img.cols, CV_8UC3);
#endif

    // OpenCV reference:
    cv::medianBlur(in_img, ocv_ref, WINDOW_SIZE);

// OpenCL section:
#if GRAY
    size_t image_in_size_bytes = in_img.rows * in_img.cols * 1 * sizeof(unsigned char);
#else
    size_t image_in_size_bytes = in_img.rows * in_img.cols * 3 * sizeof(unsigned char);
#endif
    size_t image_out_size_bytes = image_in_size_bytes;

    // Call the top function
    median_blur_accel((ap_uint<PTR_WIDTH>*)cnv_img.data, cnv_img.rows, cnv_img.cols, (ap_uint<PTR_WIDTH>*)out_img.data);
    cv::cvtColor(out_img, cnv_out_img, cv::COLOR_BGRA2BGR);

    // Write down output images:
    cv::imwrite("hls_out.jpg", cnv_out_img); // kernel output
    cv::imwrite("ref_img.jpg", ocv_ref); // reference image

    absdiff(ocv_ref, cnv_out_img, diff);
    // Save the difference image for debugging purpose:
    cv::imwrite("error.png", diff);
    float err_per;
    xf::cv::analyzeDiff(diff, 10, err_per);

    return 0;
}


xf_median_blur_accel.cpp は変更していない。
これで、 C シミュレーションを行ったところ、エラーになってしまった。
axim_medianblur_9_210112.png

/media/masaaki/Ubuntu_Disk/Xilinx_github/Vitis_Libraries/vision/L1/include/imgproc/xf_median_blur.hpp: In instantiation of ‘void xf::cv::xFMedianNxN(xf::cv::Mat<TYPE, ROWS, COLS, NPC>&, xf::cv::Mat<TYPE, ROWS, COLS, NPC>&, ap_uint<8>, uint16_t, uint16_t) [with int ROWS = 600; int COLS = 800; int PLANES = 4; int TYPE = 7; int NPC = 1; int WORDWIDTH = 0; int TC = 801; int WIN_SZ = 3; int WIN_SZ_SQ = 9; uint16_t = short unsigned int]’:
/media/masaaki/Ubuntu_Disk/Xilinx_github/Vitis_Libraries/vision/L1/include/imgproc/xf_median_blur.hpp:462:56:   required from ‘void xf::cv::medianBlur(xf::cv::Mat<TYPE, ROWS, COLS, NPC>&, xf::cv::Mat<TYPE, ROWS, COLS, NPC>&) [with int FILTER_SIZE = 3; int BORDER_TYPE = 1; int TYPE = 7; int ROWS = 600; int COLS = 800; int NPC = 1]’
../../../xf_median_blur_accel.cpp:41:104:   required from here
/media/masaaki/Ubuntu_Disk/Xilinx_github/Vitis_Libraries/vision/L1/include/imgproc/xf_median_blur.hpp:363:22: エラー: no type named ‘uname’ in ‘struct PixelType<7>’
     XF_PTUNAME(TYPE) OutputValues[XF_NPIXPERCYCLE(NPC)];
                      ^~~~~~~~~~~~
/media/masaaki/Ubuntu_Disk/Xilinx_github/Vitis_Libraries/vision/L1/include/imgproc/xf_median_blur.hpp:368:22: エラー: no type named ‘uname’ in ‘struct PixelType<7>’
     XF_PTUNAME(TYPE) src_buf[WIN_SZ][XF_NPIXPERCYCLE(NPC) + (WIN_SZ - 1)];
                      ^~~~~~~
/media/masaaki/Ubuntu_Disk/Xilinx_github/Vitis_Libraries/vision/L1/include/imgproc/xf_median_blur.hpp:368:22: エラー: no type named ‘uname’ in ‘struct PixelType<7>’
/media/masaaki/Ubuntu_Disk/Xilinx_github/Vitis_Libraries/vision/L1/include/imgproc/xf_median_blur.hpp:363:22: エラー: no type named ‘uname’ in ‘struct PixelType<7>’
     XF_PTUNAME(TYPE) OutputValues[XF_NPIXPERCYCLE(NPC)];
                      ^~~~~~~~~~~~
make: *** [obj/xf_median_blur_accel.o] Error 1
ERROR: [SIM 211-100] 'csim_design' failed: compilation error(s).
INFO: [SIM 211-3] *************** CSIM finish ***************
INFO: [HLS 200-111] Finished Command csim_design CPU user time: 4.12 seconds. CPU system time: 0.3 seconds. Elapsed time: 3.89 seconds; current allocated memory: 164.609 MB.


どうやら、 XF_8UC4 は対応していないようだ。とっても残念。。。
  1. 2021年01月12日 04:27 |
  2. Vitis_Vision
  3. | トラックバック:0
  4. | コメント:0

OpenCV Mat 形式での .data のデータフォーマットを検証する

Vitis Vision Library の examples では、OpenCV Mat 形式の .data を ap_uint<***> * でキャストして、ハードウェア化する関数に渡していた。そのフォーマットを検証してみよう。
ハードウェア化する関数に渡すDMA の 32 ビット幅のデータ形式については、”Vitis Vision Library の AXI4 Master インターフェース版 medianblur をZYBO Z7-20 で使ってみる1(準備編)”でやっていたのだが、その確認と、それに、ツィッターで @waka3_m さんに CV_8UC4 について教えていただいた。( @waka3_m さんには Mat のデータ形式についてもアドバイスいただいた。ありがとうございました。)
そのような理由から、Mat 形式の画像のピクセルを AXI4-Stream にする Vitis HLS 2020.2 の array2axi プロジェクトを作って検証してみよう。

array2axis プロジェクトは Mat 形式データを AXI4-Stream に変換するが、あくまで Mat 形式のデータを検証するためのものだ。ハードウェア化する関数にも printf() でデータフォーマットを書き出しているのだが、気にしないようにして欲しい。
まずは、 array2axis.h から貼っておく。
CONFIG_8UC3 と CONFIG_8UC4 の定義を切り替えるようになっている。
CONFIG_8UC3 が定義されている時は、画像ファイルを読み込んだデータを CV_8UC3 のまま array2axis() を呼び出す。
CONFIG_8UC4 が定義されている時は、画像ファイルを読み込んだデータを一旦 CV_8UC4 (BGRA)に変換してから、 array2axis() を呼び出す。

// array2axis.h
// 2021/01/05 by marsee
//

#ifndef __ARRAY2AXIS_H__
#define  __ARRAY2AXIS_H__

#define PICT_WIDTH      128
#define PICT_HEIGHT 128
#define PICT_LIMIT      (PICT_WIDTH * PICT_HEIGHT)
#define PICT_MAT_LIMIT  (((float)PICT_LIMIT*3.0)/4.0)

#define CONFIG_8UC3
//#define CONFIG_8UC4

#endif


array2axis.cpp を貼っておく。

// array2axis.cpp
// 2021/01/05 by marsee
//

#include <ap_int.h>
#include <hls_stream.h>
#include <ap_axi_sdata.h>

#include "array2axis.h"

ap_uint<32> cutout_bit8(ap_uint<32> data, ap_uint<32> bottom_bit){
    return((data>>bottom_bit) & 0xff);
}

int array2axis(ap_uint<32> *array_in, hls::stream<ap_axiu<32,1,1,1> >& axis_out){
#pragma HLS INTERFACE s_axilite port=return
#pragma HLS INTERFACE axis port=axis_out register_mode=both register
#pragma HLS INTERFACE s_axilite port=array_in
#pragma HLS INTERFACE m_axi port=array_in offset=slave depth=480000
    ap_uint<32> mat_array[3];
    ap_axiu<32,1,1,1> mat_pix;

    int pict_mat_8UC3 = (int)(PICT_MAT_LIMIT+(float)0.7); // Round up

#ifdef CONFIG_8UC4
    LOOP_XY: for(int xy=0; xy<PICT_LIMIT; xy++){
#else
    LOOP_XY: for(int xy=0; xy<pict_mat_8UC3; xy++){
#endif
        unsigned int temp = array_in[xy];
        printf("xy = %d , array_in[xy] = %x\n", xy, temp);

        mat_pix.data = (ap_uint<32>)temp;
        if(xy == 0)
            mat_pix.user = 1;
        else
            mat_pix.user = 0;
        mat_pix.last = 0;
        axis_out.write(mat_pix);
    }
    return(0);
}


array2axis_tb.cpp を貼っておく。

// array2axis_tb.cpp
// 2021/01/06 by marsee
//
// Refers to "Vitis_Libraries/vision/L1/examples/medianblur/xf_median_blur_tb.cpp"
// https://github.com/Xilinx/Vitis_Libraries/blob/master/vision/L1/examples/medianblur/xf_median_blur_tb.cpp
//

#include <opencv2/opencv.hpp>
#include <stdint.h>
#include <ap_int.h>
#include <hls_stream.h>
#include <ap_axi_sdata.h>

#include "array2axis.h"

int array2axis(ap_uint<32> *array_in, hls::stream<ap_axiu<32,1,1,1> >& axis_out);

int main(int argc, char** argv) {
    hls::stream<ap_axiu<32,1,1,1> > outs;
    hls::stream<ap_axiu<32,1,1,1> > outs_soft;
    ap_axiu<32,1,1,1> vals;
    ap_axiu<32,1,1,1> vals_soft;
    cv::Mat cnv_img;

    if (argc != 2) {
        fprintf(stderr, "Usage: Please input image files name");
        return(1);
    }

    cv::Mat in_img = cv::imread(argv[1], 1); // reading in the color image

#ifdef CONFIG_8UC4
    cnv_img.create(in_img.rows, in_img.cols, CV_8UC4);
    cv::cvtColor(in_img, cnv_img, cv::COLOR_BGR2BGRA);

    array2axis((ap_uint<32>*)cnv_img.data, outs);
#else
    array2axis((ap_uint<32>*)in_img.data, outs);
#endif

    return(0);
}


Vitis HLS 2020.2 の array2axis プロジェクトを示す。
axim_medianblur_5_210111.png

Project メニューから Project Settings... を選択して、 Project Settings (array2axis) ダイアログを開いた。
Simulation をクリックする。
array2axis_tb.cpp の CFLAGS に -I/usr/local/include を指定した。フルパスで設定しても相対パスに直されるようだ。
Linker Flaos には

-L/usr/local/lib -lopencv_core -lopencv_imgcodecs -lopencv_imgproc

を指定した。
Input Argument には、”Vitis Vision Library の AXI4 Master インターフェース版 medianblur をZYBO Z7-20 で使ってみる1(準備編)”で作った

128x128_blue123.png

を指定した。

128x128_blue123.png は最初のピクセルの RGB の値を 1, 2, 3 にしてある。その次のピクセルの RGB の値は 4, 5, 6 と言うように順番に RGB の値を増やしている画像で、つまりどの RGB が現在転送されているかを一目瞭然で分かるようにした画像だ。

まずは、 array2axis.h で CONFIG_8UC3 を定義して C シミュレーションを行った。結果を示す。
axim_medianblur_7_210111.png

出力された画像データのフォーマットは”Vitis Vision Library の AXI4 Master インターフェース版 medianblur をZYBO Z7-20 で使ってみる1(準備編)”と同じように見える。

xy = 0 , array_in[xy] = 6010203
xy = 1 , array_in[xy] = 8090405
xy = 2 , array_in[xy] = a0b0c07
xy = 3 , array_in[xy] = 120d0e0f
xy = 4 , array_in[xy] = 14151011
xy = 5 , array_in[xy] = 16171813
xy = 6 , array_in[xy] = 1e191a1b
xy = 7 , array_in[xy] = 20211c1d
xy = 8 , array_in[xy] = 2223241f
xy = 9 , array_in[xy] = 2a252627
xy = 10 , array_in[xy] = ff2829


次に、 array2axis.h で CONFIG_8UC4 を定義し、CONFIG_8UC4 をコメントアウトして、C シミュレーションを行った。結果を示す。
axim_medianblur_8_210111.png

CV_8UC4 は 8 バイトが 4 個、つまり 1 ピクセルが 4 バイト = 32 ビットなので、 32 ビット幅と相性が良い。

xy = 0 , array_in[xy] = ff010203
xy = 1 , array_in[xy] = ff040506
xy = 2 , array_in[xy] = ff070809
xy = 3 , array_in[xy] = ff0a0b0c
xy = 4 , array_in[xy] = ff0d0e0f
xy = 5 , array_in[xy] = ff101112
xy = 6 , array_in[xy] = ff131415
xy = 7 , array_in[xy] = ff161718
xy = 8 , array_in[xy] = ff191a1b
xy = 9 , array_in[xy] = ff1c1d1e
xy = 10 , array_in[xy] = ff1f2021
xy = 11 , array_in[xy] = ff222324
xy = 12 , array_in[xy] = ff252627
xy = 13 , array_in[xy] = ff28292a


CV_8UC4 では、一番上のバイト・レーンにアルファ・チャネルが付くので、これを無視すれば、 AXI4 Master インターフェースのビットマップ・ディスプレイ・コントローラーで、そのまま DMA すれば画像を表示できそうだ。
  1. 2021年01月11日 04:57 |
  2. OpenCV
  3. | トラックバック:0
  4. | コメント:0

ZYBO Z7-20 に xfOpenCV の median フィルタを実装する2(Vivado + Vitis 編)

ZYBO Z7-20 に xfOpenCV の median フィルタを実装する1(Vivado HLS 編)”の続き。

以前、xfOpenCV のサンプルとして Vivado HLS 2019.2 で medianblur を実装したが、それを ZYBO Z7-20 に実装してみようということで、前回は、Vivado HLS 2019.2 で medianblur を再度 IP にした。今回は、その IP を使用して、Vivado 2019.2 でプロジェクトを作成して、ブロックデザインを作成し、論理合成、インプリメンテーション、ビットストリームの生成を行う。 XSA ファイルを生成して、Vitis 2019.2 を立ち上げて、プラットフォームとアプリケーション・プロジェクトを作成し、ZYBO Z7-20 で実機検証を行う。

Vivado 2019.2 で median_filter_axis プロジェクトを作成した。
medianblur_xfopencv_14_210109.png

BMDispCaL , DMA2axis2st , axis2DMA2st , xf_median_blur(これは、Vivado HLS 2019.2 で作成した medianblur IP)をコピーした。
medianblur_xfopencv_15_210109.png

IP Catalog に先程の IP を登録した。
medianblur_xfopencv_16_210109.png

ブロックデザインを作成して、IP を追加して、配線を行った。
medianblur_xfopencv_17_210109.png

Address Editor を示す。
medianblur_xfopencv_18_210109.png

ブロックデザインの HDL Wrapper を作成した。
medianblur_xfopencv_19_210109.png

制約ファイルの median_filter_axis.xdc を作成した。
medianblur_xfopencv_20_210109.png

論理合成、インプリメンテーション、ビットストリームの生成を行った。結果を示す。
medianblur_xfopencv_21_210109.png

XSA ファイルをエクスポートした。
medianblur_xfopencv_22_210109.png

Vivado から Vitis 2019.2 を立ち上げて、プラットフォームとアプリケーション・プロジェクトを作成した。
アプリケーション・ソフトウェアを作成した。
画像のピクセルをヘッダファイルの配列に書いてある bmp_data.h と、その配列を読み込んで、RGB のデータに変換する median_filter_axis.c をプロジェクトに入れた。この RGB の配置については、”xfOpenCV の cvMat2AXIvideoxf() の AXI4-Stream の RGB バイト割当”を参照した。
medianblur_xfopencv_23_210110.png

Ubuntu 18.04 LTS から gtkterm を起動して、設定を行った。
Vitis からアプリケーション・ソフトウェアを Run すると Zynq をコンフィギュレーションして、アプリケーションを起動した。
gtkterm にプロンプトが表示された。
medianblur_xfopencv_24_210110.png

デフォルトでは、ノイズの乗った画像が ZYBO Z7-20 の HDMI OUT から出力されている。
medianblur_xfopencv_25_210110.jpg

1 をキーボードから入力するとメディアン・フィルタ処理されて、ノイズが除かれた画像が表示された。
medianblur_xfopencv_26_210110.jpg

FPGAの部屋 プレゼンツ 「 Zynq&HLSハンズオンセミナー応用編」”では、前回の記事と今回の記事の内容を私と一緒に実装していきます。皆さんもよろしければご参加ください。オンラインなので、全国何処からでもご参加いただけます。
もし、Vivado HLS や Zynq に慣れていないとう方は”FPGAの部屋 プレゼンツ 「 Zynq&HLSハンズオンセミナー基礎編」”からご参加ください。(ステマです。。。)
  1. 2021年01月10日 06:34 |
  2. reVISION, xfOpenCV
  3. | トラックバック:0
  4. | コメント:0

ZYBO Z7-20 に xfOpenCV の median フィルタを実装する1(Vivado HLS 編)

Vitis HLS 2020.2 の Vitis Vision Library の medianblur をやっていたが、今の所、どうしても AXI4-Stream インターフェースにすることができなかった。また、Mat.data のアドレスを与える方法も、そのデータフォーマットがよく分からずにハードウェアすることができていない。(ツィッターで教えてもらったので、うまく行きそうだ。ありがとうございます。)
そこで、以前やってみた AXI4-Stream 版 xfOpenCV の medianblur を ZYBO Z7-20 に実装してみよう。

これは、筑波大学の Vivado HLS セミナでやっている一部でもあり、”FPGAの部屋 プレゼンツ 「 Zynq&HLSハンズオンセミナー応用編」”で行う内容の一部でもある。ただし、HDLab では、 ZYBO Z7-10 で行う様になっているが、ZYBO Z7-20 でも同様に行うことができる。オンラインで行うので全国何処からでもご参加いただけます。もしよろしければ、”FPGAの部屋 プレゼンツ 「 Zynq&HLSハンズオンセミナー応用編」”にご参加ください。(ステマです。)

Vivado HLS 2019.2 で xfOpenCV のAXI4-Stream 版 medianblur をやってみる1
Vivado HLS 2019.2 で xfOpenCV のAXI4-Stream 版 medianblur をやってみる2
Vivado HLS 2019.2 で xfOpenCV のAXI4-Stream 版 medianblur をやってみる3
の記事の xfOpenCV の medianblur を元にやってみよう。

すでに、 xfOpenCV の xf::AXIvideo2xfMat() による AXI4-Stream の RGB のバイト・フィールドは、”xfOpenCV の cvMat2AXIvideoxf() の AXI4-Stream の RGB バイト割当”によって、分かっているので、そのAXI4-Stream の RGB フォーマットに合わせればよい。

Vivado HLS 2019.2 の xfOpenCV の medianblur プロジェクトを示す。
medianblur_xfopencv_1_210109.png

medianblur プロジェクトにノイズを拡散させた 800 x 600 ピクセルの test2.jpg 画像ファイルを追加した。
medianblur_xfopencv_2_210109.png

Solution メニューから Solution Settings... を選択して、 Clock Period を 10 ns に、Part Selection を Zybo Z7-20 (xc7z020clg400-1) に設定した。
medianblur_xfopencv_3_210109.png

Project メニューから Project Settings... を選択して、 Simulation を選択し、 Input Arguments を test2.jpg に変更した。
medianblur_xfopencv_4_210109.png

Vivado HLS 2019.2 の medianblur プロジェクトの Explorer で Test Bench を右クリックし右クリックメニューから Add Files... を選択して test2.jpg を加えた。
medianblur_xfopencv_5_210109.png

C シミュレーションを実行した。
medianblur_xfopencv_6_210109.png

medianblur/solution1/csim/build ディレクトリを示す。
hls.jpg が生成されている。
medianblur_xfopencv_7_210109.png

元画像ファイルの test2.jpg を示す。
ノイズが重畳されているのが分かると思う。
medianblur_xfopencv_8_210109.jpg

そして、この hls.jpg がハードウェアになる予定のメディアン・フィルタ処理した画像となる。ノイズが消えている。
medianblur_xfopencv_9_210109.jpg

C コードの合成結果を示す。これは以前と変化なしだ。
medianblur_xfopencv_10_210109.png

C/RTL 協調シミュレーションを行う。
Co-simulation Dialog でInput Arguments に test2.jpg を入力した。
medianblur_xfopencv_11_210109.png

結果を示す。
Latency は 488452 クロックだった。総ピクセル数は 480000 ピクセルなので、 488452 / 480000 ≒ 1.02 クロック/ピクセルだった。
medianblur_xfopencv_12_210109.png

Export RTL を行った。
最初は Timing met しなかったが、 Solution メニューから Solution Settings... を選択して、Synthesis Settings の Uncertainty を 3 ns に設定したら、Timing met した。
medianblur_xfopencv_13_210109.png
  1. 2021年01月09日 17:02 |
  2. reVISION, xfOpenCV
  3. | トラックバック:0
  4. | コメント:0

GPS と 3軸加速度センサーを使ったシステム10(部品実装)

GPS と 3軸加速度センサーを使ったシステム9(基板の作成)”の続き。

前回は、3 種類の基板が届いたところまでだった。今回は、部品を実装して動作テストを行った。

まずは 3 軸加速度センサー基板を示す。
acc_gps_sensor_44_210108.jpg

GPS 基板を示す。
acc_gps_sensor_45_210108.jpg

ZYBO Z7-20 に接続されるメイン基板を示す。
acc_gps_sensor_46_210108.jpg

ZYBO Z7-20 と接続したところだ。うまく勘合している。
acc_gps_sensor_47_210108.jpg

3 軸加速度センサー基板 1 個と GPS 基板をメイン基板に LAN ケーブルで接続して行った動作テストは問題なかった。
今後は、 3 軸加速度センサー基板を 8 個作成して完全なシステムでの動作テストを行う。
  1. 2021年01月08日 05:01 |
  2. FPGAを使用したシステム
  3. | トラックバック:0
  4. | コメント:0

GPS と 3軸加速度センサーを使ったシステム9(基板の作成)

GPS と 3軸加速度センサーを使ったシステム8(LAN ケーブルを 5m, 7m, 10m に伸ばした)”の続き。

現在はなぜか? 20m の LAN ケーブルでも I2C の伝送ミスが無くなっている。

今回は、去年の 12 月半ばに KiCad で 3 種類の基板を設計した。
1. 3 軸加速度センサーを搭載する基板
acc_gps_sensor_39_210107.png

2. GPS センサーを搭載する基板
acc_gps_sensor_40_210107.png

3. ZYBO Z7-20 の PMOD に接続するコネクタを持ち、 3 軸加速度センサー基板、GPS センサー基板を LAN ケーブルで接続するメイン基板
acc_gps_sensor_41_210107.png

FusionPCB に頼んだ 3 つの基板ができあがってきた。
acc_gps_sensor_42_210107.jpg

LAN コネクタもピッタリハマった。。。良かった〜。
しかし、早速、PMOD からの電源と USB microB コネクタからの電源を切り替える設定ピンのシルクが反対になっているのを発見した。orz

基板の裏面を示す。
acc_gps_sensor_43_210107.jpg

部品を実装してテストするのが楽しみだ。。。
  1. 2021年01月07日 04:47 |
  2. FPGAを使用したシステム
  3. | トラックバック:0
  4. | コメント:0

xfOpenCV の cvMat2AXIvideoxf() の AXI4-Stream の RGB バイト割当

Vivado HLS 2019.2 で xfOpenCV のAXI4-Stream 版 medianblur をやってみる1
Vivado HLS 2019.2 で xfOpenCV のAXI4-Stream 版 medianblur をやってみる2
Vivado HLS 2019.2 で xfOpenCV のAXI4-Stream 版 medianblur をやってみる3
この 3 つの記事でやってきた AXI4-Stream インターフェース版のメディアン・フィルタを自分の環境で使用したい。テストベンチでは cvMat2AXIvideoxf() で cv::Mat を AXI4-Stream に変換しているが、RGB の 8 ビットのバイトは 32 ビット幅でどのバイト・フィールドにあるのかを確かめる。

それを確かめるために、 im0.png を作成した。
im0.png は上 1/3 が赤、真ん中 1/3 が緑、下 1/3 が青の画像だ。これで、Vivado HLS 2019.2 で C/RTL 協調シミュレーションを行うと RGB のバイト位置が分かるはずだ。
Vitis_Vision_Library4Vitis_HLS_51_210105.png

Vivado HLS 2019.2 で xfOpenCV のAXI4-Stream 版 medianblur をやってみる2”の medianblur プロジェクトで im0.png を使用して、 C/RTL 協調シミュレーションを行った。
Vitis_Vision_Library4Vitis_HLS_52_210105.png

C/RTL 協調シミュレーションの内の入力の AXI4-Stream 波形を示す。
Vitis_Vision_Library4Vitis_HLS_53_210105.png

これで、

24 ビット 〜 16 ビットが赤
15 ビット 〜 8 ビットが緑
7 ビット 〜 0 ビットが青

ということが分かった。
  1. 2021年01月05日 20:55 |
  2. reVISION, xfOpenCV
  3. | トラックバック:0
  4. | コメント:0

Vitis Vision Library の AXI4 Master インターフェース版 medianblur をZYBO Z7-20 で使ってみる1(準備編)

Vitis Vision Library の AXI4 Master インターフェース版 medianblur をZYBO Z7-20 で実際に使ってみたい。

それで、Vitis Vision Library の AXI4 Master インターフェース版 medianblur を使用する際に確かめておかなくてはならないことがある。
それは、カメラのピクセル・データを渡す際のバイトの配置のことだ。ピクセルの RGB ごとの位置がわからないとカメラからのデータを配置することができない。

ピクセルの RGB ごとの位置を検証するために特殊な画像を作成した。
それは、PNG画像の最初のピクセルの RGB の値を 1, 2, 3 に設定し、
axim_medianblur_1_210104.png

次のピクセルの RGB の値を 4, 5, 6 に設定した。というように、順番に RGB の値を増やした画像だ。
axim_medianblur_2_210104.png

その画像を、 Vitis HLS 2020.2 の medianblur プロジェクトで C/RTL 協調シミュレーションを行って、 img_in の RDATA で読まれるピクセルの順番を検証する。
axim_medianblur_3_210104.png

img_in の RDATA で読まれるピクセルの順番をまとめた表を示す。
axim_medianblur_4_210104.png

表によると、32 ビット幅の AXI4 Master には 4 バイトのデータが入るので、 1 ピクセルの RGB 値の 3 個とぴったり合うのは最小公倍数の 3 個ということになるようだ。
メモリに、このように入れれば良いとは思うのだが、変換する IP を作る必要があるかな?
  1. 2021年01月04日 05:03 |
  2. Vitis_Vision
  3. | トラックバック:0
  4. | コメント:0

acc_sensor に WDTimer を実装する

Xilinx 社の AXI IIC IP で ADXL355 を使用する”で作った ADXL355 (3軸加速度センサー)の I2C の回路に”Watchdog timer を Vitis HLS 2020.1 で実装する3(デバック修正)”の WDTimer を実装する。

Vivado 2020.1 の acc_sensor プロジェクトを示す。
acc_sensor_22_210103.png

このプロジェクトに WDTimer IP を追加した。
acc_sensor_23_210103.png

WDTimer_0 の rst_out はインバータを通して、 rst_ps7_0_100M IP の aux_rest_in に接続されている。
WDTimer のタイムアウト時間は 5.24 ms だった。今回は、 300 Hz なので、 3.333 ms 毎に加速度データを取得しているので、その間隔でアプリケーション・ソフトウェアでAXI_GPIO 経由で rst_in に 1 を入れて WDTimer をリセットする。

Address Editor 画面を示す。
acc_sensor_24_210103.png

論理合成、インプリメンテーション、ビットストリームの生成を行った。
Project Summary を示す。
acc_sensor_25_210103.png

ハードウェアをエクスポートして XSA ファイルを作った。

Vitis 2020.1 を起動した。
すでにプラットフォームとアプリケーション・プロジェクトは作ってあるので、アップデートした。
ソフトウェアも、WDTimer をリセットするために AXI GPIO に 1 を書き込む部分を追加した。
acc_sensor_26_210103.png

今の所、 I2C が止まっていないので、よく分からない。結局、I2C の途中で止まっていると、ADXL355 をリセットできないので、ダメそうだ。 ADXL355 はリセット端子が無いので、電源をON, OFF できればよいのだが。。。

acc_sensor_test.c を貼っておく。

// acc_sensor_test.c
// 2020/10/12 by marsee
//

#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include "xtime_l.h"

#include "xparameters.h"

#define TX_FIFO_EMPTY   0x80
#define RX_FIFO_EMPTY   0x40
#define BB              0x04

void acc_sensor_init(volatile uint32_t *axi_iic_ad){
    axi_iic_ad[72] = 0x0F; // RX_FIFI_PIRQ
    axi_iic_ad[64] = 0x2; // Control Register (100h) reset tx fifo
    axi_iic_ad[64] = 0x1; // Control Register (100h) enable i2c
}

void idle_check(volatile uint32_t *axi_iic_ad){
    int32_t status_reg;
    int32_t check_bit;

    do{
        status_reg = axi_iic_ad[65]; // Status Register (104h))
        check_bit = status_reg & (TX_FIFO_EMPTY | RX_FIFO_EMPTY | BB);
    }while(check_bit != (TX_FIFO_EMPTY | RX_FIFO_EMPTY)) ;
}

void acc_sensor_write(volatile uint32_t *axi_iic_ad, uint32_t device_addr, uint32_t write_addr, uint32_t write_data){
    idle_check(axi_iic_ad);
    axi_iic_ad[66] = 0x100 | (device_addr & 0xfe); // Slave IIC Write Address, address is 0x108, i2c_tx_fifo
    axi_iic_ad[66] = write_addr & 0xff;           // address
    axi_iic_ad[66] = 0x200 | (write_data & 0xff);      // data
}

uint32_t acc_sensor_read(volatile uint32_t *axi_iic_ad, uint32_t device_addr, uint32_t read_addr){
    int32_t status_reg, rx_fifo_empty;

    idle_check(axi_iic_ad);
    axi_iic_ad[66] = 0x100 | (device_addr & 0xfe); // Slave IIC Write Address, address is 0x108, i2c_tx_fifo
    axi_iic_ad[66] = read_addr & 0xff;  // address byte
    axi_iic_ad[66] = 0x100 | device_addr & 0xff; // Slave IIC Read Address, address is 0x108, i2c_tx_fifo, with repeat start
    axi_iic_ad[66] = 0x201;      // 1 byte data, NACK condition

    do{
        status_reg = axi_iic_ad[65];
        rx_fifo_empty = status_reg & RX_FIFO_EMPTY;
    }while(rx_fifo_empty); // Wait untill not RX_FIFO_EMPTY(Status Register (104h))

    int32_t read_data = axi_iic_ad[67] & 0xff; // Read Receive FIFO (10Ch)
    return(read_data);
}

int main(){
    volatile uint32_t *axi_iic_ad;
    volatile uint32_t *axi_gpio0_ad, *axi_gpio1_ad;
    int32_t dataX, dataY, dataZ;
    int32_t read_data, data_ready;
    XTime cur_time;

    axi_iic_ad = (volatile uint32_t *)XPAR_AXI_IIC_0_BASEADDR;
    axi_gpio0_ad = (volatile uint32_t *)XPAR_AXI_GPIO_0_BASEADDR;
    axi_gpio1_ad = (volatile uint32_t *)XPAR_AXI_GPIO_1_BASEADDR;

    acc_sensor_init(axi_iic_ad);

    acc_sensor_write(axi_iic_ad, 0x3a, 0x2f, 0x52); // Reset
    axi_gpio1_ad[0] = 1; // reported reset
    usleep(1000);

    acc_sensor_write(axi_iic_ad, 0x3a, 0x2c, 0x82); // I2C speed is Hi speed, +-4g

    acc_sensor_write(axi_iic_ad, 0x3a, 0x1e, 0x00); // OFFSET_X_H
    acc_sensor_write(axi_iic_ad, 0x3a, 0x1f, 0x00); // OFFSET_X_L
    acc_sensor_write(axi_iic_ad, 0x3a, 0x20, 0x00); // OFFSET_Y_H
    acc_sensor_write(axi_iic_ad, 0x3a, 0x21, 0x00); // OFFSET_Y_L
    acc_sensor_write(axi_iic_ad, 0x3a, 0x22, 0x00); // OFFSET_Z_H
    acc_sensor_write(axi_iic_ad, 0x3a, 0x23, 0x00); // OFFSET_Z_L

    acc_sensor_write(axi_iic_ad, 0x3a, 0x2d, 0x00); // stanby clear

    do{
        read_data = acc_sensor_read(axi_iic_ad, 0x3b, 0x04);
        data_ready = read_data & 0x01;
    }while(data_ready != 0x01);

    axi_gpio0_ad[2] = 1; // WDTimer enable

    //for(int i=0; i<10000; i++){
    while(1){
        dataX = acc_sensor_read(axi_iic_ad, 0x3b, 0x08) << 12; // XDATA3
        dataX |= (acc_sensor_read(axi_iic_ad, 0x3b, 0x09) << 4); // XDATA2
        dataX |= ((acc_sensor_read(axi_iic_ad, 0x3b, 0x0a) & 0xf0) >> 4); // XDATA1
        if(dataX & 0x80000) // Is the 19th bit 1?
            dataX |= 0xfff00000; // sign extension

        dataY = acc_sensor_read(axi_iic_ad, 0x3b, 0x0b) << 12; // YDATA3
        dataY |= (acc_sensor_read(axi_iic_ad, 0x3b, 0x0c) << 4); // YDATA2
        dataY |= ((acc_sensor_read(axi_iic_ad, 0x3b, 0x0d) & 0xf0) >> 4); // YDATA1
        if(dataY & 0x80000) // Is the 19th bit 1?
            dataY |= 0xfff00000; // sign extension

        dataZ = acc_sensor_read(axi_iic_ad, 0x3b, 0x0e) << 12; // ZDATA3
        dataZ |= (acc_sensor_read(axi_iic_ad, 0x3b, 0x0f) << 4); // ZDATA2
        dataZ |= ((acc_sensor_read(axi_iic_ad, 0x3b, 0x10) & 0xf0) >> 4); // ZDATA1
        if(dataZ & 0x80000) // Is the 19th bit 1?
            dataZ |= 0xfff00000; // sign extension

        XTime_GetTime(&cur_time);
        //printf("time = %lf ms, datax = %x, dataY = %x, dataZ = %x\n", (double)((long long int)cur_time)/335000.0, (int)dataX, (int)dataY, (int)dataZ);
        printf("%lf,%x,%x,%x,%x\n", (double)((long long int)cur_time)/335000.0, (int)dataX, (int)dataY, (int)dataZ, axi_gpio1_ad[0]);

        axi_gpio0_ad[0] = 1;
        axi_gpio0_ad[0] = 0;
        axi_gpio1_ad[0] = 0;

        // usleep(2434); // for 400 kHz
        usleep(1581); // for 200 kHz
    }

    return(0);
}


  1. 2021年01月03日 20:41 |
  2. FPGAを使用したシステム
  3. | トラックバック:0
  4. | コメント:0

Vitis HLS 2020.2 で xfOpenCV の medianblur をやってみよう

Vitis Vision Library で AXI4-Stream 入出力の medianblur を実装する1”で Vitis Vision Library で AXI4-Stream 入出力の medianblur を実装したのだが、C シミュレーションはエラーで実行できなかった。
それでは、 xfOpenCV を Vitis HLS 2020.2 では使えないのだろうか? ということでやってみた。

Vivado HLS 2019.2 で xfOpenCV のAXI4-Stream 版 medianblur をやってみる1”のコードをそのまま使用して、”Vivado HLS 2019.2 で xfOpenCV の medianblur をやってみる1”の Vivado HLS の設定をそのまま使用するのだが、多少変更する必要がある。

Vitis HLS 2020.2 で xfopencv ディレクトリの下に medainblur プロジェクトを作成した。
Vitis Vision Library で AXI4-Stream 入出力の medianblur を実装する1”と同じファイルを持ってきて、Vitis HLS の Source に xf_median_blur.cpp を Test Bench に im0.jpg と xf_median_blur_tb.cpp を登録した。
Vitis_Vision_Library4Vitis_HLS_47_210102.png

Vitis HLS 2020.2 の Project メニューから Project Settings... を選択すると、Project Settings (medianblur) ダイアログが開く。
Project Settings (medianblur) ダイアログで、Simulation をクリックして、xf_median_blur_tb.cpp の CFLAGS に

-D__SDSVHLS__ -I/home/masaaki/xfopencv/include --std=c++0x -I/usr/local/include

を入力した。これは、Vitis HLS 2020.2 は OpenCV へのリンクが無いので、自分でインストールした OpenCV 3.4.9 の include へのパスを追加した。
Linker Flags に

-L/usr/local/lib -lopencv_core -lopencv_imgcodecs -lopencv_imgproc

を入力した。
Input Arguments に

im0.jpg

を設定した。
Vitis_Vision_Library4Vitis_HLS_48_210102.png

Project Settings (medianblur) ダイアログで、Synthesis をクリックして、

-D__SDSVHLS__ -I/home/masaaki/xfopencv/include --std=c++0x

を設定した。
Vitis_Vision_Library4Vitis_HLS_49_210102.png

これで準備が整った。
Project メニューから Run Simulation を選択して、 C シミュレーションをスタートしたが、エラーで終了した。
Vitis_Vision_Library4Vitis_HLS_50_210102.png

エラー内容を示す。

INFO: [SIM 2] *************** CSIM start ***************
INFO: [SIM 4] CSIM will launch GCC as the compiler.
   Compiling ../../../xf_median_blur_tb.cpp in debug mode
csim.mk:73: recipe for target 'obj/xf_median_blur_tb.o' failed
In file included from ../../../../../../../../../home/masaaki/xfopencv/include/common/xf_axi_io.h:33:0,
                 from ../../../../../../../../../home/masaaki/xfopencv/include/common/xf_infra.h:43,
                 from ../../../xf_median_blur_tb.cpp:11:
/media/masaaki/Ubuntu_Disk/tools/Xilinx/Vitis_HLS/2020.2/include/utils/x_hls_utils.h:243:40: 警告: ‘hls_preserve’ 属性指示が無視されました [-Wattributes]
 __attribute__((hls_preserve)) T reg(T d)
                                        ^
In file included from ../../../xf_median_blur_tb.cpp:12:0:
../../../../../../../../../home/masaaki/xfopencv/include/common/xf_axi.h: In instantiation of ‘void cvMat2AXIvideoxf(cv::Mat&, hls::stream<ap_axiu<W, 1, 1, 1> >&) [with int NPC = 1; int W = 32]’:
../../../xf_median_blur_tb.cpp:63:38:   required from here
../../../../../../../../../home/masaaki/xfopencv/include/common/xf_axi.h:144:18: エラー: conversion from ‘cv::Mat’ to non-scalar type ‘IplImage {aka _IplImage}’ requested
   IplImage img = cv_mat;
                  ^~~~~~
../../../../../../../../../home/masaaki/xfopencv/include/common/xf_axi.h: In instantiation of ‘void AXIvideo2cvMatxf(hls::stream<ap_axiu<W, 1, 1, 1> >&, cv::Mat&) [with int NPC = 1; int W = 32]’:
../../../xf_median_blur_tb.cpp:65:39:   required from here
../../../../../../../../../home/masaaki/xfopencv/include/common/xf_axi.h:259:17: エラー: conversion from ‘cv::Mat’ to non-scalar type ‘IplImage {aka _IplImage}’ requested
  IplImage img = cv_mat;
                 ^~~~~~
make: *** [obj/xf_median_blur_tb.o] Error 1
ERR: [SIM 100] 'csim_design' failed: compilation error(s).
INFO: [SIM 3] *************** CSIM finish ***************


エラー内容は”conversion from ‘cv::Mat’ to non-scalar type ‘IplImage {aka _IplImage}’”ということだ。
これは、”Vitis Vision Library で AXI4-Stream 入出力の medianblur を実装する1”と同じエラーだ。
  1. 2021年01月02日 05:36 |
  2. reVISION, xfOpenCV
  3. | トラックバック:0
  4. | コメント:0

Vitis Vision Library で AXI4-Stream 入出力の medianblur を実装する1

Vitis HLS 2020.2 で Vitis Vision Library を使用する4(medianblur 編)”で Vitis Vision Library で medianblur をたぶん実装できたのだが、(たぶんというのはノイジーな画像を入力していないからだ)配列からDMA してフィルタをかけた、つまり、DMA Read ー フィルタ ー DMA Write ということだ。これを AXI4-Stream ー フィルタ ー AXI4-Stream の方式でフィルタしたい。こうした方がメモリ帯域を消費しなくてすむ。

xfOpenCV 版の AXI4-Stream 入出力 medianblur を持ってきて変更する。

xfOpenCV からの変更点としては、 Vitis_Libraries/vision/L1/include/common ディレクトリにあるファイルが .h でなく .hpp になっているということだ。
Vitis_Vision_Library4Vitis_HLS_44_210101.png

common ディレクトリからのインクルード文を .h から .hpp にすべて変更した。
Vitis_Vision_Library4Vitis_HLS_45_210101.png

C シミュレーションを行ったところ、エラーになってしまった。
Vitis_Vision_Library4Vitis_HLS_46_210101.png

INFO: [SIM 2] *************** CSIM start ***************
INFO: [SIM 4] CSIM will launch GCC as the compiler.
   Compiling ../../../xf_median_blur_tb.cpp in debug mode
csim.mk:73: recipe for target 'obj/xf_median_blur_tb.o' failed
In file included from ../../../../../../../Xilinx_github/Vitis_Libraries/vision/L1/include/common/xf_structs.hpp:27:0,
                 from ../../../../../../../Xilinx_github/Vitis_Libraries/vision/L1/include/common/xf_common.hpp:20,
                 from ../../../../../../../Xilinx_github/Vitis_Libraries/vision/L1/include/common/xf_sw_utils.hpp:20,
                 from ../../../xf_headers.h:51,
                 from ../../../xf_median_blur_tb.cpp:9:
/media/masaaki/Ubuntu_Disk/tools/Xilinx/Vitis_HLS/2020.2/include/ap_axi_sdata.h:97:40: 警告: variable templates only available with -std=c++14 or -std=gnu++14
 template <typename T> constexpr size_t bitwidth = sizeof(T) * CHAR_BIT;
                                        ^~~~~~~~
/media/masaaki/Ubuntu_Disk/tools/Xilinx/Vitis_HLS/2020.2/include/ap_axi_sdata.h:99:38: 警告: variable templates only available with -std=c++14 or -std=gnu++14
 template <size_t W> constexpr size_t bitwidth<ap_int<W>> = W;
                                      ^~~~~~~~~~~~~~~~~~~
/media/masaaki/Ubuntu_Disk/tools/Xilinx/Vitis_HLS/2020.2/include/ap_axi_sdata.h:100:38: 警告: variable templates only available with -std=c++14 or -std=gnu++14
 template <size_t W> constexpr size_t bitwidth<ap_uint<W>> = W;
                                      ^~~~~~~~~~~~~~~~~~~~
/media/masaaki/Ubuntu_Disk/tools/Xilinx/Vitis_HLS/2020.2/include/ap_axi_sdata.h:102:18: 警告: variable templates only available with -std=c++14 or -std=gnu++14
 constexpr size_t bitwidth<ap_fixed<_AP_W, _AP_I, _AP_Q, _AP_O, _AP_N>> = _AP_W;
                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/media/masaaki/Ubuntu_Disk/tools/Xilinx/Vitis_HLS/2020.2/include/ap_axi_sdata.h:104:18: 警告: variable templates only available with -std=c++14 or -std=gnu++14
 constexpr size_t bitwidth<ap_ufixed<_AP_W, _AP_I, _AP_Q, _AP_O, _AP_N>> = _AP_W;
                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/media/masaaki/Ubuntu_Disk/tools/Xilinx/Vitis_HLS/2020.2/include/ap_axi_sdata.h:107:18: 警告: variable templates only available with -std=c++14 or -std=gnu++14
 constexpr size_t bytewidth = (bitwidth<T> + CHAR_BIT - 1) / CHAR_BIT;
                  ^~~~~~~~~
In file included from ../../../../../../../Xilinx_github/Vitis_Libraries/vision/L1/include/common/xf_axi.hpp:23:0,
                 from ../../../xf_median_blur.h:15,
                 from ../../../xf_median_blur_tb.cpp:10:
/media/masaaki/Ubuntu_Disk/tools/Xilinx/Vitis_HLS/2020.2/include/utils/x_hls_utils.h:243:40: 警告: ‘hls_preserve’ 属性指示が無視されました [-Wattributes]
 __attribute__((hls_preserve)) T reg(T d)
                                        ^
In file included from ../../../xf_median_blur.h:15:0,
                 from ../../../xf_median_blur_tb.cpp:10:
../../../../../../../Xilinx_github/Vitis_Libraries/vision/L1/include/common/xf_axi.hpp: In instantiation of ‘void xf::cv::cvMat2AXIvideoxf(cv::Mat&, hls::stream<hls::axis<ap_uint<W>, 1ul, 1ul, 1ul> >&) [with int NPC = 1; int W = 32]’:
../../../xf_median_blur_tb.cpp:62:41:   required from here
../../../../../../../Xilinx_github/Vitis_Libraries/vision/L1/include/common/xf_axi.hpp:124:20: エラー: conversion from ‘cv::Mat’ to non-scalar type ‘IplImage {aka _IplImage}’ requested
     IplImage img = cv_mat;
                    ^~~~~~
../../../../../../../Xilinx_github/Vitis_Libraries/vision/L1/include/common/xf_axi.hpp: In instantiation of ‘void xf::cv::AXIvideo2cvMatxf(hls::stream<hls::axis<ap_uint<W>, 1ul, 1ul, 1ul> >&, cv::Mat&) [with int NPC = 1; int W = 32]’:
../../../xf_median_blur_tb.cpp:64:42:   required from here
../../../../../../../Xilinx_github/Vitis_Libraries/vision/L1/include/common/xf_axi.hpp:221:20: エラー: conversion from ‘cv::Mat’ to non-scalar type ‘IplImage {aka _IplImage}’ requested
     IplImage img = cv_mat;
                    ^~~~~~
make: *** [obj/xf_median_blur_tb.o] Error 1
ERR: [SIM 100] 'csim_design' failed: compilation error(s).
INFO: [SIM 3] *************** CSIM finish ***************


エラー内容は”conversion from ‘cv::Mat’ to non-scalar type ‘IplImage {aka _IplImage}’”ということだ。

エラー内容を検索すると、”Vitis Library for Video-Stream Core, OpenCV Version?”が見つかった。エラー内容は同じだが、解決策は提示されていない。

Vitis Vision cores AXI4 Stream buggy”も検索できた。Vitis Vision Library で AXI4-Stream はバギーなのかも知れない?が、この例では、テストベンチでも xf::cv::Mat をインスタンスして、xf::cv::xfMat2AXIvideo() で AXI4-Stream に変換している。一度、この通りにやってみようと思う。
  1. 2021年01月01日 07:49 |
  2. Vitis_Vision
  3. | トラックバック:0
  4. | コメント:0

あけましておめでとうございます。今年もよろしくお願いします。

新年あけましておめでとうございます。今年もよろしくお願いします。

昨年中は正の FC2 Blog と副の livedoor Blog 合わせて 557,536 アクセスいただきました。
access_210101.png

そういえば、去年は Tsukuba Mini Maker Faire 2020 に参加したんですね。遠い昔のようですが、コロナ騒ぎでギリギリの開催だったけど、でられてよかったです。今年もコロナが蔓延してきているようですが、ならないように気をつけたいと思います。

今年も”FPGAの部屋”のブログをどうぞ、よろしくお願いします。
  1. 2021年01月01日 04:39 |
  2. 日記
  3. | トラックバック:0
  4. | コメント:0