FC2カウンター FPGAの部屋 2015年07月

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

FPGAの部屋

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

SDSoC 2015.2 のチュートリアル2(システム最適化)をやってみた2

SDSoC 2015.2 のチュートリアル2(システム最適化)をやってみた1”の続き。

SDSoC 環境ユーザー ガイド 入門 UG1028 (v2015.2) 2015 年 7 月 20 日”の 36 ページからの”その他の演習”をやってみた。

LinuxではMMUでメモリ管理され、ページと呼ばれる4Kバイト程度の領域に分けられています。通常はソフトウェアが利用する時は、malloc() などでメモリ領域を確保しますが、例えば64Kバイト確保したとして、その領域が物理的に連続している領域とは限りません。ハードウェアと連帯してアクセラレータとして使用している場合は、ハードウェアは物理メモリとしかやりとりが出来ません。つまり、DMAでデータを転送する場合は、4KバイトごとにDMAを区切って、ソフトウェアで使用しているアドレスに連続的にDMAしていく必要があります。それを最初に設定できるのがスキャッター・ギャザーDMAです。シンプルDMAでもできないわけじゃないですが、4KバイトDMAが終了したごとに転送設定をするのが大変です。SDSoCでは、sds_alloc() を使うと物理的にメモリの連続領域が取得できて、シンプルDMAを使うのに都合が良くなるそうです。

データ ムーバー選択の制御
ここでは、先ほど説明したDMAを pragma で明示的に指定します。
labn -> src -> mmult.h の mult() の宣言の前に下の pragma を挿入します。(”SDSoC 環境ユーザー ガイド 入門 UG1028 (v2015.2) 2015 年 7 月 20 日”の37ページから引用します

#pragma SDS data data_mover(in_A:AXIDMA_SG, in_B:AXIDMA_SIMPLE, out_C:AXIFIFO)


SDSoC_43_150731.png

セーブして、左端の Project Explorer タブで、labn を右クリックし、右クリックメニューから Clean Project を選択し、プロジェクトをクリーンにします。
左端の Project Explorer タブで、labn を右クリックし、右クリックメニューから Build Project を選択して、ビルドを行います。
SDSoC_41_150730.png

ビルド終了後に、左端の Project Explorer タブで、labn -> SDRelease -> reports -> data_motion.html をダブルクリックして開いたところです。
SDSoC_44_150731.png

mmult_0 の in_A の pragma に •data_mover:AXIDMA_SG、in_B に •data_mover:AXIDMA_SIMPLE、out_C に •data_mover:AXIFIFO が追加されていました。

D:\SDSoC\Examples\tut2\labn\SDRelease\_sds\p0\ipi のVivado プロジェクトも datamover_0 のみDMAがスキャッター・ギャザーモードになっていました。Zynq PSでは、AXI_ACPポートが使われていました。
SDSoC_45_150731.png


sds_alloc() を malloc() に変更
先ほどの data_mover の pragma を消しておきます。

labn -> src -> main.cpp を開いて、”sds_alloc()”を”malloc()”に全部置き換えます。
SDSoC_46_150731.png

次に、”sds_free()”も”free()”にすべて置き換えます。
SDSoC_47_150731.png

セーブして、左端の Project Explorer タブで、labn を右クリックし、右クリックメニューから Clean Project を選択し、プロジェクトをクリーンにします。
左端の Project Explorer タブで、labn を右クリックし、右クリックメニューから Build Project を選択使用としたのですが、ハイドされていて選べませんでした。セーブする前にビルドしてしまって、慌ててセーブしたためかもしれません。
仕方がないので、Project メニューから Build All を選択したら、ビルドしていました。

ビルド終了後に、左端の Project Explorer タブで、labn -> SDRelease -> reports -> data_motion.html をダブルクリックして開いたところです。
右端の Connection に表示されているDMAは _SG が付いていてスキャッター・ギャザーDMAになっているようでした。
SDSoC_48_150731.png

D:\SDSoC\Examples\tut2\labn\SDRelease\_sds\p0\ipi のVivado プロジェクトも AXI DMA は date_mover_0, data_mover_1 共にスキャッター・ギャザー・モードに設定されていました。
SDSoC_49_150731.png
  1. 2015年07月31日 05:07 |
  2. SDSoC
  3. | トラックバック:0
  4. | コメント:0

SDSoC 2015.2 のチュートリアル2(システム最適化)をやってみた1

SDSoC 環境ユーザー ガイド 入門 UG1028 (v2015.2) 2015 年 7 月 20 日”の 28 ページの”第3章 チュートリアル : システム最適化”をやってみた。

途中までは前回やった手順と全く一緒なので、前回の記事を途中まで貼り付ける。

最初に SDSoC 2015.2 を立ち上げるとワークスペースを聞いてくる。D:\SDSoC\Examples\tut2 フォルダを作製して初めてワークスペースに指定した。
SDSoC_39_150730.png

SDSoC 2015.2 が立ち上がった。
Create SDSoC Project をクリックした。
SDSoC_2_150726.png

New Project ダイアログが表示された。
Project name をlabn に指定した。
Platform をクリックすると、使用できるボードがプルダウンメニューで表示された。microzed や zc702, zc706, zed, zybo などが使用できるようだ。
SDSoC_3_150726.png

OSはデフォルトでLinux が選択されているので、そのままとして、Next >ボタンをクリックした。
SDSoC_4_150726.png

Templates ダイアログで、Available Templates に Matrix Multiplication and Addition が見つからない。
もしかして、ZYBO には無いのでは?という疑惑から、< Back ボタンをクリックして前の画面に戻した。
SDSoC_5_150726.png

Plaform をクリックして zc702 に変更し、Next >ボタンをクリックした。
SDSoC_6_150726.png

Templates ダイアログで、Available Templates に Matrix Multiplication and Addition が見つかった。やはり ZYBO には無かったようだ。 Finish をクリックした。
SDSoC_7_150726.png

labn プロジェクトが作成され、src フォルダの下には、C++ ソースファイルも生成されていた。また、SDSoC Project Overview が表示されている。
SDSoC_8_150726.png

Build アイコンのプルダウンメニューから SDRelease を選択する。
SDSoC_9_150726.png

するとビルドが始まる。
SDSoC_10_150726.png

ビルド終了。 labn プロジェクトにBinaries フォルダができて、その下に labn.elf が3つ入った。更にSDRelease フォルダもできて、その下にも labn.elf と labn.elf.bit ができた。
SDSoC_11_150726.png

labn/project.sdsoc で、Hardware Function の+記号(Add Hardware Function)をクリックする。
SDSoC_12_150727.png

Select function for hardware acceleration ダイアログが立ち上がった。

madd() と mmult() を選択し、OKボタンをクリックする。
SDSoC_13_150727.png

madd() と mmult() が Hardware Functions に入った。
SDSoC_14_150727.png

labn タブの Options の Gnerate Bit Stream と Gnerate SD card Image のチェックを外す。
SDSoC_38_150730.png

左端の Project Explorer タブで、labn を右クリックし、右クリックメニューから Build Project を選択して、ビルドを行う
SDSoC_15_150727.png

ビルドが始まった。
SDSoC_16_150727.png

左端の Project Explorer タブで、labn -> SDRelease -> reports -> data_motion.html をダブルクリックして開いた。
SDSoC_18_150728.png

左端の Project Explorer タブで、labn -> src -> mmult.h を開いて、void mmult() の直前に

#pragma SDS data sys_port(in_A:ACP, in_B:AFI)

を追加する。

セーブを行った。
SDSoC_40_150730.png

左端の Project Explorer タブで、labn を右クリックし、右クリックメニューから Clean Project を選択し、プロジェクトをクリーンにする。

左端の Project Explorer タブで、labn を右クリックし、右クリックメニューから Build Project を選択して、ビルドを行う
SDSoC_41_150730.png

左端の Project Explorer タブで、labn -> SDRelease -> reports -> data_motion.html をダブルクリックして開いた。

mmult_0 の in_A のPragmas に ・ sys_prot:ACP が mmult_0 の in_B のPragmas に ・ sys_prot:AFI が追加された。
SDSoC_42_150730.png
  1. 2015年07月30日 04:39 |
  2. SDSoC
  3. | トラックバック:0
  4. | コメント:0

SDSoC 2015.2 のチュートリアルのファイル構造を確認してみた

SDSoC 2015.2 のファイル構造を見ていくことにする。プロジェクトは、”SDSoC 2015.2 のチュートリアルをやってみた2(ハードウェア・アクセレーション)”とする。

SDSoC 2015.2 のチュートリアルをやってみた2(ハードウェア・アクセレーション)”の下の記事を引用するところから始める。

D:\SDSoC\labn\SDRelease\_sds\swstubs フォルダの内容を示す。
SDSoC_22_150729.png

D:\SDSoC\labn\SDRelease\_sds\swstubs の main.cpp の mmultadd_test() 関数を引用する。
hw_sds_clk_start() と hw_sds_clk_stop() で囲われている部分を見ると、_p0_mmult_0() と _p0_madd_0() が使われている。
SDSoC_20_150728.png

D:\SDSoC\labn\SDRelease\_sds\swstubs の madd.cpp の _p0_madd_0() を引用する。
これを見ると、cf_send_i (), cf_receive_i (), cf_wait (), などが使われていて、ハードウェア・アクセラレータにデータを送って、受けていることが伺われる。
SDSoC_21_150728.png

madd.cpp の _p0_madd_0 () の前には #pragma HLS が書いてある madd() があって、これはVivado HLS 用の madd() で、元々の madd() 関数だ。

mmult.cpp も同様に、 _po_mmult_0 () があって、こっちは、cf_send_i (), cf_wait (), が使われていて、 cf_receive_i () が使われていないのだが、どうしてだろうか?やはり、mmult() もあって、 #pragma HLS が書かれている。その他、mmult_kernel() があって、こちらも pragma HLS array_partition が書かれているので、やはり Vivado HLS 用のようだ。

D:\SDSoC\labn\SDRelease\_sds\iprepo\repo に xilinx_com_hls_madd_1_0 と xilinx_com_hls_mmult_1_0 があるが、どうやら Vivado HLS で作ったIP ではないか?と思う。
SDSoC_23_150729.png

xilinx_com_hls_mmult_1_0 の内容を見てみると、hdl やdoc があって IP フォルダのようだ。
SDSoC_24_150729.png

D:\SDSoC\labn\SDRelease\_sds\iprepo\repo\xilinx_com_hls_mmult_1_0\hdl\verilog フォルダを見ると mmult.v などのVerilog HDL ファイルが並んでいる。
SDSoC_25_150729.png

D:\SDSoC\labn\SDRelease\_sds\p0 フォルダを示す。
SDSoC_26_150729.png

.cf_work フォルダはC ファイルとかがあって、最下層のドライバなのだろうか?
SDSoC_27_150729.png

ipi フォルダには Vivado のプロジェクト zc702.xpr があった。
SDSoC_28_150729.png

Vivado のプロジェクト zc702.xpr を立ちあげてみた。
SDSoC_29_150729.png
zc702 のブロックデザインがあった。

Summary を見た。DSPをたくさん使っている。
SDSoC_30_150729.png

zc702 のブロックデザインを表示した。たくさんIPがある。
SDSoC_31_150729.png

datamover や AXI4-Stream Accelerator Adapter や Vivado HLS のIPの mmult, madd 等がある。
Zynq は M_AXI_GP0 ポートと S_AXI_ACP ポートを使用している。たぶん、ARMプロセッサに madd や mmult のデータをRead/Writeするには ACPポートを使っていると思う。外部ポートはハードウェア・アクセラレータに使用しているので、MIO用の固定IOとDDR のみで PL の入出力ポートは存在しない。

Zynqのプロパティで PL Fabric Clocks を見ると FCLK_CLK2 が datamover や AXI4-Stream Accelerator Adapter や Vivado HLS のIPの mmult, madd に供給されているクロックで 100 MHz だった。
SDSoC_34_150729.png

SD_card フォルダには、Linuxの起動イメージや、RAMディスク、BOOT.BIN、devicetree.dtb などが入っている。
SDSoC_35_150729.png

D:\SDSoC\labn\SDRelease\_sds\vhls フォルダの内容を示す。 madd, mmlt フォルダがある。
SDSoC_36_150729.png

madd フォルダを下がっていくと、D:\SDSoC\labn\SDRelease\_sds\vhls\madd\solution\impl\ip フォルダがあるので、ここは、Vivado HLS のプロジェクト用フォルダのようだ。
SDSoC_37_150729.png

SDSoC はいろいろなファイルを出力し、Vivado のプロジェクトも自動作成している。凄いと思った。これがうまく行けば、今まで、Vivado HLSでIP作って、Vivado に持ってきて、論理合成、インプリメント、ビットストリームを作って、ドライバを書いて、制御用アプリを書いてという手間をツールが全部やってくれるみたいだ。
しかし、これでハードウェア技術者が要らなくなるということではないと思う。今のところ、Vivado HLS を使用して性能を向上させるにはテクニックが必要だ。ソフトウェアをそのままハードウェア化しても性能が良くなるとは限らない。でも、このようなツールが普及してくるとハードウェア技術者はIPの作製のような、よりスペシャリストとしての性格が強くなるのかもしれない?
  1. 2015年07月29日 03:25 |
  2. SDSoC
  3. | トラックバック:0
  4. | コメント:2

SDSoC 2015.2 のチュートリアルをやってみた2(ハードウェア・アクセレーション)

SDSoC 2015.2 のチュートリアルをやってみた1(新規プロジェクトの作製)”の続き。

前回は、SDSoC の新規プロジェクトを作製して、テンプレートに Matrix Multiplication and Addition を選択し、SDRelease でビルドした。
今回はその続きから行う。なお、”SDSoC 環境ユーザー ガイド 入門 UG1028 (v2015.2) 2015 年 7 月 20 日”の18ページ”第 2 章 チュートリアル : プロジェクトの作成、ビルド、実行”の21ページ”ハードウェア インプリメンテーション用の関数のマーク”からをやってみる。

labn/project.sdsoc で、Hardware Function の+記号(Add Hardware Function)をクリックする。
SDSoC_12_150727.png

Select function for hardware acceleration ダイアログが立ち上がった。

madd() と mmult() を選択し、OKボタンをクリックする。
SDSoC_13_150727.png

madd() と mmult() が Hardware Functions に入った。
SDSoC_14_150727.png

左端の Project Explorer タブで、labn を右クリックし、右クリックメニューから Build Project を選択して、ビルドを行う。
SDSoC_15_150727.png

ビルドが始まった。
SDSoC_16_150727.png

、”SDSoC 環境ユーザー ガイド 入門 UG1028 (v2015.2) 2015 年 7 月 20 日”の24ページの 1. によると、C++ をVivado HLS でIP にして、Vivado を起動し、そのIPを使ってハードウェアを構築し、ビットストリームを生成する。それを(たぶん、そのハードをドライバを使って使用するソフトウェア自動生成して)コンパイルして、ELFファイルを自動生成するということのようだ。
つまり、いつも自分でやっていた作業を自動的にSDSoC がやってくれるということだろう?
この作業はハードウェアの論理合成、インプリメント、ビットストリームの生成を行うので、時間が掛かる。

ビルドが終了した。
SDSoC_17_150728.png

左端の Project Explorer タブで、labn -> SDRelease -> reports -> data_motion.html をダブルクリックして開いた。
SDSoC_18_150728.png
1024 は、配列の縦32要素 X 横32要素を掛けた数字だと思う。

D:\SDSoC\labn\SDRelease\_sds\swstubs にはハードウェア・アクセレーションされたC++ソースファイルができるようようだ。

元の main.cpp の mmultadd_test() 関数を引用する。
hw_sds_clk_start() と hw_sds_clk_stop() で囲われている部分を見て欲しい。mmult() と madd() が使われている。
SDSoC_19_150728.png

D:\SDSoC\labn\SDRelease\_sds\swstubs の main.cpp の mmultadd_test() 関数を引用する。
hw_sds_clk_start() と hw_sds_clk_stop() で囲われている部分を見ると、_p0_mmult_0() と _p0_madd_0() が使われている。
SDSoC_20_150728.png

D:\SDSoC\labn\SDRelease\_sds\swstubs の madd.cpp の _p0_madd_0() を引用する。
これを見ると、cf_send_i (), cf_receive_i (), cf_wait (), などが使われていて、ハードウェア・アクセラレータにデータを送って、受けていることが伺われる。
SDSoC_21_150728.png


疑問がわいたのだが、SDSoCでは、例えばカメラインターフェースなどのハードウェアはどうやって、使うのだろうか?ビットストリームを生成しているので、SDSoCに取り込むしか無いと思うのだが? HDLのIPを追加するようになるのかな?そのドライバは自分で書くとか?もしかしてIPに接続を許すためにIPはAXIバス専用にするとかなのだろうか?これからマニュアルを読んだりして明らかにしていきたいと思う。
それとも、ソフトウェアのアクセレーションのみにFPGAを使用するのかな?

HDMIを使った例がXilinxのSDSoCデザインサンプルに載っているそうです

ソフトウェアが使うハードウェアは何処に、どうやって実装できるのか?興味は尽きない。一回、セミナに行ったほうが良い気もしてきた。お金がないのだが。。。
  1. 2015年07月27日 05:18 |
  2. SDSoC
  3. | トラックバック:0
  4. | コメント:0

SDSoC 2015.2 のチュートリアルをやってみた1(新規プロジェクトの作製)

SDSoC 2015.2 のライセンスが手に入ったので、使ってみた。

SDSoC 環境ユーザー ガイド 入門 UG1028 (v2015.2) 2015 年 7 月 20 日”の18ページ”第 2 章 チュートリアル : プロジェクトの作成、ビルド、実行”をやってみることにした。

最初に SDSoC 2015.2 を立ち上げるとワークスペースを聞いてくる。D:SDSoC フォルダを作製して初めてワークスペースに指定した。
SDSoC_1_150726.png

SDSoC 2015.2 が立ち上がった。
Create SDSoC Project をクリックした。
SDSoC_2_150726.png

New Project ダイアログが表示された。
Project name をlabn に指定した。
Platform をクリックすると、使用できるボードがプルダウンメニューで表示された。microzed や zc702, zc706, zed, zybo などが使用できるようだ。
SDSoC_3_150726.png

OSはデフォルトでLinux が選択されているので、そのままとして、Next >ボタンをクリックした。
SDSoC_4_150726.png

Templates ダイアログで、Available Templates に Matrix Multiplication and Addition が見つからない。
もしかして、ZYBO には無いのでは?という疑惑から、< Back ボタンをクリックして前の画面に戻した。
SDSoC_5_150726.png

Plaform をクリックして zc702 に変更し、Next >ボタンをクリックした。
SDSoC_6_150726.png

Templates ダイアログで、Available Templates に Matrix Multiplication and Addition が見つかった。やはり ZYBO には無かったようだ。 Finish をクリックした。
SDSoC_7_150726.png

labn プロジェクトが作成され、src フォルダの下には、C++ ソースファイルも生成されていた。また、SDSoC Project Overview が表示されている。
SDSoC_8_150726.png

Build アイコンのプルダウンメニューから SDRelease を選択する。
SDSoC_9_150726.png

するとビルドが始まる。
SDSoC_10_150726.png

ビルド終了。 labn プロジェクトにBinaries フォルダができて、その下に labn.elf が3つ入った。更にSDRelease フォルダもできて、その下にも labn.elf と labn.elf.bit ができた。
SDSoC_11_150726.png
  1. 2015年07月26日 07:29 |
  2. SDSoC
  3. | トラックバック:0
  4. | コメント:0

ZYBOのHDMI入力をVGA出力に出力する3(バグフィックス?)

ZYBOのHDMI入力をVGA出力に出力する2(制約ファイル)”の続き。

前回はインプリメントを行ったが、エラーが出てしまった。今回はバグフィックスを行う。

忘れていたのだが、Help With A Zybo Video Design にまさに、同じ回路が載っていた。それを見ると、hdmi_out_en, hdmi_hpd が足りていなかった。更に、clk_wiz_0 はPLL である必要があるそうだ。

まずは、hdmi_out_en, hdmi_hpd をブロックデザインに追加した。
dvi2vga_18_150725.png

clk_wiz_0 をダブルクリックして、PLLに変更した。
dvi2vga_19_150725.png

論理合成を行い、hdmi_out_en, hdmi_hpd の規格とピン番号を指定した。
dvi2vga_20_150725.png

dvi2vga.xdc にもhdmi_out_en, hdmi_hpd の制約が追加された。
dvi2vga_21_150725.png

また、インプリメントでエラー発生。今度は、MMCMのVCOの周波数が範囲外だそうだ。
dvi2vga_22_150725.png

ロックがかかっているタイミング制約で、TMDS_Clk_p が 6.060 ns つまり 165 MHz に制約されている。
dvi2vga_23_150725.png
これは、dvi2rgb IP に入っている制約のようだった。

dvi2rgb IP を IP Packager で編集した。
dvi2rgb.xdc で、TMDS_Clk_p のタイミング制約を 8.334 ns , 120 MHz に制約した。
dvi2vga_24_150725.png

dvi2rgb_ooc.xdc も TMDS_Clk_p のタイミング制約を 8.334 ns , 120 MHz に制約した。
dvi2vga_25_150725.png

Package IP タブで Re-Package IP ボタンをクリックして、再パッケージした。
dvi2vga_26_150725.png

dvi2vga_test プロジェクトに戻って、IPを更新してから、ビットストリームの生成を行ったら、成功した。
dvi2vga_27_150725.png

Implemented Design を開いてみた。ロジックが配置されているのが見える。大丈夫そうだ。
dvi2vga_28_150725.png

ビットストリームの生成は成功したのだが、疑問がある。
TDMS_Clk_p はピクセル・クロックの10倍になっているはずなのだが、8.334 ns などという制約で良いのだろうか?10 倍の周波数なので、0.834 ns とかなんじゃないか?という疑問が。。。あるが、とりあえず動かしてみようと思う。。。
  1. 2015年07月25日 06:34 |
  2. ZYBO
  3. | トラックバック:0
  4. | コメント:0

ZYBOのHDMI入力をVGA出力に出力する2(制約ファイル)

ZYBOのHDMI入力をVGA出力に出力する1(プロジェクトの作製)”の続き。

前回は、Vivado 2015.2 のプロジェクトを作製して、ブロックデザインを作製し、論理合成を行った。今回は、制約ファイルを生成する。

前回からの続きで、Synthesized Design が開いたままになっていると思う。そこで、Layout メニューから I/O Planning をクリックする。

すると、Vivado 2015.2 の画面が、下に示す画面に変わった。
dvi2vga_11_150724.png

この状態で外部ピンの位置と規格を設定した。
dvi2vga_12_150724.png

次に、その制約を dvi2vga.xdc としてセーブした。
dvi2vga_13_150724.png

次にタイミング制約を行う。
左端のFlow Navigator で Synthesis -> Synthesized Design -> Edit Timing Constraints をクリックする。
Timing Constraints を見るとすでに、TMDS_Clk_p には、6.06 ns, 165 MHz、clk125 には8 ns, 125 MHz の制約がかかっていた。
dvi2vga_14_150724.png

Synthesized Design の右端のXをクリックして閉じる。

今度は、左端のFlow Navigator で Program and Debug -> Generate Bitstream をクリックして、論理合成、インプリメント、ビットストリームの生成を行う。

place でエラーが出た。

[Place 30-575] Sub-optimal placement for a clock-capable IO pin and MMCM pair. If this sub optimal condition is acceptable for this design, you may use the CLOCK_DEDICATED_ROUTE constraint in the .xdc file to demote this message to a WARNING. However, the use of this override is highly discouraged. These examples can be used directly in the .xdc file to override this clock rule.
< set_property CLOCK_DEDICATED_ROUTE BACKBONE [get_nets dvi2vga_i/dvi2rgb_0/U0/TMDS_ClockingX/CLK_IN_hdmi_clk] >

だそうだ。
CLOCK_DEDICATED_ROUTE 制約をdvi2vga.xdc に追加した。
dvi2vga_15_150724.png

もう1度、左端のFlow Navigator で Program and Debug -> Generate Bitstream をクリックして、論理合成、インプリメント、ビットストリームの生成を行った。

また、place でエラーが出た。

[Place 30-149] Unroutable Placement! A MMCM / (BUFIO/BUFR) component pair is not placed in a routable site pair. The MMCM component can use the dedicated path between the MMCM and the (BUFIO/BUFR) if both are placed in the same clock region or if they are placed in horizontally adjacent clock regions. If this sub optimal condition is acceptable for this design, you may use the CLOCK_DEDICATED_ROUTE constraint in the .xdc file to demote this message to a WARNING. However, the use of this override is highly discouraged. These examples can be used directly in the .xdc file to override this clock rule.
< set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets dvi2vga_i/dvi2rgb_0/U0/TMDS_ClockingX/CLK_OUT_5x_hdmi_clk] >


もう一度、CLOCK_DEDICATED_ROUTE 制約をdvi2vga.xdc に追加した。

左端のFlow Navigator で Program and Debug -> Generate Bitstream をクリックして、論理合成、インプリメント、ビットストリームの生成を行った。

また、place でエラーが出た。

[Place 30-512] Clock region assignment has failed. Clock buffer 'dvi2vga_i/dvi2rgb_0/U0/TMDS_ClockingX/DVI_ClkGenerator' (MMCME2_ADV) is placed at site MMCME2_ADV_X0Y0 in CLOCKREGION_X1Y0. Its loads need to be placed in the area enclosed by clock regions CLOCKREGION_X1Y0 and CLOCKREGION_X1Y0. One of its loads 'dvi2vga_i/dvi2rgb_0/U0/TMDS_ClockingX/SerialClkBuffer' (BUFIO) is placed in site BUFIO_X0Y5 in CLOCKREGION_X1Y1 which is outside the permissible area.


MMCM と BUFIO のCLOCKREGIONが違っているからダメと言っている様だ。
dvi2vga_17_150724.png

検索すると、
AR# 60610
2014.1 Artix-7 配置 - クロック配置の際に、隣接区画を駆動する基準クロックを含んだ有効なクロック コンフィギュレーションが間違って拒否される


AR# 60258
Artix-7 GTP - 隣接するクワッドへの基準クロック配線により配置エラーが発生する


が検索にヒットした。

現在の制約ファイル、dvi2vga.xdc を貼っておく。

set_property PACKAGE_PIN P20 [get_ports {vga_pBlue[0]}]
set_property PACKAGE_PIN M20 [get_ports {vga_pBlue[1]}]
set_property PACKAGE_PIN K19 [get_ports {vga_pBlue[2]}]
set_property PACKAGE_PIN J18 [get_ports {vga_pBlue[3]}]
set_property PACKAGE_PIN G19 [get_ports {vga_pBlue[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_pBlue[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_pBlue[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_pBlue[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_pBlue[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_pBlue[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_pGreen[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_pGreen[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_pGreen[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_pGreen[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_pGreen[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_pGreen[0]}]
set_property PACKAGE_PIN H18 [get_ports {vga_pGreen[0]}]
set_property PACKAGE_PIN N20 [get_ports {vga_pGreen[1]}]
set_property PACKAGE_PIN L19 [get_ports {vga_pGreen[2]}]
set_property PACKAGE_PIN J19 [get_ports {vga_pGreen[3]}]
set_property PACKAGE_PIN H20 [get_ports {vga_pGreen[4]}]
set_property PACKAGE_PIN F20 [get_ports {vga_pGreen[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_pRed[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_pRed[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_pRed[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_pRed[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_pRed[0]}]
set_property PACKAGE_PIN M19 [get_ports {vga_pRed[0]}]
set_property PACKAGE_PIN L20 [get_ports {vga_pRed[1]}]
set_property PACKAGE_PIN J20 [get_ports {vga_pRed[2]}]
set_property PACKAGE_PIN G20 [get_ports {vga_pRed[3]}]
set_property PACKAGE_PIN F19 [get_ports {vga_pRed[4]}]
set_property PACKAGE_PIN H16 [get_ports TMDS_Clk_p]
set_property PACKAGE_PIN D19 [get_ports {TMDS_Data_p[0]}]
set_property PACKAGE_PIN C20 [get_ports {TMDS_Data_p[1]}]
set_property PACKAGE_PIN B19 [get_ports {TMDS_Data_p[2]}]
set_property PACKAGE_PIN G18 [get_ports ddc_sda_io]
set_property PACKAGE_PIN G17 [get_ports ddc_scl_io]
set_property PACKAGE_PIN P19 [get_ports vga_pHSync]
set_property IOSTANDARD LVCMOS33 [get_ports vga_pHSync]
set_property IOSTANDARD LVCMOS33 [get_ports vga_pVSync]
set_property PACKAGE_PIN R19 [get_ports vga_pVSync]
set_property PACKAGE_PIN L16 [get_ports clk125]
set_property PACKAGE_PIN R18 [get_ports reset]
set_property IOSTANDARD LVCMOS33 [get_ports reset]
set_property IOSTANDARD LVCMOS33 [get_ports clk125]
set_property IOSTANDARD LVCMOS33 [get_ports ddc_scl_io]
set_property IOSTANDARD LVCMOS33 [get_ports ddc_sda_io]

set_property CLOCK_DEDICATED_ROUTE BACKBONE [get_nets dvi2vga_i/dvi2rgb_0/U0/TMDS_ClockingX/CLK_IN_hdmi_clk]
set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets dvi2vga_i/dvi2rgb_0/U0/TMDS_ClockingX/CLK_OUT_5x_hdmi_clk]

  1. 2015年07月24日 04:53 |
  2. ZYBO
  3. | トラックバック:0
  4. | コメント:0

IP Integrator のブロックデザインで使うと便利なIP

IP Integrator のブロックデザインで使うと便利なIP がある。それは、ISEのXPS 時代からも存在していた。
わかっている限りでは、Utility Vector Logic, Utility Reduced Logic, Concat, Constant, Slice だ。
IP_Integrator_Utl_IP_1_150722.png
既存のIPを加工して、現状に合わせるような状況になった時に、これらのIPを覚えておくと便利だ。

IP Integrator のブロックデザイン上で Add IP して、名前を検索すると出てくる。
IP_Integrator_Utl_IP_7_150722.png

Utility Vector Logic は各ビットごとに and, or, xor, not の演算をすることができる。C Sizeでビット幅を設定できる。not の場合は、Op2 入力ポートがハイドされる。
IP_Integrator_Utl_IP_2_150722.png

Utility Reduced Logic は、各ビット同士を演算(and, or, xor)して1ビット出力とする。
IP_Integrator_Utl_IP_3_150722.png

Concat は複数の入力ポートを1つの出力ポートにまとめる。
IP_Integrator_Utl_IP_4_150722.png

Constant は任意のビット幅の定数を与える。
IP_Integrator_Utl_IP_5_150722.png

Slice は入力ポートの任意のビット幅を出力ポートに出力する。
IP_Integrator_Utl_IP_6_150722.png
  1. 2015年07月22日 11:06 |
  2. Vivado
  3. | トラックバック:0
  4. | コメント:0

ZYBOのHDMI入力をVGA出力に出力する1(プロジェクトの作製)

ZYBOのHDMI入力を使用する(概要)”の知見をベースにHDMI入力から入力されたビデオ信号をVGAポートに出力するVivado 2015.2 のプロジェクトを作ってみようと思う。

前回の vivado-library/ip/dvi2rgb_v1_4 は DVI 信号を Xilinx社の規格の vid_io に変換する。その vid_io から VGAに出力するIP が vivado-library/ip/rgb2vga_v1_0 だ。rgb2vga IP から VGAポートに接続すれば、カメラで撮影したビデオ信号をHDMI に出力して、異なるZYBOで受け、VGAポートに出力することができるはずだ。

ZYBOのリファレンス・マニュアルの21ページに、ZYBOのZynq-7010 の L16 に125MHzのクロックが入っているので、これを使用して、MMCMで200MHzに変換して dvi2rgb のRefClk に入れることにする。

それでは、gvi2vga_test というプロジェクトをVivado 2015.2 で作製して、ブロックデザインを作製しよう。
dvi2vga_1_150722.png

IP Catalog に dvi2rgb と rgb2vga の2つのIP を追加した。
dvi2vga_2_150722.png

gvi2vga という名前のブロックデザインを作製し、clk_wiz_0, dvi2rgb_0, rgb2vga_0 を Add IP した。
dvi2vga_3_150722.png

clk_wiz_0 の設定を行った。
Clocking Options タブの Input Clock Information のPrimary のInput Frequency をManual に変更し、125 MHz を設定した。
dvi2vga_4_150722.png

Output Clocks タブでは、clk_out1 を 200MHz に設定した。
dvi2vga_5_150722.png

これでOKボタンをクリックして確定させた。

次に dvi2rgb_0 をダブルクリックして、設定を行う。
Resets active high のチェックを外して、0 でリセットに設定する。
< 120 MHz のラジオボタンをクリックする。
OKボタンをクリックする。
dvi2vga_6_150723.png

rgb2vga_0 をダブルクリックして設定を行う。
R が 5 ビット、G が 6 ビット、B が 5 ビットでZYBOのビット幅と同じだったので、そのままOKボタンをクリックする。
dvi2vga_7_150723.png

配線を行った。TMDSバスは Make External しても外部入力ポートが生成されなかったので、TMDSバスをばらして Make External を行った。配線後のブロックデザインを下に示す。
dvi2vga_8_150723.png

Validate Design アイコンをクリックして、ブロックデザインを検証してみたが成功した。

次に ブロックデザインのwapper HDL ファイルを作製した。
dvi2vga_9_150723.png

Run Synthesis ボタンをクリックして、論理合成を行った。

論理合成は無事に成功した Open Synthesis Design をクリックした所を下に示す。
dvi2vga_10_150723.png
  1. 2015年07月22日 05:55 |
  2. ZYBO
  3. | トラックバック:0
  4. | コメント:0

ZYBOのHDMI入力を使用する(概要)

ラプラシアンフィルタのソフトウェアのチューンナップはひとまず終了とすることにする。わかったのは、ソフトウェアの最適化はよくわからない?と言うことだ。やってみるまで性能が向上するのかどうかわからない?その意味では、Vivado HLSでハードウェアにしたほうが性能の向上が分かりやすいかもしれない。

さて、次のステージに行くことにする。今度は、ZYBOのHDMIはSource と Sink 両方使用することができる。つまりHDMI入力にもなれば、HDMI出力にすることもできる。これは、HDMIの信号がバッファを通したのみで Zynq-7010 チップに接続されているからなのだが、この性質を利用して、ZYBO同士を通信させようと思っている。その代わり、HDMIの8B10Bデコードなどは、自分で作る必要がある。

最初は、カメラ付きZYBOのHDMI出力をもう1つのZYBOのHDMI入力で受けて、VGA出力から出力させようと思う。これは、HDMIの入出力なのだが、なにもHDMIケーブルに画像信号を載せなければならないという決まりがあるわけでもないので、汎用の通信をさせてみようと思う。ただ、通信プロトコルは決める必要があるし、高速画像用信号授受の方法を少し変える必要があるかも知れない?

検索してみると、Digilent のGitHub に vivado-library があって、その中に、vivado-library/ip/dvi2rgb_v1_4 がある。これは、DVIをRGBに直すIPの様だが、HDMI もDVI も信号は一緒なので、使えるはずだ。なお、dvi2rgb IP のマニュアルはここにある

Digilent のフォーラムがあって、参考になりそうな記事があったので、貼っておく。
Hdmi Sink On Zybo Zynq?
Help With A Zybo Video Design
  1. 2015年07月21日 04:11 |
  2. ZYBO
  3. | トラックバック:0
  4. | コメント:0

インサイド・ヘッドを見てきました

今日はインサイド・ヘッドを見てきました。完全に子供向けかと思って見に行ったんですが、大人も楽しめる内容?でした。良かったです。
  1. 2015年07月20日 20:43 |
  2. 日記
  3. | トラックバック:0
  4. | コメント:0

新しいラプラシアンフィルタのソフトウェア実装

OpenMPを使ったラプラシアンフィルタの動作がおかしいという現象があった。ブログのコメント欄でも教えてもらったのだが、OpenMPのプラグマを書いた for 文のどの順に実行されるかわからない?ということで、元のラプラシアンフィルタをよりシンプルにしてみた。
laplacian_filter.c ~ laplacian_filter4.c までは、ラプラシアンフィルタ処理によって、画像の周りの1ピクセルのみ 0 にしていたが、今回は上と右の 2 ピクセルを 0 にしている。下にその図を示す。ブルーの画面がラプラシアンフィルタの出力画像で、黒い部分が 0 を出力している部分とする。
OpenMP_23_150720.png

ラプラシアンフィルタは3 ピクセル x 3 ラインのデータを元に真ん中の画像を出力しているので、今回のソフトウェアの方が if 文が少なくなる。
実際のラプラシアンフィルタのソフトウェアはGitHubにあげておいたので、marsee101/laplacian_filters を見て欲しい。
laplacian_filter5.c は laplacian_filter.c に対応する実装で、順に番号付けされていて、 laplacian_filter8.c は、 laplacian_filter4.c に対応する実装となっている。

下に gcc-4.8 で laplacian_filter5.c の性能を比較した時のスクリプトを示す。

gcc laplacian_filter5.c -o laplacian_filter5
gcc -O1 laplacian_filter5.c -o laplacian_filter5_O1
gcc -O2 laplacian_filter5.c -o laplacian_filter5_O2
gcc -O3 laplacian_filter5.c -o laplacian_filter5_O3
gcc -Os laplacian_filter5.c -o laplacian_filter5_Os
gcc -mcpu=cortex-a9 -mfpu=neon -ftree-vectorize -mvectorize-with-neon-quad -ffast-math laplacian_filter5.c -o laplacian_filter5_n
gcc -mcpu=cortex-a9 -mfpu=neon -ftree-vectorize -mvectorize-with-neon-quad -ffast-math -O1 laplacian_filter5.c -o laplacian_filter5_n1
gcc -mcpu=cortex-a9 -mfpu=neon -ftree-vectorize -mvectorize-with-neon-quad -ffast-math -O2 laplacian_filter5.c -o laplacian_filter5_n2
gcc -mcpu=cortex-a9 -mfpu=neon -ftree-vectorize -mvectorize-with-neon-quad -ffast-math -O3 laplacian_filter5.c -o laplacian_filter5_n3
gcc -mcpu=cortex-a9 -mfpu=neon -ftree-vectorize -mvectorize-with-neon-quad -ffast-math -Os laplacian_filter5.c -o laplacian_filter5_ns
./laplacian_filter5
./laplacian_filter5_O1
./laplacian_filter5_O2
./laplacian_filter5_O3
./laplacian_filter5_Os
./laplacian_filter5_n
./laplacian_filter5_n1
./laplacian_filter5_n2
./laplacian_filter5_n3
./laplacian_filter5_ns
objdump -S -d laplacian_filter5 | grep "vmov" -c
objdump -S -d laplacian_filter5_O1 | grep "vmov" -c
objdump -S -d laplacian_filter5_O2 | grep "vmov" -c
objdump -S -d laplacian_filter5_O3 | grep "vmov" -c
objdump -S -d laplacian_filter5_Os | grep "vmov" -c
objdump -S -d laplacian_filter5_n | grep "vmov" -c
objdump -S -d laplacian_filter5_n1 | grep "vmov" -c
objdump -S -d laplacian_filter5_n2 | grep "vmov" -c
objdump -S -d laplacian_filter5_n3 | grep "vmov" -c
objdump -S -d laplacian_filter5_ns | grep "vmov" -c


laplacian_filter5.c を他のラプラシアンフィルタの実装に切り替えながら性能を測定した。

laplacian_filter5.c の結果を示す。
OpenMP_24_150720.png

laplacian_filter6.c の結果を示す。
OpenMP_25_150720.png

laplacian_filter7.c の結果を示す。
OpenMP_26_150720.png

laplacian_filter8.c の結果を示す。
OpenMP_27_150720.png

次に、OpenMP を使った時の各ラプラシアンフィルタ処理時間を測定した。
下に gcc-4.8 の -fopenmp オプションを付けた時の性能を比較するためのスクリプトを示す。

gcc -fopenmp laplacian_filter5.c -o laplacian_filter5_mp
gcc -fopenmp -O1 laplacian_filter5.c -o laplacian_filter5_mpO1
gcc -fopenmp -O2 laplacian_filter5.c -o laplacian_filter5_mpO2
gcc -fopenmp -O3 laplacian_filter5.c -o laplacian_filter5_mpO3
gcc -fopenmp -Os laplacian_filter5.c -o laplacian_filter5_mpOs
gcc -fopenmp -mcpu=cortex-a9 -mfpu=neon -ftree-vectorize -mvectorize-with-neon-quad -ffast-math laplacian_filter5.c -o laplacian_filter5_mpn
gcc -fopenmp -mcpu=cortex-a9 -mfpu=neon -ftree-vectorize -mvectorize-with-neon-quad -ffast-math -O1 laplacian_filter5.c -o laplacian_filter5_mpn1
gcc -fopenmp -mcpu=cortex-a9 -mfpu=neon -ftree-vectorize -mvectorize-with-neon-quad -ffast-math -O2 laplacian_filter5.c -o laplacian_filter5_mpn2
gcc -fopenmp -mcpu=cortex-a9 -mfpu=neon -ftree-vectorize -mvectorize-with-neon-quad -ffast-math -O3 laplacian_filter5.c -o laplacian_filter5_mpn3
gcc -fopenmp -mcpu=cortex-a9 -mfpu=neon -ftree-vectorize -mvectorize-with-neon-quad -ffast-math -Os laplacian_filter5.c -o laplacian_filter5_mpns
./laplacian_filter5_mp
./laplacian_filter5_mpO1
./laplacian_filter5_mpO2
./laplacian_filter5_mpO3
./laplacian_filter5_mpOs
./laplacian_filter5_mpn
./laplacian_filter5_mpn1
./laplacian_filter5_mpn2
./laplacian_filter5_mpn3
./laplacian_filter5_mpns
objdump -S -d laplacian_filter5_mp | grep "vmov" -c
objdump -S -d laplacian_filter5_mpO1 | grep "vmov" -c
objdump -S -d laplacian_filter5_mpO2 | grep "vmov" -c
objdump -S -d laplacian_filter5_mpO3 | grep "vmov" -c
objdump -S -d laplacian_filter5_mpOs | grep "vmov" -c
objdump -S -d laplacian_filter5_mpn | grep "vmov" -c
objdump -S -d laplacian_filter5_mpn1 | grep "vmov" -c
objdump -S -d laplacian_filter5_mpn2 | grep "vmov" -c
objdump -S -d laplacian_filter5_mpn3 | grep "vmov" -c
objdump -S -d laplacian_filter5_mpns | grep "vmov" -c


OpenMP を使った時の新しく作ったラプラシアンフィルタの性能を測定した。
laplacian_filter5.c に OpenMP用のプラグマを入れた。
OpenMP_32_150720.png

laplacian_filter5.c の結果を示す。
OpenMP_28_150720.png

laplacian_filter5.c のラプラシアンフィルタ結果を示す。今回は正常だ。
OpenMP_36_150720.jpg

laplacian_filter6.c に OpenMP用のプラグマを入れた。
OpenMP_33_150720.png

laplacian_filter6.c の結果を示す。
OpenMP_29_150720.png

laplacian_filter6.c のラプラシアンフィルタ結果を示す。今回は正常だ。
OpenMP_37_150720.jpg

laplacian_filter7.c に OpenMP用のプラグマを入れた。
OpenMP_34_150720.png

laplacian_filter7.c の結果を示す。
OpenMP_30_150720.png

laplacian_filter7.c のラプラシアンフィルタ結果を示す。今回は正常だ。
OpenMP_38_150720.jpg

laplacian_filter8.c に OpenMP用のプラグマを入れた。
OpenMP_35_150720.png

laplacian_filter8.c の結果を示す。
OpenMP_31_150720.png

laplacian_filter8.c のラプラシアンフィルタ結果を示す。今回も真ん中に線が入ってしまった。
OpenMP_39_150720.jpg

gcc-4.8 の各最適化オプションとNEON命令を追加した時、gcc-4.8 の OpenMPで 2 スレッドにした場合の性能を表にした。表中で同じ色の背景は出力位置は違っても同じ実装であることを示す。
最初にgcc-4.8 の各最適化オプションとNEON命令を追加した時の表を示す。
OpenMP_40_150720.png

gcc-4.8 の OpenMPで 2 スレッドにした場合の性能の表を示す。
OpenMP_41_150720.png

gcc-4.8 の OpenMP、-O3、laplacian_filter8.c で最速値 72.5 ms が計測できた。まだ、laplacian_filter8.c は結果がおかしいが最速値を計測することができた。一方、ラプラシアンフィルタの実行に関しては、1 core 使用した時と、2 core 使用した時では、あまり速度差が無いと言える。
  1. 2015年07月20日 07:56 |
  2. Zynq
  3. | トラックバック:0
  4. | コメント:0

OpenMPでのラプラシアンフィルタの実行速度3

OpenMPでのラプラシアンフィルタの実行速度2”の続き。

前回は、laplacian_fiter1 と laplacian_fiter2 のOpenMP を使うようにコンパイルして、実行した結果をブログに書いた。今回は、laplacian_fiter3 と laplacian_fiter4 をやってみた。

laplacian_fiter3.c を下に示すように、Cソースコードを修正した。
OpenMP_20_150717.png

2つ目の for ループの前に、

#ifdef _OPENMP
#pragma omp parallel for private(lap_fil_val, a, b, cam_fb_addr, lap_fb_addr)
#endif

を置いた。

このCソースコードを以下のコマンドで、コンパイル、実行、NEON命令の有無を調査した。

gcc -fopenmp laplacian_filter3.c -o laplacian_filter3_mp
gcc -fopenmp -O1 laplacian_filter3.c -o laplacian_filter3_mpO1
gcc -fopenmp -O2 laplacian_filter3.c -o laplacian_filter3_mpO2
gcc -fopenmp -O3 laplacian_filter3.c -o laplacian_filter3_mpO3
gcc -fopenmp -Os laplacian_filter3.c -o laplacian_filter3_mpOs
gcc -fopenmp -mcpu=cortex-a9 -mfpu=neon -ftree-vectorize -mvectorize-with-neon-quad -ffast-math laplacian_filter3.c -o laplacian_filter3_mpn
gcc -fopenmp -mcpu=cortex-a9 -mfpu=neon -ftree-vectorize -mvectorize-with-neon-quad -ffast-math -O1 laplacian_filter3.c -o laplacian_filter3_mpn1
gcc -fopenmp -mcpu=cortex-a9 -mfpu=neon -ftree-vectorize -mvectorize-with-neon-quad -ffast-math -O2 laplacian_filter3.c -o laplacian_filter3_mpn2
gcc -fopenmp -mcpu=cortex-a9 -mfpu=neon -ftree-vectorize -mvectorize-with-neon-quad -ffast-math -O3 laplacian_filter3.c -o laplacian_filter3_mpn3
gcc -fopenmp -mcpu=cortex-a9 -mfpu=neon -ftree-vectorize -mvectorize-with-neon-quad -ffast-math -Os laplacian_filter3.c -o laplacian_filter3_mpns
./laplacian_filter3_mp
./laplacian_filter3_mpO1
./laplacian_filter3_mpO2
./laplacian_filter3_mpO3
./laplacian_filter3_mpOs
./laplacian_filter3_mpn
./laplacian_filter3_mpn1
./laplacian_filter3_mpn2
./laplacian_filter3_mpn3
./laplacian_filter3_mpns
objdump -S -d laplacian_filter3_mp | grep "vmov" -c
objdump -S -d laplacian_filter3_mpO1 | grep "vmov" -c
objdump -S -d laplacian_filter3_mpO2 | grep "vmov" -c
objdump -S -d laplacian_filter3_mpO3 | grep "vmov" -c
objdump -S -d laplacian_filter3_mpOs | grep "vmov" -c
objdump -S -d laplacian_filter3_mpn | grep "vmov" -c
objdump -S -d laplacian_filter3_mpn1 | grep "vmov" -c
objdump -S -d laplacian_filter3_mpn2 | grep "vmov" -c
objdump -S -d laplacian_filter3_mpn3 | grep "vmov" -c
objdump -S -d laplacian_filter3_mpns | grep "vmov" -c

を実行した。
結果を示す。
OpenMP_12_150717.png

laplacian_fiter2.c の時と同様に、ラプラシアンフィルタの画像的には、左側はそれなりだが、右側が強調されている気がする。
OpenMP_17_150717.jpg


laplacian_fiter4.c を下に示すように、Cソースコードを修正した。
OpenMP_21_150717.png

filter_line() 関数の for () 文の前に、

#ifdef _OPENMP
#pragma omp parallel for private(lap_fil_val, current, next, prev)
#endif

を置いた。

このCソースコードを以下のコマンドで、コンパイル、実行、NEON命令の有無を調査した。

gcc -fopenmp laplacian_filter4.c -o laplacian_filter4_mp
gcc -fopenmp -O1 laplacian_filter4.c -o laplacian_filter4_mpO1
gcc -fopenmp -O2 laplacian_filter4.c -o laplacian_filter4_mpO2
gcc -fopenmp -O3 laplacian_filter4.c -o laplacian_filter4_mpO3
gcc -fopenmp -Os laplacian_filter4.c -o laplacian_filter4_mpOs
gcc -fopenmp -mcpu=cortex-a9 -mfpu=neon -ftree-vectorize -mvectorize-with-neon-quad -ffast-math laplacian_filter4.c -o laplacian_filter4_mpn
gcc -fopenmp -mcpu=cortex-a9 -mfpu=neon -ftree-vectorize -mvectorize-with-neon-quad -ffast-math -O1 laplacian_filter4.c -o laplacian_filter4_mpn1
gcc -fopenmp -mcpu=cortex-a9 -mfpu=neon -ftree-vectorize -mvectorize-with-neon-quad -ffast-math -O2 laplacian_filter4.c -o laplacian_filter4_mpn2
gcc -fopenmp -mcpu=cortex-a9 -mfpu=neon -ftree-vectorize -mvectorize-with-neon-quad -ffast-math -O3 laplacian_filter4.c -o laplacian_filter4_mpn3
gcc -fopenmp -mcpu=cortex-a9 -mfpu=neon -ftree-vectorize -mvectorize-with-neon-quad -ffast-math -Os laplacian_filter4.c -o laplacian_filter4_mpns
./laplacian_filter4_mp
./laplacian_filter4_mpO1
./laplacian_filter4_mpO2
./laplacian_filter4_mpO3
./laplacian_filter4_mpOs
./laplacian_filter4_mpn
./laplacian_filter4_mpn1
./laplacian_filter4_mpn2
./laplacian_filter4_mpn3
./laplacian_filter4_mpns
objdump -S -d laplacian_filter4_mp | grep "vmov" -c
objdump -S -d laplacian_filter4_mpO1 | grep "vmov" -c
objdump -S -d laplacian_filter4_mpO2 | grep "vmov" -c
objdump -S -d laplacian_filter4_mpO3 | grep "vmov" -c
objdump -S -d laplacian_filter4_mpOs | grep "vmov" -c
objdump -S -d laplacian_filter4_mpn | grep "vmov" -c
objdump -S -d laplacian_filter4_mpn1 | grep "vmov" -c
objdump -S -d laplacian_filter4_mpn2 | grep "vmov" -c
objdump -S -d laplacian_filter4_mpn3 | grep "vmov" -c
objdump -S -d laplacian_filter4_mpns | grep "vmov" -c

を実行した。
結果を示す。
OpenMP_13_150717.png

ラプラシアンフィルタ処理後の画像は、真ん中に縦に線が入ってしまった。それ以外は普通だ。
OpenMP_18_150717.jpg

OpenMP を使用したラプラシアンフィルタのみの各最適化オプションでの実行時間を表にまとめたので、下に示す。使用したコンパイラは gcc-4.8 だ。
OpenMP_22_150717.png

OpenMP を使用しない gcc の各最適化オプションのラプラシアンフィルタのみの処理時間の表を下に示す。
NEON_48_150714.png

上の2つの表を比較してみると、-O無しの場合は OpenMP が明らかに速いが、それ以外の場合はあまり速くなっていないことが分かる。
  1. 2015年07月18日 04:31 |
  2. Zynq
  3. | トラックバック:0
  4. | コメント:0

OpenMPでのラプラシアンフィルタの実行速度2

OpenMPでのラプラシアンフィルタの実行速度”の続き。

Vengineer さんからツイッターで、”forループ内のローカル変数をプラグマ内で定義しないと、おかしくなりますよ”とのアドバイスを頂いた。調べてみると、”#pragma omp parallel for”の後に”private(list)”を付ける必要があるようだ。
C言語による OpenMP 入門 ”の12ページ、”5.1 private(list) ”を参照すると、各スレッドで独自の変数値を持つためには、private 指示子が必要なようだ。

最初に laplacian_filter.c だが、以下の図の様にCソースコードを修正した。
OpenMP_14_150717.png

2つ目の for ループの前に、

#ifdef _OPENMP
#pragma omp parallel for private(lap_fil_val, fl, sl, tl, a, b)
#endif

を置いた。

このCソースコードを以下のコマンドで、コンパイル、実行、NEON命令の有無を調査した。

gcc -fopenmp laplacian_filter.c -o laplacian_filter1_mp
gcc -fopenmp -O1 laplacian_filter.c -o laplacian_filter1_mpO1
gcc -fopenmp -O2 laplacian_filter.c -o laplacian_filter1_mpO2
gcc -fopenmp -O3 laplacian_filter.c -o laplacian_filter1_mpO3
gcc -fopenmp -Os laplacian_filter.c -o laplacian_filter1_mpOs
gcc -fopenmp -mcpu=cortex-a9 -mfpu=neon -ftree-vectorize -mvectorize-with-neon-quad -ffast-math laplacian_filter.c -o laplacian_filter1_mpn
gcc -fopenmp -mcpu=cortex-a9 -mfpu=neon -ftree-vectorize -mvectorize-with-neon-quad -ffast-math -O1 laplacian_filter.c -o laplacian_filter1_mpn1
gcc -fopenmp -mcpu=cortex-a9 -mfpu=neon -ftree-vectorize -mvectorize-with-neon-quad -ffast-math -O2 laplacian_filter.c -o laplacian_filter1_mpn2
gcc -fopenmp -mcpu=cortex-a9 -mfpu=neon -ftree-vectorize -mvectorize-with-neon-quad -ffast-math -O3 laplacian_filter.c -o laplacian_filter1_mpn3
gcc -fopenmp -mcpu=cortex-a9 -mfpu=neon -ftree-vectorize -mvectorize-with-neon-quad -ffast-math -Os laplacian_filter.c -o laplacian_filter1_mpns
./laplacian_filter1_mp
./laplacian_filter1_mpO1
./laplacian_filter1_mpO2
./laplacian_filter1_mpO3
./laplacian_filter1_mpOs
./laplacian_filter1_mpn
./laplacian_filter1_mpn1
./laplacian_filter1_mpn2
./laplacian_filter1_mpn3
./laplacian_filter1_mpns
objdump -S -d laplacian_filter1_mp | grep "vmov" -c
objdump -S -d laplacian_filter1_mpO1 | grep "vmov" -c
objdump -S -d laplacian_filter1_mpO2 | grep "vmov" -c
objdump -S -d laplacian_filter1_mpO3 | grep "vmov" -c
objdump -S -d laplacian_filter1_mpOs | grep "vmov" -c
objdump -S -d laplacian_filter1_mpn | grep "vmov" -c
objdump -S -d laplacian_filter1_mpn1 | grep "vmov" -c
objdump -S -d laplacian_filter1_mpn2 | grep "vmov" -c
objdump -S -d laplacian_filter1_mpn3 | grep "vmov" -c
objdump -S -d laplacian_filter1_mpns | grep "vmov" -c

を実行した。
結果を示す。
OpenMP_10_150717.png

ラプラシアンフィルタ処理後の画面はやはり、前回と同じで、最初に........が余計に出ている。
OpenMP_15_150717.jpg


次に、laplacian_filter2.c をOpenMP を使用するように gcc でオプション付けてコンパイルした。以下の様にCソースコードを修正した。
OpenMP_19_150717.png

同様に、2つ目の for ループの前に、

#ifdef _OPENMP
#pragma omp parallel for private(lap_fil_val, a, b, cam_fb_addr, lap_fb_addr)
#endif

を置いた。

このCソースコードを以下のコマンドで、コンパイル、実行、NEON命令の有無を調査した

。gcc -fopenmp laplacian_filter2.c -o laplacian_filter2_mp
gcc -fopenmp -O1 laplacian_filter2.c -o laplacian_filter2_mpO1
gcc -fopenmp -O2 laplacian_filter2.c -o laplacian_filter2_mpO2
gcc -fopenmp -O3 laplacian_filter2.c -o laplacian_filter2_mpO3
gcc -fopenmp -Os laplacian_filter2.c -o laplacian_filter2_mpOs
gcc -fopenmp -mcpu=cortex-a9 -mfpu=neon -ftree-vectorize -mvectorize-with-neon-quad -ffast-math laplacian_filter2.c -o laplacian_filter2_mpn
gcc -fopenmp -mcpu=cortex-a9 -mfpu=neon -ftree-vectorize -mvectorize-with-neon-quad -ffast-math -O1 laplacian_filter2.c -o laplacian_filter2_mpn1
gcc -fopenmp -mcpu=cortex-a9 -mfpu=neon -ftree-vectorize -mvectorize-with-neon-quad -ffast-math -O2 laplacian_filter2.c -o laplacian_filter2_mpn2
gcc -fopenmp -mcpu=cortex-a9 -mfpu=neon -ftree-vectorize -mvectorize-with-neon-quad -ffast-math -O3 laplacian_filter2.c -o laplacian_filter2_mpn3
gcc -fopenmp -mcpu=cortex-a9 -mfpu=neon -ftree-vectorize -mvectorize-with-neon-quad -ffast-math -Os laplacian_filter2.c -o laplacian_filter2_mpns
./laplacian_filter2_mp
./laplacian_filter2_mpO1
./laplacian_filter2_mpO2
./laplacian_filter2_mpO3
./laplacian_filter2_mpOs
./laplacian_filter2_mpn
./laplacian_filter2_mpn1
./laplacian_filter2_mpn2
./laplacian_filter2_mpn3
./laplacian_filter2_mpns
objdump -S -d laplacian_filter2_mp | grep "vmov" -c
objdump -S -d laplacian_filter2_mpO1 | grep "vmov" -c
objdump -S -d laplacian_filter2_mpO2 | grep "vmov" -c
objdump -S -d laplacian_filter2_mpO3 | grep "vmov" -c
objdump -S -d laplacian_filter2_mpOs | grep "vmov" -c
objdump -S -d laplacian_filter2_mpn | grep "vmov" -c
objdump -S -d laplacian_filter2_mpn1 | grep "vmov" -c
objdump -S -d laplacian_filter2_mpn2 | grep "vmov" -c
objdump -S -d laplacian_filter2_mpn3 | grep "vmov" -c
objdump -S -d laplacian_filter2_mpns | grep "vmov" -c

を実行した。
結果を示す。
OpenMP_11_150717.png

ラプラシアンフィルタの画像的には、左側はそれなりだが、右側が強調されている気がする。
OpenMP_16_150717.jpg
  1. 2015年07月17日 05:25 |
  2. Zynq
  3. | トラックバック:0
  4. | コメント:3

OpenMPでのラプラシアンフィルタの実行速度

今まで、NEON命令を使ってラプラシアンフィルタの実行速度を測ってきたが、今回はOpenMPを使って、高速化してみよう。
OpenMPとは複数のプロセッサを使ったマルチスレッディングを行うための API だそうだ。

参考にしたWebサイトは”OpenMP”と”gccのOpenMP実現について”だ。

まずは、”gccのOpenMP実現について”のhello.c をコンパイルして実行してみた。
ちなみに、”#incluce”だけではコンパイル・エラーだったので、”#incluede ”に変更した。

後は、同様に、gcc -fopenmp hello.c でコンパイルして、./a.out で実行した。
OpenMP_1_150716.png

Zynq は、Dual Core のCortex-A9 使用していて、2つのプロセッサなので、2プロセッサ使えるようだ。

次に laplacian_filter.c の 最初の for 文の前に”OpenMP”を参考にして、
#ifdef _OPENMP
#pragma omp parallel for
#endif

を入れてみた。
OpenMP_2_150716.png

gcc-4.8 で下のコマンドでコンパイルを行った。
gcc -fopenmp laplacian_filter.c -o laplacian_filter1_mp
コンパイルが正常に終了した。./laplacian_filter1_mp で動作させた。
OpenMP_3_150716.png

ラプラシアンフィルタ処理結果がおかしい。
OpenMP_7_150716.jpg

ラプラシアンフィルタのみの処理時間は、変動が大きいので、5回の平均を取った。その結果は、約 262 ms だった。
通常のlaplacian_filter1 のラプラシアンフィルタのみの処理時間は、425 ms なので、425 ms / 262 ms ≒ 1.62 倍速くなっているが、結果がおかしいのではしょうがない。

次に、laplacian_filter.c の次の for 文の前に
#ifdef _OPENMP
#pragma omp parallel for
#endif

を入れてみた。
OpenMP_4_150716.png

これで同様に、gcc -fopenmp laplacian_filter.c -o laplacian_filter1_mp
コンパイルが正常に終了した。./laplacian_filter1_mp で動作させた。
OpenMP_5_150716.png

今度は、ラプラシアンフィルタのみの処理時間は、約 243 ms だった。これも通常のラプラシアンフィルタのみの処理時間と比べてみよう。 425 ms / 243 ms ≒ 1.75 倍速かった。

今回のラプラシアンフィルタの処理画像は、最初に...... が入っているのが、バグのようだ。
OpenMP_8_150716.jpg

ちなみに、./laplacian_filter1 実行結果、つまり通常にコンパイルしたラプラシアンフィルタの処理画像を示す。
OpenMP_9_150716.jpg
  1. 2015年07月16日 05:28 |
  2. Zynq
  3. | トラックバック:0
  4. | コメント:0

gcc と clang で -Ofast 最適化オプションを付けた場合の実行速度

gcc-4.6, gcc-4.8, clang-3.4 とコンパイル時の最適化オプションによる速度の違いを検証してきた。
今回は、最適化オプションの -Ofast を gcc-4.6, gcc-4.8, clang-3.4 で試してみた。

最初は gcc-4.6 の -Ofast のみから。
NEON_40_150714.png

gcc-4.6 の自動ベクトル化の -Ofast
NEON_41_150714.png

gcc-4.8 の -Ofast のみ。
NEON_42_150714.png

gcc-4.8 の自動ベクトル化の -Ofast
NEON_43_150714.png

clang-3.4 でスクリプトを投入した。そのスクリプトを下に示す。

clang -Ofast laplacian_filter.c -o laplacian_filter1_Of
clang -Ofast laplacian_filter2.c -o laplacian_filter2_Of
clang -Ofast laplacian_filter3.c -o laplacian_filter3_Of
clang -Ofast laplacian_filter4.c -o laplacian_filter4_Of
clang -mcpu=cortex-a9 -mfpu=neon -mfloat-abi=hard -Ofast laplacian_filter.c -o laplacian_filter1_nf
clang -mcpu=cortex-a9 -mfpu=neon -mfloat-abi=hard -Ofast laplacian_filter2.c -o laplacian_filter2_nf
clang -mcpu=cortex-a9 -mfpu=neon -mfloat-abi=hard -Ofast laplacian_filter3.c -o laplacian_filter3_nf
clang -mcpu=cortex-a9 -mfpu=neon -mfloat-abi=hard -Ofast laplacian_filter4.c -o laplacian_filter4_nf
./laplacian_filter1_Of
./laplacian_filter2_Of
./laplacian_filter3_Of
./laplacian_filter4_Of
./laplacian_filter1_nf
./laplacian_filter2_nf
./laplacian_filter3_nf
./laplacian_filter4_nf


その結果を下に示す。
NEON_44_150714.png

gcc-4.6 でコンパイルした実行ファイルがNEON命令を使用しているか?を調べた。値が 0 でない実行ファイルがNEON命令を使用している。
NEON_45_150714.png

gcc-4.8 でコンパイルした実行ファイルがNEON命令を使用しているか?を調べた。値が 0 でない実行ファイルがNEON命令を使用している。
NEON_46_150714.png

clang-3.4 でコンパイルした実行ファイルがNEON命令を使用しているか?を調べた。値が 0 でない実行ファイルがNEON命令を使用している。
NEON_47_150714.png

gcc-4.6 と gcc-4.8 の -Ofast オプションのラプラシアンフィルタのみの処理時間を表に追加した。-Ofast は -O3 とほとんど変わらないようだ。
NEON_48_150714.png

clang-3.4 の -Ofast オプションのラプラシアンフィルタのみの処理時間を表に追加した。-Ofast は やはり -O3 とほとんど変わらないようだ。
NEON_49_150714.png
  1. 2015年07月14日 20:36 |
  2. Zynq
  3. | トラックバック:0
  4. | コメント:0

バケモノの子(映画)を見てきました

今日はバケモノの子(映画)(音注意です)を見てきました。
いろいろとネットでは書かれているようですが、私としてはとっても良かったです。最高でした。また見たいです。設定が甘いところはあるんでしょうけど。。。
  1. 2015年07月12日 20:15 |
  2. 日記
  3. | トラックバック:0
  4. | コメント:0

clang の最適化と自動ベクトル化を使用した時の性能

2015/07/14:修正 時間計測にバグがあったので、全面的に記事を修正しました。miyox さん、ありがとうございました)

ZYBOのUbuntu14.04 LTSにclangをインストールしてコンパイル”で、clang-3.4 + llvm-3.4 でC がコンパイルできるようになったので、clang の最適化オプションや自動ベクトル化・オプションを付けてコンパイルした時にどのくらいの性能向上があるのかを検証する。

実行環境はZYBOで、Dual Cortex-A9 650 MHz プロセッサを持ち、Ubuntu 14.04 LTS が動作している。

clang ではgcc の様に warning を取り除けなかったので、各最適化オプションの実行形式ファイルを個別に作製して、それを実行して、ラプラシアンフィルタ処理のみの時間を計測している。

最初に、laplacian_filter.c の最適化テストを行った。下にコンパイル・コマンドを示す。

clang laplacian_filter.c -o laplacian_filter1
clang -O1 laplacian_filter.c -o laplacian_filter1_O1
clang -O2 laplacian_filter.c -o laplacian_filter1_O2
clang -O3 laplacian_filter.c -o laplacian_filter1_O3
clang -Os laplacian_filter.c -o laplacian_filter1_Os
clang -mcpu=cortex-a9 -mfpu=neon -mfloat-abi=hard -O3 laplacian_filter.c -o laplacian_filter1_n3

実行結果を下に示す。自動ベクトル化オプションを付けてコンパイルした実行形式ファイルについては、以下のコマンドで、

objdump -S -d laplacian_filter1_n3 | grep "vmov"

NEONの命令の vmov があるかないかでベクトル化されているかどうかを調査した。
clang_2_150711.png
自動ベクトル化はされて無かった。

laplacian_filte2r.c の最適化テストを行った。下にコンパイル・コマンドを示す。

clang laplacian_filter2.c -o laplacian_filter2
clang -O1 laplacian_filter2.c -o laplacian_filter2_O1
clang -O2 laplacian_filter2.c -o laplacian_filter2_O2
clang -O3 laplacian_filter2.c -o laplacian_filter2_O3
clang -Os laplacian_filter2.c -o laplacian_filter2_Os
clang -mcpu=cortex-a9 -mfpu=neon -mfloat-abi=hard -O3 laplacian_filter2.c -o laplacian_filter2_n3

自動ベクトル化オプションを付けてコンパイルした実行形式ファイルについては、以下のコマンドで、

objdump -S -d laplacian_filter2_n3 | grep "vmov"

NEONの命令の vmov があるかないかでベクトル化されているかどうかを調査した。
clang_3_150711.png
自動ベクトル化されているようだ。

laplacian_filte3r.c の最適化テストを行った。下にコンパイル・コマンドを示す。

clang laplacian_filter3.c -o laplacian_filter3
clang -O1 laplacian_filter3.c -o laplacian_filter3_O1
clang -O2 laplacian_filter3.c -o laplacian_filter3_O2
clang -O3 laplacian_filter3.c -o laplacian_filter3_O3
clang -Os laplacian_filter3.c -o laplacian_filter3_Os
clang -mcpu=cortex-a9 -mfpu=neon -mfloat-abi=hard -O3 laplacian_filter3.c -o laplacian_filter3_n3

自動ベクトル化オプションを付けてコンパイルした実行形式ファイルについては、以下のコマンドで、

objdump -S -d laplacian_filter3_n3 | grep "vmov"

NEONの命令の vmov があるかないかでベクトル化されているかどうかを調査した。
clang_4_150711.png
こちらも自動ベクトル化されているようだ。

laplacian_filter4.c の最適化テストを行った。下にコンパイル・コマンドを示す。

clang laplacian_filter4.c -o laplacian_filter4
clang -O1 laplacian_filter4.c -o laplacian_filter4_O1
clang -O2 laplacian_filter4.c -o laplacian_filter4_O2
clang -O3 laplacian_filter4.c -o laplacian_filter4_O3
clang -Os laplacian_filter4.c -o laplacian_filter4_Os
clang -mcpu=cortex-a9 -mfpu=neon -mfloat-abi=hard -O3 laplacian_filter4.c -o laplacian_filter4_n3

自動ベクトル化オプションを付けてコンパイルした実行形式ファイルについては、以下のコマンドで、

objdump -S -d laplacian_filter4_n3 | grep "vmov"

NEONの命令の vmov があるかないかでベクトル化されているかどうかを調査した。
clang_5_150711.png
自動ベクトル化はされて無かった。

laplacian_filter.c の自動ベクトル化の-Oフラグによる違いを検証した。それぞれの実行形式ファイルはn の後の添字が最適化フラグ -O の値を示している。
更に、objdump -S -d laplacian_filter1_n? | grep "vmov" -Cをそれぞれの実行形式ファイルについて実行して自動ベクトル化されているかどうかを確認した。返り値が 0 でない数字の実行形式ファイルが自動ベクトル化されている。
clang_6_150711.png

laplacian_filte2r.c の自動ベクトル化の-Oフラグによる違いを検証した。それぞれの実行形式ファイルはn の後の添字が最適化フラグ -O の値を示している。
更に、objdump -S -d laplacian_filter2_n? | grep "vmov" -Cをそれぞれの実行形式ファイルについて実行して自動ベクトル化されているかどうかを確認した。返り値が 0 でない数字の実行形式ファイルが自動ベクトル化されている。
clang_7_150711.png

laplacian_filte3r.c の自動ベクトル化の-Oフラグによる違いを検証した。それぞれの実行形式ファイルはn の後の添字が最適化フラグ -O の値を示している。
更に、objdump -S -d laplacian_filter3_n? | grep "vmov" -Cをそれぞれの実行形式ファイルについて実行して自動ベクトル化されているかどうかを確認した。返り値が 0 でない数字の実行形式ファイルが自動ベクトル化されている。
clang_8_150711.png

laplacian_filter4.c の自動ベクトル化の-Oフラグによる違いを検証した。それぞれの実行形式ファイルはn の後の添字が最適化フラグ -O の値を示している。
更に、objdump -S -d laplacian_filter4_n? | grep "vmov" -Cをそれぞれの実行形式ファイルについて実行して自動ベクトル化されているかどうかを確認した。返り値が 0 でない数字の実行形式ファイルが自動ベクトル化されている。
clang_9_150711.png

以前の gcc 4.6 と 4.8 の結果を示す。自動ベクトル化された実行形式ファイルはフォントの色を赤に変更してある。
clang_10_150711.png

今回の clang-3.4, llvm-3.4 の結果を示す。自動ベクトル化された実行形式ファイルはフォントの色を赤に変更してある。
clang_11_150711.png

clang の実行時間データの中で最速のデータは、clang の 自動ベクトル化、-Osの時の laplacian_filter2 だったが、gcc-4.8 の実行時間が一番速いようだ。

自動ベクトル化すると、gcc, clang 共に速くなることがわかった。
gcc-4.6 で最速の77.7 ms が自動ベクトル化された時の実行時間だ。自動ベクトル化されていない時は 82.2 ms だったのが表からわかる。これは、83.6 ms / 77.7 ms ≒ 1.08 倍に速くなっている。

gcc-4.8 で、自動ベクトル化された最速の 74.4 ms の自動ベクトル化なしの場合は、82.2 ms だった。これは、82.2 ms / 74.4 ≒ 1.10 倍に速くなっている。

clang-3.4 で、自動ベクトル化された最速の 87.4 ms の自動ベクトル化なしの場合は、105 ms だった。これは、105 ms / 87.4 ms ≒ 1.20 倍速くなっている。

オプションによる実行時間の差が大きい事があるのが分かったので、少なくとも最適化オプションは全て確かめてみるべきだろうと思う。
  1. 2015年07月11日 20:07 |
  2. Zynq
  3. | トラックバック:0
  4. | コメント:0

ZYBOのUbuntu14.04 LTSにclangをインストールしてコンパイル

ZYBO で動作している Ubuntu14.04 LTSに clang-3.4 をインストールしました。
インストール自体は
sudo apt-get update
sudo apt-get upgrade
sudo apt-get clang

です。これで、clang-3.4 と llvm-3.4 がインストールされました。

この状態で、clang laplacian_filter.c を実行して clang でコンパイルしてみましたが、

error: unable to open output file '/tmp/laplacian_filter-79ee4c.s': 'Error
      opening output file '/tmp/laplacian_filter-79ee4c.s': Permission denied'


が出ました。
良くエラー内容を見ると、clang はどうやら中間ファイルを /tmp に書き込むようです。/tmp のパーミッションを 777 ( sudo chmod 777 /tmp ) にしたところ、clang でコンパイルができるようになりました。(下の図ではなにもしないでコンパイルが成功したように見えますが、別のターミナルから chmod してコンパイルが成功するようになりました)
clang_150710.png

clangのNEONを使用したオートベクタライズについての資料
General Cross-Compilation Options in Clang CPU, FPU, ABI
Auto-Vectorization in LLVM

clang -mcpu=cortex-a9 -mfpu=neon -mfloat-abi=hard -O3 laplacian_filter2.cでベクトル化されてNEON使用命令を出力するので速い。ラプラシアンフィルタのみの処理時間は、84.2 ms 程度。
clang -O3 laplacian_filter2.c でコンパイルすると、ラプラシアンフィルタのみの処理時間は、100 ms 程度。

詳しいデータは後日ブログに書きます。
  1. 2015年07月10日 04:48 |
  2. Linux
  3. | トラックバック:0
  4. | コメント:0

gcc の最適化と自動ベクトル化を使用した時の性能3

(2015/07/11:タイトルが間違っていたので変更しました)

gcc の最適化と自動ベクトル化を使用した時の性能2”の続き。

前回は gcc 4.8 の時のコンパイル・オプションやNEON 最適化ライブラリを使用するためのコンパイラ・オプションを使った時の性能差を表にまとめた。
今回は、NEON最適化ライブラリを使用するコンパイラ・オプションを付けた時に本当のNEONを使用するアセンブラ・コードが出ているかを検証する。

実行環境はZYBOで、Dual Cortex-A9 650 MHz プロセッサを持ち、Ubuntu 14.04 LTS が動作している。

久しぶりにくりさんがコメントを寄せてくれて、ベクトル化の情報を表示するコンパイラ・オプション -ftree-vectorizer-verbose=1 を教えて頂いた。これを使ってベクトル化されているかどうかを検証してみたい。(Introducing NEON Development Article 1.4.3. Automatic vectorization を参照した)

最初に laplacian_filter.c
gcc -mcpu=cortex-a9 -mfpu=neon -mvectorize-with-neon-quad -ffast-math -O3 -ftree-vectorizer-verbose=1 laplacian_filter.c -o laplacian_filter1
でコンパイルした。
NEON_23_150709.png

ベクトル化されていない。初期化や終了処理を含まないラプラシアンフィルタ処理時間は、128 ms だった。

次に、laplacian_filter2.c
gcc -mcpu=cortex-a9 -mfpu=neon -mvectorize-with-neon-quad -ffast-math -O3 -ftree-vectorizer-verbose=1 laplacian_filter2.c -o laplacian_filter2
でコンパイルした。
NEON_24_150709.png

ベクトル化されている。初期化や終了処理を含まないラプラシアンフィルタ処理時間は、938 ms だった。

ちなみにベクトル化するには最適化レベル3 (-O3) でする必要があるようだ。laplacian_filter2.c で -O3 以外でコンパイルすると、メッセージが何も出てこない。
NEON_25_150709.png

laplacian_filter3.c
gcc -mcpu=cortex-a9 -mfpu=neon -mvectorize-with-neon-quad -ffast-math -O3 -ftree-vectorizer-verbose=1 laplacian_filter3.c -o laplacian_filter3
でコンパイルした。
NEON_26_150709.png

これもベクトル化されている。初期化や終了処理を含まないラプラシアンフィルタ処理時間は、747 ms だった。

laplacian_filter4.c
gcc -mcpu=cortex-a9 -mfpu=neon -mvectorize-with-neon-quad -ffast-math -O3 -ftree-vectorizer-verbose=1 laplacian_filter4.c -o laplacian_filter4
でコンパイルした。
NEON_27_150709.png

ベクトル化されていない。初期化や終了処理を含まないラプラシアンフィルタ処理時間は、888 ms だった。

ベクトル化されたのは、ソフトウェアのラプラシアンフィルタのCソースコードを memcpy() で置き換えて、Vivado HLS 対応にしたものだ。laplacian_filter4.c まで Vivado HLS に最適化すると、gcc はベクトル化できないようだ。

最後に、
objdump -S -d laplacian_filter3 > laplacian_filter3.asm
で、逆アセンブルした laplacian_filter3 のobjdump 結果を貼っておく。vshl や vaddi, vsub などのNEONを使用するコードが使われている。


laplacian_filter3:     file format elf32-littlearm


Disassembly of section .init:

00008428 <_init>:
    8428:    e92d4008     push    {r3, lr}
    842c:    eb0001a8     bl    8ad4 <call_weak_fn>
    8430:    e8bd8008     pop    {r3, pc}

Disassembly of section .plt:

00008434 <.plt>:
    8434:    e52de004     push    {lr}        ; (str lr, [sp, #-4]!)
    8438:    e59fe004     ldr    lr, [pc, #4]    ; 8444 <_init+0x1c>
    843c:    e08fe00e     add    lr, pc, lr
    8440:    e5bef008     ldr    pc, [lr, #8]!
    8444:    00008bbc     .word    0x00008bbc
    8448:    e28fc600     add    ip, pc, #0, 12
    844c:    e28cca08     add    ip, ip, #8, 20    ; 0x8000
    8450:    e5bcfbbc     ldr    pc, [ip, #3004]!    ; 0xbbc
    8454:    e28fc600     add    ip, pc, #0, 12
    8458:    e28cca08     add    ip, ip, #8, 20    ; 0x8000
    845c:    e5bcfbb4     ldr    pc, [ip, #2996]!    ; 0xbb4
    8460:    e28fc600     add    ip, pc, #0, 12
    8464:    e28cca08     add    ip, ip, #8, 20    ; 0x8000
    8468:    e5bcfbac     ldr    pc, [ip, #2988]!    ; 0xbac
    846c:    e28fc600     add    ip, pc, #0, 12
    8470:    e28cca08     add    ip, ip, #8, 20    ; 0x8000
    8474:    e5bcfba4     ldr    pc, [ip, #2980]!    ; 0xba4
    8478:    e28fc600     add    ip, pc, #0, 12
    847c:    e28cca08     add    ip, ip, #8, 20    ; 0x8000
    8480:    e5bcfb9c     ldr    pc, [ip, #2972]!    ; 0xb9c
    8484:    e28fc600     add    ip, pc, #0, 12
    8488:    e28cca08     add    ip, ip, #8, 20    ; 0x8000
    848c:    e5bcfb94     ldr    pc, [ip, #2964]!    ; 0xb94
    8490:    e28fc600     add    ip, pc, #0, 12
    8494:    e28cca08     add    ip, ip, #8, 20    ; 0x8000
    8498:    e5bcfb8c     ldr    pc, [ip, #2956]!    ; 0xb8c
    849c:    e28fc600     add    ip, pc, #0, 12
    84a0:    e28cca08     add    ip, ip, #8, 20    ; 0x8000
    84a4:    e5bcfb84     ldr    pc, [ip, #2948]!    ; 0xb84
    84a8:    e28fc600     add    ip, pc, #0, 12
    84ac:    e28cca08     add    ip, ip, #8, 20    ; 0x8000
    84b0:    e5bcfb7c     ldr    pc, [ip, #2940]!    ; 0xb7c
    84b4:    e28fc600     add    ip, pc, #0, 12
    84b8:    e28cca08     add    ip, ip, #8, 20    ; 0x8000
    84bc:    e5bcfb74     ldr    pc, [ip, #2932]!    ; 0xb74
    84c0:    e28fc600     add    ip, pc, #0, 12
    84c4:    e28cca08     add    ip, ip, #8, 20    ; 0x8000
    84c8:    e5bcfb6c     ldr    pc, [ip, #2924]!    ; 0xb6c
    84cc:    e28fc600     add    ip, pc, #0, 12
    84d0:    e28cca08     add    ip, ip, #8, 20    ; 0x8000
    84d4:    e5bcfb64     ldr    pc, [ip, #2916]!    ; 0xb64

Disassembly of section .text:

000084d8 <main>:
    84d8:    e92d 4ff0     stmdb    sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr}
    84dc:    f648 4044     movw    r0, #35908    ; 0x8c44
    84e0:    ed2d 8b04     vpush    {d8-d9}
    84e4:    f5ad 5d49     sub.w    sp, sp, #12864    ; 0x3240
    84e8:    b083          sub    sp, #12
    84ea:    2102          movs    r1, #2
    84ec:    f2c0 0000     movt    r0, #0
    84f0:    f7ff efce     blx    8490 <_init+0x68>
    84f4:    2800          cmp    r0, #0
    84f6:    bfdf          itttt    le
    84f8:    f241 0348     movwle    r3, #4168    ; 0x1048
    84fc:    f648 4050     movwle    r0, #35920    ; 0x8c50
    8500:    f2c0 0301     movtle    r3, #1
    8504:    f2c0 0000     movtle    r0, #0
    8508:    f340 812d     ble.w    8766 <main+0x28e>
    850c:    2400          movs    r4, #0
    850e:    f04f 7180     mov.w    r1, #16777216    ; 0x1000000
    8512:    e88d 0011     stmia.w    sp, {r0, r4}
    8516:    2203          movs    r2, #3
    8518:    4620          mov    r0, r4
    851a:    2301          movs    r3, #1
    851c:    f7ff efc4     blx    84a8 <_init+0x80>
    8520:    900a          str    r0, [sp, #40]    ; 0x28
    8522:    2800          cmp    r0, #0
    8524:    f000 80e4     beq.w    86f0 <main+0x218>
    8528:    f500 0500     add.w    r5, r0, #8388608    ; 0x800000
    852c:    f500 0620     add.w    r6, r0, #10485760    ; 0xa00000
    8530:    08b6          lsrs    r6, r6, #2
    8532:    08ad          lsrs    r5, r5, #2
    8534:    ff87 805f     vmov.i32    q4, #255    ; 0x000000ff
    8538:    a80e          add    r0, sp, #56    ; 0x38
    853a:    4621          mov    r1, r4
    853c:    f7ff ef96     blx    846c <_init+0x44>
    8540:    1bab          subs    r3, r5, r6
    8542:    f04f 0902     mov.w    r9, #2
    8546:    009b          lsls    r3, r3, #2
    8548:    ea4f 0c86     mov.w    ip, r6, lsl #2
    854c:    00ad          lsls    r5, r5, #2
    854e:    f503 6348     add.w    r3, r3, #3200    ; 0xc80
    8552:    9307          str    r3, [sp, #28]
    8554:    f10d 0b48     add.w    fp, sp, #72    ; 0x48
    8558:    464b          mov    r3, r9
    855a:    46a0          mov    r8, r4
    855c:    4626          mov    r6, r4
    855e:    2701          movs    r7, #1
    8560:    f240 3a1f     movw    sl, #799    ; 0x31f
    8564:    46a1          mov    r9, r4
    8566:    950b          str    r5, [sp, #44]    ; 0x2c
    8568:    9907          ldr    r1, [sp, #28]
    856a:    f240 2057     movw    r0, #599    ; 0x257
    856e:    f44f 6248     mov.w    r2, #3200    ; 0xc80
    8572:    f1b8 0f00     cmp.w    r8, #0
    8576:    bf18          it    ne
    8578:    4580          cmpne    r8, r0
    857a:    fb02 f203     mul.w    r2, r2, r3
    857e:    f44f 6048     mov.w    r0, #3200    ; 0xc80
    8582:    4461          add    r1, ip
    8584:    bf08          it    eq
    8586:    2301          moveq    r3, #1
    8588:    9106          str    r1, [sp, #24]
    858a:    f60d 41c8     addw    r1, sp, #3272    ; 0xcc8
    858e:    fb00 1606     mla    r6, r0, r6, r1
    8592:    f102 0504     add.w    r5, r2, #4
    8596:    bf18          it    ne
    8598:    2300          movne    r3, #0
    859a:    440a          add    r2, r1
    859c:    2400          movs    r4, #0
    859e:    3e04          subs    r6, #4
    85a0:    440d          add    r5, r1
    85a2:    9208          str    r2, [sp, #32]
    85a4:    fb00 1707     mla    r7, r0, r7, r1
    85a8:    f102 0020     add.w    r0, r2, #32
    85ac:    9005          str    r0, [sp, #20]
    85ae:    2200          movs    r2, #0
    85b0:    bb43          cbnz    r3, 8604 <main+0x12c>
    85b2:    2c00          cmp    r4, #0
    85b4:    bf18          it    ne
    85b6:    4554          cmpne    r4, sl
    85b8:    bf08          it    eq
    85ba:    461a          moveq    r2, r3
    85bc:    d022          beq.n    8604 <main+0x12c>
    85be:    2c01          cmp    r4, #1
    85c0:    f000 80da     beq.w    8778 <main+0x2a0>
    85c4:    6832          ldr    r2, [r6, #0]
    85c6:    6871          ldr    r1, [r6, #4]
    85c8:    68b0          ldr    r0, [r6, #8]
    85ca:    f1c2 0e00     rsb    lr, r2, #0
    85ce:    f857 2c04     ldr.w    r2, [r7, #-4]
    85d2:    ebc1 0e0e     rsb    lr, r1, lr
    85d6:    6839          ldr    r1, [r7, #0]
    85d8:    ebc0 0e0e     rsb    lr, r0, lr
    85dc:    ebc2 0e0e     rsb    lr, r2, lr
    85e0:    687a          ldr    r2, [r7, #4]
    85e2:    eb0e 0ec1     add.w    lr, lr, r1, lsl #3
    85e6:    e915 0003     ldmdb    r5, {r0, r1}
    85ea:    ebc2 0e0e     rsb    lr, r2, lr
    85ee:    682a          ldr    r2, [r5, #0]
    85f0:    ebc0 000e     rsb    r0, r0, lr
    85f4:    1a41          subs    r1, r0, r1
    85f6:    1a89          subs    r1, r1, r2
    85f8:    f381 0108     usat    r1, #8, r1
    85fc:    020a          lsls    r2, r1, #8
    85fe:    eb02 4201     add.w    r2, r2, r1, lsl #16
    8602:    440a          add    r2, r1
    8604:    f84b 2024     str.w    r2, [fp, r4, lsl #2]
    8608:    3401          adds    r4, #1
    860a:    f5b4 7f48     cmp.w    r4, #800    ; 0x320
    860e:    f106 0604     add.w    r6, r6, #4
    8612:    f107 0704     add.w    r7, r7, #4
    8616:    f105 0504     add.w    r5, r5, #4
    861a:    d1c8          bne.n    85ae <main+0xd6>
    861c:    f50c 6448     add.w    r4, ip, #3200    ; 0xc80
    8620:    f109 0901     add.w    r9, r9, #1
    8624:    4660          mov    r0, ip
    8626:    4659          mov    r1, fp
    8628:    f44f 6248     mov.w    r2, #3200    ; 0xc80
    862c:    f894 f000     pld    [r4]
    8630:    f7ff ef0a     blx    8448 <_init+0x20>
    8634:    f1b9 0f03     cmp.w    r9, #3
    8638:    dd0c          ble.n    8654 <main+0x17c>
    863a:    f240 2057     movw    r0, #599    ; 0x257
    863e:    4580          cmp    r8, r0
    8640:    d01a          beq.n    8678 <main+0x1a0>
    8642:    f04f 0901     mov.w    r9, #1
    8646:    2302          movs    r3, #2
    8648:    2701          movs    r7, #1
    864a:    2600          movs    r6, #0
    864c:    f108 0801     add.w    r8, r8, #1
    8650:    46a4          mov    ip, r4
    8652:    e789          b.n    8568 <main+0x90>
    8654:    f240 2357     movw    r3, #599    ; 0x257
    8658:    4598          cmp    r8, r3
    865a:    d00d          beq.n    8678 <main+0x1a0>
    865c:    f1b9 0f02     cmp.w    r9, #2
    8660:    d006          beq.n    8670 <main+0x198>
    8662:    f1b9 0f03     cmp.w    r9, #3
    8666:    d1ee          bne.n    8646 <main+0x16e>
    8668:    2301          movs    r3, #1
    866a:    2700          movs    r7, #0
    866c:    2602          movs    r6, #2
    866e:    e7ed          b.n    864c <main+0x174>
    8670:    2300          movs    r3, #0
    8672:    464f          mov    r7, r9
    8674:    2601          movs    r6, #1
    8676:    e7e9          b.n    864c <main+0x174>
    8678:    2100          movs    r1, #0
    867a:    a810          add    r0, sp, #64    ; 0x40
    867c:    f7ff eef6     blx    846c <_init+0x44>
    8680:    f04f 7180     mov.w    r1, #16777216    ; 0x1000000
    8684:    980a          ldr    r0, [sp, #40]    ; 0x28
    8686:    f7ff ef1c     blx    84c0 <_init+0x98>
    868a:    f648 4084     movw    r0, #35972    ; 0x8c84
    868e:    2102          movs    r1, #2
    8690:    f2c0 0000     movt    r0, #0
    8694:    f7ff eefc     blx    8490 <_init+0x68>
    8698:    1e03          subs    r3, r0, #0
    869a:    dd5c          ble.n    8756 <main+0x27e>
    869c:    2000          movs    r0, #0
    869e:    9300          str    r3, [sp, #0]
    86a0:    9001          str    r0, [sp, #4]
    86a2:    f44f 3180     mov.w    r1, #65536    ; 0x10000
    86a6:    2203          movs    r2, #3
    86a8:    2301          movs    r3, #1
    86aa:    f7ff eefe     blx    84a8 <_init+0x80>
    86ae:    2800          cmp    r0, #0
    86b0:    d040          beq.n    8734 <main+0x25c>
    86b2:    f04f 52c1     mov.w    r2, #404750336    ; 0x18200000
    86b6:    f44f 3180     mov.w    r1, #65536    ; 0x10000
    86ba:    6002          str    r2, [r0, #0]
    86bc:    f7ff ef00     blx    84c0 <_init+0x98>
    86c0:    9b0f          ldr    r3, [sp, #60]    ; 0x3c
    86c2:    9811          ldr    r0, [sp, #68]    ; 0x44
    86c4:    9c10          ldr    r4, [sp, #64]    ; 0x40
    86c6:    4298          cmp    r0, r3
    86c8:    db23          blt.n    8712 <main+0x23a>
    86ca:    f85b 2c10     ldr.w    r2, [fp, #-16]
    86ce:    f648 41c4     movw    r1, #36036    ; 0x8cc4
    86d2:    1ac3          subs    r3, r0, r3
    86d4:    f2c0 0100     movt    r1, #0
    86d8:    2001          movs    r0, #1
    86da:    1aa2          subs    r2, r4, r2
    86dc:    f7ff eeea     blx    84b4 <_init+0x8c>
    86e0:    2000          movs    r0, #0
    86e2:    f50d 5d49     add.w    sp, sp, #12864    ; 0x3240
    86e6:    b003          add    sp, #12
    86e8:    ecbd 8b04     vpop    {d8-d9}
    86ec:    e8bd 8ff0     ldmia.w    sp!, {r4, r5, r6, r7, r8, r9, sl, fp, pc}
    86f0:    f241 0348     movw    r3, #4168    ; 0x1048
    86f4:    f648 4068     movw    r0, #35944    ; 0x8c68
    86f8:    f2c0 0301     movt    r3, #1
    86fc:    f2c0 0000     movt    r0, #0
    8700:    681b          ldr    r3, [r3, #0]
    8702:    2101          movs    r1, #1
    8704:    2218          movs    r2, #24
    8706:    f7ff eeac     blx    8460 <_init+0x38>
    870a:    f04f 30ff     mov.w    r0, #4294967295
    870e:    f7ff eec6     blx    849c <_init+0x74>
    8712:    f85b 2c10     ldr.w    r2, [fp, #-16]
    8716:    f500 2074     add.w    r0, r0, #999424    ; 0xf4000
    871a:    f500 7010     add.w    r0, r0, #576    ; 0x240
    871e:    f648 41c4     movw    r1, #36036    ; 0x8cc4
    8722:    1ac3          subs    r3, r0, r3
    8724:    f2c0 0100     movt    r1, #0
    8728:    1aa2          subs    r2, r4, r2
    872a:    2001          movs    r0, #1
    872c:    3a01          subs    r2, #1
    872e:    f7ff eec2     blx    84b4 <_init+0x8c>
    8732:    e7d5          b.n    86e0 <main+0x208>
    8734:    f241 0348     movw    r3, #4168    ; 0x1048
    8738:    f648 40a8     movw    r0, #36008    ; 0x8ca8
    873c:    f2c0 0301     movt    r3, #1
    8740:    f2c0 0000     movt    r0, #0
    8744:    681b          ldr    r3, [r3, #0]
    8746:    2101          movs    r1, #1
    8748:    221a          movs    r2, #26
    874a:    f7ff ee8a     blx    8460 <_init+0x38>
    874e:    f04f 30ff     mov.w    r0, #4294967295
    8752:    f7ff eea4     blx    849c <_init+0x74>
    8756:    f241 0348     movw    r3, #4168    ; 0x1048
    875a:    f648 4090     movw    r0, #35984    ; 0x8c90
    875e:    f2c0 0301     movt    r3, #1
    8762:    f2c0 0000     movt    r0, #0
    8766:    681b          ldr    r3, [r3, #0]
    8768:    2101          movs    r1, #1
    876a:    2215          movs    r2, #21
    876c:    f7ff ee78     blx    8460 <_init+0x38>
    8770:    f04f 30ff     mov.w    r0, #4294967295
    8774:    f7ff ee92     blx    849c <_init+0x74>
    8778:    f1b8 0f01     cmp.w    r8, #1
    877c:    f000 80be     beq.w    88fc <main+0x424>
    8780:    9906          ldr    r1, [sp, #24]
    8782:    f44f 6248     mov.w    r2, #3200    ; 0xc80
    8786:    9808          ldr    r0, [sp, #32]
    8788:    9302          str    r3, [sp, #8]
    878a:    f8cd c00c     str.w    ip, [sp, #12]
    878e:    f7ff ee5c     blx    8448 <_init+0x20>
    8792:    9a05          ldr    r2, [sp, #20]
    8794:    9b02          ldr    r3, [sp, #8]
    8796:    f8dd c00c     ldr.w    ip, [sp, #12]
    879a:    4611          mov    r1, r2
    879c:    4618          mov    r0, r3
    879e:    ed52 0b08     vldr    d16, [r2, #-32]    ; 0xffffffe0
    87a2:    ed52 1b06     vldr    d17, [r2, #-24]    ; 0xffffffe8
    87a6:    eff0 2070     vshr.s32    q9, q8, #16
    87aa:    f892 f0a0     pld    [r2, #160]    ; 0xa0
    87ae:    3002          adds    r0, #2
    87b0:    eff8 4070     vshr.s32    q10, q8, #8
    87b4:    28c6          cmp    r0, #198    ; 0xc6
    87b6:    4696          mov    lr, r2
    87b8:    9109          str    r1, [sp, #36]    ; 0x24
    87ba:    ef42 21d8     vand    q9, q9, q4
    87be:    f102 0220     add.w    r2, r2, #32
    87c2:    ef44 41d8     vand    q10, q10, q4
    87c6:    f101 0120     add.w    r1, r1, #32
    87ca:    ef40 01d8     vand    q8, q8, q4
    87ce:    efe2 a572     vshl.s32    q13, q9, #2
    87d2:    efe2 6574     vshl.s32    q11, q10, #2
    87d6:    ef6a a8e2     vadd.i32    q13, q13, q9
    87da:    ef66 48e4     vadd.i32    q10, q11, q10
    87de:    efe3 6570     vshl.s32    q11, q8, #3
    87e2:    efe1 a57a     vshl.s32    q13, q13, #1
    87e6:    efe4 8574     vshl.s32    q12, q10, #4
    87ea:    ef6a 28e2     vadd.i32    q9, q13, q9
    87ee:    ff66 68e0     vsub.i32    q11, q11, q8
    87f2:    ff68 48e4     vsub.i32    q10, q12, q10
    87f6:    efe3 8572     vshl.s32    q12, q9, #3
    87fa:    efe1 4574     vshl.s32    q10, q10, #1
    87fe:    efe2 6576     vshl.s32    q11, q11, #2
    8802:    ff68 28e2     vsub.i32    q9, q12, q9
    8806:    ef66 08e0     vadd.i32    q8, q11, q8
    880a:    ef62 28e4     vadd.i32    q9, q9, q10
    880e:    ef62 08e0     vadd.i32    q8, q9, q8
    8812:    eff8 0070     vshr.s32    q8, q8, #8
    8816:    ed41 0b10     vstr    d16, [r1, #-64]    ; 0xffffffc0
    881a:    ed41 1b0e     vstr    d17, [r1, #-56]    ; 0xffffffc8
    881e:    ed52 0b0c     vldr    d16, [r2, #-48]    ; 0xffffffd0
    8822:    ed52 1b0a     vldr    d17, [r2, #-40]    ; 0xffffffd8
    8826:    eff0 2070     vshr.s32    q9, q8, #16
    882a:    eff8 4070     vshr.s32    q10, q8, #8
    882e:    ef42 21d8     vand    q9, q9, q4
    8832:    ef44 41d8     vand    q10, q10, q4
    8836:    ef40 01d8     vand    q8, q8, q4
    883a:    efe2 a572     vshl.s32    q13, q9, #2
    883e:    efe2 6574     vshl.s32    q11, q10, #2
    8842:    ef6a a8e2     vadd.i32    q13, q13, q9
    8846:    ef66 48e4     vadd.i32    q10, q11, q10
    884a:    efe3 6570     vshl.s32    q11, q8, #3
    884e:    efe1 a57a     vshl.s32    q13, q13, #1
    8852:    efe4 8574     vshl.s32    q12, q10, #4
    8856:    ef6a 28e2     vadd.i32    q9, q13, q9
    885a:    ff66 68e0     vsub.i32    q11, q11, q8
    885e:    ff68 48e4     vsub.i32    q10, q12, q10
    8862:    efe3 8572     vshl.s32    q12, q9, #3
    8866:    efe1 4574     vshl.s32    q10, q10, #1
    886a:    efe2 6576     vshl.s32    q11, q11, #2
    886e:    ff68 28e2     vsub.i32    q9, q12, q9
    8872:    ef66 08e0     vadd.i32    q8, q11, q8
    8876:    ef62 28e4     vadd.i32    q9, q9, q10
    887a:    ef62 08e0     vadd.i32    q8, q9, q8
    887e:    eff8 0070     vshr.s32    q8, q8, #8
    8882:    ed41 0b0c     vstr    d16, [r1, #-48]    ; 0xffffffd0
    8886:    ed41 1b0a     vstr    d17, [r1, #-40]    ; 0xffffffd8
    888a:    d188          bne.n    879e <main+0x2c6>
    888c:    9a09          ldr    r2, [sp, #36]    ; 0x24
    888e:    e000          b.n    8892 <main+0x3ba>
    8890:    20c7          movs    r0, #199    ; 0xc7
    8892:    f96e 0add     vld1.64    {d16-d17}, [lr :64]!
    8896:    eff0 2070     vshr.s32    q9, q8, #16
    889a:    28c6          cmp    r0, #198    ; 0xc6
    889c:    eff8 4070     vshr.s32    q10, q8, #8
    88a0:    ef42 21d8     vand    q9, q9, q4
    88a4:    ef44 41d8     vand    q10, q10, q4
    88a8:    ef40 01d8     vand    q8, q8, q4
    88ac:    efe2 a572     vshl.s32    q13, q9, #2
    88b0:    efe2 6574     vshl.s32    q11, q10, #2
    88b4:    ef6a a8e2     vadd.i32    q13, q13, q9
    88b8:    ef66 48e4     vadd.i32    q10, q11, q10
    88bc:    efe3 6570     vshl.s32    q11, q8, #3
    88c0:    efe1 a57a     vshl.s32    q13, q13, #1
    88c4:    efe4 8574     vshl.s32    q12, q10, #4
    88c8:    ef6a 28e2     vadd.i32    q9, q13, q9
    88cc:    ff66 68e0     vsub.i32    q11, q11, q8
    88d0:    ff68 48e4     vsub.i32    q10, q12, q10
    88d4:    efe3 8572     vshl.s32    q12, q9, #3
    88d8:    efe1 4574     vshl.s32    q10, q10, #1
    88dc:    efe2 6576     vshl.s32    q11, q11, #2
    88e0:    ff68 28e2     vsub.i32    q9, q12, q9
    88e4:    ef66 08e0     vadd.i32    q8, q11, q8
    88e8:    ef62 28e4     vadd.i32    q9, q9, q10
    88ec:    ef62 08e0     vadd.i32    q8, q9, q8
    88f0:    eff8 0070     vshr.s32    q8, q8, #8
    88f4:    f942 0add     vst1.64    {d16-d17}, [r2 :64]!
    88f8:    d0ca          beq.n    8890 <main+0x3b8>
    88fa:    e663          b.n    85c4 <main+0xec>
    88fc:    980b          ldr    r0, [sp, #44]    ; 0x2c
    88fe:    f50d 5149     add.w    r1, sp, #12864    ; 0x3240
    8902:    3128          adds    r1, #40    ; 0x28
    8904:    f60d 42e8     addw    r2, sp, #3304    ; 0xce8
    8908:    9104          str    r1, [sp, #16]
    890a:    940c          str    r4, [sp, #48]    ; 0x30
    890c:    4601          mov    r1, r0
    890e:    930d          str    r3, [sp, #52]    ; 0x34
    8910:    4664          mov    r4, ip
    8912:    4613          mov    r3, r2
    8914:    9009          str    r0, [sp, #36]    ; 0x24
    8916:    f1a3 0020     sub.w    r0, r3, #32
    891a:    f44f 6248     mov.w    r2, #3200    ; 0xc80
    891e:    9302          str    r3, [sp, #8]
    8920:    f7ff ed92     blx    8448 <_init+0x20>
    8924:    9b02          ldr    r3, [sp, #8]
    8926:    2000          movs    r0, #0
    8928:    461a          mov    r2, r3
    892a:    4619          mov    r1, r3
    892c:    ed52 0b08     vldr    d16, [r2, #-32]    ; 0xffffffe0
    8930:    ed52 1b06     vldr    d17, [r2, #-24]    ; 0xffffffe8
    8934:    eff0 2070     vshr.s32    q9, q8, #16
    8938:    f892 f0a0     pld    [r2, #160]    ; 0xa0
    893c:    3002          adds    r0, #2
    893e:    eff8 4070     vshr.s32    q10, q8, #8
    8942:    28c6          cmp    r0, #198    ; 0xc6
    8944:    4696          mov    lr, r2
    8946:    468c          mov    ip, r1
    8948:    ef42 21d8     vand    q9, q9, q4
    894c:    f102 0220     add.w    r2, r2, #32
    8950:    ef44 41d8     vand    q10, q10, q4
    8954:    f101 0120     add.w    r1, r1, #32
    8958:    ef40 01d8     vand    q8, q8, q4
    895c:    efe2 a572     vshl.s32    q13, q9, #2
    8960:    efe2 6574     vshl.s32    q11, q10, #2
    8964:    ef6a a8e2     vadd.i32    q13, q13, q9
    8968:    ef66 48e4     vadd.i32    q10, q11, q10
    896c:    efe3 6570     vshl.s32    q11, q8, #3
    8970:    efe1 a57a     vshl.s32    q13, q13, #1
    8974:    efe4 8574     vshl.s32    q12, q10, #4
    8978:    ef6a 28e2     vadd.i32    q9, q13, q9
    897c:    ff66 68e0     vsub.i32    q11, q11, q8
    8980:    ff68 48e4     vsub.i32    q10, q12, q10
    8984:    efe3 8572     vshl.s32    q12, q9, #3
    8988:    efe1 4574     vshl.s32    q10, q10, #1
    898c:    efe2 6576     vshl.s32    q11, q11, #2
    8990:    ff68 28e2     vsub.i32    q9, q12, q9
    8994:    ef66 08e0     vadd.i32    q8, q11, q8
    8998:    ef62 28e4     vadd.i32    q9, q9, q10
    899c:    ef62 08e0     vadd.i32    q8, q9, q8
    89a0:    eff8 0070     vshr.s32    q8, q8, #8
    89a4:    ed41 0b10     vstr    d16, [r1, #-64]    ; 0xffffffc0
    89a8:    ed41 1b0e     vstr    d17, [r1, #-56]    ; 0xffffffc8
    89ac:    ed52 0b0c     vldr    d16, [r2, #-48]    ; 0xffffffd0
    89b0:    ed52 1b0a     vldr    d17, [r2, #-40]    ; 0xffffffd8
    89b4:    eff0 2070     vshr.s32    q9, q8, #16
    89b8:    eff8 4070     vshr.s32    q10, q8, #8
    89bc:    ef42 21d8     vand    q9, q9, q4
    89c0:    ef44 41d8     vand    q10, q10, q4
    89c4:    ef40 01d8     vand    q8, q8, q4
    89c8:    efe2 a572     vshl.s32    q13, q9, #2
    89cc:    efe2 6574     vshl.s32    q11, q10, #2
    89d0:    ef6a a8e2     vadd.i32    q13, q13, q9
    89d4:    ef66 48e4     vadd.i32    q10, q11, q10
    89d8:    efe3 6570     vshl.s32    q11, q8, #3
    89dc:    efe1 a57a     vshl.s32    q13, q13, #1
    89e0:    efe4 8574     vshl.s32    q12, q10, #4
    89e4:    ef6a 28e2     vadd.i32    q9, q13, q9
    89e8:    ff66 68e0     vsub.i32    q11, q11, q8
    89ec:    ff68 48e4     vsub.i32    q10, q12, q10
    89f0:    efe3 8572     vshl.s32    q12, q9, #3
    89f4:    efe1 4574     vshl.s32    q10, q10, #1
    89f8:    efe2 6576     vshl.s32    q11, q11, #2
    89fc:    ff68 28e2     vsub.i32    q9, q12, q9
    8a00:    ef66 08e0     vadd.i32    q8, q11, q8
    8a04:    ef62 28e4     vadd.i32    q9, q9, q10
    8a08:    ef62 08e0     vadd.i32    q8, q9, q8
    8a0c:    eff8 0070     vshr.s32    q8, q8, #8
    8a10:    ed41 0b0c     vstr    d16, [r1, #-48]    ; 0xffffffd0
    8a14:    ed41 1b0a     vstr    d17, [r1, #-40]    ; 0xffffffd8
    8a18:    d188          bne.n    892c <main+0x454>
    8a1a:    4662          mov    r2, ip
    8a1c:    f96e 0add     vld1.64    {d16-d17}, [lr :64]!
    8a20:    eff0 2070     vshr.s32    q9, q8, #16
    8a24:    28c6          cmp    r0, #198    ; 0xc6
    8a26:    eff8 4070     vshr.s32    q10, q8, #8
    8a2a:    ef42 21d8     vand    q9, q9, q4
    8a2e:    ef44 41d8     vand    q10, q10, q4
    8a32:    ef40 01d8     vand    q8, q8, q4
    8a36:    efe2 a572     vshl.s32    q13, q9, #2
    8a3a:    efe2 6574     vshl.s32    q11, q10, #2
    8a3e:    ef6a a8e2     vadd.i32    q13, q13, q9
    8a42:    ef66 48e4     vadd.i32    q10, q11, q10
    8a46:    efe3 6570     vshl.s32    q11, q8, #3
    8a4a:    efe1 a57a     vshl.s32    q13, q13, #1
    8a4e:    efe4 8574     vshl.s32    q12, q10, #4
    8a52:    ef6a 28e2     vadd.i32    q9, q13, q9
    8a56:    ff66 68e0     vsub.i32    q11, q11, q8
    8a5a:    ff68 48e4     vsub.i32    q10, q12, q10
    8a5e:    efe3 8572     vshl.s32    q12, q9, #3
    8a62:    efe1 4574     vshl.s32    q10, q10, #1
    8a66:    efe2 6576     vshl.s32    q11, q11, #2
    8a6a:    ff68 28e2     vsub.i32    q9, q12, q9
    8a6e:    ef66 08e0     vadd.i32    q8, q11, q8
    8a72:    ef62 28e4     vadd.i32    q9, q9, q10
    8a76:    ef62 08e0     vadd.i32    q8, q9, q8
    8a7a:    eff8 0070     vshr.s32    q8, q8, #8
    8a7e:    f942 0add     vst1.64    {d16-d17}, [r2 :64]!
    8a82:    d101          bne.n    8a88 <main+0x5b0>
    8a84:    20c7          movs    r0, #199    ; 0xc7
    8a86:    e7c9          b.n    8a1c <main+0x544>
    8a88:    9a04          ldr    r2, [sp, #16]
    8a8a:    f503 6348     add.w    r3, r3, #3200    ; 0xc80
    8a8e:    9909          ldr    r1, [sp, #36]    ; 0x24
    8a90:    4293          cmp    r3, r2
    8a92:    f501 6148     add.w    r1, r1, #3200    ; 0xc80
    8a96:    9109          str    r1, [sp, #36]    ; 0x24
    8a98:    f47f af3d     bne.w    8916 <main+0x43e>
    8a9c:    46a4          mov    ip, r4
    8a9e:    9b0d          ldr    r3, [sp, #52]    ; 0x34
    8aa0:    9c0c          ldr    r4, [sp, #48]    ; 0x30
    8aa2:    e58f          b.n    85c4 <main+0xec>

00008aa4 <_start>:
    8aa4:    f04f 0b00     mov.w    fp, #0
    8aa8:    f04f 0e00     mov.w    lr, #0
    8aac:    bc02          pop    {r1}
    8aae:    466a          mov    r2, sp
    8ab0:    b404          push    {r2}
    8ab2:    b401          push    {r0}
    8ab4:    f8df c010     ldr.w    ip, [pc, #16]    ; 8ac8 <_start+0x24>
    8ab8:    f84d cd04     str.w    ip, [sp, #-4]!
    8abc:    4803          ldr    r0, [pc, #12]    ; (8acc <_start+0x28>)
    8abe:    4b04          ldr    r3, [pc, #16]    ; (8ad0 <_start+0x2c>)
    8ac0:    f7ff ecda     blx    8478 <_init+0x50>
    8ac4:    f7ff ed02     blx    84cc <_init+0xa4>
    8ac8:    00008c35     .word    0x00008c35
    8acc:    000084d9     .word    0x000084d9
    8ad0:    00008bf5     .word    0x00008bf5

00008ad4 <call_weak_fn>:
    8ad4:    e59f3014     ldr    r3, [pc, #20]    ; 8af0 <call_weak_fn+0x1c>
    8ad8:    e59f2014     ldr    r2, [pc, #20]    ; 8af4 <call_weak_fn+0x20>
    8adc:    e08f3003     add    r3, pc, r3
    8ae0:    e7932002     ldr    r2, [r3, r2]
    8ae4:    e3520000     cmp    r2, #0
    8ae8:    012fff1e     bxeq    lr
    8aec:    eafffe64     b    8484 <_init+0x5c>
    8af0:    0000851c     .word    0x0000851c
    8af4:    0000003c     .word    0x0000003c

00008af8 <deregister_tm_clones>:
    8af8:    4b07          ldr    r3, [pc, #28]    ; (8b18 <deregister_tm_clones+0x20>)
    8afa:    f241 0048     movw    r0, #4168    ; 0x1048
    8afe:    f2c0 0001     movt    r0, #1
    8b02:    1a1b          subs    r3, r3, r0
    8b04:    2b06          cmp    r3, #6
    8b06:    d800          bhi.n    8b0a <deregister_tm_clones+0x12>
    8b08:    4770          bx    lr
    8b0a:    f240 0300     movw    r3, #0
    8b0e:    f2c0 0300     movt    r3, #0
    8b12:    2b00          cmp    r3, #0
    8b14:    d0f8          beq.n    8b08 <deregister_tm_clones+0x10>
    8b16:    4718          bx    r3
    8b18:    0001104b     .word    0x0001104b

00008b1c <register_tm_clones>:
    8b1c:    f241 0348     movw    r3, #4168    ; 0x1048
    8b20:    f241 0048     movw    r0, #4168    ; 0x1048
    8b24:    f2c0 0301     movt    r3, #1
    8b28:    f2c0 0001     movt    r0, #1
    8b2c:    1a1b          subs    r3, r3, r0
    8b2e:    109b          asrs    r3, r3, #2
    8b30:    eb03 73d3     add.w    r3, r3, r3, lsr #31
    8b34:    1059          asrs    r1, r3, #1
    8b36:    d100          bne.n    8b3a <register_tm_clones+0x1e>
    8b38:    4770          bx    lr
    8b3a:    f240 0200     movw    r2, #0
    8b3e:    f2c0 0200     movt    r2, #0
    8b42:    2a00          cmp    r2, #0
    8b44:    d0f8          beq.n    8b38 <register_tm_clones+0x1c>
    8b46:    4710          bx    r2

00008b48 <__do_global_dtors_aux>:
    8b48:    b510          push    {r4, lr}
    8b4a:    f241 044c     movw    r4, #4172    ; 0x104c
    8b4e:    f2c0 0401     movt    r4, #1
    8b52:    7823          ldrb    r3, [r4, #0]
    8b54:    b91b          cbnz    r3, 8b5e <__do_global_dtors_aux+0x16>
    8b56:    f7ff ffcf     bl    8af8 <deregister_tm_clones>
    8b5a:    2301          movs    r3, #1
    8b5c:    7023          strb    r3, [r4, #0]
    8b5e:    bd10          pop    {r4, pc}

00008b60 <frame_dummy>:
    8b60:    f640 7014     movw    r0, #3860    ; 0xf14
    8b64:    f2c0 0001     movt    r0, #1
    8b68:    b508          push    {r3, lr}
    8b6a:    6803          ldr    r3, [r0, #0]
    8b6c:    b12b          cbz    r3, 8b7a <frame_dummy+0x1a>
    8b6e:    f240 0300     movw    r3, #0
    8b72:    f2c0 0300     movt    r3, #0
    8b76:    b103          cbz    r3, 8b7a <frame_dummy+0x1a>
    8b78:    4798          blx    r3
    8b7a:    e8bd 4008     ldmia.w    sp!, {r3, lr}
    8b7e:    e7cd          b.n    8b1c <register_tm_clones>

00008b80 <conv_rgb2y>:
    8b80:    2196          movs    r1, #150    ; 0x96
    8b82:    f3c0 2207     ubfx    r2, r0, #8, #8
    8b86:    fb01 f202     mul.w    r2, r1, r2
    8b8a:    b2c3          uxtb    r3, r0
    8b8c:    214d          movs    r1, #77    ; 0x4d
    8b8e:    f3c0 4007     ubfx    r0, r0, #16, #8
    8b92:    b410          push    {r4}
    8b94:    ebc3 04c3     rsb    r4, r3, r3, lsl #3
    8b98:    fb01 2000     mla    r0, r1, r0, r2
    8b9c:    eb03 0384     add.w    r3, r3, r4, lsl #2
    8ba0:    f85d 4b04     ldr.w    r4, [sp], #4
    8ba4:    4418          add    r0, r3
    8ba6:    1200          asrs    r0, r0, #8
    8ba8:    4770          bx    lr
    8baa:    bf00          nop

00008bac <laplacian_fil>:
    8bac:    b470          push    {r4, r5, r6}
    8bae:    4240          negs    r0, r0
    8bb0:    9c03          ldr    r4, [sp, #12]
    8bb2:    1a41          subs    r1, r0, r1
    8bb4:    1a8a          subs    r2, r1, r2
    8bb6:    9d04          ldr    r5, [sp, #16]
    8bb8:    1ad3          subs    r3, r2, r3
    8bba:    9e05          ldr    r6, [sp, #20]
    8bbc:    eb03 04c4     add.w    r4, r3, r4, lsl #3
    8bc0:    9906          ldr    r1, [sp, #24]
    8bc2:    9a07          ldr    r2, [sp, #28]
    8bc4:    1b65          subs    r5, r4, r5
    8bc6:    1bae          subs    r6, r5, r6
    8bc8:    1a71          subs    r1, r6, r1
    8bca:    bc70          pop    {r4, r5, r6}
    8bcc:    1a88          subs    r0, r1, r2
    8bce:    f380 0008     usat    r0, #8, r0
    8bd2:    4770          bx    lr

00008bd4 <chkhex>:
    8bd4:    b510          push    {r4, lr}
    8bd6:    4604          mov    r4, r0
    8bd8:    7800          ldrb    r0, [r0, #0]
    8bda:    b918          cbnz    r0, 8be4 <chkhex+0x10>
    8bdc:    e007          b.n    8bee <chkhex+0x1a>
    8bde:    f814 0f01     ldrb.w    r0, [r4, #1]!
    8be2:    b120          cbz    r0, 8bee <chkhex+0x1a>
    8be4:    f7ff ec36     blx    8454 <_init+0x2c>
    8be8:    2800          cmp    r0, #0
    8bea:    d1f8          bne.n    8bde <chkhex+0xa>
    8bec:    bd10          pop    {r4, pc}
    8bee:    2001          movs    r0, #1
    8bf0:    bd10          pop    {r4, pc}
    8bf2:    bf00          nop

00008bf4 <__libc_csu_init>:
    8bf4:    e92d 43f8     stmdb    sp!, {r3, r4, r5, r6, r7, r8, r9, lr}
    8bf8:    4607          mov    r7, r0
    8bfa:    4e0c          ldr    r6, [pc, #48]    ; (8c2c <__libc_csu_init+0x38>)
    8bfc:    4688          mov    r8, r1
    8bfe:    4d0c          ldr    r5, [pc, #48]    ; (8c30 <__libc_csu_init+0x3c>)
    8c00:    4691          mov    r9, r2
    8c02:    447e          add    r6, pc
    8c04:    f7ff ec10     blx    8428 <_init>
    8c08:    447d          add    r5, pc
    8c0a:    1b76          subs    r6, r6, r5
    8c0c:    10b6          asrs    r6, r6, #2
    8c0e:    d00a          beq.n    8c26 <__libc_csu_init+0x32>
    8c10:    3d04          subs    r5, #4
    8c12:    2400          movs    r4, #0
    8c14:    3401          adds    r4, #1
    8c16:    f855 3f04     ldr.w    r3, [r5, #4]!
    8c1a:    4638          mov    r0, r7
    8c1c:    4641          mov    r1, r8
    8c1e:    464a          mov    r2, r9
    8c20:    4798          blx    r3
    8c22:    42b4          cmp    r4, r6
    8c24:    d1f6          bne.n    8c14 <__libc_csu_init+0x20>
    8c26:    e8bd 83f8     ldmia.w    sp!, {r3, r4, r5, r6, r7, r8, r9, pc}
    8c2a:    bf00          nop
    8c2c:    0000830a     .word    0x0000830a
    8c30:    00008300     .word    0x00008300

00008c34 <__libc_csu_fini>:
    8c34:    4770          bx    lr
    8c36:    bf00          nop

Disassembly of section .fini:

00008c38 <_fini>:
    8c38:    e92d4008     push    {r3, lr}
    8c3c:    e8bd8008     pop    {r3, pc}

  1. 2015年07月09日 04:33 |
  2. Zynq
  3. | トラックバック:0
  4. | コメント:0

gcc の最適化と自動ベクトル化を使用した時の性能2

(2015/07/11:タイトルが間違っていたので変更しました)

2015/07/12:修正 時間計測にバグがあったので、全面的に記事を修正しました。miyox さん、ありがとうございました)

実行環境はZYBOで、Dual Cortex-A9 650 MHz プロセッサを持ち、Ubuntu 14.04 LTS が動作している。

gcc の最適化と自動ベクトル化を使用した時の性能”の続き。

gcc の最適化とNEON 最適化ライブラリを使用した時の性能”の時は、gcc 4.6 でコンパイルしていたが、今回は gcc 4.8 にアップグレードして同様にラプラシアンフィルタの異なるソフトウェアをコンパイルして実行時間を比較してみた。
NEON_11_150708.png

使用したラプラシアンフィルタのソフトウェアの詳細については、”gcc の最適化とNEON 最適化ライブラリを使用した時の性能”を参照のこと。

最初は、 laplacian_filter1 を -O オプション無し、-O1、-O2、-O3、-Os、NEON最適化ライブラリ・オプションを付けてコンパイルし、実行してラプラシアンフィルタ処理のみの経過時間を測定した。
NEON_12_150708.png

次に、 laplacian_filter2 を -O オプション無し、-O1、-O2、-O3、-Os、NEON最適化ライブラリ・オプションを付けてコンパイルし、実行してラプラシアンフィルタ処理のみの経過時間を測定した。
NEON_13_150708.png

laplacian_filter3 を -O オプション無し、-O1、-O2、-O3、-Os、NEON最適化ライブラリ・オプションを付けてコンパイルし、実行してラプラシアンフィルタ処理のみの経過時間を測定した。
NEON_14_150708.png

laplacian_filter4 を -O オプション無し、-O1、-O2、-O3、-Os、NEON最適化ライブラリ・オプションを付けてコンパイルし、実行してラプラシアンフィルタ処理のみの経過時間を測定した。
NEON_15_150708.png

NEONを使用するように自動ベクトル化を行った場合に -O3 を付けてコンパイルしていた。これも -O3 が適当かどうかを調べるために -O オプション無し、-O1、-O2、-O3、-Os オプションを付けてラプラシアンフィルタ処理時間を測定した。(-mcpu=cortex-a9 -mfpu=neon -ftree-vectorize -mvectorize-with-neon-quad -ffast-math)

laplacian_filter1 の場合の結果を下に示す。
NEON_16_150708.png

laplacian_filter2 の場合の結果を下に示す。
NEON_17_150708.png

laplacian_filter3 の場合の結果を下に示す。
NEON_18_150708.png

laplacian_filter4 の場合の結果を下に示す。
NEON_19_150708.png

自動ベクトル化のオプションを付けてコンパイルした実行ファイルがNEON命令を使用しているのか?を検証した。
objdump -S -d <実行ファイル名> | grep "vmov" -c
コマンドの値が 0 だったらNEON命令を使用していない。0 以外だったらNEON命令を使用している。
NEON_37_150713.png

NEON_38_150713.png

次に、-mfloat-abi=hard オプションを付けた時の各ラプラシアンフィルタ・ソフトウェアをコンパイルした時の結果を示す。(-mcpu=cortex-a9 -mfpu=neon -ftree-vectorize -mvectorize-with-neon-quad -mfloat-abi=hard -ffast-math)
NEON_20_150708.png

最後に、上のコマンドから、-ftree-vectorize オプションを取り除いた時の各ラプラシアンフィルタ・ソフトウェアをコンパイルした時の結果を示す。
NEON_21_150708.png

上の2つの場合もNEON命令を使用しているのか?を確かめたが、以前と同じだった。
NEON_39_150713.png

AXI4-Stream版 laplacian filterのCソースコードの違いと最適化オプションの違いによる処理時間の表を下に示す。gcc 4.6 と gcc 4.8 の処理時間も書いた。
NEON_22_150708.png
gcc-4.8 での最速は、、laplacian_filter3 の 自動ベクトル化、-O3 を付けた場合だった。ラプラシアンフィルタ処理時間は、約 74.4 ms だった。これは、Vivado HLSでCソースコードのみ書き換えた(1.)の 80.0 ms を上回る性能だった。

gcc 4.6 と gcc 4.8 では、細かな違いはあるが大きな性能差や傾向の差は無いようだ。
  1. 2015年07月08日 04:53 |
  2. Zynq
  3. | トラックバック:0
  4. | コメント:0

gcc の最適化と自動ベクトル化を使用した時の性能

(2015/07/11:タイトルが間違っていたので変更しました)
2015/07/12:修正 時間計測にバグがあったので、全面的に記事を修正しました。miyox さん、ありがとうございました)

ZynqのSIMDエンジンNEONについての資料”の続きというか、その資料を参考にして自動ベクトル化を行う gcc のオプションを使用してラプラシアンフィルタをコンパイルし、その性能を確認した。

実行環境はZYBOで、Dual Cortex-A9 650 MHz プロセッサを持ち、Ubuntu 14.04 LTS が動作している。

NEON最適化ライブラリを使用するだけでなく、今まで最適オプション(-O?)を入れてなかったので、各種の最適オプションを入れた時の性能差も測定した。なお、gcc のバージョンは4.6 だった。

なお、ツイッターでいろいろとアドバイス、ご助言を頂いた。ありがとうございます。

性能を測定するラプラシアンフィルタのソフトウェア実装は4つとする。
最初は、”ZYBO用Ubuntu Linux のカメラで撮った画像にラプラシアンフィルタをかける”に貼ってある laplacian_filter.c のgettimeofday() をラプラシアンフィルタの処理の経過時間のみを測定するように移動させたCソースコードとする。これを laplacian_filter1 という実行ファイルにコンパイルする。このCソースコードはこの記事の最後に貼っておく。

2番目と3番目は、”ラプラシアンフィルタのソフトウェアとハードウェアの速度の比較”にCソースコードが貼ってある laplacian_filter2.c と laplacian_filter3.c とする。これらはmemcpy() を使用したVivado HLS で使用している記述となっている。

4番目は、”ラプラシアンフィルタのソフトウェアとハードウェアの速度の比較3”にCソースコードを貼った laplacian_filter4.c を使用する。これは、Vivado HLS 2014.4 で最速だった。

最初に gcc の最適化レベルによる最適化オプションについては、”3.10 Options That Control Optimization”を参照のこと。

最初は、 laplacian_filter1 を -O オプション無し、-O1、-O2、-O3、-Os、NEON最適化ライブラリ・オプションを付けてコンパイルし、実行してラプラシアンフィルタ処理のみの経過時間を測定した。
NEON_2_150705.png
laplacian_filter1 では、-O3 が最速になっている。

次に、 laplacian_filter2 を -O オプション無し、-O1、-O2、-O3、-Os、NEON最適化ライブラリ・オプションを付けてコンパイルし、実行してラプラシアンフィルタ処理のみの経過時間を測定した。
NEON_3_150705.png
laplacian_filter2 では、やはり、-O3 が最高だった。85.1 ms で 100 ms を切っている。memcpy() が影響しているのだろうか?

laplacian_filter3 を -O オプション無し、-O1、-O2、-O3、-Os、NEON最適化ライブラリ・オプションを付けてコンパイルし、実行してラプラシアンフィルタ処理のみの経過時間を測定した。
NEON_4_150705.png
laplacian_filter3 でもlaplacian_filter2 と同様の傾向があった。

laplacian_filter4 を -O オプション無し、-O1、-O2、-O3、-Os、NEON最適化ライブラリ・オプションを付けてコンパイルし、実行してラプラシアンフィルタ処理のみの経過時間を測定した。
NEON_5_150705.png
やはり、-O3 が速い。

gcc の最適化オプションを変更した結果は最適化の度合いによって、ラプラシアンフィルタの処理時間がかなり変動するという不安定な結果になった。その中でも laplacian_filter1 は結構安定した結果になっている。やはり、ソフトウェアとして書いているので、gcc も最適化がかけやすかったのだろうか?

次に、本来の目的であるNEON最適化ライブラリ・オプションなのだが、-O3 を付けてコンパイルしていた。これも -O3 が適当かどうかを調べるために -O オプション無し、-O1、-O2、-O3、-Os オプションを付けてラプラシアンフィルタ処理時間を測定した。

laplacian_filter1 の場合の結果を下に示す。
NEON_6_150705.png
-O1 の場合が最速だった。

laplacian_filter2 の場合の結果を下に示す。
NEON_7_150705.png
-O2 オプションが最速で、78.3 ms と Vivado HLSでCソースコードのみ書き換えた(1.)の 80.0 ms を上回る性能だった。

laplacian_filter3 の場合の結果を下に示す。
NEON_8_150705.png
-O2 オプションが最速で、77.7 ms と Vivado HLSでCソースコードのみ書き換えた(1.)の 80.0 ms を上回る性能だった。

laplacian_filter4 の場合の結果を下に示す。
NEON_9_150705.png
-O3 の場合が最速だった。 -O2 も速い。

全体を通して最速だったのは、laplacian_filter3 の 自動ベクトル化、-O2 を付けた場合だった。ラプラシアンフィルタ処理時間は、約 77.7 ms だった。これは、Vivado HLSでCソースコードのみ書き換えた(1.)の 80.0 ms を上回る性能だった。
これだと、”Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化14(性能が最大になる設定を探る7、まとめ)”の1. の約 1.03 倍の処理速度となり、Vivado HLSでお手軽にハードウェアしたら性能が低下してしまう。但し、11. のVivado HLS 2014.4 でハードウェアにした時とのラプラシアンフィルタ処理時間の差は、約 5.08 倍となった。

2015/07/13:追加
objdump -S -d <実行ファイル名> | grep "vmov" -c

コマンドでNEON命令を使用しているかどうか?を調べた。使用数が 0 だとNEON命令を使用しいないことになる。0 以外だとNEON命令を使用している。
NEON_35_150712.png 

NEON_36_150712.png 

NEON命令を使用しいる実行ファイル名を下の表で赤字で示す。

2015/07/07:追加 AXI4-Stream版 laplacian filterのCソースコードの違いと最適化オプションの違いによる処理時間の表を追加する。使用しているgcc のバージョンは 4.6 です。)
NEON_10_150706.png
最速の 77.7 ms は自動ベクトル化されて、NEONを使用している実行ファイルだった。

最後に、laplacian_filter1 として実行した laplacian_filter.c を下に貼っておく。
2015/07/12:修正 時間計測にバグがあったので、修正しました。miyox さん、ありがとうございました)(2015/08/02:修正

// laplacian_filter.c
// RGBをYに変換後にラプラシアンフィルタを掛ける。
// ピクセルのフォーマットは、{8'd0, R(8bits), G(8bits), B(8bits)}, 1pixel = 32bits
// 2013/09/16
// 2014/12/04 : ZYBO用Ubuntu Linux のUIO用に変更

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <sys/mman.h>
#include <fcntl.h>

#define HORIZONTAL_PIXEL_WIDTH    800
#define VERTICAL_PIXEL_WIDTH    600
#define ALL_PIXEL_VALUE    (HORIZONTAL_PIXEL_WIDTH*VERTICAL_PIXEL_WIDTH)

#define CMA_START_ADDRESS           0x17800000
#define VIDEO_BUFFER_START_ADDRESS  0x18000000  // Limit 0x18800000, 800*600*4 = 2MBytes * 2
#define LAPLACIAN_FILTER_ADDRESS    0x18200000  // 800*600*4 = 0x1d4c00

int laplacian_fil(int x0y0, int x1y0, int x2y0, int x0y1, int x1y1, int x2y1, int x0y2, int x1y2, int x2y2);
int conv_rgb2y(int rgb);
int chkhex(char *str);

int main()
{
    volatile unsigned int *fb_addr, *next_frame_addr;
    int lap_fil_val;
    int x, y;
    struct timeval start_time, temp1, temp2, end_time;
    unsigned int line_buf[3][HORIZONTAL_PIXEL_WIDTH];
    int a, b;
    int fl, sl, tl;
    int fd0, fd3;
    volatile unsigned *bmdc_axi_lites;
    volatile unsigned int *frame_buffer;

    // gettimeofday(&start_time, NULL);    // プログラム起動時の時刻を記録

    // frame_buffer にマップする
    fd3 = open("/dev/uio3", O_RDWR); // Frame Buffer
    if (fd3 < 1){
        fprintf(stderr, "/dev/uio3 open error\n");
        exit(-1);
    }
    frame_buffer = (volatile unsigned *)mmap(NULL, 0x1000000, PROT_READ|PROT_WRITE, MAP_SHARED, fd3, 0);
    if (!frame_buffer){
        fprintf(stderr, "frame_buffer mmap error\n");
        exit(-1);
    }
    fb_addr = (volatile unsigned int *)((unsigned int)frame_buffer + (unsigned int)(VIDEO_BUFFER_START_ADDRESS-CMA_START_ADDRESS));

    // ラプラシアンフィルタの結果を入れておくフレーム・バッファ
    next_frame_addr = (volatile unsigned int *)((unsigned int)frame_buffer + (unsigned int)(LAPLACIAN_FILTER_ADDRESS-CMA_START_ADDRESS));

    gettimeofday(&start_time, NULL);
    
    // RGB値をY(輝度成分)のみに変換し、ラプラシアンフィルタを掛けた。
    for (y=0; y<VERTICAL_PIXEL_WIDTH; y++){
        for (x=0; x<HORIZONTAL_PIXEL_WIDTH; x++){
            if (y==0 || y==VERTICAL_PIXEL_WIDTH-1){ // 縦の境界の時の値は0とする
                lap_fil_val = 0;
            }else if (x==0 || x==HORIZONTAL_PIXEL_WIDTH-1){ // 横の境界の時も値は0とする
                lap_fil_val = 0;
            }else{
                if (y == 1 && x == 1){ // 最初のラインの最初のピクセルでは2ライン分の画素を読み出す
                    for (a=0; a<2; a++){ // 2ライン分
                        for (b=0; b<HORIZONTAL_PIXEL_WIDTH; b++){ // ライン
                            line_buf[a][b] = fb_addr[(a*HORIZONTAL_PIXEL_WIDTH)+b];
                            line_buf[a][b] = conv_rgb2y(line_buf[a][b]);
                        }
                    }
                }
                if (x == 1) {    // ラインの最初なので、2つのピクセルを読み込む
                    for (b=0; b<2; b++){ // ライン
                        line_buf[(y+1)%3][b] = fb_addr[((y+1)*HORIZONTAL_PIXEL_WIDTH)+b];
                        // (y+1)%3 は、使用済みのラインがに読み込む、y=2 の時 line[0], y=3の時 line[1], y=4の時 line[2]
                        line_buf[(y+1)%3][b] = conv_rgb2y(line_buf[(y+1)%3][b]);
                    }
                }
                
                // 1つのピクセルを読み込みながらラプラシアン・フィルタを実行する
                line_buf[(y+1)%3][x+1] = fb_addr[((y+1)*HORIZONTAL_PIXEL_WIDTH)+(x+1)];
                // (y+1)%3 は、使用済みのラインがに読み込む、y=2 の時 line[0], y=3の時 line[1], y=4の時 line[2]
                line_buf[(y+1)%3][x+1] = conv_rgb2y(line_buf[(y+1)%3][x+1]);
                
                fl = (y-1)%3;    // 最初のライン, y=1 012, y=2 120, y=3 201, y=4 012
                sl = y%3;        // 2番めのライン
                tl = (y+1)%3;    // 3番目のライン
                lap_fil_val = laplacian_fil(line_buf[fl][x-1], line_buf[fl][x], line_buf[fl][x+1], line_buf[sl][x-1], line_buf[sl][x], line_buf[sl][x+1], line_buf[tl][x-1], line_buf[tl][x], line_buf[tl][x+1]);
            }
            // ラプラシアンフィルタ・データの書き込み
            next_frame_addr[(y*HORIZONTAL_PIXEL_WIDTH)+x] = (lap_fil_val<<16)+(lap_fil_val<<8)+lap_fil_val ;
            // printf("x = %d  y = %d", x, y);
        }
     }

    gettimeofday(&end_time, NULL);
    
    munmap((void *)frame_buffer, 0x1000000);
 
   // ラプラシアンフィルタ表示画面に切り替え
    // Bitmap Display Controller AXI4 Lite Slave (UIO0)
    fd0 = open("/dev/uio0", O_RDWR); // bitmap_display_controller axi4 lite
    if (fd0 < 1){
        fprintf(stderr, "/dev/uio0 open error\n");
        exit(-1);
    }
    bmdc_axi_lites = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd0, 0);
    if (!bmdc_axi_lites){
        fprintf(stderr, "bmdc_axi_lites mmap error\n");
        exit(-1);
    }
    bmdc_axi_lites[0] = (unsigned int)LAPLACIAN_FILTER_ADDRESS; // Bitmap Display Controller start (ラプラシアンフィルタ表示画面のアドレス)
    munmap((void *)bmdc_axi_lites, 0x10000);
    
    //gettimeofday(&end_time, NULL);
    if (end_time.tv_usec < start_time.tv_usec) {
        printf("total time = %ld.%06ld sec\n", end_time.tv_sec - start_time.tv_sec - 11000000 + end_time.tv_usec - start_time.tv_usec);
    }
    else {
        printf("total time = %ld.%06ld sec\n", end_time.tv_sec - start_time.tv_sec, end_time.tv_usec - start_time.tv_usec);
    }
    return(0);
}

// RGBからYへの変換
// RGBのフォーマットは、{8'd0, R(8bits), G(8bits), B(8bits)}, 1pixel = 32bits
// 輝度信号Yのみに変換する。変換式は、Y =  0.299R + 0.587G + 0.114B
// "YUVフォーマット及び YUV<->RGB変換"を参考にした。http://vision.kuee.kyoto-u.ac.jp/~hiroaki/firewire/yuv.html
// 2013/09/27 : float を止めて、すべてint にした
int conv_rgb2y(int rgb){
    int r, g, b, y_f;
    int y;

    b = rgb & 0xff;
    g = (rgb>>8) & 0xff;
    r = (rgb>>16) & 0xff;

    y_f = 77*r + 150*g + 29*b; //y_f = 0.299*r + 0.587*g + 0.114*b;の係数に256倍した
    y = y_f >> 8// 256で割る

    return(y);
}

// ラプラシアンフィルタ
// x0y0 x1y0 x2y0 -1 -1 -1
// x0y1 x1y1 x2y1 -1  8 -1
// x0y2 x1y2 x2y2 -1 -1 -1
int laplacian_fil(int x0y0, int x1y0, int x2y0, int x0y1, int x1y1, int x2y1, int x0y2, int x1y2, int x2y2)
{
    int y;

    y = -x0y0 -x1y0 -x2y0 -x0y1 +8*x1y1 -x2y1 -x0y2 -x1y2 -x2y2;
    if (y<0)
        y = 0;
    else if (y>255)
        y = 255;
    return(y);
}

// 文字列が16進数かを調べる
int chkhex(char *str){
    while (*str != '\0'){
        if (!isxdigit(*str))
            return 0;
        str++;
    }
    return 1;
}

  1. 2015年07月06日 04:24 |
  2. Zynq
  3. | トラックバック:0
  4. | コメント:6

ZynqのSIMDエンジンNEONについての資料

ZynqにはARMのCortex-A9 プロセッサがDualで入っているが、NEONというSIMDエンジンが搭載されている。
下にZynqの内部ブロック図の一部を示す。NEONは各プロセッサに付いている。
NEON_1_150705.png

NEONはSIMD (Single Instruction Multiple Data) 演算を行うことができる。つまり、1つの命令で複数の演算を行うということだ。

NEONを使用するとソフトウェアがどのくらい速くなるかを検証する。とりあえずは、ラプラシアンフィルタ処理を行った場合の速度を測定してみたい。

NEONに関する資料を示す。
最初は、ARM社の資料で”Cortex™-A9 NEON™ メデ ィ ア処理エンジン リビジョン : r2p2 テクニカルリ フ ァレンス マニュアル

ARM社の”ベクタ浮動小数点命令セ ッ ト クイックリファレンスカード

Xilinx社のZynq用のNEONマニュアル”NEON を使用して Zynq-7000 AP SoC でのソフ ウェア性能を向上 XAPP1206 (v1.1) 2014 年 6 月 12 日
10ページからの”ソ フトウェア性能最適化方法”の”NEON 最適化ライブラリを使用する方法”によると、gcc の最適化レベルを -O2 または -O3 にして、-mcpu=cortex-a9 -mfpu=neon -ftree-vectorize -mvectorize-with-neon-quad -mfloat-abi=softfp -ffast-math オプションを付けるとNEONのコードを出力してくれるようだ。お手軽なのでやってみよう。

ブログでは、”ARMのNEONのSIMD命令をgccのオートベクタライズの最適化で使う方法 2013年06月27日”がある。

同様にNEONの最適化ライブラリを使用する方法のブログは”ARM NEONの128bit演算とgcc(メモ)”、”ARM NEONの128bit演算とgcc (2)”がある。

インライン・アセンブラでNEON命令を直接書くブログは、”ARM NEON命令を使って画像の回転”がある。

とりあえずはお手軽な gcc のコンパイラオプションで自動ベクトル化、NEONを使うようにしてみようと思う。
  1. 2015年07月05日 04:50 |
  2. Zynq
  3. | トラックバック:0
  4. | コメント:0

ラプラシアンフィルタのソフトウェアとハードウェアの速度の比較3

ラプラシアンフィルタのソフトウェアとハードウェアの速度の比較2”の続き。

今回はVivado HLS 2014.4 で最速の性能をたたき出しているCソースコードをZYBOのLinuxで動作するソフトウェアとして性能を測定してみた。

今回、試してみるVivado HLS 2014.4 のCソースコードの記事を示す。
Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化15(性能が最大になる設定を探る8、追加1)
Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化16(性能が最大になる設定を探る8、追加2)

そのCソースコードをZYBOのLinuxで動作するソフトウェアとして書き直した laplacian_filter4.c を下に示す。
2015/07/12:修正 時間計測にバグがあったので、修正しました。miyox さん、ありがとうございました)

// laplacian_filter4.c
// RGBをYに変換後にラプラシアンフィルタを掛ける。
// ピクセルのフォーマットは、{8'd0, R(8bits), G(8bits), B(8bits)}, 1pixel = 32bits
// 2013/09/16
// 2014/12/04 : ZYBO用Ubuntu Linux のUIO用に変更
// Vivado HLS 2014.4 のプロジェクト ZYBO/lap_filter_axim_tu2_2014_4を使用したソースコードと同じものを使用する。これは、Vivado HLSで最速のCソースコードだ

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <sys/mman.h>
#include <fcntl.h>

#define HORIZONTAL_PIXEL_WIDTH    800
#define VERTICAL_PIXEL_WIDTH    600
#define ALL_PIXEL_VALUE    (HORIZONTAL_PIXEL_WIDTH*VERTICAL_PIXEL_WIDTH)

#define CMA_START_ADDRESS           0x17800000
#define VIDEO_BUFFER_START_ADDRESS  0x18000000  // Limit 0x18800000, 800*600*4 = 2MBytes * 2
#define LAPLACIAN_FILTER_ADDRESS    0x18200000  // 800*600*4 = 0x1d4c00

int laplacian_fil(int x0y0, int x1y0, int x2y0, int x0y1, int x1y1, int x2y1, int x0y2, int x1y2, int x2y2);
int conv_rgb2y(int rgb);
int chkhex(char *str);

void filter_line(unsigned int* lap_buf, unsigned int* fl, unsigned int* sl, unsigned int* tl){
    int lap_fil_val;
    int prev[3],current[3],next[3];    // 0->1ライン目, 1->2ライン目, 2->3ライン目, prev->1pixel前, current->現在, next->次pixel
    int x;

    next[0] = conv_rgb2y(fl[0]);
    next[1] = conv_rgb2y(sl[0]);
    next[2] = conv_rgb2y(tl[0]);

    for (x = 0; x < HORIZONTAL_PIXEL_WIDTH; x++){
        if (x == 0 || x == HORIZONTAL_PIXEL_WIDTH-1){
            lap_fil_val = 0;

            current[0] = next[0];
            next[0] = conv_rgb2y(fl[1]);

            current[1] = next[1];
            next[1] = conv_rgb2y(sl[1]);

            current[2] = next[2];
            next[2] = conv_rgb2y(tl[1]);
        }else{
            prev[0] = current[0];
            current[0] = next[0];
            next[0] = conv_rgb2y(fl[x+1]);

            prev[1] = current[1];
            current[1] = next[1];
            next[1] = conv_rgb2y(sl[x+1]);

            prev[2] = current[2];
            current[2] = next[2];
            next[2] = conv_rgb2y(tl[x+1]);
            lap_fil_val = laplacian_fil(prev[0], current[0], next[0],
                                        prev[1], current[1], next[1],
                                        prev[2], current[2], next[2]);
        }
        lap_buf[x] = (lap_fil_val<<16)+(lap_fil_val<<8)+lap_fil_val; // RGB同じ値を入れる
    }
}

int main()
{
    volatile unsigned int *cam_fb = 0;
    volatile unsigned int *lap_fb = 0;
    volatile unsigned int *cam_addr;
    volatile unsigned int *lap_addr;
    int lap_fil_val;
    int x, y;
    struct timeval start_time, temp1, temp2, end_time;
    unsigned int line_buf[3][HORIZONTAL_PIXEL_WIDTH];
    int a, b;
    int fl, sl, tl;
    int fd0, fd3;
      unsigned int offset_cam_addr, offset_lap_addr;
    unsigned int lap_buf[HORIZONTAL_PIXEL_WIDTH];
    volatile unsigned int *cam_fb_addr, *lap_fb_addr;
    int line_sel;
    volatile unsigned int *bmdc_axi_lites;
    volatile unsigned int *frame_buffer;

    // gettimeofday(&start_time, NULL);    // プログラム起動時の時刻を記録

    // frame_buffer にマップする
    fd3 = open("/dev/uio3", O_RDWR); // Frame Buffer
    if (fd3 < 1){
        fprintf(stderr, "/dev/uio3 open error\n");
        exit(-1);
    }
    frame_buffer = (volatile unsigned int *)mmap(NULL, 0x1000000, PROT_READ|PROT_WRITE, MAP_SHARED, fd3, 0);
    if (!frame_buffer){
        fprintf(stderr, "frame_buffer mmap error\n");
        exit(-1);
    }
    cam_addr = (volatile unsigned int *)((unsigned int)frame_buffer + (unsigned int)(VIDEO_BUFFER_START_ADDRESS-CMA_START_ADDRESS));

    // ラプラシアンフィルタの結果を入れておくフレーム・バッファ
    lap_addr = (volatile unsigned int *)((unsigned int)frame_buffer + (unsigned int)(LAPLACIAN_FILTER_ADDRESS-CMA_START_ADDRESS));

    offset_cam_addr = (volatile unsigned int)((unsigned int)cam_addr/sizeof(int));
    offset_lap_addr = (volatile unsigned int)((unsigned int)lap_addr/sizeof(int));
    
    gettimeofday(&start_time, NULL);
    
     // ラプラシアンフィルタ処理開始
    // RGB値をY(輝度成分)のみに変換し、ラプラシアンフィルタを掛けた。
     for (y=1, line_sel=0; y<VERTICAL_PIXEL_WIDTH-1; y++){
        // 最初のライン, y=1 012, y=2 120, y=3 201, y=4 012
        switch(line_sel){
            case 1 :
                fl = 0; sl = 1; tl = 2;
                break;
            case 2 :
                fl = 1; sl = 2; tl = 0;
                break;
            case 3 :
                fl = 2; sl = 0; tl = 1;
                break;
            default :
                fl = 0; sl = 1; tl = 2;
        }

        if (y == 1){
            for (a=0; a<3; a++){
 // 3ライン分
                cam_fb_addr = (int*)(cam_fb+offset_cam_addr+(a*(HORIZONTAL_PIXEL_WIDTH)));
                memcpy(line_buf[a], (unsigned int*)cam_fb_addr, HORIZONTAL_PIXEL_WIDTH*sizeof(int));
            }
        }else// 最初のラインではないので、1ラインだけ読み込む。すでに他の2ラインは読み込まれている
            cam_fb_addr = (int*)(cam_fb+offset_cam_addr+((y+1)*(HORIZONTAL_PIXEL_WIDTH)));
            memcpy(line_buf[tl], (unsigned int*)cam_fb_addr, HORIZONTAL_PIXEL_WIDTH*sizeof(int));
        }
        filter_line(lap_buf, line_buf[fl], line_buf[sl], line_buf[tl]);
        lap_fb_addr = (int *)(lap_fb+offset_lap_addr+(y*(HORIZONTAL_PIXEL_WIDTH)));
        memcpy((unsigned int*)lap_fb_addr, (unsigned int*)lap_buf, HORIZONTAL_PIXEL_WIDTH*sizeof(int));

        line_sel++;
        if (line_sel > 3){
            line_sel = 1;
        }
    }

    // 最初と最後のラインは0にする
    for (x = 0; x < HORIZONTAL_PIXEL_WIDTH; x++)
        lap_buf[x] = 0;
    lap_fb_addr = (int *)(lap_fb+offset_lap_addr+(0*(HORIZONTAL_PIXEL_WIDTH)));
    memcpy((unsigned int*)lap_fb_addr, (unsigned int*)lap_buf, HORIZONTAL_PIXEL_WIDTH*sizeof(int));
    lap_fb_addr = (int *)(lap_fb+offset_lap_addr+(VERTICAL_PIXEL_WIDTH-1)*HORIZONTAL_PIXEL_WIDTH);
    memcpy((unsigned int*)lap_fb_addr, (unsigned int*)lap_buf, HORIZONTAL_PIXEL_WIDTH*sizeof(int));
    // ラプラシアンフィルタ処理終了
    
    gettimeofday(&end_time, NULL);
    
    munmap((void *)frame_buffer, 0x1000000);
 
   // ラプラシアンフィルタ表示画面に切り替え
    // Bitmap Display Controller AXI4 Lite Slave (UIO0)
    fd0 = open("/dev/uio0", O_RDWR); // bitmap_display_controller axi4 lite
    if (fd0 < 1){
        fprintf(stderr, "/dev/uio0 open error\n");
        exit(-1);
    }
    bmdc_axi_lites = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd0, 0);
    if (!bmdc_axi_lites){
        fprintf(stderr, "bmdc_axi_lites mmap error\n");
        exit(-1);
    }
    bmdc_axi_lites[0] = (unsigned int)LAPLACIAN_FILTER_ADDRESS; // Bitmap Display Controller start (ラプラシアンフィルタ表示画面のアドレス)
    munmap((void *)bmdc_axi_lites, 0x10000);
    
    //gettimeofday(&end_time, NULL);
    if (end_time.tv_usec < start_time.tv_usec) {
        printf("total time = %ld.%06ld sec\n", end_time.tv_sec - start_time.tv_sec - 11000000 + end_time.tv_usec - start_time.tv_usec);
    }
    else {
        printf("total time = %ld.%06ld sec\n", end_time.tv_sec - start_time.tv_sec, end_time.tv_usec - start_time.tv_usec);
    }
    return(0);
}

// RGBからYへの変換
// RGBのフォーマットは、{8'd0, R(8bits), G(8bits), B(8bits)}, 1pixel = 32bits
// 輝度信号Yのみに変換する。変換式は、Y =  0.299R + 0.587G + 0.114B
// "YUVフォーマット及び YUV<->RGB変換"を参考にした。http://vision.kuee.kyoto-u.ac.jp/~hiroaki/firewire/yuv.html
// 2013/09/27 : float を止めて、すべてint にした
int conv_rgb2y(int rgb){
    int r, g, b, y_f;
    int y;

    b = rgb & 0xff;
    g = (rgb>>8) & 0xff;
    r = (rgb>>16) & 0xff;

    y_f = 77*r + 150*g + 29*b; //y_f = 0.299*r + 0.587*g + 0.114*b;の係数に256倍した
    y = y_f >> 8// 256で割る

    return(y);
}

// ラプラシアンフィルタ
// x0y0 x1y0 x2y0 -1 -1 -1
// x0y1 x1y1 x2y1 -1  8 -1
// x0y2 x1y2 x2y2 -1 -1 -1
int laplacian_fil(int x0y0, int x1y0, int x2y0, int x0y1, int x1y1, int x2y1, int x0y2, int x1y2, int x2y2)
{
    int y;

    y = -x0y0 -x1y0 -x2y0 -x0y1 +8*x1y1 -x2y1 -x0y2 -x1y2 -x2y2;
    if (y<0)
        y = 0;
    else if (y>255)
        y = 255;
    return(y);
}

// 文字列が16進数かを調べる
int chkhex(char *str){
    while (*str != '\0'){
        if (!isxdigit(*str))
            return 0;
        str++;
    }
    return 1;
}


このCソースコードを gcc laplacian_filter4.c -o laplacian_filter4 コマンドでコンパイルして、./laplacian_filter4 で実行した。
その結果を下に示す。
soft_hard_comp_16_150704.png

ラプラシアンフィルタ処理のみの経過時間は、228 ms だった。以外にもソフトウェアのラプラシアンフィルタ処理で最速の結果になった。キャッシュに入っている確率が高いからだろうか?
完全ソフトウェアとして作った laplacian_fiter1 のラプラシアンフィルタ処理のみの経過時間は 447 ms だったので、laplacian_filter4 の性能は 447 ms / 228 ms ≒ 1.96 倍となった。

今回のCソースコードでのソフトウェアと高位合成されたハードウェアのラプラシアンフィルタ処理のみの経過時間の性能差は、228 ms / 15.3 ms ≒ 14.9 倍だった。

完全にソフトウェアとして作ったCソースコードと今回のCソースコードで高位合成されたハードウェアのラプラシアンフィルタ処理のみの経過時間の性能差は447 ms / 15.3 ms ≒ 29.2 倍となった。

今回のCソースコードで高位合成されたハードウェアで測定したラプラシアンフィルタ処理のみの経過時間を下に示す。./lap_fil_hls_1shot
soft_hard_comp_17_150704.png
  1. 2015年07月04日 05:23 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

ソフトウェアのCソースコードをVivado HLS 2014.4 で高位合成したIPをシミュレーション

今回は、”ラプラシアンフィルタのソフトウェアとハードウェアの速度の比較2”で高位合成したIPをシミュレーションしてみることにした。

Vivado HLS で高位合成したIPのシミュレーションについては、以下のブログ記事を参照のこと。
Vivado HLS 2014.1で生成したラプラシアンフィルタIPをシミュレーション1(mem_sim_axi_slave IP の作製)
Vivado HLS 2014.1で生成したラプラシアンフィルタIPをシミュレーション2(reg_set_axi_lite_master IP の作製)
Vivado HLS 2014.1で生成したラプラシアンフィルタIPをシミュレーション3( mem_sim_axi_slave IP の変更1)
Vivado HLS 2014.1で生成したラプラシアンフィルタIPをシミュレーション4(mem_sim_axi_slave IP の変更2)
Vivado HLS 2014.1で生成したラプラシアンフィルタIPをシミュレーション5(シミュレーション用プロジェクトの作製)
Vivado HLS 2014.1で生成したラプラシアンフィルタIPをシミュレーション6(シミュレーション)
Vivado HLS 2014.4で生成したラプラシアンフィルタIPをシミュレーション1
Vivado HLS 2014.4で生成したラプラシアンフィルタIPをシミュレーション2

プロジェクトをコピーして、ラプラシアンフィルタIPの中身を入れ替えた。

プロジェクトを立ちあげ、IPのアップデートを行った。

シミュレーションを行った。
soft_hard_comp_11_150702.png

カメラデータをRead するAXI4 Master Read とラプラシアンフィルタ処理後のデータをWrite するAXI4 Master Writeを示す。
soft_hard_comp_12_150703.png

最初に1ライン分の 0 を Write して、次に2ラインをRead するのだが、そこだけはバースト転送になっているようだ。

次に拡大して見ていこう。
2ライン分のバースト Read が終了して、1画素ずつRead してラプラシアンフィルタ処理を行い、Writeする部分を拡大する。
soft_hard_comp_13_150703.png

Read も Write もすべてのアクセスがシングル・アクセルになっている。
この最初のラプラシアンフィルタ処理の時にRead するカメラデータのアドレスは 0x01001900 となる。この位置は185.865 us となった。

次のラインのラプラシアンフィルタ処理を見ていこう。次のラインの先頭アドレスは、0x01002580 なので、そのアドレスをRead しているところを探した。ここがそうで、455.655 us の位置だった。
soft_hard_comp_14_150703.png

つまり、1ライン分の処理に

455.655 us - 185.865 us = 269.79 us

となった。これが 600 ライン分となるといくつになるかを計算した。これは、

269.79 us * 600 = 161.874 ms

となった。
これは、実機で確認したラプラシアンフィルタ処理のみの経過時間 509 ms の 32% 程度だ。どこかで時間がかかっているのかもしれない?

最後に、AXI4 Master Write アクセス間の時間を測ってみたところ、340 ns となった。クロックは 100 MHz なので、34クロックかかっていることになる。
soft_hard_comp_15_150703.png
  1. 2015年07月03日 05:19 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

ラプラシアンフィルタのソフトウェアとハードウェアの速度の比較2

ラプラシアンフィルタのソフトウェアとハードウェアの速度の比較”の続き。

前回は2つのVivado HLS 2014.4 で使用した2つのCソースコードをソフトウェアに適用したら、どの程度の処理速度になるかを調べた。Vivado HLS 2014.4 で使用したコードをソフトウェアに移行したら速くなることが分かった。更に、ほとんど同一のCソースコードによるソフトウェアとハードウェアでのラプラシアンフィルタの処理速度の違いを測定した。

今回は、”ZYBO用Ubuntu Linux のカメラで撮った画像にラプラシアンフィルタをかける”のソフトウェアによるラプラシアンフィルタで使用したCソースコードをVivado HLS 2014.4 で高位合成したら、どの程度速くなるかを検証する。

まずは、Vivado HLS 2014.4 用のラプラシアンフィルタのCソースコードを示す。このCソースコードはmamcpy() を使用していない。つまり、AXI4バスはバースト転送が出来ないはずだ。
(2015/07/02: laplacian_filter.c が間違っていたので、修正しました。最初のラインを読むときに1ライン同じものを読んでしまってました)
(2015/07/26: バグ修正、line_buf[(y+1)%3][x+1] = cam_fb_addr[((y+1)*HORIZONTAL_PIXEL_WIDTH)+(x+1)];)

// laplacian_filter.c
// lap_filter_axim()

#include <stdio.h>
#include <string.h>

#define HORIZONTAL_PIXEL_WIDTH    800
#define VERTICAL_PIXEL_WIDTH    600
#define ALL_PIXEL_VALUE    (HORIZONTAL_PIXEL_WIDTH*VERTICAL_PIXEL_WIDTH)

int laplacian_fil(int x0y0, int x1y0, int x2y0, int x0y1, int x1y1, int x2y1, int x0y2, int x1y2, int x2y2);
int conv_rgb2y(int rgb);

int lap_filter_axim(int cam_addr, int lap_addr, volatile int *cam_fb, volatile int *lap_fb)
{
    #pragma HLS INTERFACE s_axilite port=cam_addr bundle=BUS_AXI4LS
    #pragma HLS INTERFACE s_axilite port=lap_addr bundle=BUS_AXI4LS
    #pragma HLS INTERFACE s_axilite port=return bundle=BUS_AXI4LS
    #pragma HLS INTERFACE ap_none port=cam_addr
    #pragma HLS INTERFACE ap_none port=lap_addr

    #pragma HLS INTERFACE m_axi port=cam_fb depth=1920
    #pragma HLS INTERFACE m_axi port=lap_fb depth=1920

    unsigned int line_buf[3][HORIZONTAL_PIXEL_WIDTH];
    int x, y;
    int lap_fil_val;
    int a, b;
    int fl, sl, tl;
    int *cam_fb_addr, *lap_fb_addr;

    cam_fb_addr = (int *)(cam_fb+(cam_addr/sizeof(int)));
    lap_fb_addr = (int *)(lap_fb+(lap_addr/sizeof(int)));

    // RGB値をY(輝度成分)のみに変換し、ラプラシアンフィルタを掛けた。
    for (y=0; y<VERTICAL_PIXEL_WIDTH; y++){
        for (x=0; x<HORIZONTAL_PIXEL_WIDTH; x++){
            if (y==0 || y==VERTICAL_PIXEL_WIDTH-1){ // 縦の境界の時の値は0とする
                lap_fil_val = 0;
            }else if (x==0 || x==HORIZONTAL_PIXEL_WIDTH-1){ // 横の境界の時も値は0とする
                lap_fil_val = 0;
            }else{
                if (y == 1 && x == 1){ // 最初のラインの最初のピクセルでは2ライン分の画素を読み出す
                    for (a=0; a<2; a++){ // 2ライン分
                        for (b=0; b<HORIZONTAL_PIXEL_WIDTH; b++){ // ライン
                            line_buf[a][b] = cam_fb_addr[(a*HORIZONTAL_PIXEL_WIDTH)+b];
                            line_buf[a][b] = conv_rgb2y(line_buf[a][b]);
                        }
                    }
                }
                if (x == 1) {    // ラインの最初なので、2つのピクセルを読み込む
                    for (b=0; b<2; b++){ // ライン
                        line_buf[(y+1)%3][b] = cam_fb_addr[((y+1)*HORIZONTAL_PIXEL_WIDTH)+b];
                        // (y+1)%3 は、使用済みのラインがに読み込む、y=2 の時 line[0], y=3の時 line[1], y=4の時 line[2]
                        line_buf[(y+1)%3][b] = conv_rgb2y(line_buf[(y+1)%3][b]);
                    }
                }
                
                // 1つのピクセルを読み込みながらラプラシアン・フィルタを実行する
                line_buf[(y+1)%3][x+1] = cam_fb_addr[((y+1)*HORIZONTAL_PIXEL_WIDTH)+(x+1)];
                // (y+1)%3 は、使用済みのラインがに読み込む、y=2 の時 line[0], y=3の時 line[1], y=4の時 line[2]
                line_buf[(y+1)%3][x+1] = conv_rgb2y(line_buf[(y+1)%3][x+1]);
                
                fl = (y-1)%3;    // 最初のライン, y=1 012, y=2 120, y=3 201, y=4 012
                sl = y%3;        // 2番めのライン
                tl = (y+1)%3;    // 3番目のライン
                lap_fil_val = laplacian_fil(line_buf[fl][x-1], line_buf[fl][x], line_buf[fl][x+1], line_buf[sl][x-1], line_buf[sl][x], line_buf[sl][x+1], line_buf[tl][x-1], line_buf[tl][x], line_buf[tl][x+1]);
            }
            // ラプラシアンフィルタ・データの書き込み
            lap_fb_addr[(y*HORIZONTAL_PIXEL_WIDTH)+x] = (lap_fil_val<<16)+(lap_fil_val<<8)+lap_fil_val ;
            // printf("x = %d  y = %d", x, y);
        }
     }
     return(1);
}

// RGBからYへの変換
// RGBのフォーマットは、{8'd0, R(8bits), G(8bits), B(8bits)}, 1pixel = 32bits
// 輝度信号Yのみに変換する。変換式は、Y =  0.299R + 0.587G + 0.114B
// "YUVフォーマット及び YUV<->RGB変換"を参考にした。http://vision.kuee.kyoto-u.ac.jp/~hiroaki/firewire/yuv.html
// 2013/09/27 : float を止めて、すべてint にした
int conv_rgb2y(int rgb){
    int r, g, b, y_f;
    int y;

    b = rgb & 0xff;
    g = (rgb>>8) & 0xff;
    r = (rgb>>16) & 0xff;

    y_f = 77*r + 150*g + 29*b; //y_f = 0.299*r + 0.587*g + 0.114*b;の係数に256倍した
    y = y_f >> 8// 256で割る

    return(y);
}

// ラプラシアンフィルタ
// x0y0 x1y0 x2y0 -1 -1 -1
// x0y1 x1y1 x2y1 -1  8 -1
// x0y2 x1y2 x2y2 -1 -1 -1
int laplacian_fil(int x0y0, int x1y0, int x2y0, int x0y1, int x1y1, int x2y1, int x0y2, int x1y2, int x2y2)
{
    int y;

    y = -x0y0 -x1y0 -x2y0 -x0y1 +8*x1y1 -x2y1 -x0y2 -x1y2 -x2y2;
    if (y<0)
        y = 0;
    else if (y>255)
        y = 255;
    return(y);
}


Vivado HLS 2014.4 のlap_filter_axim_soft_2014_4 で C Synthesis を行った。
soft_hard_comp_3_150701.png

soft_hard_comp_4_150701.png

次にIP 化を行った。

そのIP を”Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化14(性能が最大になる設定を探る7、まとめ)”の”6. 5. から AXI Interconnect (axi_mem_intercon_1) の設定のSlave Interface タブの Enable Register Slice を Auto、Enable Data FIFO を 32 deep に変更した。”のプロジェクトをコピーし、V_ZYBO_CAMDfL_soft_144 フォルダに改名して、ラプラシアンフィルタIP を入れ替えた。
soft_hard_comp_5_150701.png

論理合成、インプリメント、ビットストリームの生成を行った。
soft_hard_comp_6_150701.png

soft_hard_comp_7_150701.png

ハードウェアをエクスポートして、SDKを立ちあげた。

FSBLを作り直して、BOOT.bin を作製した。
soft_hard_comp_8_150701.png

soft_hard_comp_9_150701.png

BOOT.bin をSDカードにコピーして、ZYBOのSDカードを挿入してLinuxを立ちあげた。(BOOT_soft.bin)
linaro ユーザーでログインし、Apps/lap_fil_hls_1shot フォルダに移動して、./cam_disp_uio でカメラ画像をディスプレイに表示してから、./lap_fil_hls_1shot でVivado HLS 2014.4 で作製したハードウェアのラプラシアンフィルタを起動した。

ラプラシアンフィルタのみの処理時間は、509 ms だった。
soft_hard_comp_10_150701.png

ソフトウェアのラプラシアンフィルタのみの処理時間は、448 ms だった。”ラプラシアンフィルタのソフトウェアとハードウェアの速度の比較”のlaplacian_filter1 を参照のこと。

ソフトウェアをハードウェアにしたところ 448 ms / 509 ms ≒ 0.880 倍になった。つまり、ハードウェアの方が遅くなった。
この結果を見ても、Cソースコードをハードウェアに最適化するのが重要だということがよくわかると思う。
  1. 2015年07月01日 04:57 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:2