简介
本文主要介绍使用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,代表读写无误。
如需源代码或者问题咨询,可以联系我