全局观查,对整个工程的搭建的关键是要保证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 "); 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 ",Status); return XST_FAILURE; } else { xil_printf("Transfer of frames started "); } print("TEST PASS "); 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个,多一个响应的原因在于读写的主从性。
现在还比较困扰我的一个问题就是:出现显示超出视频区域的根源原因是什么了?查阅发现可能会是显示图像的分辨率太大,或者图像帧频太快,但是后来发现好像也不是那么回事,指定的大小和频率都没有超出显示的要求。