在上一篇中Xilinx DDR3 —— MIG IP核的配置(APP接口),已经观看了Xilinx官方提供的MIG IP核读写例程仿真波形,本着学习的目的,本篇开始自己编写MIG IP核读写程序,用于驱动MIG IP核进行DDR 3数据的读写。由于没有DDR实物,这里直接借助官方提供的MIG IP核读写例程中的DDR3模拟程序,即直接在Xilinx官方提供的MIG IP核读写例程进行修改。
1. 如下图所示,新建一个ddr3_rw.v,用于编写控制DDR3读写的程序
2. 如下图所示,首先定义输入输出
MIG IP核有两组接口,一组是Memory interface ports ,它连接到DDR3,无需用户来操作,只需要连接好就行;另一组是Application interface ports,它是用户端的接口,供用户直接操作的。MIG IP 会将从Application interface ports接收到的信号在内部进行时序转换,变成直接控制DDR的时序,由Memory interface ports 输出给DDR3,这个逻辑一定要清楚,因此要控制DDR的读写,只需控制好Application interface ports即可。
3. 总体代码如下:
module ddr3_rw(
input ui_clk, // MIG IP核产生的用户时钟,用于
input ui_clk_sync_rst, // MIG IP核产生的复位信号,高有效
input init_calib_complete, // DDR3初始化完成初始化完成,高电平有效,
input app_rdy, // MIG IP核读写命令接收准备完成,高电平有效。
input app_wdf_rdy, // MIG IP核数据接收准备完成,高电平有效。
input app_rd_data_valid, // 读数据有效
input [127:0] app_rd_data, // 用户要读取的数据
output reg [28:0] app_addr, // 地址,该地址位宽ADDR_WIDTH = RANK位宽 + BANK位宽+ ROW位宽 + COL位宽
output reg app_en, // MIG IP核命令写入使能,高有效。写命令时需要拉高该信号
output app_wdf_wren, // 用户写数据使能
output app_wdf_end, // 突发写当前时钟最后一个数据
output [2:0] app_cmd, // MIG IP核读写控制命令,000位写,001为读。
output reg [127:0] app_wdf_data // 用户要写入的数据
);
//***************************************************** //
//** DDR3读写逻辑 **//
//*****************************************************//
reg [4:0] wr_data_cnt;
reg [4:0] rd_data_cnt;
reg [4:0] rd_addr_cnt;
reg error_flag;
wire error;
parameter TEST_LENGTH = 20;
localparam IDLE = 2'd0,
WRITE = 2'd1,
WAIT = 2'd2,
READ = 2'd3;
reg [1:0] cur_state;
reg [1:0] next_state;
assign rst_n = !ui_clk_sync_rst;
always @(*) begin
if ((cur_state == WRITE) && (app_rdy && app_wdf_rdy)) begin // 当处于写状态时,若app_rdy和app_wdf_rdy拉高,则将app_en拉高。
app_en = 1'd1;
end
else begin
if ((cur_state == READ) && app_rdy) begin // 当处于读状态时,若app_rdy拉高,则将app_en拉高。
app_en = 1'd1;
end
else begin
app_en = 1'd0;
end
end
end
// 在写状态,命令接收(app_rdy)和数据接收(app_wdf_rdy)都准备好,此时拉高写使能
assign app_wdf_wren = ((cur_state == WRITE) && (app_rdy) && (app_wdf_rdy)) ? 1'b1:1'b0;
// 由于DDR3芯片时钟和用户时钟的分频选择4:1,突发长度为8,故两个信号相同
assign app_wdf_end = app_wdf_wren;
// 当前处于读状态则为1,写状态则为0
assign app_cmd = (cur_state == READ) ? 3'd1 :3'd0;
always @(posedge ui_clk or negedge rst_n) begin
if (!rst_n) begin
cur_state <= IDLE;
end
else begin
cur_state <= next_state;
end
end
always @(*) begin
if (!rst_n) begin
next_state = IDLE;
end
else begin
case (cur_state)
IDLE: begin
if (init_calib_complete) begin
next_state = WRITE;
end
else begin
next_state = IDLE;
end
end
WRITE:begin
if (wr_data_cnt < TEST_LENGTH) begin
next_state = WRITE;
end
else begin
next_state = WAIT;
end
end
WAIT:begin
next_state = READ;
end
READ:begin
if (rd_addr_cnt < TEST_LENGTH) begin
next_state = READ;
end
else begin
next_state = IDLE;
end
end
default: begin
if (init_calib_complete) begin
next_state = WRITE;
end
else begin
next_state = IDLE;
end
end
endcase
end
end
always @(posedge ui_clk or negedge rst_n) begin
if (!rst_n) begin
app_addr <= 29'd0;
wr_data_cnt <= 5'd0;
end
else begin
case (next_state)
IDLE: begin
app_wdf_data <= 256'd0;
wr_data_cnt <= 0;
rd_addr_cnt <= 0;
app_addr <= 0;
end
WRITE:begin
if ((app_rdy)&&(app_wdf_rdy)&&(~app_cmd)) begin
app_addr <= app_addr + 8;
app_wdf_data <= app_wdf_data + 1;
wr_data_cnt <= wr_data_cnt + 1;
end
else begin
app_addr <= app_addr;
app_wdf_data <= app_wdf_data;
wr_data_cnt <= wr_data_cnt;
end
end
WAIT:begin
app_addr <= 0;
end
READ:begin
if ((app_rdy)&&(app_cmd)) begin
app_addr <= app_addr + 8;
rd_addr_cnt <= rd_addr_cnt + 1;
end
else begin
app_addr <= app_addr;
rd_addr_cnt <= rd_addr_cnt;
end
end
default: begin
app_wdf_data <= 256'd0;
wr_data_cnt <= 0;
app_addr <= 0;
end
endcase
end
end
always @(posedge ui_clk or negedge rst_n) begin
if (!rst_n) begin
rd_data_cnt <= 5'd0;
end
else begin
if (app_rd_data_valid) begin
if (rd_data_cnt == TEST_LENGTH) begin
rd_data_cnt <= 5'd0;
end
else begin
rd_data_cnt <= rd_data_cnt + 1;
end
end
else begin
rd_data_cnt <= rd_data_cnt;
end
end
end
//读信号有效,且读出的数不是写入的数时,将错误标志位拉高
assign error = (app_rd_data_valid && (rd_data_cnt!=app_rd_data));
//寄存状态标志位
always @(posedge ui_clk or negedge rst_n) begin
if(~rst_n)
error_flag <= 0;
else if(error)
error_flag <= 1;
end
endmodule
4. 将ddr3_rw.v例化进example_top.v:
5. 同时将example_top.v中的mig_7series_v4_1_traffic_gen_top注释掉:
6. 点击run simulation仿真即可出现如下波形
7. 如图中紫线所示,error_flag一直为低,说明读出的数据与写入的数据一致。