VDMA搭建視頻通路總結


全局觀查,對整個工程的搭建的關鍵是要保證PL部分搭建成功,PS部分搭建成功,而且兩者配合的很好。

我理解的PL部分涉及到模塊的組合以及模塊或者IP之間的邏輯的整理,PL部分困擾我比較久的是自動生成的wrapper總是會把一些自己需要特殊考慮的信號全部綜合成端口,所以需要我們設計的部分就是把實現的wrapper的特殊要求端口的時序編寫好(VDMA工程中的FLC_CLK怎么處理的呢?)PS部分則主要實現的是對與ZYNQ這個PS模塊相連接模塊IP的接口的配置。要牢記的是,如果IP沒有與PS部分連接的話,就不需要在SDK部分配置接口函數,因為我之前一點也沒有接觸過ARM相關的嵌入式的開發,所以對這一部分糾結了好久。其實只要心里不要害怕,就會發現這一塊特別簡單,就是一些寫好的函數的調用而已,不需要自己編寫具體的實現API。

視頻通路中因為其大的數據流AXI_stream協議需要用的到,VDMA應運而生,VDMA相當於二維的DMA。VDMA相比DMA增加了自動循環和自動切換幀緩存的功能,通過保證圖像系統有多幅幀緩存來實現輸出數據不被撕裂,協調圖像的輸入輸出速率。VDMA可以最多支持32個幀緩存,並且經過配置能夠自動的在各個幀存之間進行切換,並自動互相避讓,從而保證圖像穩定。

VDMA

VDMA硬件部分配置比較簡單,基礎部分只需要選擇好對應的stream流數據寬度,所需的幀緩存個數即可。值得注意的是同步信號的配置。如圖,由於輸入端是采用視頻流協議中的tuser作為同步信號的,所以選擇s2mm tuser,而輸出端默認采用視頻流協議,不采用額外手段同步,所以配置為None即可。關鍵點在於GenLock Mode,及輸入輸出時序匹配模式,圖中為推薦配置,及動態選擇關系。如此配置VDMA輸入輸出端會自動避讓,如果輸入幀數與輸出幀數相同可以嘗試其他配置,但不同時必須采取如下配置,此配置風險小,兼容性更高。

 

       VDMA主要端口有5個:

              S_AXI_Lite:寄存器、配置接口,用於軟件配置VDMA,並讀取狀態信息

              S_AXIS_S2MM:視頻流從端,接收外來視頻流數據

              M_AXI_MM2S:AXI4全協議主端,從DDR中讀取數據給M_AXIS_MM2S

              M_AXI_S2MM:AXI4全協議主端,從DDR中讀取數據給S_AXIS_S2MM

              M_AXIS_MM2S:視頻流主端,向外發慫視頻流數據

       其中5個總線接口最好采用同一總線頻率,其中除了兩個Stream接口與視頻輸入輸出設備相連外,其他均與PS相連(需在PS端使能至少一個一個AFI接口)。

       VDMA配置比較簡單,除了分配幀緩存地址外,只需要配置兩個寄存器0x00和0x30,分別對應輸出配置和輸入配置,在動態模式下,均配置為0x8b即可,具體可以查閱官方手冊。

       需要注意的是,VDMA一旦打開,接收端就開始等待第一個tuser,如果輸入設備先開啟,幾乎不可能剛好對齊,VDMA就會混亂,所以順序應該是先開啟VDMA再開啟輸入設備,通過對OvSensor2AXIS中使能端的控制即可以做到。

六、注意事項

AXIS2VGA 和OvSensor2AXIS中fifo都需要配置為first word fall through的模式,否則會很尷尬。

S2MM:stream to memory mapped,即流轉地址順序存放MM2S相反。

(1.)圖像通路的DMA搭建(SDK部分接口配置) 

需要在工程中加入dma_init.c和dma_init.h文件,作為DMA相關的頭文件

#include "dma_intr.h"

其他的相關頭文件

#include "sys_intr.h"
#include "I2C_8bit.h"
#include "xgpio.h"

變量以及參數的配置

volatile int TxDone;
volatile int RxDone;
volatile int Error;

volatile u8 tx_buffer_index;
volatile u8 rx_buffer_index;

u32 *BufferPtr[3];

static XScuGic Intc; //GIC
static  XAxiDma AxiDma;
static XGpio Gpio;

#define AXI_GPIO_DEV_ID            XPAR_AXI_GPIO_0_DEVICE_ID

核心配置部分:注意需要配置中斷部分

int init_intr_sys(void)//中斷初始化部分
{
    DMA_Intr_Init(&AxiDma,0);//initial interrupt system
    Init_Intr_System(&Intc); // initial DMA interrupt system
    Setup_Intr_Exception(&Intc);
    DMA_Setup_Intr_System(&Intc,&AxiDma,TX_INTR_ID,RX_INTR_ID);//setup dma interrpt system
    DMA_Intr_Enable(&Intc,&AxiDma);
}


int main(void)
{

    u32 Status;
    BufferPtr[0] = (u32 *)BUFFER0_BASE;
    BufferPtr[1] = (u32 *)BUFFER1_BASE;
    BufferPtr[2] = (u32 *)BUFFER2_BASE;

    tx_buffer_index = 0;
    rx_buffer_index = 0;
    TxDone = 0;
    RxDone = 0;
    Error = 0;
//GPIO配置
    XGpio_Initialize(&Gpio, AXI_GPIO_DEV_ID);//指定GPIO的設備號,初始化
    XGpio_SetDataDirection(&Gpio, 1, 0);//指定GPIO的方向,0為輸出,也就是說該IO方向為從PS輸出到PL
//中斷初始化    
init_intr_sys(); //IIC配置 I2C_config_init(); XGpio_DiscreteWrite(
&Gpio, 1, 1);//指定要寫入GPIO的數據
//DMA配置 Status
= XAxiDma_SimpleTransfer(&AxiDma, (u32)BufferPtr[rx_buffer_index], MAX_PKT_LEN, XAXIDMA_DEVICE_TO_DMA); Status = XAxiDma_SimpleTransfer(&AxiDma, (u32)BufferPtr[tx_buffer_index], MAX_PKT_LEN, XAXIDMA_DMA_TO_DEVICE); while (1) ; return XST_SUCCESS; }

(2.)圖像通路的VDMA搭建(SDK部分配置)

需要在工程中加入vdma_api.c文件

VDMA涉及到的頭文件主要有:

#include "xaxivdma.h"
#include "xaxivdma_i.h"

此外涉及到系統初始化以及驅動OV772 攝像頭的IIC驅動配置,有一個與PS端相連接的AXI_GPIO,這個IP主要作用就是鏈接PS和PL,相當於PS控制AXI向GPIO發送一個使能信號控制什么時候開始產生AXIS相關的視頻傳輸時序信號。

#include "sys_intr.h"
#include "I2C_8bit.h"
#include "xgpio.h"

  然后當以涉及到的變量和參數

#define AXI_GPIO_DEV_ID            XPAR_AXI_GPIO_0_DEVICE_ID
#define IMAGE_WIDTH     640
#define IMAGE_HEIGHT    480

static XScuGic Intc; //GIC
static XGpio Gpio;

unsigned int srcBuffer = (XPAR_PS7_DDR_0_S_AXI_BASEADDR  + 0x1000000);//PS7所分配內存的的基地址0x00100000

然后就是配置的核心部分,配置如下

int main(void)
{
    u32 Status;
    XGpio_Initialize(&Gpio, AXI_GPIO_DEV_ID);//GPIO初始化
    XGpio_SetDataDirection(&Gpio, 1, 0);//GPIO設置方向

    ///中斷的配置
    Init_Intr_System(&Intc); // initial DMA interrupt system
    Setup_Intr_Exception(&Intc);
    //IIC驅動配置
    I2C_config_init();
    XGpio_DiscreteWrite(&Gpio, 1, 1);
    //VDMA配置
    XAxiVdma InstancePtr;//VDMA實例化指針
    xil_printf("Starting the first VDMA \n\r");

    Status = run_triple_frame_buffer(&InstancePtr, 0, IMAGE_WIDTH, IMAGE_HEIGHT,
                            srcBuffer, 2, 0);
        if (Status != XST_SUCCESS) {
            xil_printf("Transfer of frames failed with error = %d\r\n",Status);
            return XST_FAILURE;
        } else {
            xil_printf("Transfer of frames started \r\n");
        }
    print("TEST PASS\r\n");

    while (1) ;//程序設定為無限循環
        return XST_SUCCESS;
}

 

(1)DMA通路的邏輯部分的設計

邏輯部分設定:

@關鍵信號1
assign s_axis_s2mm_tlast = m_axis_video_tvalid & s_axis_s2mm_tready & m_axis_video_tlast &(vid_in_v_cnt == VID_IN_VS);// dma in last signal m_axis_video_tvalid:此信號是vid in IP輸出的,代表輸出數據有效 s_axis_s2mm_tready:此信號是DMA IP 輸出的,代表DMA可以接收數據 m_axis_video_tlast:這是每一行圖像數據的最后一個像素的信號標志 vid_in_v_cnt == VID_IN_VS:表示一副圖像的最后一個像素輸出。
s_axis_s2mm_tlast:所有這些信號有效的時候代表DMA的最后一個數據s_axis_s2mm_tlast信號有效。

@關鍵信號2
assign s_axis_video_tuser = m_axis_mm2s_tvalid & s_axis_video_tready & (vid_out_h_cnt == 11'd0) & (vid_out_v_cnt == 11'd0); //vid out user m_axis_mm2s_tvalid:是M_AXIS_MM2S接口(讀DMA接口)的數據有效標志。 s_axis_video_tready:vid out IP 准備好了,可以接收數據 (vid_out_h_cnt == 11'd0) & (vid_out_v_cnt == 11'd0);行計數器為0場計數器也為0說明要么這副圖像已經結束,也可以理解為下一副圖像開始前。這樣結合s_axis_video_tready,m_axis_mm2s_tvalid為1,基於FPGA時序,下一個時鍾輸出s_axis_video_tuser為1正好是一副圖像的第一個像素。 s_axis_video_tuser:因此s_axis_video_tuser代表了每一副圖像開始的第一個像素。
@關鍵信號3
assign s_axis_video_tlast = m_axis_mm2s_tvalid & s_axis_video_tready & (vid_out_h_cnt == VID_OUT_HS);//vid out last signal m_axis_mm2s_tvalid:是M_AXIS_MM2S接口(讀DMA接口)的數據有效標志。 s_axis_video_tready:vid out IP 准備好了,可以接收數據 vid_out_h_cnt == VID_OUT_HS):圖像一行數據的最后一個像素。

(2)VDMA通路的block設計(加入了HLS生成的圖像處理的的IP)

可以忽略端口之間的接通方法為AXI_Stream,因此省略了邏輯部分的指定。

 AXI協議相關

AXI4:適用於要求數據高速傳輸的場合。

AXI-Stream:如FIFO,數據傳輸不需要地址,而是主從設備間直接進行數據的讀寫,主要用於高速數據傳輸的場合,如視頻、高速AD等。

AXI-lite:可用於單個數據傳輸,主要用於訪問一些低速外設。主要用於主機對從機控制的配置信息傳遞。

4) 讀/寫通道並行地進行數據交互,明顯提高了數據吞吐量,對寫數據,從設備會返回確認信號,這樣可以保證寫數據通道的安全,讀/寫模型分別如圖1-1、圖1-2。

 

 

 

 

讀模型:主設備發送讀地址占用信號給從設備→從設備將數據寫入主設備,實現讀操作。

寫模型:主設備發送寫地址占用信號給從設備→主設備將數據寫入從設備→從設備回復確認收到信號,實現寫操作。

5) AXI協議嚴格來講是一個點對點的主/從接口協議,當多個外設需要互相交互數據時,我們需要加入一個AXI Interconnect模塊,也就是AXI互聯矩陣,AXI Interconnect的作用是將一個或多個AXI主設備連接到一個或多個AXI 從設備。

6) AXI Interconnect IP核最多支持16個主設備和16個從設備,如果需要更多的接口可以在設計中加入多個IP核。

7) ZYNQ中的AXI接口包含三個類型,共9個,主要用於PS與PL的互聯。

(1)AXI_HP接口(PL模塊作為主設備)

包括4個,主要用於PL訪問PS上的存儲器。每個接口都有兩個FIFO緩沖器,一個是讀緩沖,一個是寫緩沖。

【實例:設計視頻處理時,高清的圖像可由FPGA直接完成采集、預處理,然后通過AXI_HP接口將數據高速傳輸至DDR中,供APU(加速處理器)完成進一步的圖像處理】

(2)AXI_ACP接口(PS端是從設備端)

只有1個,又叫加速器一致性端口,適合做專用指令加速器模塊接口。PL端可直接從PS部分的Cache中拿到CPU的計算結果,同時也可以第一時間將邏輯加速運算的結果送至Cache中,延時很小。

(3)AXI_GP接口(PS端是從設備端)

通用AXI接口,總共有4個。可用於控制電機運轉,獲取傳感器信號等邏輯模塊的連接接口。

 出現的問題總結:

1.錯誤原因是多驅動源,出錯的模塊都是主機端的底層模塊。但是Synthesis時系統沒有報錯啊?Reset Implementation Run然后Reset Synthesis Run(操作入口如下)后,清除整個編譯過程,然后重新Run Synthesis。

image057.png

image059.png

2.出現部分端口未指定管腳,問題在於,在block文件中把需要指定時序的管腳引了出來,是想着要在V文件中規定其時序,而不需要分配管腳,所以這一點要注意。

3.修改了block文件后,必須要進行的步驟——reset然后generate output product然后generate wrapper,但是wrapper休要注意修改。

知識點:

AXI接口具有5個獨立通道:WriteAddress通道、Write Data通道、Write Response通道、Read Address通道、Read Address通道、Read Data通道。寫相關通道一共有3個,多一個響應的原因在於讀寫的主從性。

現在還比較困擾我的一個問題就是:出現顯示超出視頻區域的根源原因是什么了?查閱發現可能會是顯示圖像的分辨率太大,或者圖像幀頻太快,但是后來發現好像也不是那么回事,指定的大小和頻率都沒有超出顯示的要求。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM