Xilinx IP Video Timing Controllerの使い方 (2)ビデオタイミング生成のドライバの使用方法

IP
スポンサーリンク

本記事の概要

概要
  • Xilinx ソフトIPコアVTC(Video Timing Controller)のドライバについて解説
  • 以下の手順で、初期化とVTCのトランザクションを実行する:
  1. ハードウェア設定の定義:XVtc_LookupConfig()
  2. ドライバとデバイスの初期化:XVtc_CfgInitialize()
  3. 各パラメータの設定:タイミングを指定(vtcTiming)し、参照するレジスタを選択(SourceSelect)
  4. レジスタの書き込み:XVtc_SetGeneratorTiming()XVtc_SetSource()
  5. Vtc動作の開始:XVtc_EnableGenerator()

XilinxのIPコア Video Timing Controller(VTC) は、ビデオを出力するときにタイミングを同期するための信号を生成したり、逆に外部から入力されたビデオを解析し、そのビデオのタイミングやフォーマットを検出してくれる、非常に便利なIPです。

本記事ではまず最初に使うにあたって、VTCコアからタイミング信号を生成する場合を例に取り、外部のプロセッサから制御信号を送信する方法についてまとめてみました。

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

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

Video Timing Controller (VTC)コアの基本機能

VTCコアは、ビデオ信号におけるタイミング信号を検出・生成するためのIPコアです。VTCコアでできること、基本機能については、以下の記事でより詳しくまとめています。

VTCで検出したステータス信号や生成するための制御信号は、外部プロセッサからAXI4-Lite経由で参照することが可能です。

制御レジスタの設定方法は3つあります。

  1. AXI Traffic Generator IPを利用して、直接レジスタにパラメータを書き込む
  2. ドライバを活用して、プロセッサ(MicroblazeやZynqのARMコアなど)からレジスタを変更する(プロセッサのあるシステムのみ)
  3. ブロックダイアグラム上でレジスタ値を設定する。ただし、ハードウェア上で設定するので動的に変更ができない。

今回は、プロセッサのあるシステム向けに、VTCを外部から制御するためのドライバの使用方法について解説していきたいと思います。

プロセッサでデバイス(今回はVTC)を動かすためのドライバは、ザイリンクスSDK (Software Development Kit)開発環境やVitis™ 統合ソフトウェアプラットフォームで配布されています。
また、githubにも公開されています。ライブラリ内のヘッダファイル”xvtc.h“を読み込むことによって、ドライバをSDK上で扱うことができるようになります。

コード

まず、最初にコード例を書いておきます。
VTC_settingという関数を作成し、この関数の中で必要な設定をすべて行うようにしました。

#include "xvtc.h"
#include "vga_modes.h" //Digilient社より提供。VideoModeの定義。

#define DISP_VTC_ID XPAR_VTC_0_DEVICE_ID
int VTC_setting(u16 vtcId, XVtc *vtc, VideoMode vMode);

/* 構造体の定義 */
XVtc vtc;

int main(void){
    int Status;
    /* 他のIPの設定*/
    /* 略 */

    /* VTCの設定 */
    Status = VTC_setting(DISP_VTC_ID, &vtc, VMODE_640x480);

    /* frameBufへの書き込み */
    /* 略 */
    return 0;
}


int VTC_setting(u16 vtcId, XVtc *vtc, VideoMode vMode)
{
    /*---------------------------------------------------------------*/
    /* 初期化*/
    /*---------------------------------------------------------------*/
    /* ハードウェア設定を格納した構造体の定義 */
    XVtc_Config *vtcConfig;
    vtcConfig = XVtc_LookupConfig(vtcId);
    
    /* ドライバとデバイスの初期化 */
    int Status;
    Status = XVtc_CfgInitialize(vtc, vtcConfig, vtcConfig->BaseAddress);

    /*---------------------------------------------------------------*/
    /* VTC トランザクション*/
    /*---------------------------------------------------------------*/
    /* タイミングを制御するのに必要な情報を設定 */
    XVtc_Timing vtcTiming;

    vtcTiming.HActiveVideo  = vMode.width                     ;    /**< Horizontal Active Video Size */
    vtcTiming.HFrontPorch   = vMode.hps    - vMode.width      ;    /**< Horizontal Front Porch Size */
    vtcTiming.HSyncWidth    = vMode.hpe    - vMode.hps        ;    /**< Horizontal Sync Width */
    vtcTiming.HBackPorch    = vMode.hmax   - vMode.hpe + 1    ;    /**< Horizontal Back Porch Size */
    vtcTiming.HSyncPolarity = vMode.hpol                      ;    /**< Horizontal Sync Polarity */
    vtcTiming.VActiveVideo  = vMode.height                    ;    /**< Vertical Active Video Size */
    vtcTiming.V0FrontPorch  = vMode.vps    - vMode.height     ;    /**< Vertical Front Porch Size */
    vtcTiming.V0SyncWidth   = vMode.vpe    - vMode.vps        ;    /**< Vertical Sync Width */
    vtcTiming.V0BackPorch   = vMode.vmax   - vMode.vpe + 1    ;    /**< Horizontal Back Porch Size */
    vtcTiming.V1FrontPorch  = vMode.vps    - vMode.height     ;    /**< Vertical Front Porch Size */
    vtcTiming.V1SyncWidth   = vMode.vpe    - vMode.vps        ;    /**< Vertical Sync Width */
    vtcTiming.V1BackPorch   = vMode.vmax   - vMode.vpe + 1    ;    /**< Horizontal Back Porch Size */
    vtcTiming.VSyncPolarity = vMode.vpol                      ;    /**< Vertical Sync Polarity */
    vtcTiming.Interlaced    = 0                               ;    /**< Interlaced / Progressive video */

    /* Setup the VTC Source Select config structure. */
    /* 1=Generator registers are source */
    /* 0=Detector registers are source */
    XVtc_SourceSelect SourceSelect;

    memset((void *)&SourceSelect, 0, sizeof(SourceSelect));
    SourceSelect.VBlankPolSrc       = 1;
    SourceSelect.VSyncPolSrc        = 1;
    SourceSelect.HBlankPolSrc       = 1;
    SourceSelect.HSyncPolSrc        = 1;
    SourceSelect.ActiveVideoPolSrc  = 1;
    SourceSelect.ActiveChromaPolSrc = 1;
    SourceSelect.VChromaSrc         = 1;
    SourceSelect.VActiveSrc         = 1;
    SourceSelect.VBackPorchSrc      = 1;
    SourceSelect.VSyncSrc           = 1;
    SourceSelect.VFrontPorchSrc     = 1;
    SourceSelect.VTotalSrc          = 1;
    SourceSelect.HActiveSrc         = 1;
    SourceSelect.HBackPorchSrc      = 1;
    SourceSelect.HSyncSrc           = 1;
    SourceSelect.HFrontPorchSrc     = 1;
    SourceSelect.HTotalSrc          = 1;

    /* VTCの設定 */
    XVtc_SelfTest(vtc);

    XVtc_RegUpdateEnable(vtc);
    XVtc_SetGeneratorTiming(vtc, &vtcTiming);
    XVtc_SetSource(vtc, &SourceSelect);
    
    XVtc_EnableGenerator(vtc);

    return XST_SUCCESS;
}

以下では、各関数について解説していきます。

Video Timing Controller (VTC)コアのドライバの使用方法

想定環境の例

例に上げたコードでは、説明をわかりやすくするためRGB画像(画素数640×480;階調8bit)のタイミング信号を出力する場合を用いて実行フローを示しています。なお、Digilent社から提供されるvga_modes.hにおける”VMODE_640x480″という構造体を選択すれば同期幅やフロントポーチなどの値を一括で設定可能です。vga_modes.hをincludeしておきましょう。

どんな画像か想像しやすいように、ブランキング期間も含めた画像のイメージ図を示します。各ブランキング期間や同期期間は構造体VideoModeの各変数に割り当てられ、それぞれ図のような値がVMODE_640x480では指定されています。

VTC_settingのフローチャート

初期化からタイミング生成までの、関数VTC_settingのフローチャートは以下の通りです。

初期化

まず、VTCコアとドライバの初期化までを行います。
このとき、使用する関数は次の2つです。

    /* ハードウェア設定を格納した構造体の定義 */
    XVtc_Config *vtcConfig;
    vtcConfig = XVtc_LookupConfig(vtcId);
    
    /* ドライバとデバイスの初期化 */
    int Status;
    Status = XVtc_CfgInitialize(vtc, vtcConfig, vtcConfig->BaseAddress);

VTCコアのハードウェア設定ドライバインスタンス(ドライバを動かすときに必要な設定を格納した構造体)を定義しています。基本的にはおまじないとして記載すれば良いと思います。

VTCトランザクション

初期化が完了したので、ビデオ信号のタイミングを実際に生成していきます。

タイミングを制御するのに必要な情報を設定
    /* タイミングを制御するのに必要な情報を設定 */
    XVtc_Timing vtcTiming;

    vtcTiming.HActiveVideo  = vMode.width                     ;    /**< Horizontal Active Video Size */
    vtcTiming.HFrontPorch   = vMode.hps    - vMode.width      ;    /**< Horizontal Front Porch Size */
    vtcTiming.HSyncWidth    = vMode.hpe    - vMode.hps        ;    /**< Horizontal Sync Width */
    vtcTiming.HBackPorch    = vMode.hmax   - vMode.hpe + 1    ;    /**< Horizontal Back Porch Size */
    vtcTiming.HSyncPolarity = vMode.hpol                      ;    /**< Horizontal Sync Polarity */

    vtcTiming.VActiveVideo  = vMode.height                    ;    /**< Vertical Active Video Size */
    vtcTiming.V0FrontPorch  = vMode.vps    - vMode.height     ;    /**< Vertical Front Porch Size */
    vtcTiming.V0SyncWidth   = vMode.vpe    - vMode.vps        ;    /**< Vertical Sync Width */
    vtcTiming.V0BackPorch   = vMode.vmax   - vMode.vpe + 1    ;    /**< Horizontal Back Porch Size */
    vtcTiming.V1FrontPorch  = vMode.vps    - vMode.height     ;    /**< Vertical Front Porch Size */
    vtcTiming.V1SyncWidth   = vMode.vpe    - vMode.vps        ;    /**< Vertical Sync Width */
    vtcTiming.V1BackPorch   = vMode.vmax   - vMode.vpe + 1    ;    /**< Horizontal Back Porch Size */
    vtcTiming.VSyncPolarity = vMode.vpol                      ;    /**< Vertical Sync Polarity */

    vtcTiming.Interlaced    = 0                               ;    /**< Interlaced / Progressive video */

VMode640x480の設定をもとに、表示領域やフロントポーチ、同期信号の水平方向と垂直方向の幅をそれぞれvtcTimingという構造体に設定していきます。

ここで、.V0FrontPorchと.V1FrontPorchの2種類があるのは、インターレース方式に対応するためです。

ひがし
ひがし

インターレース(飛ばし走査)方式とは、走査速度が遅くてもモニターの遅れを目立たなくするために、垂直方向の走査を偶数列と奇数列に分けて交互に実行する方式です。偶数と奇数の各走査をフィールドと呼び、フィールドごとにフロントポーチや同期幅などの値を設定できるようにしています。

今回の設定では、インターレースをオフにしている(vtcTiming.Interlaced = 0)ため、V0の方だけ設定すれば自動でV1にも同じ値が設定されます。今回は一応同じ値を入れておきました。

SourceSelectを設定
    XVtc_SourceSelect SourceSelect;

    memset((void *)&SourceSelect, 0, sizeof(SourceSelect));
    SourceSelect.VBlankPolSrc       = 1;
    SourceSelect.VSyncPolSrc        = 1;
    SourceSelect.HBlankPolSrc       = 1;
    SourceSelect.HSyncPolSrc        = 1;
    SourceSelect.ActiveVideoPolSrc  = 1;
    SourceSelect.ActiveChromaPolSrc = 1;
    SourceSelect.VChromaSrc         = 1;
    SourceSelect.VActiveSrc         = 1;
    SourceSelect.VBackPorchSrc      = 1;
    SourceSelect.VSyncSrc           = 1;
    SourceSelect.VFrontPorchSrc     = 1;
    SourceSelect.VTotalSrc          = 1;
    SourceSelect.HActiveSrc         = 1;
    SourceSelect.HBackPorchSrc      = 1;
    SourceSelect.HSyncSrc           = 1;
    SourceSelect.HFrontPorchSrc     = 1;
    SourceSelect.HTotalSrc          = 1;

次に、SourceSelectという構造体を定義します。
SourceSelectとは制御レジスタの一つです。タイミング信号を生成するときに、検出したビデオフォーマットを格納するレジスタを参照するのか、外部から入力された指定のビデオフォーマットを格納するレジスタを参照するのかのいずれかを、この値によって選択することが可能になります。

下のブロックダイアグラムにおけるSourceSelectorが対応しています。

https://www.xilinx.com/support/documentation/ip_documentation/v_tc/v6_2/pg016_v_tc.pdfより抜粋
SourceSelectとは

タイミング信号を生成する際に、どのレジスタを参照するかを選択するためのレジスタ。

  • 0を指定:Detector Registerを参照 (検出したビデオフォーマットを格納するレジスタ)
  • 1を指定:Generator Registerを参照 (外部から入力された指定のビデオフォーマットを格納する生成用のレジスタ)
ひがし
ひがし

今回の例では、外部入力でビデオフォーマットを指定していくので、全て1に設定しています。

Video Timing Controllerの設定
    /* VTCの設定 */
    XVtc_SelfTest(vtc);

    XVtc_RegUpdateEnable(vtc);
    XVtc_SetGeneratorTiming(vtc, &vtcTiming);
    XVtc_SetSource(vtc, &SourceSelect);
    
    XVtc_EnableGenerator(vtc);

構造体vtcTimingとSourceSelectに基づいて、VTCコアのレジスタに設定を書き込んでいきます。
まず、関数XVtc_RegUpdateEnableによってレジスタの更新を実行できるようにした後、関数XVtc_SetGeneratorTimingによって構造体vtcTimingに格納したタイミング設定を生成レジスタ(ブロック図におけるGeneration Control Register)に書き込みます。

次に、関数XVtc_SetSourceによって構造体SourceSelectに格納しているどのレジスタを参照するかという制御信号を制御レジスタSourceSelect(ブロック図におけるControl Register Source Selector)に値を書き込みます。

最後に、関数XVtc_EnableGeneratorによってVTCコアを実行します。

ひがし
ひがし

あまり馴染みのないIPコアについては、今後も深堀りしていき、解説していきたいと思います。
ご覧いただき、ありがとうございました!

参考文献

Xilinx社のリファレンスマニュアルを参考にしました。

https://www.xilinx.com/support/documentation/ip_documentation/v_tc/v6_2/pg016_v_tc.pdf

コメント