VDMA搭建视频通路总结
阅读原文时间:2023年07月12日阅读:1

全局观查,对整个工程的搭建的关键是要保证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[];

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,);//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\[\] = (u32 \*)BUFFER0\_BASE;  
BufferPtr\[\] = (u32 \*)BUFFER1\_BASE;  
BufferPtr\[\] = (u32 \*)BUFFER2\_BASE;

tx\_buffer\_index = ;  
rx\_buffer\_index = ;  
TxDone = ;  
RxDone = ;  
Error = ;  

//GPIO配置
XGpio_Initialize(&Gpio, AXI_GPIO_DEV_ID);//指定GPIO的设备号,初始化
XGpio_SetDataDirection(&Gpio, , );//指定GPIO的方向,0为输出,也就是说该IO方向为从PS输出到PL
//中断初始化
init_intr_sys();
//IIC配置
I2C_config_init();

XGpio\_DiscreteWrite(&Gpio, , );//指定要写入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 () ;  
    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, , );//GPIO设置方向

///中断的配置  
Init\_Intr\_System(&Intc); // initial DMA interrupt system  
Setup\_Intr\_Exception(&Intc);  
//IIC驱动配置  
I2C\_config\_init();  
XGpio\_DiscreteWrite(&Gpio, , );  
//VDMA配置  
XAxiVdma InstancePtr;//VDMA实例化指针  
xil\_printf("Starting the first VDMA \\n\\r");

Status = run\_triple\_frame\_buffer(&InstancePtr, , IMAGE\_WIDTH, IMAGE\_HEIGHT,  
                        srcBuffer, , );  
    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 () ;//程序设定为无限循环  
    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个,多一个响应的原因在于读写的主从性。

现在还比较困扰我的一个问题就是:出现显示超出视频区域的根源原因是什么了?查阅发现可能会是显示图像的分辨率太大,或者图像帧频太快,但是后来发现好像也不是那么回事,指定的大小和频率都没有超出显示的要求。

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章