フリーのIPを使ってFPGAから画像出力してみました。
HDMIコネクタ接続のディスプレイにカラーバーを表示させています。
IPの入手方法からピンの設定まで分かりやすく手順を紹介します。
FPGAでHDMIから画像出力!フリーのIPを使ってみた
FPGAからHDMIコネクタを経由して画像出力することが出来ました。
解像度は1280x720でカラーバーをFPGAから出力しています。
フリーのIPを使ってFPGAのプログラムする方法含めて紹介します。
解像度の設定箇所はVerilogで自作しており、誰でも無料で対応可能です。
FPGA書き込みからディスプレイに画像出力するまでの動画も下記で紹介しています。
一連の流れが分かりますので是非一緒にご覧ください。
XilinxのFPGAの評価ボードを使用します
今回はXilinxのFPGAで画像出力を行います。
シリーズの中でもスペックの低いSpartan7でテストしています。
そのため基本的には画像出力のIFさえあれば、どのFPGAでも動作可能と思います。
XilinxのFPGAの使い方に関しては下記記事で紹介しています。(リンク先はこちら)
VivadoにIPコアはデフォルトで沢山入っている
機能がブロックになっておりポートに接続すれば簡単に使えるのがIP(コア)です。
XilinxのFPGAの開発環境のVivadoにはIPが沢山入っています。
クロック・定数など基本的な機能のIPはデフォルトで十分に使えます。
XilinxのHDMIのIPコアはフリーではない
XilinxにHDMIのIPコアもあるようですが、フリーでは使えないようです。
評価版で使うにしろ登録が必要のようです。(XilinxのHP先はこちら)
もちろんデフォルトでHDMIのIPを検索しても見つかりませんでした。
FPGAのフリーのIPコアをダウンロードする
HDMIなどのIPが無くても画像出力は可能なのですが、手間なのでフリーのIPを使います。
使用するのはXilinxの評価ボードメーカでも有名なDiligentのIPです。(リンク先はこちら)
Githubで置かれており、MTIライセンス(オープンソースのライセンス)で誰でも使用可能です。
上記サイトで「↓Code」→「Download ZIP」で簡単に入手できます。
HDMIではなくDVIのIPコア
正確に言うとHDMIではなくDVIのIPコアを使います。
「RGB-to-DVI Video Encoder (Source)」という名称です。
HDMIは音声など画像以外も出力しますが、DVIは「画像のみ」を送信します。
FPGAでフリーのIPコアの使い方
先ほどダウンロードしたDiligentのIPコアをFPGAで使えるようにします。
今回の手順に関してはあくまで筆者の使い方の一例ということでお願いします。
(設計の方もかなり省略してタイミング・マージンなどは考慮していません)
FPGAで使えるようにIPを登録する
まずはXilinxの開発環境のVivadoで空のプロジェクトを作成しておきます。
下記記事でVivadoの環境の構築からプロジェクトの作成まで紹介しています。参考ください。
XilinxのFPGAの評価ボードを購入してみた。Spartan7に入門!
VivadoのIPリポジストリに登録する
ダウンロードしたIPのフォルダをリポジストリに登録します
「Settings」→「IP」→「Respsitory」→「+」からIPのフォルダを選択すればOKです。
VivadoでIPのXMLファイルを修正する
今回筆者の使う評価ボードのFPGAは「Spartan7」です。
「Spartan7」がDiligentのIPに使えるFPGAとして登録します。
※使うFPGAがZynq,Artix7等の方は既に登録されていたので、この項目は不要と思われます。
※「Automotive用のSpartan7」の「aspartan7」は登録されていました。
IPの設定が記載されている「.XML」ファイルを修正します
「File」→「OPEN IP-XACT File」→「Compatibility」→「+」を選択します。
「Add Family Explicitly」を選び「spartan7」にチェックを入れます。
Life Cycleは特に自由で良いと思います。(筆者はBetaを選択)
これでDiligentのDVIのIPに「通常のSpartan7」も登録されました。
後は「Review and Package」の欄で「Re-Package IP」を選択して、IPを保存します。
IPを使ってFPGAのブロックを設計する
登録したDVIのIP、またデフォルトのIPを使ってFPGAのブロック設計を行います。
「IP INTEGRATOR」→「Create Block Design」を選択します。
「Diagram」の画面が出たら「+」をクリックするとIPを検索・選択できます。
これで自由にブロック設計を進めていけます。
今回筆者が使用したIPは下記3つです。仮で置いていきます。
- 「RGB-to-DVI Video Encoder (Source)」…DVIの画像出力用のIP
- 「Constant」…定数のIP
- 「Clocking Wizard」…MMCM(PLL)のクロック分周・逓倍用のIP
MMCM(PLL)のIP使い方に関しては下記記事で詳しく紹介しています。
よろしければ一緒にご覧ください。(リンク先はこちら)
FPGAで画像出力できるブロック構成を整える
IPも使えるようになったので、最終的なブロック構成に向けて編集していきます。
今回は下記のようにFPGAの大元クロック100MHzを元に画像出力する構成を整えます。
IPの機能・設定を変更する
IPをダブルクリックすると色々と画面上で設定を変えることができます。
MMCMの設定
今回のMMCMに関しては「出力を74.25MHz」にする機能以外は不要なので変更しました。
「Output Clocks」のタブで出力を「74.25」(MHz)に変更 ※解像度1280x720のため
「Output Clocks」のタブで使わない機能の「reset」「locked」のチェックを外します。
ConstantでIPのリセットを処理する
本来は大元のクロックを監視しながらリセット解除するのが良いですが、今回は手を抜きます。
DVIのIPの「aRST」が「1」でリセットするので、常に「0」にするように定数を入れます。
「Const Val」の値を「0」にしておきます。
入力ポートにクロック100MHzを設定
「Diagram」の箇所で右クリックすると「Create port」で入力のポートを配置できます。
今回の入力ポートはFPGAの大元クロック100MHzのみ使います。
Typeを「Clock」にしてFrequencyを「100」MHzにしときました。
出力ポートにTMDSを設定
出力のポートに関してはDVIのIPの出力ポートがTMDSになっているため対応します。
※TMDSはDVIで使われている出力信号の名称です
「Diagram」の箇所で右クリックして「Create interface port」で入力のポートを配置できます。
DVIのデータ・解像度の設定をVerilogで作成する
DVIのIPには画像出力に必要な下記4つのデータを送る必要があります。
1280x720の画像用のクロック(74.25MHz)を基準にVerilogで自作していきます。
- vid_pData…カラーバー表示のRGBの24bitのデータ
- vid_pHSYNC…解像度1280x720の水平同期信号
- vid_pVSYNC…解像度1280x720の垂直同期信号
- vid_VDE…解像度1280x720のデータイネーブル信号
Verilogのプログラム
今回は1280x720の解像度設定でカラーバー(白,シアン,黄,マゼンタ)表示する仕様です。
実際に使ったVerilogのプログラムが下記です。
(74.25MHz)クロック基準に画像データ、VSYNC、HSYNC、DEを設定しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
module timing( input CLK, output reg[23:0] data_out, output reg hsync_out, output reg vsync_out, output reg de_out ); /*Timing*/ reg [10:0] CounterX; // counts from 0 to 1650 always @(posedge CLK) CounterX <= (CounterX==1650) ? 0 : CounterX+1; reg [9:0] CounterY; // counts from 0 to 750 always @(posedge CLK) if(CounterX==1650) CounterY <= (CounterY==750) ? 0 : CounterY+1; /*Color*/ always @(posedge CLK)begin if((CounterX>=0) && (CounterX<320)) data_out <= 24'hffffff; else if ((CounterX>=320) && (CounterX<640)) data_out <= 24'h00ffff; else if ((CounterX>=640) && (CounterX<960)) data_out <= 24'hff00ff; else if ((CounterX>=960) && (CounterX<1280)) data_out <= 24'hffff00; end /*hsync = (CounterX>=1390) && (CounterX<1430)*/ always @(posedge CLK)begin if((CounterX>=1390) && (CounterX<1430)) hsync_out <= 1'b1; else hsync_out <= 1'b0; end /*vsync = (CounterY>=725) && (CounterY<730)*/ always @(posedge CLK)begin if((CounterY>=725) && (CounterY<730)) vsync_out <= 1'b1; else vsync_out <= 1'b0; end /*DrawArea = (CounterX<1280) && (CounterY<720)*/ always @(posedge CLK)begin if((CounterX<1280) && (CounterY<720)) de_out <= 1'b1; else de_out <= 1'b0; end endmodule |
Verilogを書く上で参考になった記事
Verilogを書く上で非常に参考になった2記事を(英語ですが)下記に挙げておきます。
・解像度(クロック基準での1280x720の解像度設定が載っています)
・プログラム(クロック基準でのHSYNC,VSYNCの書き方が参考になりました)
カラーバーの色(RGB)変更
カラーバーのRGB設定に関しては簡単に変更可能です。
24bitのRGB箇所のデータを変更するだけです。例:24'h00ffff(シアン)→24'h0000ff(緑)
※今回のDVIのIPの画像データの並び方は「RGB」ではなく「RBG」のようです。
下記例のように上述したVerilogのファイルの「Color」箇所を変更すれば対応可能です。
1 2 3 4 5 6 7 8 9 10 11 |
/*Color*/ always @(posedge CLK)begin if((CounterX>=0) && (CounterX<320)) data_out <= 24'hffffff; else if ((CounterX>=320) && (CounterX<640)) data_out <= 24'h0000ff; else if ((CounterX>=640) && (CounterX<960)) data_out <= 24'h00ff00; else if ((CounterX>=960) && (CounterX<1280)) data_out <= 24'hff0000; end |
カラーバーの表示向き変更
カラーバーの表示向きを変更するのも簡単です。
下記例のように色の切り分け条件を水平基準から垂直基準に変更すればOKです。
(CouterX→CouterYに変更して、範囲を垂直方向の720を4分割にしています)
1 2 3 4 5 6 7 8 9 10 11 |
/*Color*/ always @(posedge CLK)begin if((CounterY>=0) && (CounterY<180)) data_out <= 24'hffffff; else if ((CounterY>=180) && (CounterY<360)) data_out <= 24'h0000ff; else if ((CounterY>=360) && (CounterY<540)) data_out <= 24'h00ff00; else if ((CounterY>=540) && (CounterY<720)) data_out <= 24'hff0000; end |
VerilogをIPコアと接続する
先ほど紹介したVerilogでFPGAのプログラムを作成します。
「Add Sources」を選択して真ん中の「Add or create design sources」を選択します。
今回は「Create File」→「Verilog」で選択してファイル名を決めます。
あとは先述したVerilogのプログラムをコピーして保存しときます。
その後はIPの「Diagram」の画面で右クリックして「Add Module」を選択します。
「OK」を押して進めていけば、VerilogのファイルがIPブロック状として出てきます。
Verilogの入出力ポートを各IPに接続できるようになりました。
FPGA設計を統合する
これまでの操作でIP、Verilogを使ってFPGAで画像出力できるブロック図が完成できました。
IPコア間を配線すると下記のような形になるはずです。
最終的にコンパイルできるように一度統合します。
「Sources」にあるIPブロックを右クリックして「Create HDL Wrapper」を選びます。
おそらく選択子が出てきますが「OK」を進めていけば大丈夫です。
「Sources」にあるVerilog、IPのファイルが無事統合されました。
FPGAでTMDSのピン設定を行う
最後に評価ボードで使うFPGAのピン配置を設定していきます。
入力100MHzのクロック、DVI出力のTMDS(HDMIコネクタ内の信号)のピンを選択します。
ピン配置に関しては各個人で使うFPGAの評価ボードの回路図を参照お願いします。
HDMIでAC結合
FPGAのピン設計には影響はないのですが、今回の評価ボードではAC結合されています。
HDMI(TMDS)出力前の各信号に0.1uFのコンデンサが直列接続されています。
AC結合を行うことで、外部のディスプレイと電圧レベルが異なっても通信することができます。
下記記事でHDMIの簡易的な信号シミュレーションも実施しています。
もしハード的な信号に興味ある方は一緒にご覧ください。(リンク先はこちら)
FPGAのピン設定の制約ファイル
FPGAで使うピンを制約ファイル(.xdc)で記述したのが下記となります。
(制約ファイルの書き方も色々あると思いますので、この記述もあくまで一例です。)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
#FPGA_100MHz_clk set_property IOSTANDARD LVCMOS33 [get_ports clk_100m] set_property PACKAGE_PIN H4 [get_ports clk_100m] #TMDS_clk set_property IOSTANDARD TMDS_33 [get_ports TMDS_clk_n] set_property PACKAGE_PIN F4 [get_ports TMDS_clk_n] set_property IOSTANDARD TMDS_33 [get_ports TMDS_clk_p] set_property PACKAGE_PIN G4 [get_ports TMDS_clk_p] #TMDS_data[0] set_property IOSTANDARD TMDS_33 [get_ports TMDS_data_n[0]] set_property PACKAGE_PIN F1 [get_ports TMDS_data_n[0]] set_property IOSTANDARD TMDS_33 [get_ports TMDS_data_p[0]] set_property PACKAGE_PIN G1 [get_ports TMDS_data_p[0]] #TMDS_data[1] set_property IOSTANDARD TMDS_33 [get_ports TMDS_data_n[1]] set_property PACKAGE_PIN D2 [get_ports TMDS_data_n[1]] set_property IOSTANDARD TMDS_33 [get_ports TMDS_data_p[1]] set_property PACKAGE_PIN E2 [get_ports TMDS_data_p[1]] #TMDS_data[2] set_property IOSTANDARD TMDS_33 [get_ports TMDS_data_n[2]] set_property PACKAGE_PIN C1 [get_ports TMDS_data_n[2]] set_property IOSTANDARD TMDS_33 [get_ports TMDS_data_p[2]] set_property PACKAGE_PIN D1 [get_ports TMDS_data_p[2]] |
制約ファイルをソースに追加する
「Add Sources」から「Add or create constrains」を選択します。
「Create File」を選択して、デフォルトの「XDC」でファイル名を決めればOKです。
作成後は先述した制約ファイルを参考に記述してもらえれば完成です。
コンパイルしてFPGAに書き込み、テストします
あとは「Generate Bitstream」でコンパイルして成功するか確認します。
コンパイルが上手くいきましたら、FPGAに書き込み実際にテストを行います。
コンパイル~FPGAの書き込み方法の詳細を確認したい方は下記を参考ください。
FPGAにプログラム書きこみ前
FPGAにプログラムを書きこむ前はFPGAから何も画像出力していません。
そのためディスプレイも何も出力されていない「No Signal」表示がされています。
FPGAにプログラム書きこみ後
FPGAにプログラム書きこみ後はディスプレイに1280x720のカラーバーが出力します。
無事FPGAから画像出力が出来ました。
FPGAのディスプレイ出力のテスト動画
冒頭でも紹介しましたが、実際のFPGAのテストを動画でもアップしてます。
よろしければ一緒にご覧ください。
まとめ
今回はFPGAの画像出力に関して紹介させていただきました。
記事をまとめますと下記になります。
FPGAを使えばハード的に画像処理が簡単に実施できます。
有名処のDiligent製のArty Z7でも、今回含めて下記記事で紹介しているボードでも可能です。
ぜひ皆さまもXilinxの評価ボードを購入してFPGAを始めてみて下さい。
コメント
RGB-to-DVI Video Encoder (Source)がこのサイトと違い、入力も出力も項目が少なく出来ません。
あと、「File」→「OPEN IP-XACT File」の後直接「Compatibility」に行けず、ファイルを選択する画面になります。
RGBの左にある+ボタン、TMDSの右にある+ボタンを押したら項目が出てきました。
コンパイルでエラーが起きます。
ブロックのhsync_outの部分がhsync_out[0:0]となっています。
また、timing.vを作成する際、input・outputにCLK・data_out[23:0]等を記述しないとAdd Moduleする際エラーが起きます。
このサイト通りにやった所、以下のエラー文が出てきます。
https://docs.google.com/document/d/1W0B7ZOdgz1Pwm2dP6JKWUJB5G04aoYr3qVkU3H83QO4/edit?usp=sharing
742.5MHzになっているらしいです。
どこを直したらいいでしょうか?
とても面白い記事でした!FPGAを使ったHDMIからの画像出力について学ぶことができました。フリーのIPを利用する方法も参考になりました。実際に試してみたいと思います!ありがとうございます!