zoukankan      html  css  js  c++  java
  • VDMA搭建视频通路总结

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

    image057.png

    image059.png

    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个,多一个响应的原因在于读写的主从性。

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

  • 相关阅读:
    97. Interleaving String
    96. Unique Binary Search Trees
    95. Unique Binary Search Trees II
    94. Binary Tree Inorder Traversal
    odoo many2many字段 指定打开的form视图
    docker sentry 配置文件位置
    postgres 计算时差
    postgres 字符操作补位,字符切割
    postgres判断字符串是否为时间,数字
    odoo fields_view_get
  • 原文地址:https://www.cnblogs.com/Dinging006/p/9326352.html
Copyright © 2011-2022 走看看