[X]关闭
1

(AXI4)S02- CH01 基于FDMA内存读写测试

摘要: FDMA是MSXBO(米联客的)基于AXI4总线协议定制的一个DMA控制器。有了这个IP我们可以统一实现用FPGA代码直接读写PL的DDR或者ZYNQ PS的DDR。 如果用过ZYNQ的都知道,要直接操作PS的DDR 通常是DMA 或者VDMA,然而 ...

软件版本:VIVADO2017.4

操作系统:WIN10 64bit

硬件平台:ARTIX系列

米联客(MSXBO)论坛:www.osrc.cn答疑解惑专栏开通,欢迎大家给我提问!!

1.1概述

      FDMA是MSXBO(米联客的)基于AXI4总线协议定制的一个DMA控制器。有了这个IP我们可以统一实现用FPGA代码直接读写PL的DDR或者ZYNQ PS的DDR。

      如果用过ZYNQ的都知道,要直接操作PS的DDR 通常是DMA 或者VDMA,然而用过XILINX 的DMA IP 和VDMA IP,总有一种遗憾,那就是不够灵活,还需要对寄存器配置,真是麻烦。对于我们搞FPGA的人来说,最喜欢直接了当,直接用FPGA代码搞定。现在XILINX 的总线接口是AXI4总线,那么熟练自定义AXI4 IP挂到总线上就非常方便了。基于这个目的,本小编定义了一个基于AXI4 FULL MASTER的IP,暂且取名为MSXBO_FDMA。

       通过这个IP我们可以方便地进行AXI4 FULL MASTER的操作,比如我们经常要读写DDR,那么只要挂到AXI4总线上就可以利用这个IP实现。


1.2基于FDMA搭建的BD工程

       首先看下基于FDMA搭建的BD工程。

1.3 Setp By Step搭建FPGA BD工程

Step1:任意创建一个新的空的BD工程,如下图,选择Create Block Design ,BD 的Design name 命名为system

Step2:

选择下图箭头所指的+,输入关键词 MIG 双击添加并且配置 MIG

双击生成的IP核

选择Create Design 单击NEXT

选择DDR3 单击NEXT


设置MIG 内核时钟频率、内存型号、内存的数据位宽

设置MIG AXI4 最大支持的位宽

设置输入频率

系统和参考时钟时钟选择no buffer,MIG低电平复位

Step9:终端阻抗选择40hms,设置dci

选择Fixed Pin Out

根据原理图手动填写PIN 脚定义,或者选择Read XDC/UCF直接读入pin脚定义,本课程下提供了DDR 配置文件,直接读入既可。

填写完成后,先单击Validate再单击NEXT

继续单击NEXT

继续单击NEXT

继续单击NEXT

继续单击NEXT

最后单击Generate,至此MIG的配置完成

Step3:添加FDMA IP,如果要让VIVADO识别到自定义IP,需要先如下图设置IP的路径

修改FDMA IP参数,修改AXI BURST LEN 为256,其他参数默认。修改一下参数需要确保一次AXI BURST 的大小不超过4KB 比如当AXI BURST LEN 为256 数据位宽为32bit 那么256*32=4096bytes已经是一次标准的AXI最大传输的大小了。

Step4:添加时钟IP

以下配置的200M时钟输出用于MIG时钟的输入

Step4:采用自动连线

全部勾选默认(也可以有选择勾选,当然也可以不用自动连线,全部手动连线)

Step5:手动修改连线最终BD设计如下

Step6:最后MIG地址默认的起始地址为0x8000_0000,改为0X0000_0000

1.4编写FDMA测试代码

首先右击BD 并且单击

其次继续右击BD文件,选择Create HDL Wrapper

最后修改Wrapper并且添加测试代码,测试代码如下

`timescale 1ns / 1ps

//////////////////////////////////////////////////////////////////////////////////

// Company:CZ123 MSXBO www.osrc.cn

// Engineer: tjy

// Create Date: 2019/04/02 12:39:25

// Design Name:

// Module Name: fdma_top

// Project Name: AXI_FDMA

// Target Devices:

// Tool Versions: VIVADO

// Description: test DDR

// Dependencies:

// Revision:

// Revision 0.01 - File Created

// Additional Comments:

//////////////////////////////////////////////////////////////////////////////////

module fdma_top(

output [13:0]DDR3_addr,

output [2:0]DDR3_ba,

output DDR3_cas_n,

output [0:0]DDR3_ck_n,

output [0:0]DDR3_ck_p,

output [0:0]DDR3_cke,

output [0:0]DDR3_cs_n,

output [1:0]DDR3_dm,

inout [15:0]DDR3_dq,

inout [1:0]DDR3_dqs_n,

inout [1:0]DDR3_dqs_p,

output [0:0]DDR3_odt,

output DDR3_ras_n,

output DDR3_reset_n,

output DDR3_we_n,

input sys_clk_i

);


  wire [0:0]ui_rstn;

  wire ui_clk;

  //-----------------fmda signals--------------------------------------

  wire [31:0] pkg_wr_addr;

 (*mark_debug = "true"*)  wire  [31:0] pkg_wr_data;

 (*mark_debug = "true"*) (* KEEP = "TRUE" *)  reg         pkg_wr_areq;

 (*mark_debug = "true"*)  wire        pkg_wr_en;

 (*mark_debug = "true"*)  wire        pkg_wr_last;

  wire [31:0] pkg_wr_size;

  wire [31:0] pkg_rd_addr;

 (*mark_debug = "true"*)  wire  [31:0] pkg_rd_data;

 (*mark_debug = "true"*) (* KEEP = "TRUE" *) reg         pkg_rd_areq;

 (*mark_debug = "true"*)  wire        pkg_rd_en;

 (*mark_debug = "true"*)  wire        pkg_rd_last;

  wire [31:0] pkg_rd_size;

 //---------------------------------------------------------------------

  reg [31:0]pkg_wr_cnt;

 (*mark_debug = "true"*) (* KEEP = "TRUE" *) reg [31:0]pkg_rd_cnt;

 (*mark_debug = "true"*) (* KEEP = "TRUE" *) reg [1:0] T_S;


  reg [31:0] pkg_addr;


  parameter WRITE1 = 0;

  parameter WRITE2 = 1;

  parameter READ1  = 2;

  parameter READ2  = 3;

  //-----------------  

  assign pkg_wr_size = 1024;

  assign pkg_rd_size = 1024;

  

  assign pkg_wr_data = pkg_wr_cnt;

  (*mark_debug = "true"*) wire test_error;

  assign test_error = (pkg_rd_en && (pkg_rd_cnt != pkg_rd_data));

  

  assign pkg_wr_addr = pkg_addr;

  assign pkg_rd_addr = pkg_addr;

  

  always @(posedge ui_clk)

  begin

    if(!ui_rstn)begin

        T_S <=0;

        pkg_wr_areq <= 1'b0;

        pkg_rd_areq <= 1'b0;         

        pkg_wr_cnt<=0;

        pkg_rd_cnt<=0;

        pkg_addr<=0;       

    end

    else begin

        case(T_S)

        WRITE1:begin

            if(pkg_addr>=32'd268435455) pkg_addr<=0;

            pkg_wr_areq  <= 1'b1;

            T_S <= WRITE2;

        end

        WRITE2:begin

            pkg_wr_areq  <= 1'b0;

            if(pkg_wr_last) begin

                 T_S <= READ1;

                 pkg_wr_cnt <= 32'd0;

            end

            else if(pkg_wr_en) begin

                pkg_wr_cnt <= pkg_wr_cnt + 1'b1;

            end

        end

        READ1:begin

            pkg_rd_areq <= 1'b1;

            T_S <= READ2;   

        end   

        READ2:begin

            pkg_rd_areq <= 1'b0;

            if(pkg_rd_last) begin

                T_S <= WRITE1;  

                pkg_addr <= pkg_addr + 4096;

                pkg_rd_cnt <= 32'd0;

            end

            else if(pkg_rd_en) begin

                pkg_rd_cnt <= pkg_rd_cnt + 1'b1;

            end

        end             

        endcase

    end

  end

  

  system system_i

       (.DDR3_addr(DDR3_addr),

        .DDR3_ba(DDR3_ba),

        .DDR3_cas_n(DDR3_cas_n),

        .DDR3_ck_n(DDR3_ck_n),

        .DDR3_ck_p(DDR3_ck_p),

        .DDR3_cke(DDR3_cke),

        .DDR3_cs_n(DDR3_cs_n),

        .DDR3_dm(DDR3_dm),

        .DDR3_dq(DDR3_dq),

        .DDR3_dqs_n(DDR3_dqs_n),

        .DDR3_dqs_p(DDR3_dqs_p),

        .DDR3_odt(DDR3_odt),

        .DDR3_ras_n(DDR3_ras_n),

        .DDR3_reset_n(DDR3_reset_n),

        .DDR3_we_n(DDR3_we_n),


        .pkg_wr_addr(pkg_wr_addr),

        .pkg_wr_data(pkg_wr_data),

        .pkg_wr_areq(pkg_wr_areq),

        .pkg_wr_en  (pkg_wr_en),

        .pkg_wr_last(pkg_wr_last),

        .pkg_wr_size(pkg_wr_size),

                

        .pkg_rd_addr(pkg_rd_addr),

        .pkg_rd_data(pkg_rd_data),

        .pkg_rd_areq(pkg_rd_areq),  

        .pkg_rd_en  (pkg_rd_en),   

        .pkg_rd_last(pkg_rd_last),

        .pkg_rd_size(pkg_rd_size),


        .ui_clk(ui_clk),

        .fdma_rstn(ui_rstn),        

        .sys_clk_i(sys_clk_i)       

        );

        

endmodule

1.5测试代码状态机分析

  1. WRITE1状态:为了测试整个DDR的存储控件,所以先计算DDR大小,536870911正好是一片512MB DDR的大小。根据之前BD里面FDMA的参数设置,一次AXI4 burst大小为256,那么每次传输1024byte(256x32/8)。我们设置pkg_wr_size 和pkg_rd_size大小为1024,那么这里每次parket 传输大小为4096bytes(1024x32bit/8)。pkg_wr_size 和pkg_rd_size的大小是以32bit计算。pkg_wr_size 和pkg_rd_size大小的设置需要注意pkg_wr_size需要是FDMA里面 AXI BURST LEN的整数倍。
  2. WRITE2状态: WRITE1通过设置pkg_wr_areq为1持续1个时钟周期,后进入WRITE2状态机,并且启动启动一次FDMA的parket传输。当pkg_wr_last为1的时候表示一次parket传输传输结束。在WRITE2状态机中使用了一个计数器,把计数器的值写入到DDR中。
  3. READ1状态:在READ1状态设置pkg_rd_areq为1启动一次FMDA的parket read 传输,之后进入READ2状态。
  4. READ2状态:当pkg_rd_las 为1代表一次read传输结束,并且地址空间地址增加。在READ2状态,通过一个计数器计数并且对比和从DDR里面读出的数据,看是否有错误。

assign test_error = (pkg_rd_en && (pkg_rd_cnt != pkg_rd_data));

1.6测试结果

        可以看到上图中error 信号一直为0代表数据读写对比没有错误。再看pkg_wr_en和pkg_rd_en信号,可以看到他们是每次burst了4次每次的长度是256。证明我们的FDMA工作正常,测试程序工作正常。下面放大数据部分。

上图是写操作往内存里面写数据。

上图是读操作放大后看到的数据分别是计数器和从内存里读取到的数据。

2

路过

雷人

握手

鲜花

鸡蛋

刚表态过的朋友 (2 人)

发表评论

最新评论

引用 摘桃花换酒 2021-4-17 06:42
学习啦,正在学这块

查看全部评论(1)

本文作者
2019-10-17 17:24
  • 1
    粉丝
  • 5346
    阅读
  • 1
    回复

米联客UIsrc

独家课程 硬核科技

销售电话:18921033576
EMAIL:tjy@uisrc.com
地址:常州溧阳市天目云谷3号楼北楼
热门评论
排行榜