FC2カウンター FPGAの部屋 ZedBoard

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

FPGAの部屋

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

AXI VDMAを使ったカメラ画像回路の作製4(SDKがRun出来ない原因不明)

AXI VDMAを使ったカメラ画像回路の作製3(カメラ・インターフェースAXI_Stream IPを更新)”の続き。

SDKでFPGA をコンフィグして、デバックしようとしたら、デバック画面で main()関数の先頭行に行かずに止まってしまう現象が直りません。

いろいろとChipScope をかけるパターンを変えてやってみたりしました。AXI4 Master MM2S のみにChipScopeをかけるとか、やってみましたが、やはり、ChipScopeを入れると、SDKでデバックしようとするとデバック画面で main()関数の先頭行に行かずに止まってしまいます。困りました。

ISE14.7が全てそうかというと違って、例えば、”ZynqのAXI_ACPポートとAXI_HPポートの性能の違い2(AXI_HPポート)”では、ChipScopeが入ったXPSプロジェクトを使っています。これは、SDKで問題なくデバック出来ます。Runも出来ます。
なぜダメなのか?の理由が全くわかないので、残念ですが、ペンディングとすることにしました。
  1. 2014年04月04日 04:35 |
  2. ZedBoard
  3. | トラックバック:0
  4. | コメント:0

AXI VDMAを使ったカメラ画像回路の作製3(カメラ・インターフェースAXI_Stream IPを更新)

AXI VDMAを使ったカメラ画像回路の作製2(Xilinxアンサーを検索)”の続き。

AR# 53281の推奨事項を知ったので、カメラ・インターフェースAXI_Stream IP (mt9d111_inf_axi_stream) に、AXI Stream の TUSER と TKEEPの処理を追加した。

最初にISEのプロジェクトを示す。
Cam_VDMA_ISE_5_140331.png

XPSプロジェクトを示す。
Cam_VDMA_ISE_6_140331.png

XPSプロジェクトのPortsタブを下に示す。
Cam_VDMA_ISE_7_140331.png

s_axis_s2mm_tkeep と s_axis_s2mm_tuser の信号を追加した。

s_axis_s2mm_tkeep と s_axis_s2mm_tuser の信号のVHDLコードを下に示す。

m_axis_tuser(0) <= s2mm_fsync_node;


tuser は、Start Of Frame を示す。

m_axis_tkeep <= (others => '1') when pfifo_empty='0' else (others => '0');


tkeep は、tdataがストリームに有効なデータとして扱われるかどうかを示す。

上記の様に修正して、インプリメントして、SDKでFPGA をコンフィグして、デバックしようとしたら、デバック画面で main()関数の先頭行に行かずに止まってしまう。
Cam_VDMA_ISE_8_140331.png

FPGAをコンフィグせずに、FPGAがコンフィグされていないというワーニングを無視して、デバックを行うと、ちゃんとデバック画面で main()関数の先頭行に行った。
Cam_VDMA_ISE_9_140331.png

やはり、PL部の回路に何らかの不具合があるかもしれない?もう一度、プロジェクトを作りなおしてみることにする。

(2014/04/02:追記)
やはり、プロジェクトを作り直しても、同様でした。ChipScopeを入れなければ、SDKのデバックに問題が無いので、ChipScopeとSDKのJTAGの共有あたりの問題だと思います。
  1. 2014年03月31日 04:21 |
  2. ZedBoard
  3. | トラックバック:0
  4. | コメント:0

AXI VDMAを使ったカメラ画像回路の作製2(Xilinxアンサーを検索)

AXI VDMAを使ったカメラ画像回路の作製1(プロジェクトの作製)”の続き。

AXI VDMAを双方向でまともに動かしたことが1度もないので、動かしてみたいということで、Xilinx社のアンサーを検索してみた。
そうしたところ、”AR# 56623 LogiCORE IP AXI Video Direct Memory Access v6.0 - IPI デザインで S2MM_TREADY がディアサートされたままになる”が見つかった。
このアンサーは、VivadoのIP Integrator 用だが、 s_axis_s2mm_tkeep のデフォルト接続は現在 0 で、すべてのバイトをヌルバイトとして処理して、データをメモリに転送できなくなって、内部バッファーがFULLになると、treadyがディアサートされるというアンサーだ。
今回のAXI VDMAはVer.5.04で、Project Navigatorを使用しているが、一応、tkeep信号を 1 に駆動しておこうと思う。

次に、”AR# 53281 LogiCORE IP AXI Video Direct Memory Access - FSYNC 処理の推奨事項”が見つかった。
それによると、fsync を使ったインターフェースから、SOF(Start Of Frame) on TUSER を使ったインターフェースに移行しているそうだ。積極的に tuser を使ったほうが良いそうだ。

AR# 53281の推奨事項を以下に引用する。

・入力で Video Timing Controller コアを検出モードで使用する。

・Video In to AXI4 Stream コアを使用して入力ビデオ タイミング情報から SOF 信号を生成する。

・AXI VDMA などの該当するコアの SOF on TUSER モードをイネーブルにし、fsync 信号ではなくこれを使用してフレーム同期を行う。コアによって、SOF が自動的にシステム全体に伝搬されます。

・デザインに AXI VDMA が含まれている場合 :
   ・S2MM 側を外部フレーム同期モードに設定し、SOF on TUSER をイネーブルにする。 S2MM_DMACR レジスタの FsyncSrcSelect ビットを 2'b10 に設定してフレーム同期ソースとして TUSER を選択してください。

   ・MM2S 側をフリー ランニング モードに設定する。

   ・fsync 信号を接続する (XPS では接続しなくてもよい)。

・出力で Video Timing Controller コアを生成モードで使用し、ビデオ タイミング情報を再生成する。

・AXI4 Stream to Video Out コアを使用して出力ビデオ データを同期する。


そうか、MM2S側はフリー・ランさせて、VTCでタイミングを再生成させるのか?その場合、AXI VDMAは、fsync を出したら途切れなくデータを供給してくれないと、ビデオ信号が破綻してしまいそうだ。今の自作 custom_vtc IP には、生成モードは無いので、作る必要がある。

詳細は、『AXI4-Stream Video IP およびシステム デザイン ガイド』 (UG934)を参照して欲しいそうだ。

自分の AXI_VDMA 回路を見なおしてみよう。
  1. 2014年03月29日 05:20 |
  2. ZedBoard
  3. | トラックバック:0
  4. | コメント:0

AXI VDMAを使ったカメラ画像回路の作製1(プロジェクトの作製)

Vivado 2013.4でAXI VDMAを使ったカメラ表示回路の作製17(ハードウェアのデバック5)”まで、Vivado 2013.4でAXI VDMA を使ったカメラ画像回路を作ってきたが、Vivado 2013.4だと、動作が重いので、ISEで試作してみようと思った。Vivado 2013.4は私の家の古いパソコンではとっても重い。

まずは、Project Navigator の ZedBoard用のプロジェクトを作製した。
Cam_VDMA_ISE_1_140217.png

次に、XPSプロジェクトを下に示す。
Cam_VDMA_ISE_2_140217.png

Cam_VDMA_ISE_3_140217.png

Cam_VDMA_ISE_4_140217.png

AXI_GP0、AXI_HP0 のクロックは 100MHz だ。

下に、system_top.ucf を示す。

NET "vga_blue[0]" LOC = Y21;
NET "vga_blue[1]" LOC = Y20;
NET "vga_blue[2]" LOC = AB20;
NET "vga_blue[3]" LOC = AB19;
NET "vga_blue[0]" IOSTANDARD = LVCMOS33;
NET "vga_blue[1]" IOSTANDARD = LVCMOS33;
NET "vga_blue[2]" IOSTANDARD = LVCMOS33;
NET "vga_blue[3]" IOSTANDARD = LVCMOS33;
INST "vga_blue_0_OBUF" IOB =FORCE;
INST "vga_blue_1_OBUF" IOB =FORCE;
INST "vga_blue_2_OBUF" IOB =FORCE;
INST "vga_blue_3_OBUF" IOB =FORCE;
NET "vga_green[0]" LOC = AB22;
NET "vga_green[1]" LOC = AA22;
NET "vga_green[2]" LOC = AB21;
NET "vga_green[3]" LOC = AA21;
NET "vga_green[0]" IOSTANDARD = LVCMOS33;
NET "vga_green[1]" IOSTANDARD = LVCMOS33;
NET "vga_green[2]" IOSTANDARD = LVCMOS33;
NET "vga_green[3]" IOSTANDARD = LVCMOS33;
INST "vga_green_0_OBUF" IOB =FORCE;
INST "vga_green_1_OBUF" IOB =FORCE;
INST "vga_green_2_OBUF" IOB =FORCE;
INST "vga_green_3_OBUF" IOB =FORCE;
NET "vga_red[0]" LOC = V20;
NET "vga_red[1]" LOC = U20;
NET "vga_red[2]" LOC = V19;
NET "vga_red[3]" LOC = V18;
NET "vga_red[0]" IOSTANDARD = LVCMOS33;
NET "vga_red[1]" IOSTANDARD = LVCMOS33;
NET "vga_red[2]" IOSTANDARD = LVCMOS33;
NET "vga_red[3]" IOSTANDARD = LVCMOS33;
INST "vga_red_0_OBUF" IOB =FORCE;
INST "vga_red_1_OBUF" IOB =FORCE;
INST "vga_red_2_OBUF" IOB =FORCE;
INST "vga_red_3_OBUF" IOB =FORCE;
NET "vga_hsync" LOC = AA19;
NET "vga_vsync" LOC = Y19;
NET "vga_hsync" IOSTANDARD = LVCMOS33;
NET "vga_vsync" IOSTANDARD = LVCMOS33;
INST "vga_hsync_OBUF" IOB =FORCE;
INST "vga_vsync_OBUF" IOB =FORCE;

INST "hdmi_clk_OBUF" IOB =FORCE;
NET "hdmi_clk" LOC = W18;
NET "hdmi_clk" IOSTANDARD = LVCMOS33;
INST "hdmi_vsync_OBUF" IOB =FORCE;
NET "hdmi_vsync" LOC = W17;
NET "hdmi_vsync" IOSTANDARD = LVCMOS33;
INST "hdmi_hsync_OBUF" IOB =FORCE;
NET "hdmi_hsync" LOC = V17;
NET "hdmi_hsync" IOSTANDARD = LVCMOS33;
INST "hdmi_data_e_OBUF" IOB =FORCE;
NET "hdmi_data_e" LOC = U16;
NET "hdmi_data_e" IOSTANDARD = LVCMOS33;
INST "hdmi_data_0_OBUF" IOB =FORCE;
NET "hdmi_data[0]" LOC = Y13;
NET "hdmi_data[0]" IOSTANDARD = LVCMOS33;
INST "hdmi_data_1_OBUF" IOB =FORCE;
NET "hdmi_data[1]" LOC = AA13;
NET "hdmi_data[1]" IOSTANDARD = LVCMOS33;
INST "hdmi_data_2_OBUF" IOB =FORCE;
NET "hdmi_data[2]" LOC = AA14;
NET "hdmi_data[2]" IOSTANDARD = LVCMOS33;
INST "hdmi_data_3_OBUF" IOB =FORCE;
NET "hdmi_data[3]" LOC = Y14;
NET "hdmi_data[3]" IOSTANDARD = LVCMOS33;
INST "hdmi_data_4_OBUF" IOB =FORCE;
NET "hdmi_data[4]" LOC = AB15;
NET "hdmi_data[4]" IOSTANDARD = LVCMOS33;
INST "hdmi_data_5_OBUF" IOB =FORCE;
NET "hdmi_data[5]" LOC = AB16;
NET "hdmi_data[5]" IOSTANDARD = LVCMOS33;
INST "hdmi_data_6_OBUF" IOB =FORCE;
NET "hdmi_data[6]" LOC = AA16;
NET "hdmi_data[6]" IOSTANDARD = LVCMOS33;
INST "hdmi_data_7_OBUF" IOB =FORCE;
NET "hdmi_data[7]" LOC = AB17;
NET "hdmi_data[7]" IOSTANDARD = LVCMOS33;
INST "hdmi_data_8_OBUF" IOB =FORCE;
NET "hdmi_data[8]" LOC = AA17;
NET "hdmi_data[8]" IOSTANDARD = LVCMOS33;
INST "hdmi_data_9_OBUF" IOB =FORCE;
NET "hdmi_data[9]" LOC = Y15;
NET "hdmi_data[9]" IOSTANDARD = LVCMOS33;
INST "hdmi_data_10_OBUF" IOB =FORCE;
NET "hdmi_data[10]" LOC = W13;
NET "hdmi_data[10]" IOSTANDARD = LVCMOS33;
INST "hdmi_data_11_OBUF" IOB =FORCE;
NET "hdmi_data[11]" LOC = W15;
NET "hdmi_data[11]" IOSTANDARD = LVCMOS33;
INST "hdmi_data_12_OBUF" IOB =FORCE;
NET "hdmi_data[12]" LOC = V15;
NET "hdmi_data[12]" IOSTANDARD = LVCMOS33;
INST "hdmi_data_13_OBUF" IOB =FORCE;
NET "hdmi_data[13]" LOC = U17;
NET "hdmi_data[13]" IOSTANDARD = LVCMOS33;
INST "hdmi_data_14_OBUF" IOB =FORCE;
NET "hdmi_data[14]" LOC = V14;
NET "hdmi_data[14]" IOSTANDARD = LVCMOS33;
INST "hdmi_data_15_OBUF" IOB =FORCE;
NET "hdmi_data[15]" LOC = V13;
NET "hdmi_data[15]" IOSTANDARD = LVCMOS33;
NET "hdmi_iic_Scl" LOC = AA18;
NET "hdmi_iic_Scl" IOSTANDARD = LVCMOS33;
NET "hdmi_iic_Sda" LOC = Y16;
NET "hdmi_iic_Sda" IOSTANDARD = LVCMOS33;
NET "hdmi_data[15]" SLEW = FAST;
NET "hdmi_data[14]" SLEW = FAST;
NET "hdmi_data[13]" SLEW = FAST;
NET "hdmi_data[12]" SLEW = FAST;
NET "hdmi_data[11]" SLEW = FAST;
NET "hdmi_data[10]" SLEW = FAST;
NET "hdmi_data[9]" SLEW = FAST;
NET "hdmi_data[8]" SLEW = FAST;
NET "hdmi_data[7]" SLEW = FAST;
NET "hdmi_data[6]" SLEW = FAST;
NET "hdmi_data[5]" SLEW = FAST;
NET "hdmi_data[4]" SLEW = FAST;
NET "hdmi_data[3]" SLEW = FAST;
NET "hdmi_data[2]" SLEW = FAST;
NET "hdmi_data[1]" SLEW = FAST;
NET "hdmi_data[0]" SLEW = FAST;
NET "vga_blue[3]" SLEW = FAST;
NET "vga_blue[2]" SLEW = FAST;
NET "vga_blue[1]" SLEW = FAST;
NET "vga_blue[0]" SLEW = FAST;
NET "vga_green[3]" SLEW = FAST;
NET "vga_green[2]" SLEW = FAST;
NET "vga_green[1]" SLEW = FAST;
NET "vga_green[0]" SLEW = FAST;
NET "vga_red[3]" SLEW = FAST;
NET "vga_red[2]" SLEW = FAST;
NET "vga_red[1]" SLEW = FAST;
NET "vga_red[0]" SLEW = FAST;
NET "hdmi_clk" SLEW = FAST;
NET "hdmi_data_e" SLEW = FAST;
NET "hdmi_hsync" SLEW = FAST;
NET "hdmi_iic_Scl" SLEW = SLOW;
NET "hdmi_vsync" SLEW = FAST;
NET "vga_hsync" SLEW = FAST;
NET "vga_vsync" SLEW = FAST;
NET "mt9d111_d[7]" LOC = W12;
NET "mt9d111_d[6]" LOC = V12;
NET "mt9d111_d[5]" LOC = W11;
NET "mt9d111_d[4]" LOC = W10;
NET "mt9d111_d[3]" LOC = V10;
NET "mt9d111_d[2]" LOC = V9;
NET "mt9d111_d[1]" LOC = W8;
NET "mt9d111_d[0]" LOC = V8;
NET "mt9d111_d[7]" IOSTANDARD = LVCMOS33;
NET "mt9d111_d[6]" IOSTANDARD = LVCMOS33;
NET "mt9d111_d[5]" IOSTANDARD = LVCMOS33;
NET "mt9d111_d[4]" IOSTANDARD = LVCMOS33;
NET "mt9d111_d[3]" IOSTANDARD = LVCMOS33;
NET "mt9d111_d[2]" IOSTANDARD = LVCMOS33;
NET "mt9d111_d[1]" IOSTANDARD = LVCMOS33;
NET "mt9d111_d[0]" IOSTANDARD = LVCMOS33;
NET "mt9d111_href" LOC = AB10;
NET "mt9d111_pclk" LOC = AA9;
NET "mt9d111_Scl" LOC = Y11;
NET "mt9d111_Sda" LOC = AB11;
NET "mt9d111_standby" LOC = Y10;
NET "mt9d111_vsync" LOC = AA11;
NET "mt9d111_xck" LOC = AA8;
NET "mt9d111_href" IOSTANDARD = LVCMOS33;
NET "mt9d111_pclk" IOSTANDARD = LVCMOS33;
NET "mt9d111_Scl" IOSTANDARD = LVCMOS33;
NET "mt9d111_Sda" IOSTANDARD = LVCMOS33;
NET "mt9d111_standby" IOSTANDARD = LVCMOS33;
NET "mt9d111_vsync" IOSTANDARD = LVCMOS33;
NET "mt9d111_xck" IOSTANDARD = LVTTL;
NET "mt9d111_d[0]" OFFSET = IN 10.8 ns VALID 21.7 ns BEFORE "mt9d111_pclk" RISING;
NET "mt9d111_d[1]" OFFSET = IN 10.8 ns VALID 21.7 ns BEFORE "mt9d111_pclk" RISING;
NET "mt9d111_d[2]" OFFSET = IN 10.8 ns VALID 21.7 ns BEFORE "mt9d111_pclk" RISING;
NET "mt9d111_d[3]" OFFSET = IN 10.8 ns VALID 21.7 ns BEFORE "mt9d111_pclk" RISING;
NET "mt9d111_d[4]" OFFSET = IN 10.8 ns VALID 21.7 ns BEFORE "mt9d111_pclk" RISING;
NET "mt9d111_d[5]" OFFSET = IN 10.8 ns VALID 21.7 ns BEFORE "mt9d111_pclk" RISING;
NET "mt9d111_d[6]" OFFSET = IN 10.8 ns VALID 21.7 ns BEFORE "mt9d111_pclk" RISING;
NET "mt9d111_d[7]" OFFSET = IN 10.8 ns VALID 21.7 ns BEFORE "mt9d111_pclk" RISING;
NET "mt9d111_href" OFFSET = IN 10.8 ns VALID 21.7 ns BEFORE "mt9d111_pclk" RISING;
NET "mt9d111_vsync" OFFSET = IN 10.8 ns VALID 21.7 ns BEFORE "mt9d111_pclk" RISING;
NET "mt9d111_Scl" PULLUP;
NET "mt9d111_Sda" PULLUP;


現在、論理合成中だ。

”AXI VDMAを使ったカメラ画像回路の作製2(Xilinxアンサーを検索)”に続く。
  1. 2014年02月17日 04:42 |
  2. ZedBoard
  3. | トラックバック:0
  4. | コメント:0

Vivado 2013.4でAXI VDMAを使ったカメラ表示回路の作製1

Vivado 2013.4のIP Integrator でAXI VDMAを使ったカメラ表示回路を作製してみようと思う。

長くなるので、肝心なところ以外は端折っていきます。
参考にするFPGAの部屋のブログ記事は、”Vivado IP Integrator のチュートリアル(Lab1)1(Vivado プロジェクトの生成)”から始まるVivado IP Integrator のチュートリアルシリーズです。

・まずは、Vivado 2013.4のZedBoardのプロジェクトを作製した。
Cam_VDMA_1_140111.png

・左の Flow Navigator -> Project Manager -> IP Catalog をクリックする。

・右に IP Catalog のウインドウが出る。そこで右クリックして、右クリックメニューから IP Setting... を選択する。
Cam_VDMA_2_140111.png

・Project Setting のダイアログが出る。Add Repository... ボタンをクリックして、自作したIPをインポートする。
Cam_VDMA_3_140111.png

・custom_vtc, mt9d111_inf_axis, video_out_zed, custom_axi4s_video の4つのIPをリポジトリに追加する。
Cam_VDMA_4_140111.png

・IP Catalog のBase IP に追加されているのがわかる。
Cam_VDMA_5_140111.png

・左の Flow Navigator -> Project Manager -> Create Block Design をクリックする。

・Create Block Design ダイアログが出る。CamD_VDMAと入力して、OKボタンをクリックする。
Cam_VDMA_6_140111.png

・Diagramウインドウが開く。

・Add IPをクリックして、IPコアをインポートする。
Cam_VDMA_7_140111.png

・ZYNQ 7 Processing System をダブルクリックする。
Cam_VDMA_8_140111.png

・ZYNQがインポートされた。
Cam_VDMA_9_140111.png

・Run Block Automation をクリックした。

・Run Block Automation ダイアログが開いた。OKボタンをクリックした。
Cam_VDMA_10_140111.png

・ポートが増えて、DDRとFIXED_IOの外部ポートが追加された。
Cam_VDMA_11_140111.png

Vivado 2013.4でAXI VDMAを使ったカメラ表示回路の作製2”に続く。
  1. 2014年01月11日 05:38 |
  2. ZedBoard
  3. | トラックバック:0
  4. | コメント:0

ZedBoardのRevisionの違い

前から使用しているZedBoardはRev.Cです。もう1枚、ZedBoardを最近購入して、それは、Rev.Dでした。

Rev.CのZedBoardです。
ZedBoard_1_140104.jpg

Rev.DのZedBoardです。外見上の大きな違いはZynqに付いているヒートシンクです。
ZedBoard_2_140104.jpg

Rev.Dに”ZedBoard用CMOSカメラ回路の作製11(正常に写った)”のSDカードを挿入して、電源をONしました。最初に感じることは、Rev.Cよりも ZedBoard のDONE LEDが点灯するのが速いです。かなり違います。
そして、Rev.Dでは、HDMIに正常に表示されないこともあります。

Rev.Cに付いているのは、ES(Engineering Sample)のZynq、Rev.Dに付いているのは、たぶんC(Commercial)のZynqです。

このディスプレイでは、Rev.C, Rev.D両方共HDMIに表示できますが、Rev.Dでは、他のディスプレイでは、色がおかしく、もう1つのディスプレイでは表示できませんでした。ディスプレイを選ぶようです。ソフトウェアの設定かハードウェアかがおかしいのかもしれません?

Rev.Cで、今使っている他のディスプレイで確かめて見たいと思います。今思い出しましたが、Rev.CでもKEKのZynq勉強会の時にディスプレイを借りて表示しましたが、全く問題なかったです。Rev.Dだけの問題でしょうかね?

やはり、Rev.C, Rev.Dでディスプレイを変えてテストしてみる必要がありそうです。
  1. 2014年01月04日 07:01 |
  2. ZedBoard
  3. | トラックバック:0
  4. | コメント:0

ZedBoard Linux上でカメラの画像を処理する7(ラプラシアンフィルタ5)

”ZedBoard Linux上でカメラの画像を処理する6(ラプラシアンフィルタ4)”でラプラシアン・フィルタが出来たと思ったのだが、次にやってみたら、Linuxカーネルが落ちてしまった。どうやら偶然に出来たようだ。そこで、mmap() で同時に領域を取らないようにしてみた。つまり、ReadとWriteで同時に mmap() して領域を確保していたが、Readで mmap() を使用して、領域を確保したら、一度、munmap() してから、Write用に mmap() で領域を確保するように書き換えた。
Readするときにいちいち mmap() してからReadして、munmap() しているので、とっても遅い。今度は、ラプラシアン・フィルタリングを完了するまでに、139秒かかっている。

ラプラシアン・フィルタを掛けるためには3x3の画像のピクセルを使用して、真ん中の1点を演算する。(”画像のエッジ検出6(3X3での方式の検討)”参照)
下の図で説明すると、オレンジ色の四角の9点を使用して、黄緑色の1点を計算するのだが、9点のメモリを読みために、それぞれ mmap() してからReadして、munmap() している。つまり、9回、 mmap() と munmap() を繰り返している。その後、Writeのために mmap() で領域を確保し、そして、munmap() で領域を開放している。そのため、遅くなっていると考えられる。
image_process_Zed_linux_14_130923.png

下にCのソースコードを示す。
(2013/09/24:追記)今日は、このコードもカーネルパニックで動作しません。昨日は完全に動作していたんですが、何が悪いんでしょうか?
(2013/09/25:Cソースコード修正)フレーム・バッファの開始アドレスをページ境界に設定したら、うまく動作しているようです。

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

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <dirent.h>
#include <fcntl.h>
#include <assert.h>
#include <ctype.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/kernel.h>

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

#define PAGE_SIZE (4*1024)
#define BLOCK_SIZE (4*1024)
#define ALLC_SIZE (PAGE_SIZE * 469)    // 800*600*4 を超えるPAGE_SIZEの倍数

#define BUFSIZE    1024

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);
volatile unsigned int *setup_io(off_t mapped_addr, unsigned int *buf_addr);
void Xil_DCacheInvalidateLine(unsigned int adr);
void Xil_DCacheFlushLine(unsigned int adr);

int main()
{
    FILE *fd;
    int xy[3][3];
    char buf[BUFSIZE], *token;
    unsigned int read_num;
    unsigned int bitmap_dc_reg_addr;
    volatile unsigned *bm_disp_cnt_reg;
    unsigned int fb_addr, next_frame_addr;
    int x, y;
    unsigned int val;
    int i, j;
    int lap_fil_val;
    int *r_pixel, *w_pixel;
    unsigned int r_addr, w_addr;
    unsigned int r_addr_page, w_addr_page;
    unsigned int r_addr_page_pre=0, w_addr_page_pre=0;
    unsigned int r_addr_offset, w_addr_offset;
    unsigned int r_buf, w_buf, bitmap_buf;
    
    // fb_start_addr.txt の内容をパイプに入れる
    memset(buf, '\0'sizeof(buf)); // buf すべてに\0 を入れる
    // fb_start_addr.txt を開く
    fd = popen("cat /Apps/fb_start_addr.txt""r");
    if (fd != NULL){
        read_num = fread(buf, sizeof(unsigned char), BUFSIZE, fd);
        if (read_num > 0){
            sscanf(buf, "%x\n", &fb_addr);
        }
    }
    pclose(fd);
    
    // ラプラシアンフィルタの結果を入れておくフレーム・バッファ
    next_frame_addr = ((fb_addr + (ALL_PIXEL_VALUE*4)) & (~(int)(PAGE_SIZE-1))) + PAGE_SIZE;

    // 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{
                for (j=-1; j<2; j++){
                    for (i=-1; i<2; i++){
                        r_addr = fb_addr+((y+j)*HORIZONTAL_PIXEL_WIDTH+(x+i))*4;
                        r_addr_page = r_addr & (~(int)(PAGE_SIZE-1));
                        r_addr_offset = r_addr & ((int)(PAGE_SIZE-1));
                        r_pixel = setup_io((off_t)r_addr_page, &r_buf);

                        xy[i+1][j+1] = *(volatile int *)((unsigned int)r_pixel + r_addr_offset);
                        munmap(r_pixel, BLOCK_SIZE);
                        free((char *)r_buf);
                        xy[i+1][j+1] = conv_rgb2y(xy[i+1][j+1]);
                    }
                }
                lap_fil_val = laplacian_fil(xy[0][0], xy[1][0], xy[2][0], xy[0][1], xy[1][1], xy[2][1], xy[0][2], xy[1][2], xy[2][2]);
            }
            
            w_addr = next_frame_addr+(y*HORIZONTAL_PIXEL_WIDTH + x)*4;
            w_addr_page = w_addr & (~(int)(PAGE_SIZE-1));
            w_addr_offset = w_addr & ((int)(PAGE_SIZE-1));
            w_pixel = setup_io((off_t)w_addr_page, &w_buf);
            *(volatile int *)((unsigned int)w_pixel + w_addr_offset) = (lap_fil_val<<16)+(lap_fil_val<<8)+lap_fil_val ;
            munmap(w_pixel, BLOCK_SIZE);
            free((char *)w_buf);
            // printf("x = %d  y = %d", x, y);
        }
    }
    
    // bitmap-disp-cntrler-axi-master のアドレスを取得
    memset(buf, '\0'sizeof(buf)); // buf すべてに\0 を入れる
    // ls /sys/devices/axi.0 の内容をパイプに入れる
    fd = popen("ls /sys/devices/axi.0""r");
    if (fd != NULL){
        read_num = fread(buf, sizeof(unsigned char), BUFSIZE, fd);
        if (read_num > 0){
            token = buf;
            if ((token=strtok(token, ".\n")) != NULL){
                do {
                    if (chkhex(token)){ // 16進数
                        sscanf(token, "%x", &val);
                    } else {
                        if (strcmp(token, "bitmap-disp-cntrler-axi-master") == 0)
                            bitmap_dc_reg_addr = val;
                    }
                }while((token=strtok(NULL, ".\n")) != NULL);
            }
        }
    }
    pclose(fd);
    
    // ラプラシアンフィルタの掛かった画像のスタートアドレスを bitmap-disp-cntrler-axi-master にセット
    bm_disp_cnt_reg = setup_io((off_t)bitmap_dc_reg_addr, &bitmap_buf);
    *bm_disp_cnt_reg = next_frame_addr;

    munmap((unsigned int *)bm_disp_cnt_reg, BLOCK_SIZE);
    free((char *)bitmap_buf);

    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
//
int conv_rgb2y(int rgb){
    float r, g, b, y_f;
    int y;
    
    b = (float)(rgb & 0xff);
    g = (float)((rgb>>8) & 0xff);
    r = (float)((rgb>>16) & 0xff);
    
    y_f = 0.299*r + 0.587*g + 0.114*b;
    y = (int)y_f;
    
    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)
{
     return(abs(-x0y0 -x1y0 -x2y0 -x0y1 +8*x1y1 -x2y1 -x0y2 -x1y2 -x2y2));
}

//
// Set up a memory regions to access GPIO
//
volatile unsigned int *setup_io(off_t mapped_addr, unsigned int *buf_addr)
// void setup_io()
{
    int  mem_fd;
    char *gpio_mem, *gpio_map;

   /* open /dev/mem */
   if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) {
      printf("can't open /dev/mem \n");
      printf("mapped_addr = %x\n", (unsigned int)mapped_addr);
      exit (-1);
   }

   /* mmap GPIO */

   // Allocate MAP block
   if ((gpio_mem = malloc(BLOCK_SIZE + (PAGE_SIZE-1))) == NULL) {
      printf("allocation error \n");
      exit (-1);
   }
    *buf_addr = (unsigned int)gpio_mem;    // mallocしたアドレスをコピー
    
   // Make sure pointer is on 4K boundary
   if ((unsigned long)gpio_mem % PAGE_SIZE)
     gpio_mem += PAGE_SIZE - ((unsigned long)gpio_mem % PAGE_SIZE);

   // Now map it
   gpio_map = (unsigned char *)mmap(
      (caddr_t)gpio_mem,
      BLOCK_SIZE,
      PROT_READ|PROT_WRITE,
      MAP_SHARED|MAP_FIXED,
      mem_fd,
      mapped_addr
   );

   if ((long)gpio_map < 0) {
      printf("mmap error %d\n", (int)gpio_map);
      printf("mapped_addr = %x\n", (unsigned int)mapped_addr);
      exit (-1);
   }

   close(mem_fd); // /dev/mem のクローズ

   // Always use volatile pointer!
   // gpio = (volatile unsigned *)gpio_map;
   return((volatile unsigned *)gpio_map);

// setup_io

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

  1. 2013年09月23日 18:09 |
  2. ZedBoard
  3. | トラックバック:0
  4. | コメント:0
»