码迷,mamicode.com
首页 > 其他好文 > 详细

xilinx vivado zynq vdma仿真及应用详解(一)

时间:2016-06-06 01:13:31      阅读:1139      评论:0      收藏:0      [点我收藏+]

标签:

很多人用zynq平台做视频图像开发,但是对vdma了解比较少,上手起来稍微有些困难,我针对这一现象,做了一个基于vivado和modelsim的仿真和应用测试工程,并写篇文章做些介绍,希望能对大家有帮助。
一:xilinx vdma IP例化以及接口介绍
技术分享
上面图片就是在vivado2015.4中例化vdma的界面,首先对参数做些介绍:
Frame Buffers :选择vdma缓存几帧图像,这里默认是写通道和读通道都设置相同的缓存帧数,具体设置多少帧合适一般根据应用来定,比如读写带宽相同,想用ddr作为一个乒乓buffer,那就可以设置成2帧,写第一个地址,读第二个地址,写第二个地址,读第一个地址。这里面设置几帧,就要在vdma寄存器配置的时候设置几个帧起始地址。
Memory Map Data Width:代表数据到达AXI4总线上的位宽,比如这里设置成64,那就代表M_AXI_XX总线上的数据位宽是64bit,这时候如果stream上的数据是32bit,那vdma内部会有一个带宽转换模块,把数据拼成64bit。
Burst Size : AXI总线上突发传输的长度,一般设置为16
Stream Data Width:vdma与pl逻辑部分通过axi stream协议交互数据,这里代表stream数据位宽
Line Buffer Depth:vdma内部会有一个行缓存fifo,stream数据会先写入fifo,然后AXI总线逻辑会读出到总线上,这个深度就代表fifo的深度。设置原则(个人理解):如果AXI总线数据带宽是stream总线数据带宽的1.5倍以上,这个fifo深度可以设置的小一点,如果AXI总线带宽小于1.5倍的stream总线带宽,那fifo的深度至少要是图像一个有效行的一半。
Advanced : 这里面只说一下Fsync Options,这个信号是什么意思呢,就是告诉vdma什么时候开始运行,一般s2mm通道选择tuser,就是说在tuser 拉高的时候开始传输。mm2s通道,可以选择none,也可以选择 mm2s_fsync,这里介绍一下这两个的区别。
none : 就是没有同步信号,但这并不是说没有开始信号,而是只要mm2s_stream通道tready拉高,就开始传输,相当于free模式
mm2s_fsync:当这个信号发生一个下降沿的时候开始传输,如果没有这个下降沿,即使mm2s_stream通道tready拉高也不会传输
下面是接口介绍:
M_AXI_XX : axi4总线接口,用来与ddr交互数据
M_AXIS_XX , S_AXIS_XX : axi stream接口,用来与pl交互数据
S_AXI_LITE :控制总线,接到ps的gp口或者写一个axilite master总线去配置
其他接口不做介绍
二:下面开始一步步的详解如何搭建一个vdma的仿真工程
FPGA的开发,离不开仿真,很少有人能直接写好代码上板就成功的,仿真必不可少。但是有些应用要用到vdma,vdma又要和ddr做数据交互,这样做起来就很麻烦了,我这里就实现了一个简单的方法,可以测试vdma,又不用去例化MIG搞什么ddr。下面开始!
系统框图:
技术分享
(1)因为是要仿真vdma,vdma顾名思义就是video dma,那肯定要先做一个视频模块,注意,我这里除了vdma和fifo用xilinx的ip。其他的都不用ip,这样更通用性。
我这里就把这个视频发生模块叫做sensor,可以理解为xilin的tpg模块,sensor模块的接口如下:

module sensor 
( 
    input rst, 
    input clk, 
    output reg vsync, 
    output reg hsync,
    output reg de,
    output reg vblank,
    output reg[31:0]pix_out
);

parameter SENSOR_ACT_W = 640;
parameter SENSOR_ACT_H = 480;
parameter SENSOR_WIDTH = 800;
parameter SENSOR_HEIGHT = 600;
parameter H_START       = 80; 
parameter V_START       = 60; 

我这里构建了一个图像传感器,总像素数是600*800,有效像素是 480*640,水平有效像素开始位置是80,垂直有效像素开始位置是60,这个模块会读取一个本地图像数据,rgb格式,这里为了测试方便,直接把像素输出位宽设置为32bit。
(2)video转axis模块,相当于 xilinx的vid in to stream模块,接口如下:

module video2axis #
(
    parameter DW = 32,
    parameter WIDTH = 640,
    parameter HEIGHT = 480
)
(
    input axis_clk,
    input axis_aresetn,

    // axis
    input              reg_axis_s2mm_start,
    output [DW-1:0] m_axis_tdata,
    output  [DW/8-1:0] m_axis_tkeep,
    output reg         m_axis_tvalid,
    output             m_axis_tlast,
    output             m_axis_tuser,
    input              m_axis_tready,
    // video data
    input              video_clk,
    input              video_rst,
    input              video_hsync,
    input              video_vsync,
    input              video_hblank,
    input              video_vblank,
    input              video_de,
    input  [DW-1:0]    video_data

);

这个模块主要用到一个fifo来做数据缓存,只要注意一下stream协议的握手操作即可,由于stream协议比较简单,这里就不多说了。至此,video数据就转换到了stream数据。
(3)axis转video模块,接口如下:

module axis2video#
(
    parameter DW = 32,
    parameter WIDTH = 640,
    parameter HEIGHT = 480
)
(
    input axis_clk,
    input axis_aresetn,

    // axis
    input             reg_axis_mm2s_start,
    input [DW-1:0]    s_axis_tdata,

    input             s_axis_tvalid,
    input             s_axis_tlast,
    input             s_axis_tuser,
    output reg        s_axis_tready,
    // video data
    input              video_clk,
    input              video_rst,

    input              video_hsync_i,
    input              video_vsync_i,
    input              video_hblank_i,
    input              video_vblank_i,
    input              video_de_i,

    output             video_hsync_o,
    output             video_vsync_o,
    output             video_hblank_o,
    output             video_vblank_o,
    output             video_de_o,  
    output  reg[DW-1:0]   video_data
);

这个模块相当于xilinx的vid out模块,我这里是简化版的,xilinx的ip写的太复杂了,而且不容易用起来,其实也就是用一个fifo做数据缓存,然后根据外部video时序从fifo读出到输出。
(4)video timing gen模块,接口如下:

module video_timing_gen #
(
    parameter SENSOR_ACT_W = 640,
    parameter SENSOR_ACT_H = 480,
    parameter SENSOR_WIDTH = 800,
    parameter SENSOR_HEIGHT = 600,
    parameter SENSOR_HSYNC_START = 0,
    parameter SENSOR_HSYNC_STOP = 40,
    parameter SENSOR_VSYNC_START = 0,
    parameter SENSOR_VSYNC_STOP = 4
)
( 
    input rst_n, 
    input video_clk, 
    input  [12:0]reg_h_start,
    input  [12:0]reg_v_start,
    output reg vsync, 
    output reg hsync,
    output reg de,
    output reg vblank,
    output reg hblank
);

此模块产生视频时序,提供给 axis2video模块,相当于xilinx的vtc模块。
(5) axi slave模块,接口如下:

`define C_S_AXI_ADDR_WIDTH 32

module axi_slave #(
  parameter integer C_S_AXI_ID_WIDTH = 6,
  parameter integer C_S_AXI_DATA_WIDTH = 32
) (

  input  wire S_AXI_ACLK,
  input  wire S_AXI_ARESETN,
  input  wire [C_S_AXI_ID_WIDTH-1:0] S_AXI_AWID,
  input  wire [`C_S_AXI_ADDR_WIDTH-1:0] S_AXI_AWADDR,
  input  wire [7:0] S_AXI_AWLEN,
  input  wire [2:0] S_AXI_AWSIZE,
  input  wire [1:0] S_AXI_AWBURST,
  input  wire S_AXI_AWVALID,
  output wire S_AXI_AWREADY,
  input  wire [C_S_AXI_DATA_WIDTH-1:0] S_AXI_WDATA,
  input  wire [C_S_AXI_DATA_WIDTH/8-1:0] S_AXI_WSTRB,
  input  wire S_AXI_WLAST,
  input  wire S_AXI_WVALID,
  output wire S_AXI_WREADY,
  output wire [C_S_AXI_ID_WIDTH-1:0] S_AXI_BID,
  output wire [1:0] S_AXI_BRESP,
  output wire S_AXI_BVALID,
  input  wire S_AXI_BREADY,
  input  wire [C_S_AXI_ID_WIDTH-1:0] S_AXI_ARID,
  input  wire [`C_S_AXI_ADDR_WIDTH-1:0] S_AXI_ARADDR,
  input  wire [7:0] S_AXI_ARLEN,
  input  wire [2:0] S_AXI_ARSIZE,
  input  wire [1:0] S_AXI_ARBURST,
  input  wire S_AXI_ARVALID,
  output wire S_AXI_ARREADY,
  output wire [C_S_AXI_ID_WIDTH-1:0] S_AXI_RID,
  output wire [C_S_AXI_DATA_WIDTH-1:0] S_AXI_RDATA,
  output wire [1:0] S_AXI_RRESP,
  output wire S_AXI_RLAST,
  output wire S_AXI_RVALID,
  input  wire S_AXI_RREADY
);

这块模块是根据xilinx官方提供的参考设计基础上修改而来的(xapp1168),协议部分完全没有改动,这里拿他当做ddr,具体修改是这样的,
reg [31:0] mem [32’h01000000:0];
用寄存器组来模拟ddr
此模块会根据axi master的时序来计算出要读写的地址
assign write_mem_address = axi_awv_awr_flag ? axi_awaddr: 0;
assign read_mem_address = axi_arv_arr_flag ? axi_araddr: 0;
写操作:mem[write_mem_address>>2] <= #1 S_AXI_WDATA;
读操作:mem_data_out <= mem[read_mem_address>>2];
做此修改以后,这个模块就可以当做ddr来用,为仿真提供了很大的方便
(6)vdma模块,这个就用xilinx的vdma ip,注意,我这里不是在block design里面例化,所以端口需要自己在hdl里面做连接的。
这里还有一个模块是 axi lite master模块,作用是用来配置vdma的寄存器,这个模块也是xilinx提供的,只需要做小量修改即可

always @(write_index)
  begin
     case (write_index)
       // AXI VDMA 0 Set Up
       1:  awaddr <= 32‘h43000030; 
       2:  awaddr <= 32‘h43c000ac;  
       3:  awaddr <= 32‘h43c000b0;  
       4:  awaddr <= 32‘h43c000a8;
       5:  awaddr <= 32‘h43c000a4;
       6:  awaddr <= 32‘h43c000a0;
   7:  awaddr <= 32‘h43000000; 
       8:  awaddr <= 32‘h43c0005c;  
       9:  awaddr <= 32‘h43c00060; 
       10: awaddr <= 32‘h43c00058;
       11: awaddr <= 32‘h43c00054;
       12: awaddr <= 32‘h43c00050;
       default: awaddr <= 32‘h00000000;
     endcase 
  end

always @(write_index)
  begin
     case (write_index)
       // AXI VDMA 0 Set Up
       1:  wdata <= 32‘h00000003;   
       2:  wdata <= 32‘h00800000;    
       3:  wdata <= 32‘h00000000; 
       4:  wdata <= 640*4;
       5:  wdata <= 640*4;
       6:  wdata <= 480;
   7:  wdata <= 32‘h00000003;   
       8:  wdata <= 32‘h00000000;    
       9:  wdata <= 32‘h00800000;   
       10: wdata <= 640*4;
       11:  wdata <= 640*4;
       12: wdata <= 480;
       default: wdata <= 32‘h00000000;
     endcase
  end

我这里是把ddr作为一个乒乓buffer,所以vdma缓存帧数选择2帧,寄存器配置里面就配置两个传输地址。这样就完成了对vdma的寄存器配置。
编写test bench top文件,把这些模块连接起来,至此,仿真工程就全部写好了。
三:仿真
(1)vdma工作流程介绍
第一步,对vdma寄存器进行配置,并打开使能,这时候vdma处于待命状态,什么时候开始传输呢,下面详细介绍
对于S2MM通道:之前在讲vdma配置的时候有一个Advanced选项,里面有Fsync Options选项,可选none,s2mm_fsync,s2mm_tuer,三种同步模式。
none就是只要vdma就绪,就立马准备接收数据,不需要同步信号。
s2mm_fsync,当选择此模式时,vdma 模块会有一个s2mm_fsync引脚,一般情况下是把视频帧同步信号连到这上面,当检测到s2mm_fsync引脚有一个下降沿的时候,vdma正式进入传输状态。
s2mm_tuer,这个信号和s2mm_fsync这个信号类似,但他是在stream协议里面的,vdma检测到s2mm_tuer拉高以后(tuser只在一帧数据的第一个像素位置拉高),正式进入传输状态
对于MM2S通道,同样在vdma配置的Advanced选项里面有 none,mm2s_fsync两种选择模式。
none不需要同步信号,只要axis_mm2s通道的tready拉高,就开始从ddr读取数据进行传输,选择这种模式一般主要是把ddr里面的数据读到pl里面进行处理,而不是转成视频
mm2s_fsync,选择此同步模式,一般是把ddr的数据转成视频数据,注意,这里重点讲这个同步模式,当vdma的读通道选择此同步模式的时候,vdma模块会有一个mm2s_fsync信号,这个信号在读操作中非常重要。当vdma寄存器配置完成并开启传输,mm2s通道进入等待过程,一直等到mm2s引脚信号出现一个下降沿,这时候vdma启动读操作,会从ddr预读一些数据到内部linebuffer,等到axis_mm2s通道的tready信号拉高,数据就开始传输,进入axis2video模块的fifo,当axis2video内部fifo满了以后,会拉低tready,这时候就会反馈到vdma,暂停读操作,一直等到axis2video模块的视频时序输入数据有效信号,这时候视频开始输出,axis2video内部fifo数据减少,axis_mm2s通道开始恢复传输,继续从vdma读出数据,vdma再通过axi总线从ddr读取数据,如此反复,完成ddr数据到video数据的转换
(2)仿真实践
首先在vivado平台例化一个vdma ip,然后添加进上述的各个模块,代码层级如下:
技术分享
在tb_top里面对各个模块做连接,这部分源码如下:

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// 
// Engineer: EEPROM
// 
//////////////////////////////////////////////////////////////////////////////////


module tb_top();

reg reset = 1‘b1;
reg video_clk = 1‘b0;
reg axis_clk = 1‘b0;
reg axi_clk = 1‘b0;
wire axi_lite_clk = video_clk;

wire sensor_vsync;
wire sensor_hsync;
wire sensor_de;
wire sensor_vblank;
wire [31:0]sensor_data;
wire [31:0]axis_s2mm_tdata;
wire axis_s2mm_tvalid;
wire axis_s2mm_tlast;
wire axis_s2mm_tuser;
wire axis_s2mm_tready;

wire [31:0]axis_mm2s_tdata;
wire axis_mm2s_tvalid;
wire axis_mm2s_tlast;
wire axis_mm2s_tuser;
wire axis_mm2s_tready;
//axi lite
wire [31:0]axi_lite_master_araddr;
wire [2:0]axi_lite_master_arprot;
wire axi_lite_master_arready;
wire axi_lite_master_arvalid;
wire [31:0]axi_lite_master_awaddr;
wire [2:0]axi_lite_master_awprot;
wire axi_lite_master_awready;
wire axi_lite_master_awvalid;
wire axi_lite_master_bready;
wire [1:0]axi_lite_master_bresp;
wire axi_lite_master_bvalid;
wire [31:0]axi_lite_master_rdata;
wire axi_lite_master_rready;
wire [1:0]axi_lite_master_rresp;
wire axi_lite_master_rvalid;
wire [31:0]axi_lite_master_wdata;
wire axi_lite_master_wready;
wire [3:0]axi_lite_master_wstrb;
wire axi_lite_master_wvalid;
// axi
wire   [31:0]   S_AXI_awaddr ;
wire   [1:0]    S_AXI_awburst;
wire   [3:0]    S_AXI_awcache;
wire   [5:0]    S_AXI_awid   ;
wire   [7:0]    S_AXI_awlen  ;
wire   [1:0]    S_AXI_awlock ;
wire   [2:0]    S_AXI_awprot ;
wire   [3:0]    S_AXI_awqos  ;
wire            S_AXI_awready;
wire   [2:0]    S_AXI_awsize ;
wire            S_AXI_awvalid;
wire   [5:0]    S_AXI_bid    ;
wire            S_AXI_bready ;
wire   [1:0]    S_AXI_bresp  ;
wire            S_AXI_bvalid ;
wire   [31:0]   S_AXI_wdata  ;
wire   [5:0]    S_AXI_wid    ;
wire            S_AXI_wlast  ;
wire            S_AXI_wready ;
wire   [3:0]    S_AXI_wstrb  ;
wire            S_AXI_wvalid ;
wire            S_AXI_arready;
wire            S_AXI_rlast;
wire            S_AXI_rvalid;
wire  [1:0]     S_AXI_rresp;
wire  [31:0]    S_AXI_rdata;
wire  [5:0]     S_AXI_rid;
wire            S_AXI_arvalid; 
wire            S_AXI_rready;
wire [1:0]      S_AXI_arburst;
wire [1:0]      S_AXI_arlock;
wire [2:0]      S_AXI_arsize;
wire [2:0]      S_AXI_arprot;
wire [31:0]     S_AXI_araddr;
wire [3:0]      S_AXI_arcache;
wire [7:0]      S_AXI_arlen;
wire [3:0]      S_AXI_arqos;
wire [5:0]      S_AXI_arid;

initial begin
#100;
reset=0;
end
always # 10 video_clk = ~video_clk;
always # 5 axis_clk = ~axis_clk;
always # 2 axi_clk = ~axi_clk;
sensor u_sensor
( 
    .rst     (reset), 
    .clk     (video_clk), 
    .vsync   (sensor_vsync), 
    .hsync   (sensor_hsync),
    .de      (sensor_de),
    .vblank  (sensor_vblank), 
    .pix_out (sensor_data)
);
video2axis u_video2axis
(
    .axis_clk       (axis_clk),
    .axis_aresetn   (!reset),
    .reg_axis_start (1‘b1),
    .m_axis_tdata   (axis_s2mm_tdata ),
    .m_axis_tkeep   (axis_s2mm_tkeep ),
    .m_axis_tvalid  (axis_s2mm_tvalid),
    .m_axis_tlast   (axis_s2mm_tlast ),
    .m_axis_tuser   (axis_s2mm_tuser ),
    .m_axis_tready  (axis_s2mm_tready),
    .video_clk      (video_clk),
    .video_rst      (reset),
    .video_hsync    (sensor_hsync),
    .video_vsync    (sensor_vsync),
    .video_hblank   (1‘b0),
    .video_vblank   (sensor_vblank),
    .video_de       (sensor_de),
    .video_data     (sensor_data)
);
wire vid_hsync_i;
wire vid_vsync_i;
wire vid_hblank_i;
wire vid_vblank_i;
wire vid_de_i;
wire vid_hsync_o;
wire vid_vsync_o;
wire vid_hblank_o;
wire vid_vblank_o;
wire vid_de_o;
wire [31:0] vid_video_o;
video_timing_gen 
#(
    .SENSOR_ACT_W       (640),
    .SENSOR_ACT_H       (480),
    .SENSOR_WIDTH       (800),
    .SENSOR_HEIGHT      (600),
    .SENSOR_HSYNC_START (0 ),
    .SENSOR_HSYNC_STOP  (40),
    .SENSOR_VSYNC_START (0 ),
    .SENSOR_VSYNC_STOP  (4 )
)
u_video_timing_gen
( 
    .rst_n       (!reset), 
    .video_clk   (video_clk), 
    .reg_h_start (80),
    .reg_v_start (60),
    .vsync       (vid_vsync_i), 
    .hsync       (vid_hsync_i),
    .de          (vid_de_i),
    .vblank      (vid_vblank_i),
    .hblank      (vid_hblank_i)
);
axis2video u_axis2video
(
    .axis_clk            (axis_clk),
    .axis_aresetn        (!reset),
    .reg_axis_mm2s_start (1‘b1),
    .s_axis_tdata        (axis_mm2s_tdata),
    .s_axis_tvalid       (axis_mm2s_tvalid),
    .s_axis_tlast        (axis_mm2s_tlast),
    .s_axis_tuser        (axis_mm2s_tuser),
    .s_axis_tready       (axis_mm2s_tready),
    .video_clk           (video_clk),
    .video_rst           (reset),
    .video_hsync_i       (vid_hsync_i),
    .video_vsync_i       (vid_vsync_i),
    .video_hblank_i      (vid_hblank_i),
    .video_vblank_i      (vid_vblank_i),
    .video_de_i          (vid_de_i),
    .video_hsync_o       (vid_hsync_o),
    .video_vsync_o       (vid_vsync_o),
    .video_hblank_o      (vid_hblank_o),
    .video_vblank_o      (vid_vblank_o),
    .video_de_o          (vid_de_o),    
    .video_data          (vid_video_o)
);
axi_lite_master u_axi_lite_master 
(
   .M_AXI_ACLK    (axi_lite_clk           ), 
   .M_AXI_ARESETN (!reset ), 
   .M_AXI_AWADDR  (axi_lite_master_awaddr  ), //[8:0]
   .M_AXI_AWPROT  (                 ),
   .M_AXI_AWVALID (axi_lite_master_awvalid ),
   .M_AXI_AWREADY (axi_lite_master_awready ),
   .M_AXI_WDATA   (axi_lite_master_wdata   ),
   .M_AXI_WSTRB   (                 ),
   .M_AXI_WVALID  (axi_lite_master_wvalid  ),
   .M_AXI_WREADY  (axi_lite_master_wready  ),
   .M_AXI_BRESP   (axi_lite_master_bresp   ),
   .M_AXI_BVALID  (axi_lite_master_bvalid  ),
   .M_AXI_BREADY  (axi_lite_master_bready  ),
   .M_AXI_ARADDR  (axi_lite_master_araddr  ), //[8:0]
   .M_AXI_ARPROT  (                 ),
   .M_AXI_ARVALID (axi_lite_master_arvalid ),
   .M_AXI_ARREADY (axi_lite_master_arready ),
   .M_AXI_RDATA   (axi_lite_master_rdata   ),
   .M_AXI_RRESP   (axi_lite_master_rresp   ),
   .M_AXI_RVALID  (axi_lite_master_rvalid  ),
   .M_AXI_RREADY  (axi_lite_master_rready  ),
   .DDRX_PHY_INIT_DONE(1‘b1), 
   .DONE_SUCCESS  (                   )
);
axi_vdma_test u_axi_vdma_test (
  .s_axi_lite_aclk(axi_lite_clk), 
  .m_axi_mm2s_aclk(axi_clk),      
  .m_axis_mm2s_aclk(axis_clk),    
  .m_axi_s2mm_aclk(axi_clk),      
  .s_axis_s2mm_aclk(axis_clk),    
  .axi_resetn(!reset),            
  .s_axi_lite_awvalid(axi_lite_master_awvalid), 
  .s_axi_lite_awready(axi_lite_master_awready), 
  .s_axi_lite_awaddr (axi_lite_master_awaddr[8:0]),
  .s_axi_lite_wvalid (axi_lite_master_wvalid),   
  .s_axi_lite_wready (axi_lite_master_wready),   
  .s_axi_lite_wdata  (axi_lite_master_wdata),    
  .s_axi_lite_bresp  (axi_lite_master_bresp),    
  .s_axi_lite_bvalid (axi_lite_master_bvalid),   
  .s_axi_lite_bready (axi_lite_master_bready),   
  .s_axi_lite_arvalid(axi_lite_master_arvalid),  
  .s_axi_lite_arready(axi_lite_master_arready),  
  .s_axi_lite_araddr (axi_lite_master_araddr[8:0]), 
  .s_axi_lite_rvalid (axi_lite_master_rvalid),   
  .s_axi_lite_rready (axi_lite_master_rready),   
  .s_axi_lite_rdata  (axi_lite_master_rdata),    
  .s_axi_lite_rresp  (axi_lite_master_rresp),    
  //.s2mm_frame_ptr_in(),                          
  //.s2mm_frame_ptr_out(),                         
  .m_axi_s2mm_awaddr (S_AXI_awaddr),   
  .m_axi_s2mm_awlen  (S_AXI_awlen),    
  .m_axi_s2mm_awsize (S_AXI_awsize),   
  .m_axi_s2mm_awburst(S_AXI_awburst),  
  .m_axi_s2mm_awprot (S_AXI_awprot),   
  .m_axi_s2mm_awcache(S_AXI_awcache),  
  .m_axi_s2mm_awvalid(S_AXI_awvalid),  
  .m_axi_s2mm_awready(S_AXI_awready),  
  .m_axi_s2mm_wdata  (S_AXI_wdata),    
  .m_axi_s2mm_wstrb  (S_AXI_wstrb),    
  .m_axi_s2mm_wlast  (S_AXI_wlast),    
  .m_axi_s2mm_wvalid (S_AXI_wvalid),   
  .m_axi_s2mm_wready (S_AXI_wready),   
  .m_axi_s2mm_bresp  (S_AXI_bresp),    
  .m_axi_s2mm_bvalid (S_AXI_bvalid),   
  .m_axi_s2mm_bready (S_AXI_bready),   

  .m_axi_mm2s_araddr (S_AXI_araddr),   
  .m_axi_mm2s_arlen  (S_AXI_arlen),    
  .m_axi_mm2s_arsize (S_AXI_arsize),   
  .m_axi_mm2s_arburst(S_AXI_arburst),  
  .m_axi_mm2s_arprot (S_AXI_arprot),   
  .m_axi_mm2s_arcache(S_AXI_arcache),  
  .m_axi_mm2s_arvalid(S_AXI_arvalid),  
  .m_axi_mm2s_arready(S_AXI_arready),  
  .m_axi_mm2s_rdata  (S_AXI_rdata),    
  .m_axi_mm2s_rresp  (S_AXI_rresp),    
  .m_axi_mm2s_rlast  (S_AXI_rlast),    
  .m_axi_mm2s_rvalid (S_AXI_rvalid),   
  .m_axi_mm2s_rready (S_AXI_rready),   

  .s_axis_s2mm_tdata(axis_s2mm_tdata),    
  .s_axis_s2mm_tkeep(4‘b1111),            
  .s_axis_s2mm_tuser(axis_s2mm_tuser),    
  .s_axis_s2mm_tvalid(axis_s2mm_tvalid),  
  .s_axis_s2mm_tready(axis_s2mm_tready),  
  .s_axis_s2mm_tlast(axis_s2mm_tlast),    

  .m_axis_mm2s_tdata(axis_mm2s_tdata),  
  .m_axis_mm2s_tkeep(),  
  .m_axis_mm2s_tuser(axis_mm2s_tuser),  
  .m_axis_mm2s_tvalid(axis_mm2s_tvalid),
  .m_axis_mm2s_tready(axis_mm2s_tready),
  .m_axis_mm2s_tlast(axis_mm2s_tlast),  
  .mm2s_fsync(!vid_vsync_i),
  .s2mm_introut()                         
);
axi_slave u_axi_slave
(

  .S_AXI_ACLK      (axi_clk),
  .S_AXI_ARESETN   (!reset),
  .S_AXI_AWID      (S_AXI_awid   ),
  .S_AXI_AWADDR    (S_AXI_awaddr ),
  .S_AXI_AWLEN     (S_AXI_awlen  ),
  .S_AXI_AWSIZE    (S_AXI_awsize ),
  .S_AXI_AWBURST   (S_AXI_awburst),
  .S_AXI_AWVALID   (S_AXI_awvalid),
  .S_AXI_AWREADY   (S_AXI_awready),
  .S_AXI_WDATA     (S_AXI_wdata  ),
  .S_AXI_WSTRB     (S_AXI_wstrb  ),
  .S_AXI_WLAST     (S_AXI_wlast  ),
  .S_AXI_WVALID    (S_AXI_wvalid ),
  .S_AXI_WREADY    (S_AXI_wready ),
  .S_AXI_BID       (S_AXI_bid    ),
  .S_AXI_BRESP     (S_AXI_bresp  ),
  .S_AXI_BVALID    (S_AXI_bvalid ),
  .S_AXI_BREADY    (S_AXI_bready ),

  .S_AXI_ARID    (S_AXI_arid   ),
  .S_AXI_ARADDR  (S_AXI_araddr ),
  .S_AXI_ARLEN   ({4‘b0,S_AXI_arlen}  ),
  .S_AXI_ARSIZE  (S_AXI_arsize ),
  .S_AXI_ARBURST (S_AXI_arburst),
  .S_AXI_ARVALID (S_AXI_arvalid),
  .S_AXI_ARREADY (S_AXI_arready),
  .S_AXI_RID     (S_AXI_rid    ),
  .S_AXI_RDATA   (S_AXI_rdata  ),
  .S_AXI_RRESP   (S_AXI_rresp  ),
  .S_AXI_RLAST   (S_AXI_rlast  ),
  .S_AXI_RVALID  (S_AXI_rvalid ),
  .S_AXI_RREADY  (S_AXI_rready )
);

integer file_fd;
reg vblank_o_r;
reg [1:0] frame_cnt=0;
always @(posedge video_clk) vblank_o_r <= vid_vblank_o;
always @(posedge video_clk)
begin
    if((~vblank_o_r) & vid_vblank_o)
        frame_cnt <= frame_cnt + 1‘b1;

end
initial begin
    file_fd = $fopen("output.rgb","wb");
end
always @(posedge video_clk)
begin
    if(frame_cnt == 1 && vid_de_o)
    begin
        $fwrite(file_fd,"%c%c%c%c",vid_video_o[31:24],vid_video_o[23:16],vid_video_o[15:8],vid_video_o[7:0]);
    end
    else if(frame_cnt == 2)
    begin
        $fclose(file_fd);
        $stop;
    end
end

endmodule

接下来,就要准备仿真用数据了,我这里用matlab将一副图片的图像数据取出来,写成rgb文件,当做视频数据源

image=imread(‘test.bmp‘);
image=imresize(image,[480,640]);
imshow(image);
w=640;
h=480;
fd=fopen(‘input.rgb‘,‘wb‘);
for i=1:h
    for j=1:w
        pix=[0,image(i,j,1),image(i,j,2),image(i,j,3)];
        fwrite(fd,pix‘,‘uint8‘);
    end
end
fclose(fd);

接下来在sensor模块里面读出这个图像数据,然后根据视频时序发出
sensor.v代码

`timescale 1ns / 1ps
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// 
// Engineer: EEPROM
// 
//////////////////////////////////////////////////////////////////////////////////
module sensor 
( 
    input rst, 
    input clk, 
    output reg vsync, 
    output reg hsync,
    output reg de,
    output reg vblank,
    output reg[31:0]pix_out
);

parameter SENSOR_ACT_W = 640;
parameter SENSOR_ACT_H = 480;
parameter SENSOR_WIDTH = 800;
parameter SENSOR_HEIGHT = 600;
parameter H_START       = 80; 
parameter V_START       = 60; 

parameter DATA_SIZE = SENSOR_WIDTH*SENSOR_HEIGHT;
reg [12:0] hcnt;
reg [12:0] vcnt;

reg  [31:0] raw_array [DATA_SIZE-1:0];
integer i;
integer file_hdl;

initial
  begin
    file_hdl = $fopen("input.rgb", "rb");
    for ( i=0; i<DATA_SIZE; i=i+1) begin
        raw_array[i][7:0] = $fgetc(file_hdl);
        raw_array[i][15:8] = $fgetc(file_hdl);
        raw_array[i][23:16] = $fgetc(file_hdl);
        raw_array[i][31:24] = $fgetc(file_hdl);
    end 
        $fclose(file_hdl);
        $display("read raw data ok\n");
  end

always @(posedge clk)
begin
    if(rst)
    begin
        hcnt <= 13‘d0;
    end
    else if(hcnt == SENSOR_WIDTH-1)
        hcnt <= 13‘d0;
    else 
        hcnt <= hcnt + 1;
end
always @(posedge clk)
begin
    if(rst)
    begin
        vcnt <= 13‘d0;
    end
    else if(hcnt == SENSOR_WIDTH-1 && vcnt == SENSOR_HEIGHT-1)
        vcnt <= 13‘d0;
    else if(hcnt == SENSOR_WIDTH-1)
        vcnt <= vcnt + 1;
end

always @(posedge clk)
begin
    if(rst)
    begin
        hsync <= 1‘b0;
    end
    else if(hcnt <= 13‘d40)
        hsync <= 1‘b1;
    else
        hsync <= 1‘b0;
end
always @(posedge clk)
begin
    if(rst)
    begin
        de <= 1‘b0;
    end
    else if(hcnt >= H_START && hcnt < H_START+SENSOR_ACT_W && vcnt >= V_START && vcnt < V_START+SENSOR_ACT_H )
        de <= 1‘b1;
    else
        de <= 1‘b0;
end
always @(posedge clk)
begin
    if(rst)
    begin
        vblank <= 1‘b1;
    end
    else if(vcnt >= V_START-1 && vcnt <= V_START+SENSOR_ACT_H )
        vblank <= 1‘b0;
    else
        vblank <= 1‘b1;
end
always @(posedge clk)
begin
    if(rst)
    begin
        vsync <= 1‘b0;
    end
    else if(vcnt <= 4)
        vsync <= 1‘b1;
    else
        vsync <= 1‘b0;
end

always @(posedge clk)
 pix_out <= (hcnt >= H_START &&hcnt < H_START+SENSOR_ACT_W && vcnt >= V_START && vcnt < V_START+SENSOR_ACT_H) ? raw_array[(vcnt-V_START)*SENSOR_ACT_W+hcnt-H_START]:0;
endmodule

视频数据源有了,那么为了验证vdma工作正常,即视频数据读写ddr正常,就需要把读回来的数据也存储一下,代码在tb_top最后有写,至于为什么要等fram_cnt为1的时候开始写,那是因为我做的是一个乒乓buffer,vdma读出来的第一帧数据是无效数据,第二帧开始才是有效数据。
代码都准备完毕,开始仿真,我这里调用的modelsim,vivado如果使用modelsim仿真,这里不做介绍,网上搜一下资料还是比较多的
仿真关键信号时序:
技术分享
这里面是做两帧的仿真,牢骚一句,仿真真的太慢了
仿真结束以后,会把从DDR读回来的视频数据存储到output.rbg文件里面,还是用matlab对这个数据做处理:

w=640;
h=480;
dst = zeros(h,w);
fd=fopen(‘output.rgb‘,‘r‘);
for i=1:h
    for j=1:w
        %pix=[0,image(i,j,1),image(i,j,2),image(i,j,3)];
        pix=fread(fd,[1,4],‘uint8‘);
        dst(i,j)=pix(3);
        dst(i,j,2)=pix(2);
        dst(i,j,3)=pix(1);
    end
end
fclose(fd);
figure
imshow(uint8(dst));

下面是输入图片和输出图片对比:
技术分享
可见vdma工作正常
四:总结
通过对vdma的仿真,可以更深入的了解vdma的工作原理,工作流程,给实际应用做好准备工作。同时,这么做也有更多的意义,对于一些需要DDR缓存才能完成的图像算法,比如视频3D降噪,运动物体检测帧差法,HDR图像合成等等,完全可以在此基础上进行仿真,能更大程度的模拟FPGA实际工作状况,提高算法移植效率。

这篇主要是针对仿真,下一篇将重点介绍vdma的实际应用,也就是上板测试。
注:本工程源码不免费开放,有任何疑问可以评论,或者入群交流。
欢迎加入zynq FPGA图像算法交流群:296633958
本人QQ:707200828

xilinx vivado zynq vdma仿真及应用详解(一)

标签:

原文地址:http://blog.csdn.net/zhangyu_eeprom/article/details/51462448

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!