【FPGA】 DDR3读写(基于User Interface)
DDR3概述
DDR3 (double data rate 3 synchronous dynamic RAM) 第三代双倍数据速率同步动态随机存储器
- 同步:数据的速去和写入时钟同步
- 动态:数据掉电无法保存,需要周期性刷新才能保持数据
- 随机存取:能够对任意地址进行操作
- 双倍数据速率:时钟的上升沿和下降沿都发生数据传输
DDR3存储器模型
用户可通过MIG IP核控制DDR3
MIG IP核的配置
- 修改component name
- 选择是否使用AXI4接口(此处选用User Interface接口)
-
clock period
物理层时钟周期,即DDR3芯片的输入时钟
-
PHY to Controller Clock Ratio
DDR3输入时钟和MIG控制器时钟频率比例,此处选择4:1说明MIG控制器时钟为100MHz,因此用户侧逻辑也会按照100MHz进行编写
由于DDR3是时钟双边沿读写,假设DDR3的位宽是16bits,则带宽为400MHz*2*16bits
而对应的用户侧则通过扩展的方式匹配,其位宽为400MHz*2*16bits/100MHz = 128bits
-
Vccaux_io
-
Memory Type
DDR3内存类型,如果是DDR3芯片颗粒,则选用components,如果是内存条的形式,则选择对应的类型
-
Memory Part
根据DDR3型号选择,此处选用位宽为16bits
-
Data Width
-
ECC
-
Data Mask
是否启用数据掩码,即可通过app_data_mask端口去控制写入部分位(例如高8位)数据
-
Number of Bank Machines
与使用的DDR3有关,如果拉满则效率最高,看需求
-
ORDERING
Strict即保持数据的读写顺序,Normal即允许控制器改变执行cmd的顺序,提高读写速率
-
Input Clock Period
输入时钟周期,通过MIG内部的PLL得到最终输入DDR3的时钟
-
system clock
系统时钟类型
-
reference clock
DDR3的正常运行需要200MHz参考时钟,如果前面选择了200MHz的输入时钟,则此处可以复用为User System Clock
-
system reset polarity
- 后者是因为硬件已经设计好了,引脚是固定的
- 前者会自动选择最优的引脚去驱动
MIG IP核的使用
MIG控制器的三种时序:写命令、写数据、读数据都有对应的FIFO,因此操作起来更为灵活,限制较小
写命令
写数据
当app_wdf_rdy为高电平时,即可拉高app_wdf_en写入数据
数据可在命令之前、同时或最大不慢于2个周期写入
读数据
写入读命令后,数据可能在若干个周期后读出,伴随app_rd_data_valid信号拉高
如何对自己的DDR3读写模块进行仿真?
由于仿真需要DDR3模型,而自己写一个DDR3模型过于复杂,因此可通过利用example design的代码,并换上自己的DDR3读写模块实现,具体可参考该UP的教学ddr3搭建仿真平台_哔哩哔哩_bilibili
1、Open IP Example Design会自动生成一个项目
2、找到项目imports文件夹下的这些文件
并复制到原先的工程文件夹中,同时vivado中add sim文件
3、修改example_top.v文件,换成自己的模块
top_module.v:
`timescale 1ns / 1ps
module top_module(
// inouts
ddr3_dq,
ddr3_dqs_n,
ddr3_dqs_p,
// outputs
ddr3_addr,
ddr3_ba,
ddr3_ras_n,
ddr3_cas_n,
ddr3_we_n,
ddr3_reset_n,
ddr3_ck_p,
ddr3_ck_n,
ddr3_cke,
ddr3_cs_n,
ddr3_dm,
ddr3_odt,
init_calib_complete,
// inputs
sys_clk_i,
sys_rst
);
inout [15:0] ddr3_dq;
inout [1:0] ddr3_dqs_n;
inout [1:0] ddr3_dqs_p;
output [13:0] ddr3_addr;
output [2:0] ddr3_ba;
output ddr3_ras_n;
output ddr3_cas_n;
output ddr3_we_n;
output ddr3_reset_n;
output [0:0] ddr3_ck_p;
output [0:0] ddr3_ck_n;
output [0:0] ddr3_cke;
output [0:0] ddr3_cs_n;
output [1:0] ddr3_dm;
output [0:0] ddr3_odt;
output init_calib_complete;
input sys_clk_i;
input sys_rst;
wire [27:0] app_addr;
wire [2:0] app_cmd;
wire app_en;
wire [127:0] app_wdf_data;
wire app_wdf_end;
wire app_wdf_wren;
wire [127:0] app_rd_data;
wire app_rd_data_end;
wire app_rd_data_valid;
wire app_rdy;
wire app_wdf_rdy;
wire app_sr_req;
wire app_ref_req;
wire app_zq_req;
wire app_sr_active;
wire app_ref_ack;
wire app_zq_ack;
wire ui_clk;
wire ui_clk_sync_rst;
wire [15:0] app_wdf_mask;
wire sys_clk_i;
wire sys_rst;
// mig_16bits instance
mig_16bits mig_16bits_inst(
// memory ports
.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_ras_n(ddr3_ras_n),
.ddr3_reset_n(ddr3_reset_n),
.ddr3_we_n(ddr3_we_n),
.ddr3_dq(ddr3_dq),
.ddr3_dqs_n(ddr3_dqs_n),
.ddr3_dqs_p(ddr3_dqs_p),
.init_calib_complete(init_calib_complete),
.ddr3_cs_n(ddr3_cs_n),
.ddr3_dm(ddr3_dm),
.ddr3_odt(ddr3_odt),
// user app ports
.app_addr(app_addr),
.app_cmd(app_cmd),
.app_en(app_en),
.app_wdf_data(app_wdf_data),
.app_wdf_end(app_wdf_end),
.app_wdf_wren(app_wdf_wren),
.app_rd_data(app_rd_data),
.app_rd_data_end(app_rd_data_end),
.app_rd_data_valid(app_rd_data_valid),
.app_rdy(app_rdy),
.app_wdf_rdy(app_wdf_rdy),
.app_wdf_mask(app_wdf_mask),
.app_sr_req(1'b0),
.app_ref_req(1'b0),
.app_zq_req(1'b0),
.app_sr_active(app_sr_active),
.app_ref_ack(app_ref_ack),
.app_zq_ack(app_zq_ack),
// clock ports
.ui_clk(ui_clk),
.ui_clk_sync_rst(ui_clk_sync_rst),
.sys_clk_i(sys_clk_i),
.sys_rst(sys_rst)
);
// mig data read write test
wire test_reset;
mig_test mig_test_inst(
.init_calib_complete(init_calib_complete),
.ui_clk(ui_clk),
.ui_rst_in(ui_clk_sync_rst),
.app_addr(app_addr),
.app_cmd(app_cmd),
.app_en(app_en),
.app_wdf_data(app_wdf_data),
.app_wdf_end(app_wdf_end),
.app_wdf_mask(app_wdf_mask),
.app_wdf_wren(app_wdf_wren),
.app_wdf_rdy(app_wdf_rdy),
.app_rd_data(app_rd_data),
.app_rd_data_end(app_rd_data_end),
.app_rd_data_valid(app_rd_data_valid),
.app_rdy(app_rdy),
.test_result(test_reset)
);
endmodule
mig_test.v:
向DDR3中写入16个数据之后读出
`timescale 1ns / 1ps
module mig_test(
init_calib_complete,
ui_clk,
ui_rst_in,
app_addr,
app_cmd,
app_en,
app_wdf_data,
app_wdf_end,
app_wdf_mask,
app_wdf_wren,
app_wdf_rdy,
app_rd_data,
app_rd_data_end,
app_rd_data_valid,
app_rdy
);
input init_calib_complete;
input ui_clk;
input ui_rst_in;
wire ui_rst;
assign ui_rst = ~ui_rst_in;
output wire [27:0] app_addr;
reg [27:0] app_addr_write = 0;
reg [27:0] app_addr_read = 0;
output wire [2:0] app_cmd;
output reg app_en = 0;
output reg [127:0] app_wdf_data = 0;
output reg app_wdf_end = 0;
output reg [15:0] app_wdf_mask = 0;
output reg app_wdf_wren = 0;
input app_wdf_rdy;
input [127:0] app_rd_data;
input app_rd_data_end;
input app_rd_data_valid;
input app_rdy;
reg [127:0] rd_data_reg = 0;
wire wr_done;
wire rd_done;
reg [7:0] counter_write_cmd = 16;
reg [7:0] counter_write = 16;
reg [7:0] counter_read_cmd = 0;
reg [7:0] counter_read = 0;
localparam STATE_IDLE = 4'b0001;
localparam STATE_WRITE = 4'b0010;
localparam STATE_READ = 4'b0100;
localparam STATE_END = 4'b1000;
reg [3:0] state = STATE_IDLE ;
reg [3:0] next_state = STATE_IDLE ;
// next state logic
always @(*) begin
if (~ui_rst | ~init_calib_complete) begin
next_state = STATE_IDLE;
end
else begin
case (state)
STATE_IDLE:
begin
next_state = STATE_WRITE;
end
STATE_WRITE:
begin
if(wr_done) begin
next_state = STATE_READ;
end
else begin
next_state = STATE_WRITE;
end
end
STATE_READ:
begin
if(rd_done) begin
next_state = STATE_END;
end
else begin
next_state = STATE_READ;
end
end
STATE_END:
begin
next_state = STATE_END;
end
default: ;
endcase
end
end
// state change
always @(posedge ui_clk) begin
if(~ui_rst | ~init_calib_complete) begin
state <= STATE_IDLE;
end
else begin
state <= next_state;
end
end
// output logic
// app_cmd
assign app_cmd = (state == STATE_WRITE)?3'b000:3'b001;
//app_en
always @(*) begin
if(~ui_rst) begin
app_en <= 0;
end
else begin
// write data
if(state == STATE_WRITE && app_rdy && counter_write_cmd != 0) begin
app_en <= 1;
// counter_write_cmd <= counter_write_cmd - 1;
end
else if(state == STATE_READ && app_rdy && counter_read_cmd != 8'd16) begin
app_en <= 1;
// counter_read_cmd <= counter_read_cmd + 1;
end
else begin
app_en <= 0;
end
end
end
// counter_write_cmd / counter_read_cmd
always @(posedge ui_clk) begin
if(~ui_rst) begin
counter_write_cmd <= 16;
counter_read_cmd <= 0;
end
else begin
if(state == STATE_WRITE && app_rdy && app_en && counter_write_cmd != 0) begin
counter_write_cmd <= counter_write_cmd - 1;
end
else if(state == STATE_READ && app_rdy && app_en && counter_read_cmd != 16) begin
counter_read_cmd <= counter_read_cmd + 1;
end
end
end
// app_wdf_data
always @(posedge ui_clk) begin
if(~ui_rst) begin
app_wdf_data <= 0;
app_wdf_wren <= 0;
app_wdf_end <= 0;
end
else if(state == STATE_WRITE && app_wdf_rdy && counter_write != 0) begin
app_wdf_wren <= 1;
app_wdf_data <= app_wdf_data + 1'd1;
app_wdf_end <= 1;
counter_write <= counter_write - 1'd1;
end
else begin
app_wdf_wren <= 0;
app_wdf_end <= 0;
end
end
// app_addr_write
always @(posedge ui_clk) begin
if(~ui_rst) begin
app_addr_write <= 0;
end
else begin
if(state == STATE_WRITE && app_rdy && counter_write_cmd!=0) begin
app_addr_write <= app_addr_write + 28'd8;
end
end
end
// app_addr_read
always @(posedge ui_clk) begin
if(~ui_rst) begin
app_addr_read <= 0;
end
else begin
if(state == STATE_READ && app_rdy && counter_read_cmd!=16) begin
app_addr_read <= app_addr_read + 28'd8;
end
end
end
// app_addr
assign app_addr = (state == STATE_WRITE)?app_addr_write:app_addr_read;
// app_rd_data
always @(posedge ui_clk) begin
if (~ui_rst) begin
rd_data_reg <= 0;
end
else begin
if(app_rd_data_valid)
begin
rd_data_reg <= app_rd_data;
counter_read <= counter_read + 1'd1;
end
end
end
// wr_done
assign wr_done = (counter_write_cmd == 0)&&(counter_write == 0);
// rd_done
assign rd_done = (counter_read_cmd == 16)&&(counter_read == 16);
endmodule
标签:wire,FPGA,wdf,DDR3,app,rd,User,ddr3,data
From: https://www.cnblogs.com/lzbmeee/p/17632571.html