1500円ZYNQ基板でZyboのHDMIデモを動かす

#はじめに
1500円 ZYNQ 基板( EBAZ4205 )で、Digilent Zybo Z7-10 の HDMI デモを動かしてみます。

当初、Vivado/Vitis 2020.1 で 2018.2 版を試してみましたが、うまくいきませんでした。

原因を探したところ、治すよりも先に 2020.1 版が仮リリースされていました。
こちらでやっていきたいと思います。
2020.2 でも、そのまま使えるようです。

[Zybo Z7 HDMI Demo (2020.1)](https://reference.digilentinc.com/reference/programmable-logic/zybo-z7/demos/hdmi)

#使用環境

1. Windows10 Pro (20H2)
2. Vivado 2020.1 (Windows 版)
3. Vitis 2020.1 (同上)
4. EBAZ4205
5. HDMI Source (信号発生器 ASTRODESIGN VG-870) - HDMI Sink の EDID が読めなくても、強制的に RGB で信号を出せるもの
6. HDMI Monitor (波形モニタ Leader LV5382)

#参考資料
[Zybo Z7 HDMI Input/Output Demo](https://reference.digilentinc.com/reference/programmable-logic/zybo-z7/demos/hdmi)
[github/Digilent/Zybo-Z7-10-HDMI](https://github.com/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 のプロジェクトが出てきます。

hw.xpr を Vivado で開きます。
![10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/955220/8e1a58df-4d5a-5166-0e38-4b8c17aeeac2.png)


プロジェクトを EBAZ4205 に対応させるため、ハードウェア設定を変更します。

変更が必要になるのは、下記 3 箇所です。

1. PS の外部 I/O ピン (ブロックデザインの ZYNQ7 Processing System 設定を変更)
2. DDR メモリの型番、bit 幅、タイミング (同上)
3. PL の HDMI I/O ピン (制約ファイル .xdc を入れ替え)

##1. PS の外部 I/O ピンの変更
design_1_i をダブルクリックして、Block Diagram を表示します。
![11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/955220/a4589a96-ba60-c6f0-8dda-2f2aaa86616b.png)

真ん中列の下にある ZYNQ7 Processing System をダブルクリックして、おなじみの PS 設定を開きます。
![12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/955220/862ee07a-730a-87de-4094-7868060957a4.png)

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 のまま)

![13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/955220/7632d019-61e4-ff9f-01a4-b4c09c95810c.png)


##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 つづつ)
![14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/955220/992732f3-4592-a943-77a3-ede4688fedd8.png)

終わったら、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でパターン表示](https://qiita.com/kan573/items/44ced51037d221a4d171) で先に実現していますので、そちらも参照してみてください。

今回は、内部制約の都合で HDMI 出力ピンを以前の記事から変えています。

制約ファイル EBAZ4205-Master.xdc を示します。

```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 入力の機能が、
それぞれ割り当てられます。

(UG865 図1-1)
![15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/955220/80a7552c-9e90-a3c1-d05c-4068777262ef.png)


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 から接続されています。
![16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/955220/25426175-4b39-286b-4658-a26639135486.png)


差動 I/O ペアとして使えるピンを寄せてみると、このようになります。
BANK 35 側で 14 対、BANK 34 側で 3 対の差動ペア信号(対)が使用できます。
![17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/955220/620071a0-cc81-6921-474b-e9c918061559.png)


ただし、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 の信号割り当ては、下のようになりました。
![18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/955220/2095fa88-990d-b941-29f0-9ac1077dc169.png)


#3. HDMI 入出力コネクタの作成
前回と同様、aitendo の [HDMI コネクタ with 基板](https://www.aitendo.com/product/6363) と、
![19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/955220/36ef14e4-e1e5-ed29-cd81-f456a0b509bb.png)


[★2.0★2列ピンソケット [PS20DV] 2x10](https://www.aitendo.com/product/17637) を使って作成します。
![20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/955220/2c694e4b-e2d7-4077-a11b-2c45f7b7ca1a.png)


ピン配置結果を元に、配線図を作成しました。
![21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/955220/f0aff532-58c1-b702-95be-8e139931a157.png)


最初はストレートで作成したのですが、640x480/60p までしか映りませんでした。
![22.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/955220/55d157ef-9aae-33ef-d6e9-8f360f1d806e.jpeg)


差動ペア線の片側の線を外して、＋－をツイストしたところ、多少不安定ながらも 1080/60p まで映るようになりました。
![23.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/955220/6173c3d1-8d8c-d792-aedc-f999ff11b8bb.jpeg)


#4. ダンピング抵抗の 22Ω → 0Ω 交換と、TMDS 終端抵抗の追加
[以前の記事](https://qiita.com/kan573/items/44ced51037d221a4d171) でも書きましたが、ZYNQ の I/O ピンを TMDS_33 に設定して、直接 HDMI の TMDS 信号を入出力します。
![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/955220/00ee8164-55b1-9efe-e526-cd7b0ce72c58.png)


TMDS_33 で使用するため、DATA1～DATA3 コネクタへの途中にある 22Ω のダンピング抵抗を 0Ωに交換します。
今回は、使う信号線だけ交換するのではなく、まとめて全部 0Ω に交換してしまいます。

また、TMDS_33 の受信側には、正負それぞれ 50Ω で 3.3V への終端抵抗を設置します。
これは HDMI IN の信号線のみに設置するので、抵抗の番号を調べています。
![24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/955220/f0b41ce6-c114-372f-1321-67c17a68a117.png)


作業後の基板はこのようになりました。
U23 の端子に 3.3V が来ていたので、そこから 50Ω を吊っています。
![6.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/955220/13d849ca-890d-bfee-809c-a9f8bc04f3d8.jpeg)


あと、DATA1 コネクタから接続する HDMI OUT の +5V Power 端子に電源を出力するため、逆流防止ダイオード D21 にジャンパを追加します。
![25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/955220/5dadeec4-9666-2488-1def-2f3b2e6a7552.png)

![26.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/955220/0f08fc08-44b5-ba0c-69ca-ed68a272294c.jpeg)



#5. ソフトウェアの設定
アプリケーション Zybo-Z7-10-HDMI-2020.1-1-sw.ide.zip の中から、Zybo-Z7-10-HDMI フォルダだけを、デスクトップに解凍しておきます。
このフォルダに、design_1_wrapper.xsa をコピーします。
![30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/955220/39814902-0b50-0c64-22e5-14ddf7f30d67.png)


Vitis を起動したら、File - Switch Workspace... - Other... で Zybo-Z7-10-HDMI フォルダをワークスペースに指定して、Launch します。
![31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/955220/c156b962-cf2c-81a4-e417-84d2f8639a64.png)


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... を選びます。
![32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/955220/79061b33-1f3f-0529-553e-fa961bef78c3.png)
 

Import Sources で、src と Overwrite existing resources without warning にチェックを入れて Finish。
![33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/955220/996e5614-fe18-ef24-8216-4f18f08ec966.png)


これで、アプリケーションプロジェクトをビルドします。
![34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/955220/41ce58f6-c8f0-48e4-d9a5-848a1ca21980.png)

ビルドが終わったら、準備完了です。


#6. 動作テスト

基板に電源を入れて、Vivado/Vitis から認識できるようにしておきます。
TeraTerm を起動してから、デバッグに入ります。

Debug Configuraion で、Single Applicaion Debug をダブルクリックして、
![35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/955220/711f89fd-f829-1ea6-ab50-b1d63fb75d7b.png)


開いた画面で Debug ボタンを押して、デバッグを起動します。
![36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/955220/05ac93c1-780c-6dfb-dd08-00593689e306.png)


Resume を押す前に、HDMI 信号発生器からテスト信号を出して、基板へ入力しておきます。
__HDMI 入力に信号が入っていないと、アプリケーションがエラーを出して停止します。__
(これで半日も悩んだ)

![1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/955220/3343f907-582d-548b-6c7e-27466ac4bd21.jpeg)


HDMI モニタも接続しておきます。
![2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/955220/1bcd9e63-5dde-c8c9-c37c-36196798e35e.jpeg)


Resume を押すと、TeraTerm にデモのメニュー画面が表示され、動き出します。
![38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/955220/b87fbfda-114c-fc17-fd6c-18b005b546fb.png)


操作は、数字キーを入力します。
1. 出力解像度の変更
2. 表示するフレームバッファ番号の変更 (1,2,3 から選択)
3. 表示中のフレームバッファにグラデーションパターンを描画
4. 表示中のフレームバッファにカラーバーを描画
5. HDMI IN 入力信号のフレームバッファへの書込みの START/STOP (トグルする)
6. HDMI IN から書き込むフレームバッファ番号の変更 (1,2,3 から選択)
7. HDMI IN から 1 フレームをフレームバッファに取り込み、色を反転
8. HDMI IN から 1 フレームを、フレームバッファの表示サイズにリサイズして取り込み
9. 終了

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 入力を取り込んで重ねています。
![3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/955220/d0f425ab-b32a-0aaf-927b-fd74ea491990.jpeg)
![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/955220/62ff332d-cac4-be5a-e736-a6f8562563f5.png)


HDMI IN は、連続的にフレームバッファに取り込めるので、動画で PinP として映ります。

https://twitter.com/kan573/status/1389925399560261632?ref_src=twsrc%5Etfw


#7. デバッグ用エラー表示の追加
何らかの原因で、プログラムがハングしてしまうことがあります。
何も表示しないで固まってしまうので、調べるのが大変です。

このような時は、プラットフォームプロジェクトの、
/ps7_cortexa9_0/standalone_domain/bsp/ps7_cortexa9_0/libsrc/standalone_v7_2/xdebug.h に、

>\#define DEBUG
>\#undef NDEBUG

を追加します。
![39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/955220/8a6e3fb9-6846-6a56-ac97-7d6082cba562.png)


ソース中に xdbg_printf( ) で埋め込まれたデバッグメッセージが表示されるようになり、原因推定に役立ちます。

下記は HDMI IN に信号が入っていない時に、停止して表示されるエラーです。
理由は分かりませんが、HDMI IN に信号(おそらくクロック)が入っていないと、エラーが出て止まります。
![40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/955220/03afcf9b-a8a7-8044-935c-138704f03317.png)


#まとめ
EBAZ4205 で、Zybo Z7 HDMI Input/Output Demo を動かすことができました。