http://www.wiki.xilinx.com/Linux+I2C+Driver
https://github.com/Xilinx/device-tree-xlnx/releases
VDMA的使用
https://blog.csdn.net/RZJMPB/article/details/50516181
在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流协议
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即可,具体可以查阅官方手册。
#include <stdio.h> #include "platform.h" #include "xil_io.h" int main() { init_platform(); xil_printf("----------The test is start......----------\n\r"); Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0x30, 0x4); //reset S2MM VDMA Control Register Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0x30, 0x8); //genlock Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0xAC, 0x08000000);//S2MM Start Addresses Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0xAC+4, 0x0A000000); Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0xAC+8, 0x09000000); Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0xA4, 1920*3);//S2MM Horizontal Size Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0xA8, 0x01002000);//S2MM Frame Delay and Stride Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0x30, 0x3);//S2MM VDMA Control Register Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0xA0, 1080);//S2MM Vertical Size start an S2MM transfer //AXI VDMA1 Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0x0, 0x4); //reset MM2S VDMA Control Register Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0x0, 0x8); //gen-lock Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0x5C, 0x08000000); //MM2S Start Addresses Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0x5C+4, 0x0A000000); Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0x5C+8, 0x09000000); Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0x54, 1920*3);//MM2S HSIZE Register Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0x58, 0x01002000);//S2MM FRMDELAY_STRIDE Register 1920*3=5760 对齐之后为8192=0x2000 Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0x0, 0x03);//MM2S VDMA Control Register Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0x50, 1080);//MM2S_VSIZE 启动传输 cleanup_platform(); return 0; }
Linux下使用VDMA的示例
#include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <sys/mman.h> #include <pthread.h> /* Register offsets */ #define OFFSET_PARK_PTR_REG 0x28 #define OFFSET_VERSION 0x2c #define OFFSET_VDMA_MM2S_CONTROL_REGISTER 0x00 #define OFFSET_VDMA_MM2S_STATUS_REGISTER 0x04 #define OFFSET_VDMA_MM2S_VSIZE 0x50 #define OFFSET_VDMA_MM2S_HSIZE 0x54 #define OFFSET_VDMA_MM2S_FRMDLY_STRIDE 0x58 #define OFFSET_VDMA_MM2S_FRAMEBUFFER1 0x5c #define OFFSET_VDMA_MM2S_FRAMEBUFFER2 0x60 #define OFFSET_VDMA_MM2S_FRAMEBUFFER3 0x64 #define OFFSET_VDMA_MM2S_FRAMEBUFFER4 0x68 #define OFFSET_VDMA_S2MM_CONTROL_REGISTER 0x30 #define OFFSET_VDMA_S2MM_STATUS_REGISTER 0x34 #define OFFSET_VDMA_S2MM_IRQ_MASK 0x3c #define OFFSET_VDMA_S2MM_REG_INDEX 0x44 #define OFFSET_VDMA_S2MM_VSIZE 0xa0 #define OFFSET_VDMA_S2MM_HSIZE 0xa4 #define OFFSET_VDMA_S2MM_FRMDLY_STRIDE 0xa8 #define OFFSET_VDMA_S2MM_FRAMEBUFFER1 0xac #define OFFSET_VDMA_S2MM_FRAMEBUFFER2 0xb0 #define OFFSET_VDMA_S2MM_FRAMEBUFFER3 0xb4 #define OFFSET_VDMA_S2MM_FRAMEBUFFER4 0xb8 /* S2MM and MM2S control register flags */ #define VDMA_CONTROL_REGISTER_START 0x00000001 #define VDMA_CONTROL_REGISTER_CIRCULAR_PARK 0x00000002 #define VDMA_CONTROL_REGISTER_RESET 0x00000004 #define VDMA_CONTROL_REGISTER_GENLOCK_ENABLE 0x00000008 #define VDMA_CONTROL_REGISTER_FrameCntEn 0x00000010 #define VDMA_CONTROL_REGISTER_INTERNAL_GENLOCK 0x00000080 #define VDMA_CONTROL_REGISTER_WrPntr 0x00000f00 #define VDMA_CONTROL_REGISTER_FrmCtn_IrqEn 0x00001000 #define VDMA_CONTROL_REGISTER_DlyCnt_IrqEn 0x00002000 #define VDMA_CONTROL_REGISTER_ERR_IrqEn 0x00004000 #define VDMA_CONTROL_REGISTER_Repeat_En 0x00008000 #define VDMA_CONTROL_REGISTER_InterruptFrameCount 0x00ff0000 #define VDMA_CONTROL_REGISTER_IRQDelayCount 0xff000000 /* S2MM status register */ #define VDMA_STATUS_REGISTER_HALTED 0x00000001 // Read-only #define VDMA_STATUS_REGISTER_VDMAInternalError 0x00000010 // Read or write-clear #define VDMA_STATUS_REGISTER_VDMASlaveError 0x00000020 // Read-only #define VDMA_STATUS_REGISTER_VDMADecodeError 0x00000040 // Read-only #define VDMA_STATUS_REGISTER_StartOfFrameEarlyError 0x00000080 // Read-only #define VDMA_STATUS_REGISTER_EndOfLineEarlyError 0x00000100 // Read-only #define VDMA_STATUS_REGISTER_StartOfFrameLateError 0x00000800 // Read-only #define VDMA_STATUS_REGISTER_FrameCountInterrupt 0x00001000 // Read-only #define VDMA_STATUS_REGISTER_DelayCountInterrupt 0x00002000 // Read-only #define VDMA_STATUS_REGISTER_ErrorInterrupt 0x00004000 // Read-only #define VDMA_STATUS_REGISTER_EndOfLineLateError 0x00008000 // Read-only #define VDMA_STATUS_REGISTER_FrameCount 0x00ff0000 // Read-only #define VDMA_STATUS_REGISTER_DelayCount 0xff000000 // Read-only typedef struct { unsigned int baseAddr; int vdmaHandler; int width; int height; int pixelLength; int fbLength; unsigned int* vdmaVirtualAddress; unsigned char* fb1VirtualAddress; unsigned char* fb1PhysicalAddress; unsigned char* fb2VirtualAddress; unsigned char* fb2PhysicalAddress; unsigned char* fb3VirtualAddress; unsigned char* fb3PhysicalAddress; pthread_mutex_t lock; } vdma_handle; int vdma_setup(vdma_handle *handle, unsigned int baseAddr, int width, int height, int pixelLength, unsigned int fb1Addr, unsigned int fb2Addr, unsigned int fb3Addr) { handle->baseAddr=baseAddr; handle->width=width; handle->height=height; handle->pixelLength=pixelLength; handle->fbLength=pixelLength*width*height; handle->vdmaHandler = open("/dev/mem", O_RDWR | O_SYNC); handle->vdmaVirtualAddress = (unsigned int*)mmap(NULL, 65535, PROT_READ | PROT_WRITE, MAP_SHARED, handle->vdmaHandler, (off_t)handle->baseAddr); if(handle->vdmaVirtualAddress == MAP_FAILED) { perror("vdmaVirtualAddress mapping for absolute memory access failed.\n"); return -1; } handle->fb1PhysicalAddress = fb1Addr; handle->fb1VirtualAddress = (unsigned char*)mmap(NULL, handle->fbLength, PROT_READ | PROT_WRITE, MAP_SHARED, handle->vdmaHandler, (off_t)fb1Addr); if(handle->fb1VirtualAddress == MAP_FAILED) { perror("fb1VirtualAddress mapping for absolute memory access failed.\n"); return -2; } handle->fb2PhysicalAddress = fb2Addr; handle->fb2VirtualAddress = (unsigned char*)mmap(NULL, handle->fbLength, PROT_READ | PROT_WRITE, MAP_SHARED, handle->vdmaHandler, (off_t)fb2Addr); if(handle->fb2VirtualAddress == MAP_FAILED) { perror("fb2VirtualAddress mapping for absolute memory access failed.\n"); return -3; } handle->fb3PhysicalAddress = fb3Addr; handle->fb3VirtualAddress = (unsigned char*)mmap(NULL, handle->fbLength, PROT_READ | PROT_WRITE, MAP_SHARED, handle->vdmaHandler, (off_t)fb3Addr); if(handle->fb3VirtualAddress == MAP_FAILED) { perror("fb3VirtualAddress mapping for absolute memory access failed.\n"); return -3; } memset(handle->fb1VirtualAddress, 255, handle->width*handle->height*handle->pixelLength); memset(handle->fb2VirtualAddress, 255, handle->width*handle->height*handle->pixelLength); memset(handle->fb3VirtualAddress, 255, handle->width*handle->height*handle->pixelLength); return 0; } void vdma_halt(vdma_handle *handle) { vdma_set(handle, OFFSET_VDMA_S2MM_CONTROL_REGISTER, VDMA_CONTROL_REGISTER_RESET); vdma_set(handle, OFFSET_VDMA_MM2S_CONTROL_REGISTER, VDMA_CONTROL_REGISTER_RESET); munmap((void *)handle->vdmaVirtualAddress, 65535); munmap((void *)handle->fb1VirtualAddress, handle->fbLength); munmap((void *)handle->fb2VirtualAddress, handle->fbLength); munmap((void *)handle->fb3VirtualAddress, handle->fbLength); close(handle->vdmaHandler); } unsigned int vdma_get(vdma_handle *handle, int num) { return handle->vdmaVirtualAddress[num>>2]; } void vdma_set(vdma_handle *handle, int num, unsigned int val) { handle->vdmaVirtualAddress[num>>2]=val; } void vdma_status_dump(int status) { if (status & VDMA_STATUS_REGISTER_HALTED) printf(" halted"); else printf("running"); if (status & VDMA_STATUS_REGISTER_VDMAInternalError) printf(" vdma-internal-error"); if (status & VDMA_STATUS_REGISTER_VDMASlaveError) printf(" vdma-slave-error"); if (status & VDMA_STATUS_REGISTER_VDMADecodeError) printf(" vdma-decode-error"); if (status & VDMA_STATUS_REGISTER_StartOfFrameEarlyError) printf(" start-of-frame-early-error"); if (status & VDMA_STATUS_REGISTER_EndOfLineEarlyError) printf(" end-of-line-early-error"); if (status & VDMA_STATUS_REGISTER_StartOfFrameLateError) printf(" start-of-frame-late-error"); if (status & VDMA_STATUS_REGISTER_FrameCountInterrupt) printf(" frame-count-interrupt"); if (status & VDMA_STATUS_REGISTER_DelayCountInterrupt) printf(" delay-count-interrupt"); if (status & VDMA_STATUS_REGISTER_ErrorInterrupt) printf(" error-interrupt"); if (status & VDMA_STATUS_REGISTER_EndOfLineLateError) printf(" end-of-line-late-error"); printf(" frame-count:%d", (status & VDMA_STATUS_REGISTER_FrameCount) >> 16); printf(" delay-count:%d", (status & VDMA_STATUS_REGISTER_DelayCount) >> 24); printf("\n"); } void vdma_s2mm_status_dump(vdma_handle *handle) { int status = vdma_get(handle, OFFSET_VDMA_S2MM_STATUS_REGISTER); printf("S2MM status register (%08x):", status); vdma_status_dump(status); } void vdma_mm2s_status_dump(vdma_handle *handle) { int status = vdma_get(handle, OFFSET_VDMA_MM2S_STATUS_REGISTER); printf("MM2S status register (%08x):", status); vdma_status_dump(status); } void vdma_start_triple_buffering(vdma_handle *handle) { // Reset VDMA vdma_set(handle, OFFSET_VDMA_S2MM_CONTROL_REGISTER, VDMA_CONTROL_REGISTER_RESET); vdma_set(handle, OFFSET_VDMA_MM2S_CONTROL_REGISTER, VDMA_CONTROL_REGISTER_RESET); // Wait for reset to finish while((vdma_get(handle, OFFSET_VDMA_S2MM_CONTROL_REGISTER) & VDMA_CONTROL_REGISTER_RESET)==4); while((vdma_get(handle, OFFSET_VDMA_MM2S_CONTROL_REGISTER) & VDMA_CONTROL_REGISTER_RESET)==4); // Clear all error bits in status register vdma_set(handle, OFFSET_VDMA_S2MM_STATUS_REGISTER, 0); vdma_set(handle, OFFSET_VDMA_MM2S_STATUS_REGISTER, 0); // Do not mask interrupts vdma_set(handle, OFFSET_VDMA_S2MM_IRQ_MASK, 0xf); int interrupt_frame_count = 3; // Start both S2MM and MM2S in triple buffering mode vdma_set(handle, OFFSET_VDMA_S2MM_CONTROL_REGISTER, (interrupt_frame_count << 16) | VDMA_CONTROL_REGISTER_START | VDMA_CONTROL_REGISTER_GENLOCK_ENABLE | VDMA_CONTROL_REGISTER_INTERNAL_GENLOCK | VDMA_CONTROL_REGISTER_CIRCULAR_PARK); vdma_set(handle, OFFSET_VDMA_MM2S_CONTROL_REGISTER, (interrupt_frame_count << 16) | VDMA_CONTROL_REGISTER_START | VDMA_CONTROL_REGISTER_GENLOCK_ENABLE | VDMA_CONTROL_REGISTER_INTERNAL_GENLOCK | VDMA_CONTROL_REGISTER_CIRCULAR_PARK); while((vdma_get(handle, 0x30)&1)==0 || (vdma_get(handle, 0x34)&1)==1) { printf("Waiting for VDMA to start running...\n"); sleep(1); } // Extra register index, use first 16 frame pointer registers vdma_set(handle, OFFSET_VDMA_S2MM_REG_INDEX, 0); // Write physical addresses to control register vdma_set(handle, OFFSET_VDMA_S2MM_FRAMEBUFFER1, handle->fb1PhysicalAddress); vdma_set(handle, OFFSET_VDMA_S2MM_FRAMEBUFFER2, handle->fb1PhysicalAddress); vdma_set(handle, OFFSET_VDMA_S2MM_FRAMEBUFFER3, handle->fb1PhysicalAddress); vdma_set(handle, OFFSET_VDMA_MM2S_FRAMEBUFFER1, handle->fb2PhysicalAddress); vdma_set(handle, OFFSET_VDMA_MM2S_FRAMEBUFFER2, handle->fb2PhysicalAddress); vdma_set(handle, OFFSET_VDMA_MM2S_FRAMEBUFFER3, handle->fb2PhysicalAddress); // Write Park pointer register vdma_set(handle, OFFSET_PARK_PTR_REG, 0); // Frame delay and stride (bytes) vdma_set(handle, OFFSET_VDMA_S2MM_FRMDLY_STRIDE, handle->width*handle->pixelLength); vdma_set(handle, OFFSET_VDMA_MM2S_FRMDLY_STRIDE, handle->width*handle->pixelLength); // Write horizontal size (bytes) vdma_set(handle, OFFSET_VDMA_S2MM_HSIZE, handle->width*handle->pixelLength); vdma_set(handle, OFFSET_VDMA_MM2S_HSIZE, handle->width*handle->pixelLength); // Write vertical size (lines), this actually starts the transfer vdma_set(handle, OFFSET_VDMA_S2MM_VSIZE, handle->height); vdma_set(handle, OFFSET_VDMA_MM2S_VSIZE, handle->height); } int vdma_running(vdma_handle *handle) { // Check whether VDMA is running, that is ready to start transfers return (vdma_get(handle, 0x34)&1)==1; } int vdma_idle(vdma_handle *handle) { // Check whtether VDMA is transferring return (vdma_get(handle, OFFSET_VDMA_S2MM_STATUS_REGISTER) & VDMA_STATUS_REGISTER_FrameCountInterrupt)!=0; } int main() { int j, i; vdma_handle handle; // Setup VDMA handle and memory-mapped ranges vdma_setup(&handle, 0x43010000, 640, 480, 4, 0x1f400000, 0x1f500000, 0x1f600000); // Start triple buffering vdma_start_triple_buffering(&handle); // Run for 10 seconds, just monitor status registers for(i=0; i<10; i++) { vdma_s2mm_status_dump(&handle); vdma_mm2s_status_dump(&handle); memset(handle.fb2VirtualAddress, 0xCC+i, 640*480*4); printf("FB1:\n"); for (j = 0; j < 256; j++) printf(" %02x", handle.fb1VirtualAddress[j]); printf("\n"); sleep(1); } // Halt VDMA and unmap memory ranges vdma_halt(&handle); }main函数测试方法很简单,就是往MM2S写数据,然后把S2MM的数据打印出来看看是不是对的;不过先要把vivado工程里的VDMA stream端的MM2S和S2MM直接连起来,形成一个回环才能测试;还要把VDMA加到设备树里面去,下面是个例子,要注意地址是否与vivado分配的地址一致
axi_vdma_2: fast_corner_vdma@43020000 { compatible = "xlnx,axi-vdma-1.00.a"; #dma-cells = <1>; reg = <0x43020000 0x10000>; xlnx,num-fstores = <0x8>; xlnx,flush-fsync = <0x1>; interrupt-parent = <&intc>; dma-channel@43020000 { compatible = "xlnx,axi-vdma-mm2s-channel"; interrupts = <0 52 0x4>; xlnx,datawidth = <0x40>; }; dma-channel@43020030 { compatible = "xlnx,axi-vdma-s2mm-channel"; interrupts = <0 51 0x4>; xlnx,datawidth = <0x40>; }; };
参考:
https://blog.csdn.net/luotong86/article/details/52486365