【外部映像送受信(3)】Zynq上で外部映像を送受信するアプリケーション(受信系の作成① HDMI接続時の割り込みハンドラの構成)

Vitis
スポンサーリンク

本記事の概要

概要

Digilent社では、Zybo用のデモサンプル「Zybo HDMI Input Demo」が提供されています。デモサンプルでは、
(a) DDRメモリから画像を読み出しVGA信号を送信
(b) 外部からのHDMI信号をZynqが受信しDDRメモリに格納
ということを行っています。
本記事では、デモサンプル内のアプリケーションプロジェクトを参考に、(b)の「外部からのHDMI信号をZynqが受信しDDRメモリに格納」するアプリケーションを作成するために、割り込みの構成について整理しました。

以前の記事から、外部から受信した動画像の映像信号をZynqで受信し、それを外部に出力するHDMI入出力デモサンプルを試しています。
以下の記事では、Vivado上でハードウェアデザインを読み込み、ブロックデザインの構成と動作原理を確認しました。

統合ソフトウェア開発環境「Vitis」上で、アプリケーションプロジェクトの作成方法について3回の記事に分けて説明していきます。
前回の記事ではディスプレイへの送信系を作成しました。引き続き、HDMI送信機からの受信系を作成します。本記事ではHDMIケーブルを接続し、信号を受信したときに発生する割り込みの構成について理解することをめざします。

この記事の対象読者
  • FPGAの開発に興味のある学生
  • Vitisを用いたアプリケーション開発は未経験のエンジニア
  • FPGAでの動画像処理を行いたいエンジニア
ひがし
ひがし

それでは、興味のある方はぜひ最後までご覧ください!

目標と工程

開発目標

本記事全体の目標は次の通りです:

統合ソフトウェア開発環境VitisとFPGA設計環境Vivadoを用いて
「HDMI信号を受信しDDRメモリに保存し、それを外部ディスプレイに出力する」
アプリケーションを作成する

開発工程

開発工程

① DigilentのHPからHDMI入力デモのサンプルプログラムをダウンロード
② デモサンプルからZynq SoC内部のハードウェア構成を定めるプロジェクトを読み込む
③ Zynq SoCのARM CPUで動作する”HDMI出力”プログラムを作成

 ③-(a) DDRメモリから画像を読み出しVGA信号を送信
 ③-(b) 外部からのHDMI信号をZynqが受信しDDRメモリに格納(本記事)
④ ①と②を実装し、動作確認

デバイス構成

Zynqの構成

Zynqは外部から映像信号を受信し、PL(Programmable Logic)部からDDRメモリに映像信号を書き込みます。PL内部のIPコアの構成を説明します。HDMI入出力編(1)では、以下の図のようなブロック図の構成に基づき、Vivadoのブロックデザインを作成しました。

構成は大きく、「(a)送信系(ディスプレイ側)」「(b)受信系(ビデオ側)」の2つに分けることができます。

本記事では、「(b)受信系(ビデオ側)」を動かすためのアプリケーションを作成します。

受信系では、HDMIケーブルを繋ぎ外部から映像信号を受信したときに、VTCにおいて映像のタイミング信号を検出し、VDMAにおいて映像をDDRメモリへ格納する必要があります。「HDMIケーブルの接続」をトリガーに特定のプログラムを動作させるために、「割り込み」をかける処理を行います。

割り込みとは

CPUでは、基本的にメモリに書いてある順番に従って、シーケンシャルに命令を実行していきます。このメインルーチンで処理を行っている間に、何らかのイベントをトリガとしてその処理を中断し、別のプログラムで別の処理を行うことは「割り込み」と呼ばれています。このようなトリガとなるイベントは「割り込み要求」、呼び出される別のプログラムは「割り込みハンドラ」や「割り込みサービスルーチン」と呼ばれています。
以下の記事で、単純なタイマー割り込みを例に割り込み処理に用いるAPIの使用方法を解説しています。

HDMI入力I/Oの構成

まず、HDMIケーブルをZyboに接続したときの回路の動作を整理しましよう。

上の図はHDMI送信機(ソースと呼ばれる;本記事ではPC)と受信機(シンクと呼ばれる;本記事ではZybo)のブロック図です。

HDMIケーブルとの接続に用いる信号線は以下の4種類です。今回、Vivadoのブロックダイアグラム上では、CECは使用せずに、他の3種類のI/Oポートを設定しました。

HDMIバスの構成
  • TMDS (Transition Minimized Differential Signaling):3本のデータチャネルと1本のクロック信号チャネルで構成。ソース(送信側)機器からシンク(受信側)機器へ映像や音声を送信。
  • DDC (Display Data Channel):シンク(受信側)機器の構成や状態などの情報をソース(送信側)機器に伝達。
  • HPD (Hot Plug Detect):ソース(送信側)機器がシンク(受信側)機器に接続/切断されたことを、ソース(送信側)機器に認識させる。
  • CEC (Consumer Electronics Control):シンク(受信側)機器とソース(送信側)機器の間で制御信号をやり取りし、機器間の連携動作を可能にする。今回は使用しない。
ひがし
ひがし

余談ですが、Vivadoで作成したブロックダイアグラムには、HDMI_OENという出力を設けています。この出力に応じて、Zyboを受信機にするか(HDMI_OEN=0)、送信機にするか(HDMI_OEN=1)を設定することが可能です。以下のZyboのマニュアルの、HDMI_OUT_ENが該当します。

Zybo Reference Manual - Digilent Reference
Zybo Reference Manual Note The Zybo Zynq-7000 has been retired and replaced by the Zybo Z7. If you need assistance with migration to the Zybo Z7, please follo...

HDMIケーブルを接続したときの動作

フローチャート

では、HDMIケーブルを接続したときの動作を順を追ってみていきましょう。

HDMIケーブルを接続すると、HPDチャネルを介してシンク機器からソース機器に、HDMI接続されたことを認識する信号が送られます(図中1の矢印)。シンク機器、すなわちZynqではGPIOチャネル1を常にHigh状態にしておき、物理的にHDMIケーブルを接続したときにソース機器のHPDチャネルがHigh状態にアサートされるようにしました。

ソース機器は、HPDの接続を認識すると映像の情報を含むデータ信号とクロック信号をTMDSチャネルを通じてシンク機器に送信します。データ信号はDVI形式で送信されます。Zynqで受信したDVI形式のデータ信号は、Digilent社提供のdvi2rgb IPによりrgb信号に変換されます。dvi2rgb IPの使用方法は以下のリファレンス資料を参考にしました。
https://github.com/Digilent/vivado-library/blob/master/ip/dvi2rgb/docs/dvi2rgb.pdf

データ信号とクロック信号を受信し、問題なく通信のクロック動作が確立すると、aPixelClkLckdがHigh状態にアサートされます(図中2の矢印)。このアサートをGPIOチャネル2が検出し、割り込み信号をCPUに送るようにしています。この割り込み信号が「HDMIケーブルが接続され、外部からデータ信号を受信した」というトリガーとなります。これをトリガーに、VTCによるタイミング検出を開始します。

RGB信号は映像信号とタイミング信号とに分けられ、それぞれVDMA IPとVTC IP(ビデオ側)とに送信されます。

ひがし
ひがし

今回、VTC IPコアを2つ使用しています。一つは、ディスプレイに映像をVGA出力するためのタイミング信号の生成に用いられています。もう一つは、ソース機器からデータを受信してタイミング信号を検出するのに用いられています。前者をVTC IP(ディスプレイ側)、後者をVTC IP(ビデオ側)と呼び、区別することにしました。

VDMA IPとVTC IP(ビデオ側)のうち、VTC IP(ビデオ側)でのタイミング検出をまず最初に行います。この理由は、VDMA IPで映像信号をメモリに格納する際に、その映像のサイズをビデオ側VTC IPで検出したタイミング信号をもとに設定したいからです。VTC IP(ビデオ側)でのタイミング信号の検出が完了すると、VTC IPがロック(locked)状態になり、かつ割り込み信号がCPUに送られます(図中3の矢印)。割り込み信号を受信してから、VDMA IPの映像受信時の設定を行い、DMA(Direct Memory Access)動作を開始します。

以上のHDMI送信機(ソース)をZynqに接続したときの、Zynqの動作をフローチャートにまとめました。

VTCの割り込みハンドラ

VTC IPで発生した割り込み要求に対する割り込みハンドラを実行するとき、CPUではAXI4-Liteを通じてVTC IPのIRQ Enable Registerを参照します。IRQ Enable Registerの値から割り込み要求の要因を区別し、その要因に応じて実行する割り込みハンドラを分岐することができます。

例えば、今回はVTCにおけるIRQ Enable Registerでは、”Lock”というビットがHigh状態になります。下表は、XilinxのVTC(Video timing controller) IPのリファレンスマニュアルから抜粋したIRQ Enable Registerのレジスタマップです。VTC IPでの検出が完了すると、レジスタの8ビット目がイネーブルされます。

https://www.xilinx.com/support/documentation/ip_documentation/v_tc/v6_1/pg016_v_tc.pdfより抜粋

受信系の状態遷移図

Zynqの動作を示すフローチャートと合わせて、割り込み要求を受信するたびに状態がどのように遷移するかを状態遷移図にまとめました。

HDMIケーブルを接続し、GPIOの割り込みが発生すると、VTC IP(ビデオ側)によるタイミング検出を行い、VTC IP(ビデオ側)の検出が完了するのを待ちます。

VTC IP(ビデオ側)の検出が完了し割り込みが発生すると、DMA動作が開始され、HDMI映像が外部ディスプレイに表示されるようになります。

ひがし
ひがし

今回の記事では、映像を出力するところまでを行います。

しかし、例えば、「HDMI映像出力中」の状態からHDMIケーブルを抜き、信号を切断することも想定されます。このとき、「静止画出力中」の状態に戻すのが一つの選択肢なのですが、今回はそこまでは行いません。

割り込みハンドラ表の構成

最後に、デバイスの構成に基づいて、割り込みハンドラ表の構成を計画します。

デバイス上で割り込み要求の発生源となるIPは2つ、GPIO IPVTC IP(ビデオ側)です。それぞれの割り込み信号はApplication Processor Unit (APU)内のGIC(Generic Interrupt Controller; 汎用割り込みコントローラ)を介して、CPUに割り込み要求をかけます。

このように、割り込み要求を発生させる要因が一つではないとき、種々の割り込み要因に対して、対応する割り込みハンドラへと分岐することが必要です。この対応する割り込みハンドラへ分岐するための手続きを記述したプログラムは、割り込みベクタテーブルと呼ばれています。割り込みベクタテーブルには、予め「割り込みハンドラの関数ポインタ(Handler)」と「割り込みハンドラに送る引数(CallBackRef)」を登録しておきます。

割り込みハンドラの分岐を行うときに、アプリケーションに入力される割り込み信号の分岐だけではなく、GICの入力源に対しても割り込み要求を分類し、適切な割り込みハンドラに分岐させることが可能です。GICインスタンスには、割り込みベクタテーブルと同様の、割り込みハンドラテーブルが用意されています。

下の図のように、GICインスタンスと実際のGICを紐付けておき、GICの入力源に従って対応する割り込みハンドラを登録しておけば、割り込み要求に応じて適切な割り込みハンドラへと分岐させることが可能になります。

さらに、前述の通り、VTC IPコアでもIRQ Enable Registerを参照し、割り込み要求の要因に応じて、割り込みハンドラを区別することが可能です。VTCから割り込み要求を受けたときは、割り込み要求の要因に応じて分岐を設定すると、複雑な処理が理解しやすくなります。

まとめると、以下の通りです。割り込みハンドラが分岐して、適切なプログラムが実行されます。

割り込みハンドラテーブルの分岐
  • 割り込み要求を受信したとき、GICインスタンスの割り込みハンドラテーブルに分岐
    • 割り込み要求の発生源がGPIO IP→GpioIsrという関数を実行し、VTC IP(ビデオ側)の設定とタイミング信号の検出を開始
    • 割り込み要求の発生源がVTC IP(ビデオ側)→vtc_videoインスタンスの割り込みハンドラテーブルに分岐
      • 割り込み要求の要因がLock→VtcIsrという関数を実行し、VDMAの受信設定とDMA動作を開始
ひがし
ひがし

今回は、HDMIケーブルを接続したときの割り込み動作を解説しました。
次回の記事では、このフローチャートと割り込みハンドラ表の分岐に基づいて、ソースコード例とその解説をまとめていきます。

最後までご覧いただきありがとうございました。

次回の記事へのリンク

コメント