前言:为了使用DDR3 SDRAM IP 完成读写操作,前面做了很多的铺垫比如:
mig ip核的调用
根据手册了解该ip核的一些基础知识
mig ip核使用之前如何进行初始化操作
下面开始基于 A7 的 DDR3 SDRAM IP的写实现、读实现、以及单次的读写实现(在test bench中分时给出读写命令。)
一、DDR3 Sdram IP 写时序:
1、写命令和写数据总线介绍
DDR3 SDRAM控制器IP核主要预留了两组总线:
- 一组是直接绑定到DDR3 SDRAM芯片端口(绿框),信号以ddr_ 开头。
- 一组是留给用户端自定义使用(红框),信号以app_开头,其中user fpga logic → ip核的信号 需要我们产生。
中间部分就是我们调用的ip核。
先了解app侧写数据总线和命令总线功能:
app端命令总线:(input 和output是相对于ip核)
可以看到:只有app_en 和app_rdy同时拉高(③)cmd才能被ip接收。
app端写数据总线:(input 和output是相对于ip核)
即在下图写数据总线波形中只有(③)位置的数据被ip接收。
这里对app_wdf_end信号深入分析:
其有效信号表示当前为app_wdf_data的最后一个数据。
这里要知道,用户端写数据位宽(根据芯片不同而不同 这里是128bit)和实际DDR3的数据位宽(16bit)不一样。
在 A7 DDR3 控制器 IP 核中,burst length=8(固定),即每次突发均为:16bit(该ddr3芯片数据位宽) x 8 =128bit;
在前文调用ip核时,可以进行2:1和4:1的选择配置。
- 4:1时app_wdf_data 为 128bit,此时每一个发送的有效 app_wdf_data 数据均为当前 8 突发的第一个数据,同时也是最后一个数据。即: app_wdf_end 信号与 app_wdf_wren 信号同步;
- 2:1时app_wdf_data 为 64bit,此时每一个发送的有效 app_wdf_data 数据均为当前突发的 4 个数据,即:每两拍app_wdf_data ,app_wdf_end 信号与 app_wdf_wren 信号同步一次。
这里ddr3的一个内存地址app_addr存储16bit数据,则不同比例模式下,app_addr的偏移情况:
2、写命令和写数据间关系介绍
之前介绍了写数据有三种写入方式,为了保证写入数据的稳定性,一般使用1,2方式,本次写数据实现采用第2种方式,即:写数据提前于写命令
3、写控制模块框图设计
其中a7_wr_ctrl是需要我们设计的模块。
接口描述:
4、进行波形设计(绿色的是输入信号,黄色为输出信号,白色为内部信号。)
5、testbench及仿真效果
`timescale 1ns/1ps
module tb_top_ddr3_hdmi (); /* this is automatically generated */
reg srst_n;
reg clk;
//FOR SIM
reg sclk;
reg rst;
reg data_req;
reg wr_cmd_start;
reg [2:0] wr_cmd_instr;
reg [27:0] wr_cmd_addr;
reg [6:0] wr_cmd_bl;
reg [15:0] wr_cmd_mask;
reg [127:0] data_128bit;
initial begin
wr_cmd_start =0;
wr_cmd_instr =0;
wr_cmd_addr =0;
wr_cmd_mask =0;
wr_cmd_bl =64;
data_128bit =0;
force sclk = inst_top_ddr3_hdmi.inst_a7_ddr3_wr_ctrl.sclk;
force rst = inst_top_ddr3_hdmi.inst_a7_ddr3_wr_ctrl.rst;
force data_req = inst_top_ddr3_hdmi.inst_a7_ddr3_wr_ctrl.data_req;
force inst_top_ddr3_hdmi.wr_cmd_start=wr_cmd_start;
force inst_top_ddr3_hdmi.wr_cmd_instr=wr_cmd_instr;
force inst_top_ddr3_hdmi.wr_cmd_addr=wr_cmd_addr;
force inst_top_ddr3_hdmi.wr_cmd_mask=wr_cmd_mask;
force inst_top_ddr3_hdmi.wr_cmd_bl=wr_cmd_bl;
force inst_top_ddr3_hdmi.data_128bit=data_128bit;
end
initial begin
#100
gen_cmd();
end
initial begin
#100
gen_data();
end
task gen_cmd;
integer i;
begin
@(negedge rst);
@(posedge sclk);
@(posedge sclk);
@(posedge sclk);
wr_cmd_start =1;
@(posedge sclk);
wr_cmd_start =0;
end
endtask
task gen_data;
integer i;
begin
@(posedge data_req);
for(i=0;i<64;i=i+1) begin
if (data_req==1'b1) begin
data_128bit={
96'd0,i[31:0]};
end
else begin
i=i-1;
end
@(posedge sclk);
end
data_128bit = 0; // 循环结束后,将 data_128bit 清零
@(posedge sclk);
end
endtask
// clock
initial begin
clk = 0;
forever #(10) clk = ~clk;
end
// reset
initial begin
srst_n <= 0;
#200
repeat (5) @(posedge clk);
srst_n <= 1;
end
// (*NOTE*) replace reset, clock, others
wire [15:0] ddr3_dq;
wire [1:0] ddr3_dqs_n;
wire [1:0] ddr3_dqs_p;
wire [13:0] ddr3_addr;
wire [2:0] ddr3_ba;
wire ddr3_ras_n;
wire ddr3_cas_n;
wire ddr3_we_n;
wire ddr3_reset_n;
wire [0:0] ddr3_ck_p;
wire [0:0