Reference:https://china.xilinx.com/content/dam/xilinx/support/documentation/ip_documentation/axi_vdma/v6_3/pg020_axi_vdma.pdf
AXI VDMA core是专门针对视频流的直接存储器,以视频帧为单位。
(1)兼容AXI4接口协议;(2)AXI4接口的数据位宽支持32,64,128,256,512和1024 bits;(3)AXI4-Stream接口的数据位宽支持8的整数倍位宽,最高为1024bits;(4)可选择的数据寻址自动对齐 (5)可选择的锁同步机制(6)独立异步的访存读写通道(7)AXI4-Stream接口的时钟域支持动态时钟频率变化(8)可选择的帧重复功能,发生error时(9)支持最多32个frame buffers(10)支持64-bit的寻址空间(11)支持Vertical Flip(没用过...)
(1)不支持AXI4接口的User signals(2)不支持Locked transfers(3)不支持Exclusive transfers(4)不支持FIXED and WRAP Burst transfers(但是可以设置burst大小,疑惑)
图 1
图1所示为VDMA的整体结构图。VDMA用于将AXI Stream格式的数据流转换为Memory Map格式或将Memory Map格式的数据转换为AXI Stream数据流,也就是说VDMA IP旨在提供从AXI4接口协议到AXI4-Stream接口协议的视频读/写传输功能,反之亦然,从而实现系统内存(DDR)和基于AXI4-Stream的视频处理IP之间的大量数据移动。
AXI4-Lite可以对寄存器(Registers)进行配置,从而实现软件动态配置VDMA的功能。通过AXI4-Lite接口对寄存器进行配置后,控制/状态逻辑块(Control and Status)会为DataMover生成相应的命令,以在AXI4主接口上启动写入/读取命令。可配置的异步Line Buffer用于将像素数据写入AXI4-Memory Map接口或者AXI4-Stream接口之前临时保存像素数据。
VDMA数据接口可以分为读,写两个通道,且写入和读取是独立的。用户可以通过写通道将AXI-Stream类型的数据流写入DDR。在读通道中,VDMA使用AXI4主接口从系统存储器读取数据并在AXI4-Stream主接口上输出。因此,VDMA本质上是一个数据搬运的IP,可以看作是为视频图像处理做特殊优化的带有帧缓冲功能的高性能DMA,为数据进出系统存储器提供了一种不依靠CPU,针对视频流的便捷方案。
VDMA IP不仅具有帧缓冲功能,而且集成了视频专用功能,如Gen-Lock和帧同步,用于完全同步的帧DMA操作和2D DMA传输。除了同步之外,还可以使用帧存储编号和scatter gather或者寄存器直接模式操作,以便CPU控制。
和DMA相比,VDMA增加了帧缓存的缓冲机制和同步锁相(GenLock)等功能,是为了针对视频图像应用而做的专用DMA。VDMA集成了视频专用功能,如帧同步和2D DMA传输等,非常适合基于ZYNQ架构上的图像和视频处理,缩短了开发周期。
什么是帧缓存?帧缓存(Frame Buffer),也常被称为显存,是为显示设备提供数据缓存的一片存储区域。一般图像输入源和图像显示的传输速率不匹配,这个时候就需要一片存储区域来缓存输入的数据,以便显示设备读取数据,同时也方便后续对视频数据做图像处理。注意,帧缓存的每一个存储单元对应屏幕上的一个像素,整个帧缓存对应一帧图像。
对于使用帧缓存来缓存图像数据来说,可以采用单帧缓存或者多帧缓存的方案。单帧缓存是指图像的输入和图像的显示都是通过读写同一片存储区域来实现的。单帧缓存的问题是读出的单帧图像是输入的两帧图像或者更多帧图像数据叠加在一起的结果,可能会导致显示设备显示的图像出现割裂的现象。
在很多的视频应用中,图像输入端和输出端的数据速率一般不匹配,通常使用帧缓存来避免因速率不匹配而导致的问题。为了解决单帧缓存区域带来的图像叠加问题,通过分配多个帧缓存区域保存数据,图像输入端在写入其中一个帧缓存时,输出端读取其他的帧缓存。
VDMA支持四种同步锁相模式,分别是Genlock Master(同步锁相主模式),Genlock Slave(同步锁从模式),Dynamic Genlock Master(动态同步锁相模式),Dynamic Genlock Slave(动态同步锁相从模式)。
VDMA有一个写通道(S2MM)和一个读通道(MM2S),用户通过写通道将输入端数据写入帧缓存,通过读通道将从帧缓存中读出数据。VDMA的每一个通道可以选择以上四种模式中的一种。
当写通道(S2MM)或者读通道(MM2S)配置为Genlock Master时,该通道不会跳过或者重复任一帧缓存区域,按照帧缓存顺序读出数据。配置为 Genlock Slave 的通道应当紧跟 Genlock Master 通道变化,但有一定的延迟,延迟的大小在寄存器(*frmdly_stride[28:24])中配置。
当写通道(S2MM)或者读通道(MM2S)配置为 Genlock Slave 时,该通道会通过跳过或者重复一些帧缓存区域的方式,尝试与 Genlock Master 通道同步。
当写通道(S2MM)或者读通道(MM2S)配置为 Dynamic Genlock Master 时,该通道会跳过 Dynamic Genlock Slave 通道正在操作的帧缓存,通过跳过或者重复一些帧缓存区域的方式来完成。
这里以分配三个帧缓存为例。当配置为 Dynamic Genlock Master 的通道访问帧缓存时,没有检测到 Slave通道访问的帧缓存,那么它会循环访问帧缓存 0 1 2 0 1 2;而如果检测到Slave 访问的帧缓存区域,它会跳过该区域并开始访问下一帧缓存。因此,如果 Slave 通道长时间访问帧缓存 1,则 Master 会循环访问帧缓存 2 和 3。
当写通道(S2MM)或者读通道(MM2S)配置为 Dynamic Genlock Slave 时,该通道会操作Dynamic Genlock Master 通道上一周期操作的帧。
下面以配置为 Genlock 模式为例,写通道(S2MM)配置为 Genlock Master 模式,读通道(MM2S)配置为 Genlock Slave 模式,操作示意图如图2所示。
图 2
由上图2可知,写通道在循环访问帧缓存 0,1,2;而由于读通道的帧速率比写通道慢,读通道会紧跟写通道,在操作 0 之后,跳过 1,而去处理 2。
图 3
以配置为 Dynamic Genlock 模式为例,写通道(S2MM)配置为 Dynamic Genlock Master模式,读通道(MM2S)配置为 Dynamic Genlock Slave 模式,操作示意如图3。
在动态同步锁相模式下,Master 会跳过 Slave 当前操作的帧缓存,Slave 工作在 Master 上一次操作过的帧缓存。在上图中,写通道循环访问 0,1,2,当再次返回至 0 时,由于 Slave在操作 0,所以 Master 跳过 0,而去访问 1。而读通道操作 Master 上一次访问过的帧缓存。
由此我们可以得出结论,如果想要避开读通道和写通道同时访问同一帧缓存,那么 VMDA必须配置成动态同步锁相的模式,且帧缓存数量要大于等于 3。由于分配过多的帧缓存区域对效率的提升已经微乎其微,且会占用更多的存储空间和消耗 CPU 的时间,因此在摄像头图像显示的应用中,帧缓存空间一般设置为 3,并采用动态同步锁相的模式。
需要注意的是,VDMA 只是 Xilinx 提供的 IP 核,本身不提供存储的功能,帧缓存一般设置在外置的存储器(如 DDR3)中,VDMA 只是提供了用于访问 DDR3 的接口。
AXI VDMA的寄存器被内存映射到无cache缓存的内存空间。该内存空间必须以32-bit边界对齐。
所有的寄存器都是小端字节序,如图所示。关于为什么是小端字节序而不是大端字节序。这参考格林佛斯游记中吃鸡蛋大端派和小端派的争论。
该寄存器用于控制Memory Map到Stream VDMA通道。
1)IRQDelayCount:该值用于延迟计数中断,延迟计数中断是使MM2S通道在延迟超时后产生中断的一种机制,当设置该值为0时,不使能延迟计数中断。
2)IRQFrameCount:该值用于设置中断阈值。当一帧数据开始传输时,一个内部计数器从中断帧计数设置开始递减计数,当这个值为0时,MM2S通道产生一个中断。该值的最小设置值为0x01。当DMACR.FrameCntEn = 1时,该值决定了要处理的帧缓冲区的数量。
3)Repeat_En:当遇到帧错误时,允许重复帧或advance frame(当前要访问帧的下一帧)。该功能是在AXI VDMA被设置为Genlock Master或者Dynamic Genlock Master时有效。
4)Err_IrqEn:使能Error Interrupt,当设置为1时,允许VDMASR.Err_Irq产生中断。
5)DlyCnt_IrqEn:使能Delay Count Interrupt,当设置为1时,允许DMASR.DlyCnt_Irq产生中断。
6)FrmCnt_IrqEn:使能Frame Count Complete Interrupt,当设置为1时,允许DMASR.FrmCnt_Irq产生中断
7)RdPntrNum:表示主机在控制状态当MM2S通道被配置为Genlock slave/Dynamic Genlock Master/Dynamic Genlock Slave或者Reserved时。
8)GenlockSrc:选择内部或者外部的genlock bus.
9)FrameCntEn:配置MM2S通道允许只有IRQFrameCount数量的transfers发生。
10)GenlockEn:使能Genlock或者Dynamic Genlock同步。
11)Reset:AXI VDMA MM2S通道的软件复位。将该设置为1,使得AXI VDMA MM2S通道被复位。Pending commands/transfers are flushed or completed.
12)Circular_Park:选择frame buffer Circular mode或者frame buffer Park mode.
0 = Park Mode -- 将会停在PARK_PTR_REG.RdFrmPntrRef指向的frame buffer.
1 = Circular Mode -- 连续循环的遍历frame buffer.
13)RS:启动或者停止VDMA通道。
0 = Stop: VDMA停止,同时保证当前的VDMA操作完成。
1 = Run: 启动VDMA操作。
该寄存器显示了VDMA中MM2S通道中的状态。
该寄存器被保留,如果在IDE中设置的Frame Buffers的数量少于17个或者寻址空间超过32位。
该寄存器为MM2S和S2MM传输提供了Park Pointer registers.
1)WrFrmStore:Write Frame Store number.指示当前被S2MM通道操作的frame number.每帧被处理的时候,VDMA操作该值持续更新。
2)RdFrmStore:Read Frame Store number.指示当前被MM2S通道操作的frame number。每帧被处理的时候,VDMA操作该值持续更新。
3)WrFrmPtrRef:Write Frame Pointer Reference.当Parked(S2MM_VDMACR.Circular_Park=0)时,S2MM通道parks的frame number由WrFrmPtrRef指定。
4)RdFrmPtrRef:Read Frame Pointer Reference.当Parked(MM2S_VDMACR.Circular_Park=0)时,MM2S通道parks的frame number由RdFrmPtrRef指定。
这个Vertical Size寄存器有两个作用:第一个是用来保存图像行大小的值,第二是启动MM2Stransfer.注意,该寄存器必须针对特定的通道被最后写入。
1)Frame Delay:指定Genlock slave在locked master后的最小数目的帧缓存。该字段只有在通道被用于Genlock Slave操作时有效。
2)Stride(Bytes):指定第一个像素值地址到下一视频行地址的字节为单位的大小。
最多32个帧缓存,指示video buffer的起始地址。
用于S2MM通道中的图像数据在垂直方向实现翻转。 1 - Enable Vertical Flip; 0 - Disable Vertical Flip.
AXI VDMA提供了两种时钟模式 ---- asynchronous and synchronous
对于asynchronous operation,AXI VDMA中可以含有五个不同的时钟域
1)AXI4-Lite总线接口处于s_axi_lite_aclk时钟域
2)MM2S在memory map side总线接口处于m_axi_mm2s_aclk时钟域
3)S2MM在memory map side总线接口处于m_axi_s2mm_aclk时钟域
4)S2MM在streaming side总线接口处于s_axis_s2mm_aclk时钟域
5)MM2S在streaming side总线接口处于m_axis_mm2s_aclk时钟域
这里要注意:
1) 在asynchronous mode, s_axi_lite_aclk时钟频率必须比 m_axi_mm2s_aclk和 m_axi_s2mm_aclk时钟频率都小。
2)保证memory map side的时钟频率等于或者大于streaming side的时钟频率。
3)在synchronous mode,m_axi_mm2s_aclk, m_axi_s2mm_aclk, m_axis_mm2s_aclk, s_axis_s2mm_aclk必须在同一个时钟域中,而s_axi_lite_aclk可以在相对较低的时钟频率下。
4)只有AXI4-Stream接口支持时钟频率的动态改变
/* 1, VDMA Read channel Configuration */
ReadvdmaConfig = XAxiVdma_LookupConfig(READ_VDMA_ID);
XAxiVdma_CfgInitialize(&Readvdma,ReadvdmaConfig,ReadvdmaConfig->BaseAddress);
/* Part 1: Initialize the VMDA Configuration*/
ReadvdmaConfig.FrameDelay = 0;
ReadvdmaConfig.EnableCircularBuf = 1;
ReadvdmaConfig.EnableSync = 0;
ReadvdmaConfig.PointNum = 0;
ReadvdmaConfig.EnableFrameCounter = 0;
/* Part 2: Configure the VDMA to access a frame with the same dimensions
as the current mode
*/
ReadvdmaConfig.VertSizeInput = HEIGHT;
ReadvdmaConfig.HoriSizeInput = WIDTH*3;
ReadvdmaConfig.FixedFrameStoreAddr = curFrame;
/* Part 3:Reset the stride and address values, in case the user manually changed them */
ReadvdmaConfig.Stride = STRIDE;
for(int i=0;i
/* 2, VDMA Write channel Configuration */
WritevdmaConfig = XAxiVdma_LookupConfig(WRITE_VDMA_ID);
XAxiVdma_CfgInitialize(&Writevdma,WritevdmaConfig,WritevdmaConfig->BaseAddress);
/* Part 1: Initialize the VDMA Configuration */
WritevdmaConfig.FrameDelay = 0;
WritevdmaConfig.EnableCircularBuf = 1;
WritevdmaConfig.EnableSync = 1;
WritevdmaConfig.PointNum = 1;
WritevdmaConfig.EnableFrameCounter = 0;
/* Part 2: Configuration the VMDA to access a frame with the same dimensions
as the current mode
*/
WritevdmaConfig.VertSizeInput = HEIGHT;
WritevdmaConfig.HoriSizeInput = WIDTH*3;
WritevdmaConfig.FixedFrameStoreAddr = curFrame;
/* Part 3: Reset the stride and address values, in case the user manually changed them */
WritevdmaConfig.Stride = STRIDE;
WritevdmaConfig.FrameStoreStartAddr[0]= FrameStoreStartAddr;
/* Part 4: Perform the VDMA driver calls required to start a transfer */
XAxiVdma_DmaConfig(&Writevdma,XAXIVDMA_WRITE,WritevdmaConfig);
XAxiVdma_DmaSetBufferAddr(&Writevdma,XAXIVDMA_WRITE,WritevdmaConfig->FrameStoreStartAddr);
XAxiVdma_DmaStart(&Writevdma,XAXIVDMA_WRITE);