全局觀查,對整個工程的搭建的關鍵是要保證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。
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個,多一個響應的原因在於讀寫的主從性。
現在還比較困擾我的一個問題就是:出現顯示超出視頻區域的根源原因是什么了?查閱發現可能會是顯示圖像的分辨率太大,或者圖像幀頻太快,但是后來發現好像也不是那么回事,指定的大小和頻率都沒有超出顯示的要求。