我们上一节介绍了mig ip核的时钟结构,搞清楚时钟结构后我们尝试着读写下ddr3芯片。我们使用黑金的AX7325平台进行实验。
1.我们建立vivado该工程
2.我们建立好工程,然后添加设计文件
3.添加代码
`timescale 1ps/1psmodule top(// Inoutsinout [63:0] ddr3_dq,inout [7:0] ddr3_dqs_n,inout [7:0] ddr3_dqs_p,// Outputsoutput [14: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 [7:0] ddr3_dm, output [0:0] ddr3_odt, //Differential system clocksinput sys_clk_p,input sys_clk_n,input sys_clk, output error,output fan_pwm,input rst_n);localparam nCK_PER_CLK = 4;
localparam DQ_WIDTH = 64;
localparam ADDR_WIDTH = 29;
localparam DATA_WIDTH = 64;
localparam PAYLOAD_WIDTH = 64;localparam APP_DATA_WIDTH = 2 * nCK_PER_CLK * PAYLOAD_WIDTH;
localparam APP_MASK_WIDTH = APP_DATA_WIDTH / 8;wire init_calib_complete;
assign fan_pwm = 1'b0;// Wire declarationswire sys_clk_200MHz; IBUFDS sys_clk_ibufgds(.O (sys_clk_200MHz),.I (sys_clk_p),.IB (sys_clk_n));//下面接口与ddr核相连 wire [ADDR_WIDTH-1:0] app_addr;wire [2:0] app_cmd;wire app_en;wire app_rdy;wire [APP_DATA_WIDTH-1:0] app_rd_data;wire app_rd_data_end;wire app_rd_data_valid;wire [APP_DATA_WIDTH-1:0] app_wdf_data;wire app_wdf_end;wire [APP_MASK_WIDTH-1:0] app_wdf_mask;wire app_wdf_rdy;wire app_sr_active;wire app_ref_ack;wire app_zq_ack;wire app_wdf_wren;wire clk;wire rst;ddr3 u_ddr3 //参考 Xilinx 提供的文档“ug586_7Series_MIS.pdf”(// Memory interface 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_we_n (ddr3_we_n),.ddr3_dq (ddr3_dq),.ddr3_dqs_n (ddr3_dqs_n),.ddr3_dqs_p (ddr3_dqs_p),.ddr3_reset_n (ddr3_reset_n),.init_calib_complete (init_calib_complete),.ddr3_cs_n (ddr3_cs_n),.ddr3_dm (ddr3_dm),.ddr3_odt (ddr3_odt),// Application interface 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_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),.ui_clk (clk),.ui_clk_sync_rst (rst),.app_wdf_mask (app_wdf_mask),// System Clock Ports.sys_clk_i (sys_clk_200MHz),.sys_rst (rst_n));
// End of User Design top instancewire wr_burst_data_req;wire wr_burst_finish;wire rd_burst_finish;wire rd_burst_req;wire wr_burst_req;wire[9:0] rd_burst_len;wire[9:0] wr_burst_len;wire[28:0] rd_burst_addr;wire[28:0] wr_burst_addr;wire rd_burst_data_valid;wire[512 - 1 : 0] rd_burst_data;wire[512 - 1 : 0] wr_burst_data;//对ddr ip核包装,只用对注释的接口进行控制就可实现对ddr的控制
mem_burst
#(.MEM_DATA_BITS(APP_DATA_WIDTH),.ADDR_BITS(ADDR_WIDTH)
)mem_burst_m0
(.rst(rst), /*复位*/.mem_clk(clk), /*接口时钟*/.rd_burst_req(rd_burst_req), /*读请求*/.wr_burst_req(wr_burst_req), /*写请求*/.rd_burst_len(rd_burst_len), /*读数据长度*/.wr_burst_len(wr_burst_len), /*写数据长度*/.rd_burst_addr(rd_burst_addr), /*读首地址*/.wr_burst_addr(wr_burst_addr), /*写首地址*/.rd_burst_data_valid(rd_burst_data_valid), /*读出数据有效*/.wr_burst_data_req(wr_burst_data_req), /*写数据信号*/.rd_burst_data(rd_burst_data), /*读出的数据*/.wr_burst_data(wr_burst_data), /*写入的数据*/.rd_burst_finish(rd_burst_finish), /*读完成*/.wr_burst_finish(wr_burst_finish), /*写完成*/.burst_finish(), /*读或写完成*////.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_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),.ui_clk_sync_rst(), .init_calib_complete(init_calib_complete)
);
//测试文件,读写数据
mem_test
#(.MEM_DATA_BITS(APP_DATA_WIDTH),.ADDR_BITS(ADDR_WIDTH)
)
mem_test_m0
(.rst(rst), .mem_clk(clk), .rd_burst_req(rd_burst_req), .wr_burst_req(wr_burst_req), .rd_burst_len(rd_burst_len), .wr_burst_len(wr_burst_len), .rd_burst_addr(rd_burst_addr), .wr_burst_addr(wr_burst_addr), .rd_burst_data_valid(rd_burst_data_valid), .wr_burst_data_req(wr_burst_data_req), .rd_burst_data(rd_burst_data), .wr_burst_data(wr_burst_data), .rd_burst_finish(rd_burst_finish), .wr_burst_finish(wr_burst_finish), .error(error)
);wire probe0;
wire probe1;
wire probe2;
wire probe3;
wire probe4;
wire probe5;
wire probe6;
wire probe7;
wire [511 : 0] probe8;
wire [511 : 0] probe9;
wire [28 : 0] probe10;ila_0 u_ila_0(.clk(clk),.probe0(probe0),.probe1(probe1),.probe2(probe2),.probe3(probe3),.probe4(probe4),.probe5(probe5),.probe6(probe6),.probe7(probe7),.probe8(probe8),.probe9(probe9),.probe10(probe10)
);
assign probe0 = rd_burst_req;
assign probe1 = wr_burst_req;
assign probe2 = rd_burst_data_valid;
assign probe3 = wr_burst_data_req;
assign probe4 = rd_burst_finish;
assign probe5 = wr_burst_finish;
assign probe6 = error;
assign probe7 = init_calib_complete;
assign probe8 = wr_burst_data[511:0];
assign probe9 = rd_burst_data[511:0];
assign probe10 = app_addr[28:0];
endmodule
module mem_test
#(parameter MEM_DATA_BITS = 64,parameter ADDR_BITS = 24
)
(input rst, /*复位*/input mem_clk, /*接口时钟*/output reg rd_burst_req, /*读请求*/output reg wr_burst_req, /*写请求*/output reg[9:0] rd_burst_len, /*读数据长度*/output reg[9:0] wr_burst_len, /*写数据长度*/output reg[ADDR_BITS - 1:0] rd_burst_addr, /*读首地址*/output reg[ADDR_BITS - 1:0] wr_burst_addr, /*写首地址*/input rd_burst_data_valid, /*读出数据有效*/input wr_burst_data_req, /*写数据信号*/input[MEM_DATA_BITS - 1:0] rd_burst_data, /*读出的数据*/output[MEM_DATA_BITS - 1:0] wr_burst_data, /*写入的数据*/input rd_burst_finish, /*读完成*/input wr_burst_finish, /*写完成*/output reg error
);
localparam IDLE = 3'd0;
localparam MEM_READ = 3'd1;
localparam MEM_WRITE = 3'd2;reg[2:0] state;
reg[7:0] wr_cnt;
reg[MEM_DATA_BITS - 1:0] wr_burst_data_reg;
assign wr_burst_data = wr_burst_data_reg;
reg[7:0] rd_cnt;
always@(posedge mem_clk or posedge rst)
beginif(rst)error <= 1'b0;else if((state == MEM_READ) && rd_burst_data_valid && (rd_burst_data != {(MEM_DATA_BITS/8){rd_cnt}}))error <= 1'b1;elseerror <= error;//写入数据和读出数据不一致
end
always@(posedge mem_clk or posedge rst)
beginif(rst)beginwr_burst_data_reg <= {MEM_DATA_BITS{1'b0}};wr_cnt <= 8'd0;endelse if(state == MEM_WRITE)beginif(wr_burst_data_req)beginwr_burst_data_reg <= {(MEM_DATA_BITS/8){wr_cnt}};//写入数据wr_cnt <= wr_cnt + 8'd1;endelse if(wr_burst_finish)wr_cnt <= 8'd0;end
end
//读数据
always@(posedge mem_clk or posedge rst)
beginif(rst)beginrd_cnt <= 8'd0;endelse if(state == MEM_READ)beginif(rd_burst_data_valid)beginrd_cnt <= rd_cnt + 8'd1;endelse if(rd_burst_finish)rd_cnt <= 8'd0;endelserd_cnt <= 8'd0;
end
//状态机
always@(posedge mem_clk or posedge rst)
beginif(rst)beginstate <= IDLE;wr_burst_req <= 1'b0;rd_burst_req <= 1'b0;rd_burst_len <= 10'd128;//数据长度wr_burst_len <= 10'd128;rd_burst_addr <= 0;wr_burst_addr <= 0;endelsebegincase(state)IDLE:beginstate <= MEM_WRITE;wr_burst_req <= 1'b1;wr_burst_len <= 10'd128;endMEM_WRITE:beginif(wr_burst_finish)beginstate <= MEM_READ;wr_burst_req <= 1'b0;rd_burst_req <= 1'b1;rd_burst_len <= 10'd128;rd_burst_addr <= wr_burst_addr;//地址endendMEM_READ:beginif(rd_burst_finish)beginstate <= MEM_WRITE;wr_burst_req <= 1'b1;wr_burst_len <= 10'd128;rd_burst_req <= 1'b0;wr_burst_addr <= wr_burst_addr + 128;endenddefault:state <= IDLE;endcaseend
endendmodule
/*本模块完成对ddr2 IP的包装,方便后续模块使用,也方便程序的移植,如果更换平台,更新这个文件即可
*/
module mem_burst
#(parameter MEM_DATA_BITS = 64,parameter ADDR_BITS = 24
)
(input rst, /*复位*/input mem_clk, /*接口时钟*/input rd_burst_req, /*读请求*/input wr_burst_req, /*写请求*/input[9:0] rd_burst_len, /*读数据长度*/input[9:0] wr_burst_len, /*写数据长度*/input[ADDR_BITS - 1:0] rd_burst_addr, /*读首地址*/input[ADDR_BITS - 1:0] wr_burst_addr, /*写首地址*/output rd_burst_data_valid, /*读出数据有效*/output wr_burst_data_req, /*写数据信号*/output[MEM_DATA_BITS - 1:0] rd_burst_data, /*读出的数据*/input[MEM_DATA_BITS - 1:0] wr_burst_data, /*写入的数据*/output rd_burst_finish, /*读完成*/output wr_burst_finish, /*写完成*/output burst_finish, /*读或写完成*////与ddrIP核相连的接口output[ADDR_BITS-1:0] app_addr,output[2:0] app_cmd,output app_en,output [MEM_DATA_BITS-1:0] app_wdf_data,output app_wdf_end,output [MEM_DATA_BITS/8-1:0] app_wdf_mask,output app_wdf_wren,input [MEM_DATA_BITS-1:0] app_rd_data,input app_rd_data_end,input app_rd_data_valid,input app_rdy,input app_wdf_rdy,input ui_clk_sync_rst, input init_calib_complete
);assign app_wdf_mask = {MEM_DATA_BITS/8{1'b0}};localparam IDLE = 3'd0;
localparam MEM_READ = 3'd1;
localparam MEM_READ_WAIT = 3'd2;
localparam MEM_WRITE = 3'd3;
localparam MEM_WRITE_WAIT = 3'd4;
localparam READ_END = 3'd5;
localparam WRITE_END = 3'd6;
localparam MEM_WRITE_FIRST_READ = 3'd7;
reg[2:0] state;
reg[9:0] rd_addr_cnt;
reg[9:0] rd_data_cnt;
reg[9:0] wr_addr_cnt;
reg[9:0] wr_data_cnt;reg[2:0] app_cmd_r;
reg[ADDR_BITS-1:0] app_addr_r;
reg app_en_r;
reg app_wdf_end_r;
reg app_wdf_wren_r;
assign app_cmd = app_cmd_r;
assign app_addr = app_addr_r;
assign app_en = app_en_r;
assign app_wdf_end = app_wdf_end_r;
assign app_wdf_data = wr_burst_data;
assign app_wdf_wren = app_wdf_wren_r & app_wdf_rdy;
assign rd_burst_finish = (state == READ_END);
assign wr_burst_finish = (state == WRITE_END);
assign burst_finish = rd_burst_finish | wr_burst_finish;assign rd_burst_data = app_rd_data;
assign rd_burst_data_valid = app_rd_data_valid;assign wr_burst_data_req = (state == MEM_WRITE) & app_wdf_rdy ;always@(posedge mem_clk or posedge rst)
beginif(rst)beginapp_wdf_wren_r <= 1'b0;endelse if(app_wdf_rdy)app_wdf_wren_r <= wr_burst_data_req;
end
//DDR Burst 写和读,可以参考实验教程“DDR读写测试实验.pdf”
always@(posedge mem_clk or posedge rst)
beginif(rst)beginstate <= IDLE;app_cmd_r <= 3'b000;app_addr_r <= 0;app_en_r <= 1'b0;rd_addr_cnt <= 0;rd_data_cnt <= 0;wr_addr_cnt <= 0;wr_data_cnt <= 0;app_wdf_end_r <= 1'b0;endelse if(init_calib_complete === 1'b1)begincase(state)IDLE:beginif(rd_burst_req)//读请求beginstate <= MEM_READ;app_cmd_r <= 3'b001;app_addr_r <= {rd_burst_addr,3'd0};//向IP核发送指令app_en_r <= 1'b1;endelse if(wr_burst_req)//写请求beginstate <= MEM_WRITE;app_cmd_r <= 3'b000;app_addr_r <= {wr_burst_addr,3'd0};//向IP核发送指令app_en_r <= 1'b1;wr_addr_cnt <= 0;app_wdf_end_r <= 1'b1;wr_data_cnt <= 0;endendMEM_READ:beginif(app_rdy)beginapp_addr_r <= app_addr_r + 8;if(rd_addr_cnt == rd_burst_len - 1)beginstate <= MEM_READ_WAIT;rd_addr_cnt <= 0;app_en_r <= 1'b0;endelserd_addr_cnt <= rd_addr_cnt + 1;endif(app_rd_data_valid)beginif(rd_data_cnt == rd_burst_len - 1)beginrd_data_cnt <= 0;state <= READ_END;endelsebeginrd_data_cnt <= rd_data_cnt + 1;endendendMEM_READ_WAIT:beginif(app_rd_data_valid)beginif(rd_data_cnt == rd_burst_len - 1)beginrd_data_cnt <= 0;state <= READ_END;endelsebeginrd_data_cnt <= rd_data_cnt + 1;endendendMEM_WRITE_FIRST_READ:beginapp_en_r <= 1'b1;state <= MEM_WRITE;wr_addr_cnt <= 0;endMEM_WRITE:beginif(app_rdy)beginapp_addr_r <= app_addr_r + 'b1000;if(wr_addr_cnt == wr_burst_len - 1)beginapp_wdf_end_r <= 1'b0;app_en_r <= 1'b0;endelsebeginwr_addr_cnt <= wr_addr_cnt + 1;endendif(wr_burst_data_req)beginif(wr_data_cnt == wr_burst_len - 1)beginstate <= MEM_WRITE_WAIT;endelsebeginwr_data_cnt <= wr_data_cnt + 1;endendendREAD_END:state <= IDLE;MEM_WRITE_WAIT:beginif(app_rdy)beginapp_addr_r <= app_addr_r + 'b1000;if(wr_addr_cnt == wr_burst_len - 1)beginapp_wdf_end_r <= 1'b0;app_en_r <= 1'b0;if(app_wdf_rdy) state <= WRITE_END;endelsebeginwr_addr_cnt <= wr_addr_cnt + 1;endendelse if(~app_en_r & app_wdf_rdy)state <= WRITE_END;endWRITE_END:state <= IDLE;default:state <= IDLE;endcaseend
end
endmodule
4.添加ip核
这里我们暂时不选择axi接口,使用native
不需要兼容其他型号
我们使用的芯片位ddr3
如下图,1处为给ddr3芯片的时钟,我们使用800M,2处comporment表示是芯片颗粒,下拉菜单里有其他选项,表示内存条。3处是我们使用的芯片型号,我们这里使用MT41J256M16。4处表示总位宽,这里我们看下四片ddr3芯片的连接方式为16*4=64位。
input clock period表示ip核的输入时钟,我们这里提供200M的时钟。
下图1处表示输入时钟的形式,有单端,差分和单端no buff,我们这里选择nobuff形式。2处表示参考时钟来源,我们参考时钟使用系统时钟就行。
然后进行管脚约束
添加管脚文件
验证通过
默认下一步
如下图,IP核生成成功。
下来我们生成ila的ip核
如下设置
两个ip核都能使用了
5.我们再添加下约束文件,这里主要约束了时钟周期和一些管脚。
接下来进行网表编译
网表编译完进行布局布线。
布局布线完成后生成下载文件
然后连接芯片
连接上芯片后就下载程序
7.下载完我们可以看到波形,我们详细看下:
可以看到在1处我们发出了wr_burst_req,当wr_burst_data_valid拉高时我们开始往芯片里面写数据,每次数据累加1,直到7f为止。
然后我们将写入的数据读出来,我们先发出读请求,当rd_burst_data_valid拉高时,表示读取数据有效。直到读取完毕,我们可以看到写入的数据与读出的数据一致。
到这里我们就初步完成了读写实验,但是代码逻辑是怎样的,我们后面再分析,进行仿真实验。