はじめに
base overlayでもHDMIスルーやキャプチャはできたのですが、ちょっと物足りない。せっかくなのでなにか画面を出力する回路を作りたい。小林優著「FPGAプログラミング大全 Xilinx編」を頼りにして、VGAのテスト信号をHDMIに出力するoverlayをつくってみました。
第1回 Setup PYNQ-Z1
第2回 はじめてのJupyter
第3回 pythonとPYNQ PL資源へのアクセス
第4回 PYNQ OVERLAY
第5回 PYNQのOverlayを作ってみた
第6回 カスタムIPを作ってPYNQ overlayに組み込む
第7回 PYNQのHDMI出力overlayを作る
番外編 python ジャンプスタート
方針
道具立て
とりあえず、小林さんの書籍を基にして640x480のVGAのテスト信号を作成する回路をつくり、Digilentさんからvivado用ライブラリとして提供されている「RGB信号をHDMIに出力するrgb2DVI IPコア」を使うことにします。
構成
(phase 1)テスト信号の出力を目指す
とりあえず、ZynqのPSから制御はいらないのですが、あとあと信号を変えたり、フォーマットを変えたりしたくなるので、AXI GPIOで接続する周辺IPとします。
(phase 2)PYNQから自由に作画できるVRAMをつくる
小林さんの本のとおり。
テスト信号作成回路をつくる
プロジェクトをつくる
- Zynq (ZC7020)の新規プロジェクトをつくる。
- Settingから開発言語を VHDL にかえる
回路をつくる
小林さんの書籍の「2-3 PC用ディスプレイにパターンを表示」を参考ににして回路を作り、シミュレーションで動作を確認します。
- 信号フォーマットを確認する。
VGA信号は、ピクセルクロックが25MHz、有効画素640x480ピクセル、その周りに水平・垂直同期期間があるので、信号のフレーム周期は800x525/25MHz=1/59.52です。 - クロックジェネレータ回路(pckgen)を作成
- 同期信号とピクセル座標を作成する回路(syncgen)を作成
- ピクセル座標に対応する信号を作成する回路(pattern)を作成
- テストベンチ回路を作成
- チェック
細かな内容は、書籍を参照していただくとして、VHDLで書く時のポイント、書籍とちょっと変えたところなど以下に示します。
pclgen (VGAクロック作成)
クロックジェネレータで150MHzから25MHzのピクセルクロックを作る。クロックイングウィザードを使わず、直接プリミティブにパラメータを入れてインスタンス化している。
将来、VGA以外のサイズに対応させるときに、周波数を変えねばならないため、分周比などのパラメータはGenericで指定する形が良いかと。
syncgen (同期信号作成)
verilogからVHDLに変更するために、以下の書き換えを行う。
- 信号フォーマットのパラメータをpackageファイルにまとめる。
- VHDLではverilogのように型変換を自動でしてくれないため、変換を多段階に分解し、各段階に応じた信号を定義する。
pattern
下記のとおり(課題のgraduationも同様)。
- 書籍のクロックは125MHzだが、AXIクロック100MHzに対応するため、
pclgen
でパラメタライズした値をGenericポートに追加する - vga2DVIでは、pixcel clock、EN(有効画面)が必要なのでこれらの信号を出力ポートに追加
- 注意点はCLKとPCK(ピクセルクロック)を間違えないようにすること。
- ピクセル座標をもとにピクセルデータを計算するのだが、ラッチするため1クロック遅延が発生する
overlayをつくる
プロジェクトをつくる
- Zynq (ZC7020)の新規プロジェクトをつくる。
- Settingから開発言語をVHDLにかえる
- ブロックデザインを作る
カスタムIP作成
- メニューバーのTools->Create and Package IP...を選択するとwizardが出てくる
- Create a new AXI4 peripheralを選択
- (中略)
- Edit IPを選択してFinish -->カスタムIPのプロジェクトができる
- 既に作成した信号作成のファイルを追加する。
- RGB信号、同期信号、EN、ピクセルクロックのポートを追加して配線する
rgb2dvi IPの修正と接続
配布されているIPをそのまま結線するとMMCMの実装エラーがでました。VCOの周波数が低すぎるとのこと。marseeさんのFPGAの部屋 AXI VDMAのMM2Sを使用してビデオ出力6(インプリメントしたがエラー)を参考にして解決しました。
- エラーはピクセルクロックからピクセルクロックとその5倍のクロックを作成する(ClockGen.vhd)のMMCMで起こっている。
- ピクセルクロック(25MHz)が低すぎてVCOの下限周波数を下回る。
- MMCMのフィードバック係数の値(分周比)を大きくする修正を加えることで、VCOの周波数を高くする。
- VCOの周波数はrgb2dviの設定メニューで変更することができるのだが、配布されたパッケージでは分周比は5(=1x5)もしくは10(=2x5)にしか設定できない。そこで、以下のようにIPコアを修正し25(=5x5)を選択できるようにする。
(IPコアの修正)
1. rgb2dviを右クリックしてIP packagerで開く
2. Customization Parameters をクリックし
3. kClkRange の Value Validation List に 25MHz (5)を追加する。この項を選択すると、(5)を5倍した値(5 x 5 =25)が分周比となるので、25MHzx25=625MHzがVCOの周波数になる。
4. Re-packageし、IP packagerを終了する
(IPコア修正をプロジェクトに反映)
1. Reportメニューから[Report IP Status]を選び
2. 下段のペインのIP Status タグを選び
3. rgb2dviを選択し [Upgrade Selected]ボタンをクリック
(rgb2dviへの接続)
R,G,Bの信号をcatで結合し、24bitの信号pDataをつくる。ここで、MSBからR,B,Gの順序で結合すること。
pData<-R_DATA&B_DATA&G_DATA
参考 rgb2dviのドキュメント
参考2 tom01hさんの雑多な趣味の記録帳
(bitファイルを作成しPYNQ-Z1にコピー)
(略)
テスト
HDMI TXとディスプレーを接続しておき、Jupyter notebook にて新たに作成したbitファイルを読み込めば、信号が出力される。
VRAM化
VRAMのメモリアドレスをちゃんと確保してやらないといけません。
第3回 pythonとPYNQ PL資源へのアクセスを参考にしてCMAメモリを確保します。
import numpy as np
import pynq
def get_pynq_buffer(shape, dtype):
return pynq.Xlnk().cma_array(shape, dtype)
buffer = get_pynq_buffer(shape=(480,640), dtype=np.uint16)
from pynq import MMIO
mmio=MMIO(int(PL.ip_dict["axi_gpio_0"]['phys_addr']),4,True)
mmio.write(0, buffer.physical_address)
あとは、buffer(y,x)にピクセルデータをかくだけ。
まとめ
質問あれば、更新します。
こちらも参考になりそうです。@iwatake2222 さんの記事ZYBO (Zynq) でHDMI出力をする