#はじめに
1500円 ZYNQ 基板( EBAZ4205 )で、Digilent Zybo Z7-10 の HDMI デモを動かしてみます。
当初、Vivado/Vitis 2020.1 で 2018.2 版を試してみましたが、うまくいきませんでした。
原因を探したところ、治すよりも先に 2020.1 版が仮リリースされていました。
こちらでやっていきたいと思います。
2020.2 でも、そのまま使えるようです。
#使用環境
- Windows10 Pro (20H2)
- Vivado 2020.1 (Windows 版)
- Vitis 2020.1 (同上)
- EBAZ4205
- HDMI Source (信号発生器 ASTRODESIGN VG-870) - HDMI Sink の EDID が読めなくても、強制的に RGB で信号を出せるもの
- HDMI Monitor (波形モニタ Leader LV5382)
#参考資料
Zybo Z7 HDMI Input/Output Demo
github/Digilent/Zybo-Z7-10-HDMI
#1. 設計ファイルのダウンロード
https://github.com/Digilent/Zybo-Z7/releases/tag/10/HDMI/2020.1-1?_ga=2.211899860.1991324237.1620292101-43363897.1611581796
から、設計ファイルをダウンロードします。
Zybo-Z7-10-HDMI-2020.1-1-hw.xpr.zip
が、ハードウェア(Vivado)で、
Zybo-Z7-10-HDMI-2020.1-1-sw.ide.zip
が、アプリケーション(Vitis)です。
#2. ハードウェアの設定
ハードウェア設計ファイル Zybo-Z7-10-HDMI-2020.1-1-hw.xpr.zip を解凍すると、Vivado のプロジェクトが出てきます。
プロジェクトを EBAZ4205 に対応させるため、ハードウェア設定を変更します。
変更が必要になるのは、下記 3 箇所です。
- PS の外部 I/O ピン (ブロックデザインの ZYNQ7 Processing System 設定を変更)
- DDR メモリの型番、bit 幅、タイミング (同上)
- PL の HDMI I/O ピン (制約ファイル .xdc を入れ替え)
##1. PS の外部 I/O ピンの変更
design_1_i をダブルクリックして、Block Diagram を表示します。
真ん中列の下にある ZYNQ7 Processing System をダブルクリックして、おなじみの PS 設定を開きます。
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 のまま)
##2. DDR メモリの変更
同じく、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 の設定を終わります。
##3. PL の HDMI I/O ピンの変更
HDMI の入力と出力は、ZYNQ-PL の I/O ピンに直接割り当てて、EBAZ4205 の拡張コネクタ(DATA1, DATA2, DATA3)から引き出します。
PL の I/O ピン設定は、制約ファイル .xdc で指定します。
HDMI 出力については、以前の記事 1500円ZYNQ基板でFPGAプログラミング大全Xilinx編(第2版)の実習 2-3 HDMIでパターン表示 で先に実現していますので、そちらも参照してみてください。
今回は、内部制約の都合で HDMI 出力ピンを以前の記事から変えています。
制約ファイル EBAZ4205-Master.xdc を示します。
## This file is a general .xdc for the EBAZ4205
set_property CLOCK_DEDICATED_ROUTE BACKBONE [get_nets design_1_i/dvi2rgb_0/U0/TMDS_ClockingX/CLK_IN_hdmi_clk];
#HDMI RX
set_property -dict { PACKAGE_PIN H20 IOSTANDARD LVCMOS33 } [get_ports { hdmi_in_ddc_scl_io }]; #IO_L17N_T2_AD5N_35 Sch=hdmi_rx_scl
set_property -dict { PACKAGE_PIN J20 IOSTANDARD LVCMOS33 } [get_ports { hdmi_in_ddc_sda_io }]; #IO_L17P_T2_AD5P_35 Sch=hdmi_rx_sda
set_property -dict { PACKAGE_PIN K18 IOSTANDARD TMDS_33 } [get_ports { hdmi_in_clk_n }]; #IO_L12N_T1_MRCC_35 Sch=hdmi_rx_clk_n
set_property -dict { PACKAGE_PIN K17 IOSTANDARD TMDS_33 } [get_ports { hdmi_in_clk_p }]; #IO_L12P_T1_MRCC_35 Sch=hdmi_rx_clk_p
set_property -dict { PACKAGE_PIN U20 IOSTANDARD TMDS_33 } [get_ports { hdmi_in_data_n[0] }]; #IO_L15N_T2_DQS_34 Sch=hdmi_rx_n[0]
set_property -dict { PACKAGE_PIN T20 IOSTANDARD TMDS_33 } [get_ports { hdmi_in_data_p[0] }]; #IO_L15P_T2_DQS_34 Sch=hdmi_rx_p[0]
set_property -dict { PACKAGE_PIN P20 IOSTANDARD TMDS_33 } [get_ports { hdmi_in_data_n[1] }]; #IO_L14N_T2_SRCC_34 Sch=hdmi_rx_n[1]
set_property -dict { PACKAGE_PIN N20 IOSTANDARD TMDS_33 } [get_ports { hdmi_in_data_p[1] }]; #IO_L14P_T2_SRCC_34 Sch=hdmi_rx_p[1]
set_property -dict { PACKAGE_PIN P18 IOSTANDARD TMDS_33 } [get_ports { hdmi_in_data_n[2] }]; #IO_L23N_T3_34 Sch=hdmi_rx_n[2]
set_property -dict { PACKAGE_PIN N17 IOSTANDARD TMDS_33 } [get_ports { hdmi_in_data_p[2] }]; #IO_L23P_T3_34 Sch=hdmi_rx_p[2]
set_property -dict { PACKAGE_PIN J18 IOSTANDARD LVCMOS33 } [get_ports { hdmi_in_hpd_tri_o }]; #IO_L14P_T2_AD4P_SRCC_35 Sch=hdmi_rx_hpd
#HDMI TX
set_property -dict { PACKAGE_PIN F20 IOSTANDARD TMDS_33 } [get_ports { hdmi_out_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_out_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_out_data_n[0] }]; #IO_L4N_T0_35 Sch=hdmi_tx_n[0]
set_property -dict { PACKAGE_PIN D19 IOSTANDARD TMDS_33 } [get_ports { hdmi_out_data_p[0] }]; #IO_L4P_T0_35 Sch=hdmi_tx_p[0]
set_property -dict { PACKAGE_PIN B20 IOSTANDARD TMDS_33 } [get_ports { hdmi_out_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_out_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_out_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_out_data_p[2] }]; #IO_L2P_T0_AD8P_35 Sch=hdmi_tx_p[2]
set_property -dict { PACKAGE_PIN H18 IOSTANDARD LVCMOS33 } [get_ports { hdmi_out_ddc_scl_io }]; # Sch=hdmi_tx_scl
set_property -dict { PACKAGE_PIN E19 IOSTANDARD LVCMOS33 } [get_ports { hdmi_out_ddc_sda_io }]; # Sch=hdmi_tx_sda
##4. コンパイルとハードウェア情報のエクスポート
準備ができたら、Generate Bitstream で一式コンパイルして、Export Hardware でハードウェア情報(design_1_wrapper.xsa)を出力します。
これで回路は完成しました。
##5. HDMI 入力の差動ピン配置について
HDMI 入力は、最初に適当な差動ピンに割り当ててコンパイルしてみたところ、配置できずにエラーとなりました。
これは、XC7Z010 の内部リソースの配置による制限でした。
HDMI のクロック信号処理には、CMT(Clock Managemnt Tiles) が使われます。
XC7Z010 には、CMT が 2 つ搭載されています。
CMT には MMCM と PLL が搭載されていて、どちらかの機能を使います。
HDMI 入力(DVI2RGB)と、HDMI 出力(RGB2DVI)で、MMCM が 1 つづつ使用されます。
デモ回路をコンパイルしてみると、
上側 CMT の MMCM 11 に HDMI 出力の機能が、
下側 CMT の MMCM 10 に HDMI 入力の機能が、
それぞれ割り当てられます。
CMT には、すぐ隣のファブリックしか駆動できない制約があります。
関連する I/O ピン(HDMI のクロック以外の信号ピン 3 対)も、そのファブリックに接続できるピンに割り当てないといけません。
つまり、
上側のファブリックから接続可能な BANK 35 の差動 I/O ピンに HDMI OUT を、
下側のファブリックから接続可能な BANK 34 の差動 I/O ピンに HDMI IN を、
それぞれ割り当てる必要があります。
EBAZ4205 の DATA1,DATA2,DATA3 に出ている I/O ピンは、下記の通りです。
全体の 2/3 が BANK 35、1/3 が BANK 34 から接続されています。
差動 I/O ペアとして使えるピンを寄せてみると、このようになります。
BANK 35 側で 14 対、BANK 34 側で 3 対の差動ペア信号(対)が使用できます。
ただし、BANK 35 側の 14 対のうち、4 対はコネクタを跨いで配置されていて、差動で使うには少し都合が悪いです。
高速差動信号は、対の信号線間の距離を、できるだけ離さずに配線しないといけません。
HDMI 信号は、クロック 1対 + 信号 3対の、合計 4 対の差動信号線で構成されています。
BANK 35 側は、HDMI OUT の 4 対がそのまま出せますが、
BANK 34 側は、3 対しかないので、HDMI IN には 1 対不足します。
そこで、HDMI IN 側のクロックだけ、BANK 35 側の対を借りて接続します。
実は、クロック信号入力は、クロック専用入力の MRCC ピン(対)へ入れれば、上下 BANK を跨いだ MMCM に接続できます。
このおかげで、I/O ピンの限られている EBAZ4205 に、HDMI IN と HDMI OUT の両方を実装することができました。
Vivado で何度かコンパイルして、配置状態を見ながら決定した HDMI IN/OUT の信号割り当ては、下のようになりました。
#3. HDMI 入出力コネクタの作成
前回と同様、aitendo の HDMI コネクタ with 基板 と、
★2.0★2列ピンソケット [PS20DV] 2x10 を使って作成します。
最初はストレートで作成したのですが、640x480/60p までしか映りませんでした。
差動ペア線の片側の線を外して、+-をツイストしたところ、多少不安定ながらも 1080/60p まで映るようになりました。
#4. ダンピング抵抗の 22Ω → 0Ω 交換と、TMDS 終端抵抗の追加
以前の記事 でも書きましたが、ZYNQ の I/O ピンを TMDS_33 に設定して、直接 HDMI の TMDS 信号を入出力します。
TMDS_33 で使用するため、DATA1~DATA3 コネクタへの途中にある 22Ω のダンピング抵抗を 0Ωに交換します。
今回は、使う信号線だけ交換するのではなく、まとめて全部 0Ω に交換してしまいます。
また、TMDS_33 の受信側には、正負それぞれ 50Ω で 3.3V への終端抵抗を設置します。
これは HDMI IN の信号線のみに設置するので、抵抗の番号を調べています。
作業後の基板はこのようになりました。
U23 の端子に 3.3V が来ていたので、そこから 50Ω を吊っています。
あと、DATA1 コネクタから接続する HDMI OUT の +5V Power 端子に電源を出力するため、逆流防止ダイオード D21 にジャンパを追加します。
#5. ソフトウェアの設定
アプリケーション Zybo-Z7-10-HDMI-2020.1-1-sw.ide.zip の中から、Zybo-Z7-10-HDMI フォルダだけを、デスクトップに解凍しておきます。
このフォルダに、design_1_wrapper.xsa をコピーします。
Vitis を起動したら、File - Switch Workspace... - Other... で Zybo-Z7-10-HDMI フォルダをワークスペースに指定して、Launch します。
Create Platform Project で、EBAZ-HDMI としてプロジェクトを作成し、design_1_wrapper.xsa を指定して Finish。
次に File - New - Application Project... で EBAZ-HDMI を platform に選択し、EBAZ-HDMI-app という名前で Next。
standalone_domain で Next して、Empty Applicaion (C++じゃない方)を選び Finish。
EBAZ-HDMI-app_system の src フォルダで右クリックして、Inport Sources... を選びます。
Import Sources で、src と Overwrite existing resources without warning にチェックを入れて Finish。
ビルドが終わったら、準備完了です。
#6. 動作テスト
基板に電源を入れて、Vivado/Vitis から認識できるようにしておきます。
TeraTerm を起動してから、デバッグに入ります。
Debug Configuraion で、Single Applicaion Debug をダブルクリックして、
開いた画面で Debug ボタンを押して、デバッグを起動します。
Resume を押す前に、HDMI 信号発生器からテスト信号を出して、基板へ入力しておきます。
HDMI 入力に信号が入っていないと、アプリケーションがエラーを出して停止します。
(これで半日も悩んだ)
Resume を押すと、TeraTerm にデモのメニュー画面が表示され、動き出します。
操作は、数字キーを入力します。
- 出力解像度の変更
- 表示するフレームバッファ番号の変更 (1,2,3 から選択)
- 表示中のフレームバッファにグラデーションパターンを描画
- 表示中のフレームバッファにカラーバーを描画
- HDMI IN 入力信号のフレームバッファへの書込みの START/STOP (トグルする)
- HDMI IN から書き込むフレームバッファ番号の変更 (1,2,3 から選択)
- HDMI IN から 1 フレームをフレームバッファに取り込み、色を反転
- HDMI IN から 1 フレームを、フレームバッファの表示サイズにリサイズして取り込み
- 終了
HDMI 出力解像度は、下記から選択できます。
1 - 640x480@60Hz
2 - 800x600@60Hz
3 - 1280x720@60Hz
4 - 1280x1024@60Hz
5 - 1600x900@60Hz
6 - 1920x1080@60Hz
q - Quit (don't change resolution)
HDMI 入力解像度は、自動判別します。
1440x288/50p とかの普通使われない解像度も受け付けてくれます。
フレームレートも、50/60 以外でも受け付けてくれます。
720x480/240p も映りました。
(出力側は 60p しか出さないので、1 枚出すまでに 3 回書き換えてる?)
例では、1920x1080/60p 出力のグラデーションに、1280x720/60p の HDMI 入力を取り込んで重ねています。
HDMI IN は、連続的にフレームバッファに取り込めるので、動画で PinP として映ります。
#7. デバッグ用エラー表示の追加
何らかの原因で、プログラムがハングしてしまうことがあります。
何も表示しないで固まってしまうので、調べるのが大変です。
このような時は、プラットフォームプロジェクトの、
/ps7_cortexa9_0/standalone_domain/bsp/ps7_cortexa9_0/libsrc/standalone_v7_2/xdebug.h に、
#define DEBUG
#undef NDEBUG
ソース中に xdbg_printf( ) で埋め込まれたデバッグメッセージが表示されるようになり、原因推定に役立ちます。
下記は HDMI IN に信号が入っていない時に、停止して表示されるエラーです。
理由は分かりませんが、HDMI IN に信号(おそらくクロック)が入っていないと、エラーが出て止まります。
#まとめ
EBAZ4205 で、Zybo Z7 HDMI Input/Output Demo を動かすことができました。