Xilinx DDR3 手把手教会你DDR3驱动

简介

        本文主要介绍使用datamover ip驱动DDR的读写功能,受环境影响需要使用BRAM代替DDR3做数据仿真。

IP介绍

        Datamover IP作为PL端DDR3的驱动,可以很方便的对DDR做高速读写操作,该IP的原理图如下图所示:

        从上图可以看出Datamover对DDR是通过AXI总线驱动,在上图是AXI4 MASTER总线,读和写都是通过命令操控,操控选用AXI4-Stream数据流总线,均有命令和状态数据。对于写数据 由AXI4-Stream输入转换成AXI4 MASTER(Write)输出,而对于读数据则由AXI4 MASTER(Read)输入由该模块转换成AXI4-Stream输出。

        首先IP搜索Datamover,设置如下:

        1、使能命令读写,其中MM2S是读,S2MM是写命令;

        2、选择命令模式,有完整和基础模式,这里选择完整模式;

        3、映射到AXI4总线的读写数据位宽;

        4、AXI4-Stream总线读写位宽;

        5、突发最大长度,简单理解就是连续不间断数据长度;

        6、本帧数据长度,以Byte为单位;

        7、自定义信号,类似于AXIS-Stream tuser;

        8、9、原文是说暴露所有的控制信号的状态信号,启动后需要自己链接,我们默认不选;

        10、AXI地址宽度。

        1、通过Allow Unaligned Transfers使能Data Realignment Engine (DRE),不过不使能该功能,Start Address、Hszie和Stride必须是M_AXI_S2MM_DATA_WIDTH的倍数

        2、支持缓存和转发功能,存储转发方式更像是一种“审慎”的处理方式,它在转发之前进行全面的检查,以确保网络的稳定性。这种方式很像人们在做决策时会先全面地考虑所有因素,然后再行动;

        3、ID功能,针对不同ID,STREAM总线可以分类;

        4、合并AXI4读写总线,也可以将AXI4读写分开;

       5、AXI DataMover内核为S2MM通道支持可选的不确定BTT模式。 当输入S2MM流通道上要接收的字节数未知时,需要使用此方法。

        FPGA设计工程       

        接下来创建工程,上面说到选用BRAM做DATAMOVER IP 的读写仿真,接线图如下:

        DATAMOVER通过AXI桥链接BRAM模块,这里BRAM需要使用一个BRAM_CTRL模块将AXI总线转换成普通模式对BRAM进行读写。

        图形连接完成后自动生成.v文件,代码如下:

//Copyright 1986-2020 Xilinx, Inc. All Rights Reserved.
//--------------------------------------------------------------------------------
//Tool Version: Vivado v.2020.2 (win64) Build 3064766 Wed Nov 18 09:12:45 MST 2020
//Date        : Wed Sep 18 19:42:17 2024
//Host        : wyr running 64-bit major release  (build 9200)
//Command     : generate_target design_1_wrapper.bd
//Design      : design_1_wrapper
//Purpose     : IP block netlist
//--------------------------------------------------------------------------------
`timescale 1 ps / 1 ps

module design_1_wrapper
   (M_AXIS_MM2S_STS_tdata,
    M_AXIS_MM2S_STS_tkeep,
    M_AXIS_MM2S_STS_tlast,
    M_AXIS_MM2S_STS_tready,
    M_AXIS_MM2S_STS_tvalid,
    M_AXIS_MM2S_tdata,
    M_AXIS_MM2S_tkeep,
    M_AXIS_MM2S_tlast,
    M_AXIS_MM2S_tready,
    M_AXIS_MM2S_tvalid,
    M_AXIS_S2MM_STS_tdata,
    M_AXIS_S2MM_STS_tkeep,
    M_AXIS_S2MM_STS_tlast,
    M_AXIS_S2MM_STS_tready,
    M_AXIS_S2MM_STS_tvalid,
    S_AXIS_MM2S_CMD_tdata,
    S_AXIS_MM2S_CMD_tready,
    S_AXIS_MM2S_CMD_tvalid,
    S_AXIS_S2MM_CMD_tdata,
    S_AXIS_S2MM_CMD_tready,
    S_AXIS_S2MM_CMD_tvalid,
    S_AXIS_S2MM_tdata,
    S_AXIS_S2MM_tkeep,
    S_AXIS_S2MM_tlast,
    S_AXIS_S2MM_tready,
    S_AXIS_S2MM_tvalid,
    sys_clk,
    sys_rst_n);
  output [7:0]M_AXIS_MM2S_STS_tdata;
  output [0:0]M_AXIS_MM2S_STS_tkeep;
  output M_AXIS_MM2S_STS_tlast;
  input M_AXIS_MM2S_STS_tready;
  output M_AXIS_MM2S_STS_tvalid;
  output [127:0]M_AXIS_MM2S_tdata;
  output [15:0]M_AXIS_MM2S_tkeep;
  output M_AXIS_MM2S_tlast;
  input M_AXIS_MM2S_tready;
  output M_AXIS_MM2S_tvalid;
  output [7:0]M_AXIS_S2MM_STS_tdata;
  output [0:0]M_AXIS_S2MM_STS_tkeep;
  output M_AXIS_S2MM_STS_tlast;
  input M_AXIS_S2MM_STS_tready;
  output M_AXIS_S2MM_STS_tvalid;
  input [71:0]S_AXIS_MM2S_CMD_tdata;
  output S_AXIS_MM2S_CMD_tready;
  input S_AXIS_MM2S_CMD_tvalid;
  input [71:0]S_AXIS_S2MM_CMD_tdata;
  output S_AXIS_S2MM_CMD_tready;
  input S_AXIS_S2MM_CMD_tvalid;
  input [127:0]S_AXIS_S2MM_tdata;
  input [15:0]S_AXIS_S2MM_tkeep;
  input S_AXIS_S2MM_tlast;
  output S_AXIS_S2MM_tready;
  input S_AXIS_S2MM_tvalid;
  input sys_clk;
  input sys_rst_n;

  wire [7:0]M_AXIS_MM2S_STS_tdata;
  wire [0:0]M_AXIS_MM2S_STS_tkeep;
  wire M_AXIS_MM2S_STS_tlast;
  wire M_AXIS_MM2S_STS_tready;
  wire M_AXIS_MM2S_STS_tvalid;
  wire [127:0]M_AXIS_MM2S_tdata;
  wire [15:0]M_AXIS_MM2S_tkeep;
  wire M_AXIS_MM2S_tlast;
  wire M_AXIS_MM2S_tready;
  wire M_AXIS_MM2S_tvalid;
  wire [7:0]M_AXIS_S2MM_STS_tdata;
  wire [0:0]M_AXIS_S2MM_STS_tkeep;
  wire M_AXIS_S2MM_STS_tlast;
  wire M_AXIS_S2MM_STS_tready;
  wire M_AXIS_S2MM_STS_tvalid;
  wire [71:0]S_AXIS_MM2S_CMD_tdata;
  wire S_AXIS_MM2S_CMD_tready;
  wire S_AXIS_MM2S_CMD_tvalid;
  wire [71:0]S_AXIS_S2MM_CMD_tdata;
  wire S_AXIS_S2MM_CMD_tready;
  wire S_AXIS_S2MM_CMD_tvalid;
  wire [127:0]S_AXIS_S2MM_tdata;
  wire [15:0]S_AXIS_S2MM_tkeep;
  wire S_AXIS_S2MM_tlast;
  wire S_AXIS_S2MM_tready;
  wire S_AXIS_S2MM_tvalid;
  wire sys_clk;
  wire sys_rst_n;

  design_1 design_1_i
       (.M_AXIS_MM2S_STS_tdata(M_AXIS_MM2S_STS_tdata),
        .M_AXIS_MM2S_STS_tkeep(M_AXIS_MM2S_STS_tkeep),
        .M_AXIS_MM2S_STS_tlast(M_AXIS_MM2S_STS_tlast),
        .M_AXIS_MM2S_STS_tready(M_AXIS_MM2S_STS_tready),
        .M_AXIS_MM2S_STS_tvalid(M_AXIS_MM2S_STS_tvalid),
        .M_AXIS_MM2S_tdata(M_AXIS_MM2S_tdata),
        .M_AXIS_MM2S_tkeep(M_AXIS_MM2S_tkeep),
        .M_AXIS_MM2S_tlast(M_AXIS_MM2S_tlast),
        .M_AXIS_MM2S_tready(M_AXIS_MM2S_tready),
        .M_AXIS_MM2S_tvalid(M_AXIS_MM2S_tvalid),
        .M_AXIS_S2MM_STS_tdata(M_AXIS_S2MM_STS_tdata),
        .M_AXIS_S2MM_STS_tkeep(M_AXIS_S2MM_STS_tkeep),
        .M_AXIS_S2MM_STS_tlast(M_AXIS_S2MM_STS_tlast),
        .M_AXIS_S2MM_STS_tready(M_AXIS_S2MM_STS_tready),
        .M_AXIS_S2MM_STS_tvalid(M_AXIS_S2MM_STS_tvalid),
        .S_AXIS_MM2S_CMD_tdata(S_AXIS_MM2S_CMD_tdata),
        .S_AXIS_MM2S_CMD_tready(S_AXIS_MM2S_CMD_tready),
        .S_AXIS_MM2S_CMD_tvalid(S_AXIS_MM2S_CMD_tvalid),
        .S_AXIS_S2MM_CMD_tdata(S_AXIS_S2MM_CMD_tdata),
        .S_AXIS_S2MM_CMD_tready(S_AXIS_S2MM_CMD_tready),
        .S_AXIS_S2MM_CMD_tvalid(S_AXIS_S2MM_CMD_tvalid),
        .S_AXIS_S2MM_tdata(S_AXIS_S2MM_tdata),
        .S_AXIS_S2MM_tkeep(S_AXIS_S2MM_tkeep),
        .S_AXIS_S2MM_tlast(S_AXIS_S2MM_tlast),
        .S_AXIS_S2MM_tready(S_AXIS_S2MM_tready),
        .S_AXIS_S2MM_tvalid(S_AXIS_S2MM_tvalid),
        .sys_clk(sys_clk),
        .sys_rst_n(sys_rst_n));
endmodule

        创建顶层文件,例化模块,代码如下:

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2024/09/18 16:53:12
// Design Name: 
// Module Name: top
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module top(
	input sys_clk,
	input sys_rst_n,
	
	input[22:0] w_data_length,
	input[31:0] w_bram_addr,
	input [127:0]s_axis_tdata,
	input s_axis_tvalid,
	output s_axis_tready,
	input s_axis_tlast,
	
	input[31:0] rd_bram_addr,
	input[22:0] rd_data_length,  
	input rd_bram_en	
	
    );
	
	
wire [7:0]M_AXIS_MM2S_STS_tdata;
wire [0:0]M_AXIS_MM2S_STS_tkeep;
wire M_AXIS_MM2S_STS_tlast;
wire M_AXIS_MM2S_STS_tready;
wire M_AXIS_MM2S_STS_tvalid;
wire [127:0]M_AXIS_MM2S_tdata;
wire [15:0]M_AXIS_MM2S_tkeep;
wire M_AXIS_MM2S_tlast;
wire M_AXIS_MM2S_tready;
wire M_AXIS_MM2S_tvalid;
wire [7:0]M_AXIS_S2MM_STS_tdata;
wire [0:0]M_AXIS_S2MM_STS_tkeep;
wire M_AXIS_S2MM_STS_tlast;
wire M_AXIS_S2MM_STS_tready;
wire M_AXIS_S2MM_STS_tvalid;
wire [71:0]S_AXIS_MM2S_CMD_tdata;
wire S_AXIS_MM2S_CMD_tready;
wire S_AXIS_MM2S_CMD_tvalid;
wire [71:0]S_AXIS_S2MM_CMD_tdata;
wire S_AXIS_S2MM_CMD_tready;
wire S_AXIS_S2MM_CMD_tvalid;
wire [127:0]S_AXIS_S2MM_tdata;
wire [15:0]S_AXIS_S2MM_tkeep;
wire S_AXIS_S2MM_tlast;
wire S_AXIS_S2MM_tready;
wire S_AXIS_S2MM_tvalid;
wire sys_clk;
wire sys_rst_n;
	


data_mover_top	data_mover_top_inst(
    .sys_clk(sys_clk),
    .sys_rst_n(sys_rst_n),
	
	.s_axis_tdata(s_axis_tdata),
	.s_axis_tvalid(s_axis_tvalid),
	.s_axis_tready(s_axis_tready),
	.s_axis_tlast(s_axis_tlast),	
	.w_bram_addr(w_bram_addr),
	.w_data_length(w_data_length),
	
	
	.rd_bram_addr(rd_bram_addr),
	.rd_data_length(rd_data_length), 
	.rd_bram_en(rd_bram_en),	
	
		

    .S_AXIS_MM2S_CMD_tdata(S_AXIS_MM2S_CMD_tdata),
    .S_AXIS_MM2S_CMD_tready(S_AXIS_MM2S_CMD_tready),
    .S_AXIS_MM2S_CMD_tvalid(S_AXIS_MM2S_CMD_tvalid),
    .S_AXIS_S2MM_CMD_tdata(S_AXIS_S2MM_CMD_tdata),
    .S_AXIS_S2MM_CMD_tready(S_AXIS_S2MM_CMD_tready),
    .S_AXIS_S2MM_CMD_tvalid(S_AXIS_S2MM_CMD_tvalid),
	
	
    .S_AXIS_S2MM_tdata(S_AXIS_S2MM_tdata),
    .S_AXIS_S2MM_tkeep(S_AXIS_S2MM_tkeep),
    .S_AXIS_S2MM_tlast(S_AXIS_S2MM_tlast),
    .S_AXIS_S2MM_tready(S_AXIS_S2MM_tready),
    .S_AXIS_S2MM_tvalid(S_AXIS_S2MM_tvalid),
	
    .M_AXIS_MM2S_tdata(M_AXIS_MM2S_tdata),
    .M_AXIS_MM2S_tkeep(M_AXIS_MM2S_tkeep),
    .M_AXIS_MM2S_tlast(M_AXIS_MM2S_tlast),
    .M_AXIS_MM2S_tready(M_AXIS_MM2S_tready),
    .M_AXIS_MM2S_tvalid(M_AXIS_MM2S_tvalid)	


);	


	
	
design_1_wrapper design_1_wrapper_inst(
	.M_AXIS_MM2S_STS_tdata(M_AXIS_MM2S_STS_tdata),
    .M_AXIS_MM2S_STS_tkeep(M_AXIS_MM2S_STS_tkeep),
    .M_AXIS_MM2S_STS_tlast(M_AXIS_MM2S_STS_tlast),
    .M_AXIS_MM2S_STS_tready(1'b1),
    .M_AXIS_MM2S_STS_tvalid(M_AXIS_MM2S_STS_tvalid),
    .M_AXIS_MM2S_tdata(M_AXIS_MM2S_tdata),
    .M_AXIS_MM2S_tkeep(M_AXIS_MM2S_tkeep),
    .M_AXIS_MM2S_tlast(M_AXIS_MM2S_tlast),
    .M_AXIS_MM2S_tready(1'b1),
    .M_AXIS_MM2S_tvalid(M_AXIS_MM2S_tvalid),
    .M_AXIS_S2MM_STS_tdata(M_AXIS_S2MM_STS_tdata),
    .M_AXIS_S2MM_STS_tkeep(M_AXIS_S2MM_STS_tkeep),
    .M_AXIS_S2MM_STS_tlast(M_AXIS_S2MM_STS_tlast),
    .M_AXIS_S2MM_STS_tready(1'b1),
    .M_AXIS_S2MM_STS_tvalid(M_AXIS_S2MM_STS_tvalid),
    .S_AXIS_MM2S_CMD_tdata(S_AXIS_MM2S_CMD_tdata),
    .S_AXIS_MM2S_CMD_tready(S_AXIS_MM2S_CMD_tready),
    .S_AXIS_MM2S_CMD_tvalid(S_AXIS_MM2S_CMD_tvalid),
    .S_AXIS_S2MM_CMD_tdata(S_AXIS_S2MM_CMD_tdata),
    .S_AXIS_S2MM_CMD_tready(S_AXIS_S2MM_CMD_tready),
    .S_AXIS_S2MM_CMD_tvalid(S_AXIS_S2MM_CMD_tvalid),
    .S_AXIS_S2MM_tdata(S_AXIS_S2MM_tdata),
    .S_AXIS_S2MM_tkeep(S_AXIS_S2MM_tkeep),
    .S_AXIS_S2MM_tlast(S_AXIS_S2MM_tlast),
    .S_AXIS_S2MM_tready(S_AXIS_S2MM_tready),
    .S_AXIS_S2MM_tvalid(S_AXIS_S2MM_tvalid),
    .sys_clk(sys_clk),
    .sys_rst_n(sys_rst_n)
	
	);	
	
	
	
	
	
	
	
	
endmodule

        图中data_mover_top为DATAMOVER IP的控制模块,代码如下:

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2024/09/18 17:12:29
// Design Name: 
// Module Name: data_mover_top
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module data_mover_top(
  input sys_clk,
  input sys_rst_n,
 

  input [127:0]s_axis_tdata,
  input s_axis_tvalid,
  output s_axis_tready,
  input s_axis_tlast,	
  input[31:0] w_bram_addr,
  input[22:0] w_data_length,
  
  
  input[31:0] rd_bram_addr,
  input[22:0] rd_data_length,  
  input rd_bram_en,
  
  input [127:0]M_AXIS_MM2S_tdata,
  input [15:0]M_AXIS_MM2S_tkeep,
  input M_AXIS_MM2S_tlast,  
  output M_AXIS_MM2S_tready, 
  input M_AXIS_MM2S_tvalid,
  
 
  
  output [127:0]S_AXIS_S2MM_tdata,
  output [15:0]S_AXIS_S2MM_tkeep,
  output S_AXIS_S2MM_tlast, 
  input S_AXIS_S2MM_tready,  
  output S_AXIS_S2MM_tvalid,  
  

  output reg [71:0]S_AXIS_MM2S_CMD_tdata,  
  input S_AXIS_MM2S_CMD_tready,  
  output reg S_AXIS_MM2S_CMD_tvalid,
  
  output reg[71:0]S_AXIS_S2MM_CMD_tdata,  
  input S_AXIS_S2MM_CMD_tready, 
  output reg S_AXIS_S2MM_CMD_tvalid
  
    );
	
	
wire axis_prog_full;	
		
reg[71:0] cmd_w_data;
reg cmd_w_data_en;
reg[71:0] cmd_rd_data;
reg cmd_rd_data_en;


assign M_AXIS_MM2S_tready = 'd1;

	
。。。。。。
	
	
endmodule



        以上代码实现DATAMOVER IP的读写功能,读写数据宽度是128bit,写的信号如下:

        写入的数据也是AXI-Stream总线,接收到一帧数据后根据地址和数据宽度转换成命令实现写命令操作。

        读操作信号如下:

        当rd_bram_en信号有效时,实现读操作命令。

        DATAMOVER完整命令格式如下:

        

        xCACHE: 这个一共4bit. 用于配置axi总线一些cache性质的。感兴趣参考AXI的文档。不关心全给成0. 主要是一个cache相关的东西。

        xUSER: 4bit.这个是你自己定义的信号。。也就是说你想用axi总线穿点儿私货。。那就要把数据写到这个地方。到时候axi传输的时候会出现在user端口。

        RSVD: 表示Reserved,无定义;        

        TAG: 这个是给该条指令个一个标签。有这个TAG在出了错debug的时候可以报是哪条指令出错了。当然你要是头铁觉得不用DEBUG. TAG全给0.

        SADDR: 读写DDR里的初始地址。要注意的是如果你想非对齐传输,配置Datamover的时候要把非对齐传输点开。(例如ddr指定的是32bit, 对齐传输是指你从0,4,8这些地址开始读,非对齐传输是指你想从1开始读)

        DRR: 这个数据就比较绕了。。。这个数据是后面DSA的开关。主要是为了调整Stream端(而不是memory端,memory端直接由SADDR地址就能确定)非对齐操作的位置,也就是说,这个值表示该条指令对应的传输需要对stream端数据做调整。具体怎么调整看后面的DSA. 用在MM2S.

        EOF: 这个数据一样比较绕。先说简单的。

        在S2MM的过程中,如果INDETERMINATE BTT模式关闭,你想用tlast信号表示你写完了,那么EOF要置高。

        在MM2S的过程中,同时存在多条指令。你想让最后一条读出来后再来tlast. 那就只在最后一条置高这个EOF. 如果你开启来非对齐传输。。。最好每条都置高。否则你得注意两条指令之间不会不有废数据。

        DSA: 这个是为了调节stream侧的数据非对齐的。。。举个栗子。假如DDR是32bit的。那么它应该有4个byte. (从高到低 m3 m2 m1 m0)。这个DSA如果等于1。表明你会把数据m1放到stream的第一个数据。m0被丢掉了。。。所以如果需要这种操作,就打开DRR, 配置DSA. 否则DRR也不需要打开。

        Type: 这个表示你的地址要不要累加。。如果不累加你就是在起始地址疯狂写。所以一般传输要是一整块的话这个要置1.

        BTT: 这个简单。你要传输多少个数。。。注意单位是byte. 不是你axi_stream的宽度。

FPGA设计仿真

        代码仿真代码如下:

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2024/09/18 17:45:09
// Design Name: 
// Module Name: top_tb
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module top_tb(

    );
	
reg sys_clk;
reg sys_rst_n;
reg[22:0] w_data_length;
reg[31:0] w_bram_addr;
reg[127:0] s_axis_tdata;	
reg s_axis_tvalid;
reg s_axis_tlast;
wire s_axis_tready;

reg[22:0] rd_data_length;
reg[31:0] rd_bram_addr;
reg rd_bram_en;


always #5 sys_clk = ~sys_clk;

initial begin
	sys_clk = 'd0;
	sys_rst_n = 'd0;



。。。。。。




end

	
	
	
	
top top_inst(
	.sys_clk(sys_clk),
	.sys_rst_n(sys_rst_n),
	.w_data_length(w_data_length),
	.w_bram_addr(w_bram_addr),
	.s_axis_tdata(s_axis_tdata),
	.s_axis_tvalid(s_axis_tvalid),
	.s_axis_tready(s_axis_tready),
	.s_axis_tlast(s_axis_tlast),
	
	.rd_bram_addr(rd_bram_addr),
	.rd_data_length(rd_data_length), 
	.rd_bram_en(rd_bram_en)	
	
    );	
	
	
	
endmodule

        以上仿真先对BRAM进行写操作,再进行读操作,BRAM仿真结果如下:

        读操作仿真如下:

        数据返回状态如下:

        返回状态原文解释如下:

        根据以上描述,返回状态为0x80,代表读写无误。

        如需源代码或者问题咨询,可以联系我 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值