(2021.5.21 更新)
8-2. Digilent 製 IP のアップグレード の項目を追加しました。
これをやらないと、エクスポートした .xsa に含まれるドライバの makefile が古い物になり、プロットフォームプロジェクトのビルドでエラーになります。
EBAZ4205_CSI-2_RX.xdc も更新。
(SCL/SDA が逆だったので入れ替え、MIPI も一部修正)
#1. はじめに
1500円 ZYNQ 基板( EBAZ4205 )で、Digilent Zybo Z7-20 の Pcam 5C デモを動かしてみます。
このデモでは、CMOS カメラから映像を入力し、HDMI 出力してモニタに映すことができます。
前回投稿した Zybo Z7 HDMI Demo と違って信号発生器が不要で、実施しやすいデモになっています。
カメラは、手持ちの Raspberry Pi Camera Module V2 を使用します。
記述が長くなるため、記事を前編と後編に分けています。
前編: Vivado での作業
後編: 接続ケーブルの作成と、Vitis での動作確認
前編の今回は、Vivado を使ってプロジェクトを合成し、エクスポートするまでの作業を行います。
#2. 使用環境
- Windows10 Pro (20H2)
- Vivado 2020.1 (Windows 版)
- Vitis 2020.1 (同上)
- EBAZ4205
- Raspberry Pi Camera Module V2 (SONY IMX219)
- HDMI Monitor (Leader LV5382)
#3. 参考資料
Zybo Z7 Pcam 5C Demo
OV5640 datasheet
IMX219 datasheet
XAPP894 D-PHY Solutions
UG471 7Series FPGAs SelectIO Resources User Guide
#4. Zybo Pcam 5C デモと使用カメラについて
Pcam 5C プロジェクトは、もともと Zybo Z7-20 (XC7Z020) と Pcam 5C の組み合わせ用に作成されています。
EBAZ4205 はそれより小さい XC7Z010 を搭載していて、PL のロジック規模は XC7Z020 の 1/3 しかありません。
容量的に無謀かと思いましたが、FPGA で CSI-2 受信は、個人的に画像処理プロセスの大きなテーマです。
試しにプロジェクトを開いてみると、リソース使用量は非常に少ないことがわかりました。
試しにコンパイルしてみると、XC7Z010 でもそのまま移植できそうなことがわかりました。
本家の Zybo Z7-10 も、Pcam コネクタは搭載されていますので、ターゲットデバイス変更で動くのではないでしょうか。
デモで使用するカメラは、Digilent Pcam 5Cが指定されています。
結構いいお値段です。
ここは、手持ちのカメラ Raspberry Pi Camera Module V2 を使用してみることにします。
2つの製品で、搭載カメラモジュールに、
Pcam 5C : Ominivision OV5640 (2592 x 1944, 5M pixel)
PiCam V2 : SONY IMX219 (3280 x 2464, 8M pixel)
の違いがあります。
PiCam V2 の方が画素数が多く、裏面照射型の高感度センサで画質が良いのに、安価に入手できます。
ただし、Pcam 5C の代わりに使用するうえで、注意しないといけない点がいくつかあります。
##4-1. 相違点1: カメラ内部設定
基板から出力されるコネクタのピン配置と、設定を I2C で行うところは同じですが、設定内容には互換性がありません。
IMX219 用のカメラ設定を、新規に作成します。
##4-2. 相違点2: ベイヤー配列の色順
OV5640 と IMX219 ではベイヤー配列の画素の配置が違っています。
ベイヤー配列では、イメージセンサの田の字の単位画素の中に、緑が2つ、赤と青がそれぞれ一つづつ配置されています。
OV5640 と IMX219 を比較してみると、緑の位置は同じですが、赤と青の位置が逆になっています。
OV5640 用のベイヤーから RGB に変換する IP をそのまま使うと、赤と青のデータが入れ替わって色がおかしくなってしまいます。
Digilent 社の Bayer2RGB の IP を修正して対応します。
##4-3. CSI-2 の信号速度
カメラ接続で使われる MIPI CSI-2 規格は、映像を高速シリアル差動信号で伝送します。
IMX219 は OV5640 より画素数が多い分、信号の周波数が高くなります。
IMX219 の出力は、最大 912 Mbps/レーン (2レーン接続時)とされています。
送出速度は解像度とフレームレートで決まりますので、適切な設定であれば、ZYNQ の通常の I/O ピンでも受信することができます。
##4-4. その他の注意点
プログラムは、最小限の改造で、とりあえず動作確認ができるところまでを目指します。
実機で確認はしていませんが、本家 Zybo Z7 で Raspberry Pi Camera Module V2 を使う時も、このプログラムが流用できる筈です。
ZYNQ の I2C からカメラの設定を行うだけなので、Zybo Z7 でもプログラムは変わりません。
#5. CSI-2 入力時の I/O ピン規格について
Xapp894 の Figure 11 に、7 シリーズの HR I/O ピンを使って MIPI D-PHY (CSI-2 or DSI) 入力を実装する例が示されています。
MIPI の HS 信号を LVDS_25 規格の差動入力で、
LP 信号を HSUL_12 規格のシングルエンド入力で、
通常、LVDS_25 規格を使用するとき、その BANK の I/O 電源(VCCO)は 2.5V を供給します。
(UG471 Table 1-55)
Zybo Z7 基板では、カメラコネクタの CSI-2 入力信号は、すべて ZNYQ の BANK_35 の I/O ピンに接続されています。
LVDS_25 規格で使う場合、VCCO_35 = 3.3V が供給されているのはまずいような感じがします。
しかし、UG471 Table 1-55 の注記: 1 で、下記の記述があります。
- これらの規格の差動入力は、出力の要求レベルと異なる VCCO レベルのバンクに配置できます。この場合に考慮すべき注意事項を次に示します。
a. VCCO 電圧が出力で要求されるレベルでない限り、オプションの内部差動終端は使用されない(デフォルトで DIFF_TERM = FALSE)
b. 入力ピンの差動信号は、各デバイスファミリのデータシートに記載されている推奨動作条件を示す表の VIN 要件を満たしている。
c. 入力ピンの差動信号は、各デバイスファミリのデータシートに記載されている、 対応する LVDS または LVDS_25 DC 仕様の表にある VIDIFF および VICM の要件を満たしている。
MIPI D-PHY 信号では a.~c. の条件を満たすので、VCCO = 3.3V を供給しつつ LVDS_25 設定した HR I/O に直結できるようです。
HSUL_12 規格の入力は、VREF に内蔵 0.6V を指定すれば、VCCO 供給電圧は標準範囲内(1.8~3.3V)であれば OK です。
(前掲 UG471 Table 1-55 に ANY 記載あり)
forum 参考情報:
https://forums.xilinx.com/t5/Video-and-Audio/Can-one-use-HSUL-12-S-HR-with-Artix-7-board/td-p/1190393
ということで、EBAZ4205 (VCCO_34, VCCO_35 どちらも 3.3V 供給)でも、カメラの CSI-2 信号を直結して受信することができます。
#6. 設計ファイルのダウンロード
から、Vivado 2019.1 の設計ファイル Zybo-Z7-20-pcam-5c-2019.1.zip をダウンロードします。
zip を展開後、
Zybo-Z7-20-pcam-5c-2019.1
が、ハードウェア(Vivado)の設計ファイルのフォルダで、
sdk_appsrc
が、アプリケーション(Vitis)のフォルダです。
例では、デスクトップに ts というフォルダを作成して、その中に zip を展開しておきます。
#7. Vivado プロジェクトの準備
ハードウェアフォルダの中にある、Zybo-Z7-20-pcam-5c.xpr を Vivado 2020.1 で開きます。
プロジェクトを保存した Vivado とバージョンが違うため、アップグレードするかを聞かれます。
Automatically upgrade to the current version を選択して OK します。
少し待つと、IP のアップデートを聞かれるので、ここでは Ignore を選びます。
##7-1. ターゲットデバイスの変更
Flow Navigator から、PROJECT MANAGER - Settings をクリックして、プロジェクト設定を開きます。
General の下に Project device: の設定があるので、デバイスを
##7-2. IP のアップグレード
Sources タブの system_i をダブルクリックして、Block Diagram を表示します。
IP のアップグレードを要求されますので、Report IP Status を押します。
Report が表示されたら、そのまま Upgrade Selected を押して、IP をアップグレードします。
IP のアップグレードが終わると、合成するか聞かれますが、Skip します。
バージョン違いによるエラー回避のため、デザインを一度 tcl スクリプトで出力して、再生成してから合成を行いますので、ここは Skip でかまいません。
##7-3. PS の外部 I/O ピン変更
右側にある ZYNQ7 Processing System をダブルクリックして、おなじみの PS 設定を開きます。
PS の変更が必要になるのは、下記 3 箇所です。
- PS の外部 I/O ピン (ブロックデザインの ZYNQ7 Processing System 設定を変更)
- DDR メモリの bit 幅、サイズ (同上)
- PL の I/O ピン (制約ファイル .xdc を入れ替え。最後にやる)
Peripheral I/O Pins を選択して、いつもの GUI で MIO 割り当てピンを変更します。
・Quad SPI Flash から NAND Flash に変更
・Ethernet 0 を EMIO に変更
同、MDIO も EMIO に変更
・USB 0 のチェックを外す
・SD0 を 40-45 ピンに移動
同、Card Detect を 34 ピンに移動
・UART1 を 24/25pin に移動
・I2C0 は EMIO のまま
・GPIO EMIO はチェックしてあっても構いません
##7-4. DDR メモリの bit 幅とサイズの変更
同じく、PS 設定の DDR Configuration で、DDR メモリ設定を変更します。
・Memory Part: MT41K128M16JT-125
・Effective DRAM Bus Width: 16 Bit
DQS to Clock Delay と Board Delay は、Zybo Z7 基板の固有値が入っています。
EBAZ4205 の固有値は不明なため、PS 設定の初期値に戻しておきます。
(0.0 と 0.25 が 4 つづつ)
終わったら、OK を押して PS の設定を終わります。
IP Status が Rerun を求めて来ますが、無視して進めます。
##7-5. tcl スクリプト書き出し
バージョンの違う Vivado で作成されたプロジェクトを、そのままコンパイルすると、何かとエラーが出てきます。
主に IP 間接続のクロック周波数設定でエラーが出るのですが、これを解決するのは難しいです。
一旦デザインを tcl スクリプトで吐き出してから、新しいバージョンのプロジェクトとして再生成させます。
メニューバーから、File - Project - Write Tcl...
を選択します。
Write all properties、Copy sources to new project、Recreate Block Designs using Tcl にチェックを入れます。
出力ディレクトリには、元の設計ファイルの展開先フォルダ(例では デスクトップ\ts)を指定して OK を押します。
tcl 書き出しが終わったら、File - Close Project を選んで、プロジェクトを閉じます。
このプロジェクトは Don't Save で閉じて構いません。
#8. Vivado プロジェクトの再生成
.tcl スクリプトからプロジェクトを再生成しますが、その前に IP の変更を行います。
##8-1. BayerToRGB の変更
前述の CMOS センサのベイヤー配列の違いを吸収するため、ベイヤーから RGB に画素変換する IP を修正します。
適当なテキストエディタで、プロジェクトの中にある AXI_BayerToRGB.vhd を開きます。
最後にある AXI 出力データの記述で、Blue と Red を入れ替えます。
(設計ファイルを展開したフォルダ)\Zybo-Z7-20-pcam-5c-2019.1\Zybo-Z7-20-pcam-5c-2019.1\vivado_proj\Zybo-Z7-20-pcam-5c.ipdefs\repo_0\local\ip\AXI_BayerToRGB\hdl\AXI_BayerToRGB.vhd
(変更前)
-- Assign AXI stream output interface signals.
(略)
m_axis_video_tdata <= "00" & std_logic_vector(sAXIMasterRed) &
std_logic_vector(sAXIMasterBlue) &
std_logic_vector(sAXIMasterGreen(kBayerWidth downto 1));(変更後)
m_axis_video_tdata <= "00" & std_logic_vector(sAXIMasterBlue) &
std_logic_vector(sAXIMasterRed) &
std_logic_vector(sAXIMasterGreen(kBayerWidth downto 1));
##8-2. Digilent 製 IP のアップグレード
MIPI_CSI_2_RX
MIPI_D_PHY_RX
rgb2dvi
の3つのIPを最新版に差し替えます。
最新版を、github からフォルダごと clone します。
\Zybo-Z7-20-pcam-5c-2019.1\vivado_proj\Zybo-Z7-20-pcam-5c.ipdefs\repo_0\vivado-library\ip
に、プロジェクトで使用している IP のフォルダがあります。
##8-3. tcl 読み込みでプロジェクト再生成
Vivado のオープニング画面の下に、Tcl Console があります。
Type a Tcl commmand here のところにコマンドを打ち込んでいきます。
・カレントフォルダを、先ほど tcl を書きだしたフォルダに移動します。
・source コマンドで、tcl ファイルを指定して読み込みます
.tcl の書き出しフォルダの場所が悪いと、絶対パスで指定された一部のファイルが見つからずにエラーで失敗します。
.tcl を正しい場所に置いて、再度実行してください。
例では、デスクトップに ts というフォルダを作成して、そこに設計ファイルを展開しています。
##8-4. AXI アドレスの割り当て
Sources ペインの sysytem_i をダブルクリックして、ブロックデザインを開きます。
Address Editor のタブを開くと、下2つの IP のアドレスが Unassigned になっていますので、アドレスを割り当てます。
/video_dynclk
/vtg
の順に、それぞれ右クリックして、Assign を選びます。
##8-5. CSI-2 RX のピン配置の指定
PL の I/O ピン設定を、制約ファイル .xdc で指定します。
HDMI 出力ピンの割り当ては、前回の記事 1500円ZYNQ基板でZyboのHDMIデモを動かす で決めた配置を、そのまま流用します。
CSI-2 入力ピンは、新たに ZYNQ-PL の I/O ピンに割り当てて、EBAZ4205 の DATA2 コネクタから引き出します。
各入力ピンの割り当ては、何度かコンパイルして最適なところを選択しました。
##8-6. 制約ファイルの登録
ピン配置表から、制約ファイル EBAZ4205_CSI-2_RX.xdc を作成しました。
#EBAZ4205 HDMI TX
set_property -dict { PACKAGE_PIN F20 IOSTANDARD TMDS_33 } [get_ports { hdmi_tx_clk_n }]; #IO_L15N_T2_DQS_AD12N_35 Sch=hdmi_tx_clk_n
set_property -dict { PACKAGE_PIN F19 IOSTANDARD TMDS_33 } [get_ports { hdmi_tx_clk_p }]; #IO_L15P_T2_DQS_AD12P_35 Sch=hdmi_tx_clk_p
set_property -dict { PACKAGE_PIN D20 IOSTANDARD TMDS_33 } [get_ports { hdmi_tx_data_n[0] }]; #IO_L4N_T0_35 Sch=hdmi_tx_n[0]
set_property -dict { PACKAGE_PIN D19 IOSTANDARD TMDS_33 } [get_ports { hdmi_tx_data_p[0] }]; #IO_L4P_T0_35 Sch=hdmi_tx_p[0]
set_property -dict { PACKAGE_PIN B20 IOSTANDARD TMDS_33 } [get_ports { hdmi_tx_data_n[1] }]; #IO_L1N_T0_AD0N_35 Sch=hdmi_tx_n[1]
set_property -dict { PACKAGE_PIN C20 IOSTANDARD TMDS_33 } [get_ports { hdmi_tx_data_p[1] }]; #IO_L1P_T0_AD0P_35 Sch=hdmi_tx_p[1]
set_property -dict { PACKAGE_PIN A20 IOSTANDARD TMDS_33 } [get_ports { hdmi_tx_data_n[2] }]; #IO_L2N_T0_AD8N_35 Sch=hdmi_tx_n[2]
set_property -dict { PACKAGE_PIN B19 IOSTANDARD TMDS_33 } [get_ports { hdmi_tx_data_p[2] }]; #IO_L2P_T0_AD8P_35 Sch=hdmi_tx_p[2]
#set_property -dict { PACKAGE_PIN H18 IOSTANDARD LVCMOS33 } [get_ports { hdmi_tx_scl }]; # Sch=hdmi_tx_scl
#set_property -dict { PACKAGE_PIN E19 IOSTANDARD LVCMOS33 } [get_ports { hdmi_tx_sda }]; # Sch=hdmi_tx_sda
#EBAZ4205 MIPI (BANK35 は VCCO=3.3V でいい)
set_property PACKAGE_PIN K17 [get_ports {cam_gpio_tri_io[0]}]; # IO_L12P_T1_MRCC_35
set_property IOSTANDARD LVCMOS33 [get_ports {cam_gpio_tri_io[0]}]; #
set_property PULLUP true [get_ports {cam_gpio_tri_io[0]}]; #
set_property -dict {PACKAGE_PIN G20 IOSTANDARD LVCMOS33} [get_ports cam_iic_scl_io]; # IO_L18N_T2_AD13N_35
set_property -dict {PACKAGE_PIN G19 IOSTANDARD LVCMOS33} [get_ports cam_iic_sda_io]; # IO_L18P_T2_AD13P_35
set_property INTERNAL_VREF 0.6 [get_iobanks 35]; #
set_property -dict {PACKAGE_PIN M20 IOSTANDARD HSUL_12} [get_ports dphy_clk_lp_n]; #IO_L7N_T1_AD2N_35
set_property -dict {PACKAGE_PIN K18 IOSTANDARD HSUL_12} [get_ports dphy_clk_lp_p]; #IO_L12N_T1_MRCC_35
set_property -dict {PACKAGE_PIN M18 IOSTANDARD HSUL_12} [get_ports {dphy_data_lp_n[0]}]; #IO_L8N_T1_AD10N_35
set_property -dict {PACKAGE_PIN J20 IOSTANDARD HSUL_12} [get_ports {dphy_data_lp_p[0]}]; #IO_L17P_T2_AD5P_35
set_property -dict {PACKAGE_PIN J18 IOSTANDARD HSUL_12} [get_ports {dphy_data_lp_n[1]}]; #IO_L14P_T2_AD4P_SRCC_35
set_property -dict {PACKAGE_PIN H20 IOSTANDARD HSUL_12} [get_ports {dphy_data_lp_p[1]}]; #IO_L17N_T2_AD5N_35
set_property -dict {PACKAGE_PIN L17 IOSTANDARD LVDS_25} [get_ports dphy_hs_clock_clk_n]; #IO_L11N_T1_SRCC_35
set_property -dict {PACKAGE_PIN L16 IOSTANDARD LVDS_25} [get_ports dphy_hs_clock_clk_p]; #IO_L11P_T1_SRCC_35
set_property -dict {PACKAGE_PIN L20 IOSTANDARD LVDS_25} [get_ports {dphy_data_hs_n[0]}]; #IO_L9N_T1_DQS_AD3N_35
set_property -dict {PACKAGE_PIN L19 IOSTANDARD LVDS_25} [get_ports {dphy_data_hs_p[0]}]; #IO_L9P_T1_DQS_AD3P_35
set_property -dict {PACKAGE_PIN J19 IOSTANDARD LVDS_25} [get_ports {dphy_data_hs_n[1]}]; #IO_L10N_T1_AD11N_35
set_property -dict {PACKAGE_PIN K19 IOSTANDARD LVDS_25} [get_ports {dphy_data_hs_p[1]}]; #IO_L10P_T1_AD11P_35
これを、Vivado に制約ファイルとして登録します。
プロジェクトには、制約ファイルが 3 つ登録されています。
このうち、外部 I/O ピンの制約は ZyboZ7_A.xdc で決められています。
ZyboZ7_A.xdc の登録を解除して、代わりに EBAZ4205_CSI-2_RX.xdc を登録しておきます。
##8-7. 合成とハードウェア情報のエクスポート
準備ができたら、Generate Bitstream で合成します。
合成が完了したら、File - Export - Export Hardware で Include bitstream としてハードウェア情報(system_wrapper.xsa)を出力します。
これで回路は完成しました。
ここから先は、後編の Vitis での作業になります。
#9. 前編のまとめ
EBAZ4205 で、Zybo Z7 Pcam 5C Demo を実行する準備ができました。
後編では、CSI-2 入力 + HDMI 出力ケーブルを作成し、Vitis を使って実際に動作させてみます。