06 uifdma_dbuf+fdma实现数据流方案

发布时间 2023-12-30 11:36:21作者: 米联客(milianke)

软件版本:vitis2021.1(vivado2021.1)

操作系统:WIN10 64bit

硬件平台:适用XILINX A7/K7/Z7/ZU/KU系列FPGA

登录"米联客"FPGA社区-www.uisrc.com视频课程、答疑解惑!

6.1概述    

uifdma_dbuf3.0开始支持了full和empty信号, ud_wfull信号为0代表FIFO未满,写通道可以写入数据,uifdma_dbuf的ud_rempty信号为0代表读通道的FIFO中有数据可以读出。

当uifdma_dbuf设置非视频模式的时候,ud_vs信号仅仅用于uifdma_dbuf内部的传输状态机的启动和停止。当ud_vs为高电平,数据传输开始后,通过de控制数据的读或者写。本方案在实际的应用中需要注意,ud_rvs有效的时候,只要uifdma_dbuf中读控制部分的FIFO准备好,就会从DDR读数据写入到uifdma_dbuf的读控制部分的fifo,这样就存在一个问题,必须确保读出该地址空间的内存数据都是有效的。

所以在本方案中设计的测试环境是写入数据流速度,和读出数据流速度是一致的,并且读操作会晚于写操作一段时间,这样可以确保读的内存地址空间里面的数据都是有效的。

6.2系统框图

6.3基于图形化逻辑设计部分

基于图形化的编程思想主要是把所有的开发都进行标准化的IP化,通过可视化的绘制IP之间的连线即可完成逻辑部分的设计工作。基于这套思想,我们可以把所有一切可以标准化的代码制作为IP,这样以后需要使用的时候只需要通过调用图形化的IP即可完成编程设计。实现FPGA所见即所得的可视化编程。

以下代码中,我们把数据的输入,输出接口引出到顶层模块,由用户自由控制。

1:uifdma_dbuf设置

通过把uifdma_dbuf配置为非视频模式,可以无需vs进行帧同步,即可进行数据的传输。当uifdma_dbuf配置为非视频模式的时候,vs信号高电平时候,可以进行fdma数据搬运传输。如下图所示,写通道和读通道,我们都设置了3个缓存地址,每个缓存的大小为1024*1024*32bit=4MB。ud写数据端接口数据位宽为32,ud读数据接口数据位宽为32。

缓存1的起始地址为:0x01000000+2^24*0

缓存2的起始地址为:0x01000000+2^24*1

缓存3的起始地址为:0x01000000+2^24*2

2:uifdma设置

我们这里演示的demo输入输出的数据时钟为50M,数据速度为50M*32bit,而MIG出来的时钟远高于50M并且这里配置AXI4总线位宽是128bit所以总线带宽非常充裕,我们可以设置每次burst的长度为合理值,无需太大,我们这里设置64

3:编写测试代码

本方案中,演示从DDR中连续写入数据,之后连续读出,并且写入的速度和读出的速度一致。

`timescale 1ns / 1ps

/*******************************MILIANKE*******************************

*Company : MiLianKe Electronic Technology Co., Ltd.

*WebSite:https://www.milianke.com

*TechWeb:https://www.uisrc.com

*tmall-shop:https://milianke.tmall.com

*jd-shop:https://milianke.jd.com

*taobao-shop1: https://milianke.taobao.com

*Create Date: 2021/10/15

*Module Name:

*File Name:

*Description:

*config sensor resgister

*The reference demo provided by Milianke is only used for learning.

*We cannot ensure that the demo itself is free of bugs, so users

*should be responsible for the technical problems and consequences

*caused by the use of their own products.

*Copyright: Copyright (c) MiLianKe

*All rights reserved.

*Revision: 1.0

*Signal description

*1) _i input

*2) _o output

*3) _n activ low

*4) _dg debug signal

*5) _r delay or register

*6) _s state mechine

*********************************************************************/

module fdma_ddr_test(

output [14:0]DDR3_0_addr,

output [2 :0]DDR3_0_ba,

output DDR3_0_cas_n,

output [0 :0]DDR3_0_ck_n,

output [0 :0]DDR3_0_ck_p,

output [0 :0]DDR3_0_cke,

output [0 :0]DDR3_0_cs_n,

output [3 :0]DDR3_0_dm,

inout  [31:0]DDR3_0_dq,

inout  [3:0]DDR3_0_dqs_n,

inout  [3:0]DDR3_0_dqs_p,

output [0 :0]DDR3_0_odt,

output DDR3_0_ras_n,

output DDR3_0_reset_n,

output DDR3_0_we_n,

input sysclk_p

);

 

wire resetn,init_calib_complete_0;

wire wr_rd_clk;

wire ud_rempty_0,error;

reg ud_wvs_0, ud_wde_0 ,ud_rvs_0 ;

wire [31:0]ud_wdata_0 , ud_rdata_0;

reg [15:0]wr_cnt , rd_cnt, delay_cnt;

 

assign ud_rde_0 = !ud_rempty_0;

assign ud_wdata_0 = {16'd0,wr_cnt};

 

assign resetn = init_calib_complete_0;

 

always @(posedge wr_rd_clk)begin

    if(resetn == 1'b0)begin

        delay_cnt <= 0;

        ud_rvs_0 <= 1'b0;

    end

    else if(delay_cnt[13:12] == 2'b11) begin //DDR初始化后,通过delay_cnt控制写入和读出的开始时机

        delay_cnt <= delay_cnt;

        ud_rvs_0 <= 1'b1;

    end

    else begin

        delay_cnt <= delay_cnt + 1'b1;

    end

end

 

//写数据启动部分代码

always @(posedge wr_rd_clk)begin

    if(delay_cnt[13] == 1'b0)begin //DDR初始化后,通过delay_cnt控制写入和读出的开始时机

        ud_wvs_0 <= 1'b0;

        ud_wde_0 <= 1'b0;

    end

    else begin

        ud_wvs_0 <= 1'b1;

        ud_wde_0 <= 1'b1;

    end

end

 

//写入16bits计数器值到缓存中

always @(posedge wr_rd_clk)begin

    if(resetn == 1'b0)begin

        wr_cnt <= 0;

    end

    else begin

        if(ud_wde_0)

            wr_cnt <= wr_cnt + 1'b1;

    end

end

 

//当读信号ud_rde有效,通过计数器计数

always @(posedge wr_rd_clk)begin

    if(resetn == 1'b0)begin

        rd_cnt <= 0;

    end

    else begin

        if(ud_rde_0)

            rd_cnt <= rd_cnt + 1'b1;

        else

            rd_cnt <= rd_cnt;

    end

end

 

//通过读出的数据和我们读计数器的值是否一致判断数据是否真确

assign error = ud_rde_0&&(rd_cnt != ud_rdata_0[15:0]);

 

//通过ILA观察数据

ila_0 your_instance_name (

    .clk(wr_rd_clk), // input wire clk

    .probe0({ud_rde_0,rd_cnt,ud_rdata_0[15:0],ud_wde_0,ud_wdata_0[15:0],ud_rempty_0, error}) // input wire [17:0]  probe0  

);

 

  system system_i

       (.DDR3_0_addr(DDR3_0_addr),

        .DDR3_0_ba(DDR3_0_ba),

        .DDR3_0_cas_n(DDR3_0_cas_n),

        .DDR3_0_ck_n(DDR3_0_ck_n),

        .DDR3_0_ck_p(DDR3_0_ck_p),

        .DDR3_0_cke(DDR3_0_cke),

        .DDR3_0_cs_n(DDR3_0_cs_n),

        .DDR3_0_dm(DDR3_0_dm),

        .DDR3_0_dq(DDR3_0_dq),

        .DDR3_0_dqs_n(DDR3_0_dqs_n),

        .DDR3_0_dqs_p(DDR3_0_dqs_p),

        .DDR3_0_odt(DDR3_0_odt),

        .DDR3_0_ras_n(DDR3_0_ras_n),

        .DDR3_0_reset_n(DDR3_0_reset_n),

        .DDR3_0_we_n(DDR3_0_we_n),

 

        .ud_rdata_0(ud_rdata_0),

        .ud_rde_0(ud_rde_0),

        .ud_rempty_0(ud_rempty_0),

        .ud_rvs_0(ud_rvs_0),

 

        .ud_wdata_0(ud_wdata_0),

        .ud_wde_0(ud_wde_0),

        .ud_wvs_0(ud_wvs_0),

 

        .init_calib_complete_0(init_calib_complete_0),

        .wr_rd_clk(wr_rd_clk),

        .sysclk(sysclk_p)

         

        );

endmodule

6.4实验结果

1:RTL仿真结果

仿真需要的时间比较长,大概在180us处可以看到读出的数据部分,我们可以看到,error一直为0,代表没有错误。

观察FDMA部分的数据传输

观察读出的数据是否正确

2:上板验证

编译下载,后通过在线逻辑分析仪观察结果