本人大四小白最近尝试使用ZYNQ进行图像处理,第一步自然是实现输入输出,参考了南京米联电子等前辈的实现方法后,自己尝试实现了一种新方法,并实现了OV7725的视频输入输出,在此分享一下经验~
一、简介
ZYNQ包括一个Cortex A9双核处理器(ps)和外部FPGA(pl),两者之间除了少量EMIO相互连接外,PS提供了对外的几个AXI总线接口。
在ZYNQ体系中,DDR控制器处于PS部分,所以PL必须通过AFI接口来使用ddr资源。在图像系统中,帧缓存是必要的,但是PL部分可用的BRAM十分有限,且是实现其他逻辑的关键资源,不宜作为帧缓存使用,在图像分辨率高时,其大小也不够,所以一个完备的图像系统必然需要使用DDR作为帧缓存介质。
为了实现高效传输,对于大量数据通常使用DMA(Direct Memory Acces)进行,为了PL端与PS的通信,Xilinx官方提供了AXIDMA,AXI VDMA IP,使PL可以通过AFI接口直接访问DDR,这些DMA的IP对外部输入输出均为AXI Stream流协议,所以该系统的关键在于将OV系列摄像头与VGA显示器封装为AXIS协议接口以及其附加问题,VDMA的使用较为简单。
二、AXI Stream协议及视频流格式
AXI Stream关键的只有两根信号线,及tvalid核tready。tvalid是主设备驱动的信号,表示Stream上的数据是有效的,tready由从设备驱动,表示从设备下一个时钟到来时能够接收数据。AXI Stream的特点是这两个信号不存在互相等待的关系,及数据传输只发生再两者均有效的时候,从而效率很高,可以认为是连续传输,避免了死锁的情况。
AXI Stream还有一些附加信号,通常是伴随再数据中传输,主要包括tuser,tlast,tkeep等。tlast在标准流协议中表示一个数据包结束,伴随最后一个数据传出,tuser可以很多位,是用户定义信号,用来表达用户自己需要传输的数据。tkeep是字节修饰符,位数位数据宽度/8,当总线数据某字节有效时,tkeep对应位就为高。
Xilinx 所有与视频有关的IP均遵循一套AXI Stram视频流协议,其中对tlast与tuser赋予了特殊的含义。视频流中除了数据以外,包好start of frame和end of line信号,其中eol用tlast表示,sof用tuser表示。这两个信号均伴随像素数据传输,只保持一次有效传输的时间,sof和一帧第一个像素一起传出,eol和一行最后一个像素一起传输。视频流中有时会加入tkeep信号,无特殊情况时需全部拉高。
三、OVSensor2Axis
目前采用的摄像头对外均为8位数据,vsync,href,pclk这些信号线,时序如下
DVP格式无法与官方提供的Video in to AXI Stream IP核适应,米联公司采用的做法是写了一个IP核将输入时序转化为上述IP所需要的时序,这个做法一是有些拖泥带水,二是使用起来不方便,不友好。因此我参考官方IP的结构,写了一个将OV摄像头时序直接转化为AXI Stream格式的IP。
以下为官方Video in to AXI Stream的内部逻辑结构图。
为了适配OV系列的摄像头,主要对Data Formatter内逻辑进行修改。首先需要对data作8位转16位变换,使用移位寄存器即可。其次,wr_en只能发生在一次有效的16位上,所以需要在Href为高后生成一个频率为pclk一半的信号供wr_en使用。
图中可以看出,sof和eol信号是组合进数据存入FIFO中的。提取sof和eol是难点,其中sof信号提取可采用RS触发器,当VSYNC上升沿时,将sof至一,在之后第一次wr_en发生时,清零,这样就可以使sof被正常存入了fifo中。eol信号的提取依赖于Href的下降沿,下降沿发生后一个有效数据及为一行的最后一个像素,此时产生eol存入。
为了同步系统,加入使能信号,当使能信号被拉高后,等待下一个vsync后才开始一次传输,保证传输的是完整的一帧图像。
因为AXIS总线频率一般与整个系统的总线频率一致,所以FIFO主要起到了跨时钟域传输的作用。Tvalid信号发生在FIFO不为空的时候,rd_en信号发生在tvalid与tready同时为高的时候。
因为采用传统封装的摄像头PCLK频率受限在96Mhz以内,算上消隐阶段的PCLK数量,在保证帧率大于30(肉眼无法察觉到明显卡顿)的情况下,仅能支持到720p(1280*720)30fps图像的输入,否则根据试验结果,会发现PCLK已经无法被系统正常采样到了。如果想实现更高分辨率,需要尝试采用MIPI接口封装的摄像头。
四、Axis2VGA
VGA显示时序比较简单,关键再与该IP无发以地址方式直接访问DDR,只能从流接口中获得数据和同步信号,该IP主要就负责处理这方面的事务。
因为VGA显示时视频时连续的,所以总线输入速率一定要大于显示速率,否则视频显示就失败了。根据Xilinx官方文档,Artix7-1的芯片AXIS最高速率为150Mhz,则根据VGA60FPS时序标准的要求,在此类芯片上最高只能支持1440*900分辨率的视频显示,及129W像素的分辨率,更高无法实现。由于当前系统仅能支持到720p的分辨率,所以尚能兼容。
由于这个项目本身就是为了节省BRAM而设计的,所以该IP不可能再有一个完整的帧缓冲供使用,只能继续采用检测同步加FIFO的方式。因为tuser信号伴随第一个像素被输出,当tready为低时将会在总线上保持,所以当检测到tuser信号时,开启整个系统,使能FIFO输入端,并开始运行VGA时序。为了保证VGA时钟能从FIFO中取出数据,VGA时序开启将落后开启FIFO 8个总线时钟周期,在此之后由于总线速率大于VGA速率,将不存在都不到数据的情况。从此,VGA将能始终正常运行。为了能实现一定的控制,该模块也添加了使能端。
为了增强通用性,VGA时序所用的同步信号等时常均被参数化,当需要使用不同分辨率时只要在例化时修改即可。由于很多分辨率对应的参数不易查询,可以先加入一个Video Timing Generator的IP,选择对应分辨率后可以看到对应参数,记录下后再删除IP即可。
五、AXI VDMA使用
VDMA是DMA针对视频传输的改进版本,不同之处在于它加入了自动循环和自动切换帧缓存地址的功能。因为在很多系统中,图像输入速率不一定能与输出速率匹配,且有时需要对图像作一些处理,导致该空间不能被送显,所以一个完整的图像系统中通常有多个帧缓存,来保证输出数据不会被撕裂。VDMA可以最多支持32个帧缓存,并且经过配置能够自动的在各个帧存之间进行切换,并自动互相避让,从而保证图像稳定。
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相反。
系统如下,是不是简洁多了。。。。