AXI VDMAでHDMI出力

概要

AXI VDMAの使い方についてのメモです。
ArtyZ7でHDMI出力をしてみたいと思います。
回路構成はArty-Z7のHDMI OUTのサンプルデザインを参考にデザインを一から作り直して調べていこうと思います。
この記事は、調べながら順に更新をしていこうと思います。

必要なもの

・ArtyZ7 (HDMI出力があるボード)
・Vivado 2017.3
DigilentのIPコア

Vivadoプロジェクトの作成

全体図

ZYNQ、VDMA、VTC、AXI4-Stream to Video Out、RGB2DVIなどを接続していきます。

VDMAの設定

VDMAの設定は次のようにしました。
今回は、CPUからuint32型で

31:24 23:16 15:8 7:0
0 R B G

のようにメモリに書き込むため、Stream Data Widthを32bitずつ読み出す設定にします。後段のaxi stream to video outではaxi streamのtdataは24bitとなるため、axis_subset_converterを挟んで上位8bitを切り捨てます。

VTCの設定

1080pで設定していきます。
画像2枚目の1080pの設定はVivado2017.1以降から追加されているようです。(たぶん…)

AXI4-Stream to Video Out

FIFO Depth
AXI4-StreamとVideo Interface間のクロックドメインをまたぐために使われるFIFOの長さです。それぞれのクロックにも依りますが、おおよそ画像の1ライン分の長さより多ければ大丈夫そうです。今回は多めに4096に設定しました。
・Clock Mode
AXI4-StreamとVideo Outのクロックを同じにするか別のものにするか設定できます。
・Timing Mode

RGB2DVI

Reset active high Disable
Generate SerialClk internally from pixel clock Disable
MMCM/PLL どっちでもいい?
TMDS clocking range >=120 MHz (1080p)
Invert TMDS lanes チェックなし

AXIS Subset Converter

AXIS Subset Converterは次のように設定します。
ビデオ信号のAXISではTREADY、TDATA、TLAST(HSYNC)、TUSER(VSINC)を使います。この4つはそれぞれManualで設定します。
TDATAはVDMAで4bytes読み出しているので3bytesに切り取りました。

SDKプロジェクトの作成

ソースコード

ソースコード全体はこのようになりました。
必要最低限の構成です。

#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xaxivdma.h"

XAxiVdma vdma;

u32 framebuf[3][1920*1080];
u32 *pFrames[3];

int main()
{
    int Status;
    XAxiVdma_Config *vdmaConfig;
    XAxiVdma_DmaSetup vdmaDmaSetup;

    init_platform();

    for (int i=0; i<3; i++){
    	pFrames[i] = framebuf[i];
    }

    vdmaConfig = XAxiVdma_LookupConfig(XPAR_AXI_VDMA_0_DEVICE_ID);
    if (!vdmaConfig) return XST_FAILURE;

    Status = XAxiVdma_CfgInitialize(&vdma, vdmaConfig, vdmaConfig->BaseAddress);
    if(Status != XST_SUCCESS) return XST_FAILURE;

    // VDMAの設定
    vdmaDmaSetup.FrameDelay = 0;
    vdmaDmaSetup.EnableCircularBuf = 1;
    vdmaDmaSetup.EnableSync = 0;
    vdmaDmaSetup.PointNum = 0;
    vdmaDmaSetup.EnableFrameCounter = 0;
    vdmaDmaSetup.VertSizeInput = 1080;
    vdmaDmaSetup.HoriSizeInput = 1920 * 4;  // HWIDTH * data_bytes
    vdmaDmaSetup.FixedFrameStoreAddr = 0;
    vdmaDmaSetup.Stride = 1920 * 4;
    vdmaDmaSetup.FrameStoreStartAddr[0] = pFrames[0];
    vdmaDmaSetup.FrameStoreStartAddr[1] = pFrames[1];
    vdmaDmaSetup.FrameStoreStartAddr[2] = pFrames[2];

    Status = XAxiVdma_DmaConfig(&vdma, XAXIVDMA_READ, &vdmaDmaSetup);
    if(Status != XST_SUCCESS) return XST_FAILURE;

    Status = XAxiVdma_DmaSetBufferAddr(&vdma, XAXIVDMA_READ, vdmaDmaSetup.FrameStoreStartAddr);
    if(Status != XST_SUCCESS) return XST_FAILURE;

    Status = XAxiVdma_DmaStart(&vdma, XAXIVDMA_READ);
    if(Status != XST_SUCCESS) return XST_FAILURE;

    Status = XAxiVdma_StartParking(&vdma, 0, XAXIVDMA_READ);
    if(Status != XST_SUCCESS) return XST_FAILURE;

    for(int i=0; i<1920*1080; i++){
    	framebuf[0][i] = 0x000000ff; // 緑
    }

    while(1);

    cleanup_platform();
    return 0;
}

結果

HDMIをモニタに接続したところ、緑色の画面が表示されました。
何故か下の方に黒いラインが幾つか入ってしまいましたが、これの原因はまだ分かっていません。そのうち分かったら追記しようと思います。