一、IP核生成不成功可能原因
1、打开 Quartus II 软件时,请右键选择以管理员方式运行,切记,否则可能导致 IP 生成不成功。
2、创建工程时不要将工程创建在和 Quartus II 安装目录相同的盘符下,否则可能导致生产 IP 失败。
3、如果在生产 IP 界面长时间卡住,无法生成 IP,请关闭掉 Windows 系统的自动更新和防火墙后重新尝试。
二、modelsim仿真时出错可能原因
IP 请直接创建在 Quartus 的工程根目录下,不要再创建子文件夹,否则仿真的时候会报错。提示找不到文件,实际是因为文件太多,IP 中有些文件路径包含时候处理不好导致的,属于 IP 核设计的小 bug。
三、信号分析
1. 功能
循环写入255个数后再读出。再在接下来的地址写入、读出。
2. 关于代码中突发长度的几个信号区别
rd/wr_burst_len=255 ,用户需要读写的数据量;
burst_size=2 (=burst_length) ,访问DDR2一次连续访问的数据量,即提供一次地址,能连续读 burst_size长度的数据,然后下次的地址+ burst_size;
length初始值=rd/wr_burst_len,用于状态的判断。
ip核信号:
local_address:将访问SDRAM的地址作为输入,无论是读还是写。因此需要在各种状态下,对local_address进行赋值。
3. 程序状态示意图
四、代码
1. IP核封装
/*本模块完成对ddr2 IP的包装,方便后续模块使用,也方便程序的移植,如果更换平台,更新这个文件即可
*/
module mem_burst_v2
#(parameter MEM_DATA_BITS = 32,parameter ADDR_BITS = 24,parameter LOCAL_SIZE_BITS = 3
)
(input rst_n, /*复位*/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, /*读或写完成*/////*以下是altera ddr2 IP的接口,可参考altera相关文档*/input local_init_done,output ddr_rst_n,input local_ready,output local_burstbegin,output[MEM_DATA_BITS - 1:0] local_wdata,input local_rdata_valid,input[MEM_DATA_BITS - 1:0] local_rdata,output local_write_req,output local_read_req,output reg[23:0] local_address,output[MEM_DATA_BITS/8 - 1:0] local_be,output reg[LOCAL_SIZE_BITS - 1:0] local_size
);
parameter IDLE = 3'd0;
parameter MEM_READ = 3'd1;
parameter MEM_READ_WAIT = 3'd2;
parameter MEM_WRITE = 3'd3;
parameter MEM_WRITE_BURST_BEGIN = 3'd4;
parameter MEM_WRITE_FIRST = 3'd5;
parameter burst_size = 10'd2;
reg[2:0] state = 3'd0;
reg[2:0] next_state = 3'd0;
reg[9:0] rd_addr_cnt = 10'd0;
reg[9:0] rd_data_cnt = 10'd0;reg[9:0] length = 10'd0;
reg[11:0] cnt_timer = 12'd0;
reg[11:0] ddr_reset_timer = 12'd0;
reg ddr_rst_n_reg = 1'b1;reg [LOCAL_SIZE_BITS - 1:0] burst_remain;
reg last_wr_burst_data_req;
reg[9:0] wr_remain_len;/*写入数据请求信号*/
assign wr_burst_data_req = (state == MEM_WRITE_FIRST ) || (((state == MEM_WRITE_BURST_BEGIN) || (state == MEM_WRITE)) && local_ready && ~last_wr_burst_data_req);
assign burst_finish = rd_burst_finish | wr_burst_finish;/*local写请求信号*/
assign local_write_req = ((state == MEM_WRITE_BURST_BEGIN) || (state == MEM_WRITE));/*初始化计时器, 在本程序没有用到*/
always@(posedge mem_clk or negedge rst_n)
beginif(~rst_n)cnt_timer <= 12'd0;else if(state == IDLE || ~local_init_done)cnt_timer <= 12'd0;elsecnt_timer <= cnt_timer + 12'd1;
end/*DDR读等待计数器, 在本程序没有用到*/
always@(posedge mem_clk or negedge rst_n)
beginif(~rst_n)ddr_reset_timer <= 12'd0;else if(state == MEM_READ_WAIT)ddr_reset_timer <= ddr_reset_timer + 12'd1;elseddr_reset_timer <= 12'd0; ddr_rst_n_reg <= (ddr_reset_timer !=12'd200);
end
assign ddr_rst_n = ddr_rst_n_reg;/*状态锁存*/
always@(posedge mem_clk or negedge rst_n)beginif(~rst_n)state <= IDLE;else if(~local_init_done ) //IP核的初始化结束信号state <= IDLE;elsestate <= next_state;end/*burst读写处理程序*/
always@(*)begincase(state)IDLE:beginif(rd_burst_req && rd_burst_len != 10'd0) /*接收到burst读请求*/next_state <= MEM_READ; /*状态转为发burst读命令*/else if(wr_burst_req && wr_burst_len != 10'd0) /*接收到burst写请求*/next_state <= MEM_WRITE_FIRST; /*状态转为第一次写*/elsenext_state <= IDLE;endMEM_READ: /*burst读命令*/beginif( (rd_addr_cnt + burst_size >= length) && local_read_req && local_ready) /*判断burst读请求的有效的长度*/next_state <= MEM_READ_WAIT;elsenext_state <= MEM_READ;endMEM_READ_WAIT: /*等待burst数据读完成*/begin if(rd_data_cnt == length - 10'd1 && local_rdata_valid) /*判断读出的有效数据长度*/next_state <= IDLE;elsenext_state <= MEM_READ_WAIT;endMEM_WRITE_FIRST: /*第一次写状态, 用于准备写入的数据*/next_state <= MEM_WRITE_BURST_BEGIN;MEM_WRITE_BURST_BEGIN: /*产生burst begin信号*/ beginif(local_ready && wr_remain_len == 10'd1) /*如果写的剩余数据长度为1, Burst写结束*/next_state <= IDLE;else if(burst_remain == 1 && local_ready) /*一次local burst写完成, 重新回到burst begin */ next_state <= MEM_WRITE_BURST_BEGIN;else if(local_ready)next_state <= MEM_WRITE; /*burst begin完成, 转到数据写入*/ elsenext_state <= MEM_WRITE_BURST_BEGIN;endMEM_WRITE: beginif(wr_remain_len == 10'd1 && local_ready) /*如果写的剩余数据长度为1, Burst写结束*/next_state <= IDLE;else if(burst_remain == 1 && local_ready) /*一次local burst写完成,重新回到burst begin */ next_state <= MEM_WRITE_BURST_BEGIN;else next_state <= MEM_WRITE;enddefault:next_state <= IDLE;endcaseendassign local_burstbegin = ((state == MEM_WRITE_BURST_BEGIN) || (state == MEM_READ)); /*产生local burst begin信号*//*计算最后一个burst数据写请求信号*/
always@(posedge mem_clk)beginif(state == MEM_WRITE_BURST_BEGIN || state == MEM_WRITE)if(wr_remain_len == 10'd2 && local_ready)last_wr_burst_data_req <= 1'b1;elselast_wr_burst_data_req <= last_wr_burst_data_req;elselast_wr_burst_data_req <= 1'b0;end/*计算外部burst写的剩余数据长度*/
always@(posedge mem_clk)begincase(state)IDLE:if(wr_burst_req)wr_remain_len <= wr_burst_len; /*wr_remain_len赋初值*/elsewr_remain_len <= wr_remain_len;MEM_WRITE_BURST_BEGIN: if(local_ready)wr_remain_len <= wr_remain_len - 10'd1;elsewr_remain_len <= wr_remain_len;MEM_WRITE:if(local_ready)wr_remain_len <= wr_remain_len - 10'd1;elsewr_remain_len <= wr_remain_len;default:wr_remain_len <= wr_remain_len;endcaseend/*计算一次local burst的剩余数*/
always@(posedge mem_clk)beginif(next_state == MEM_WRITE_BURST_BEGIN)burst_remain <= burst_size; /*burst size is 2*/else if( ((state == MEM_WRITE_BURST_BEGIN) || (state == MEM_WRITE)) && local_ready) /*一次数据写入有效*/burst_remain <= burst_remain - 1;elseburst_remain <= burst_remain;end /*计算local size, 需要判断剩余的数据是否大于burst_size*/
always@(posedge mem_clk)beginif(state == IDLE && rd_burst_req)local_size <= (rd_burst_len >= burst_size) ? burst_size : rd_burst_len ;else if(state == IDLE && wr_burst_req)local_size <= (wr_burst_len >= burst_size) ? burst_size : wr_burst_len;else if(state == MEM_WRITE && (next_state == MEM_WRITE_BURST_BEGIN))if((wr_remain_len - 1) > burst_size) /*判断剩余写数据长度是否大于burst_size*/local_size <= burst_size;elselocal_size <= wr_remain_len - 1;else if(state == MEM_WRITE_BURST_BEGIN && (next_state == MEM_WRITE_BURST_BEGIN) && local_ready)if((wr_remain_len - 1) > burst_size) /*判断剩余写数据长度是否大于burst_size*/local_size <= burst_size;elselocal_size <= wr_remain_len - 1;else if(state == MEM_READ && local_ready )local_size <= (rd_addr_cnt + burst_size > length) ? 1 : burst_size; elselocal_size <= local_size;end/*计算地址local_address*/
always@(posedge mem_clk)begincase(state)IDLE:beginif(rd_burst_req) /*读burst请求有效*/beginlocal_address <= rd_burst_addr;rd_addr_cnt <= 10'd0;endelse if(wr_burst_req) /*读burst请求有效*/beginlocal_address <= wr_burst_addr;rd_addr_cnt <= 10'd0;endelsebeginlocal_address <= local_address;rd_addr_cnt <= 10'd0;endendMEM_READ:beginif(local_ready)beginlocal_address <= local_address + {14'd0,burst_size}; /*Bust读,地址加burst_size*/rd_addr_cnt <= rd_addr_cnt + burst_size;endelsebeginlocal_address <= local_address;rd_addr_cnt <= rd_addr_cnt;end endMEM_WRITE_BURST_BEGIN:beginif(local_ready && (next_state == MEM_WRITE_BURST_BEGIN))beginlocal_address <= local_address + {14'd0,burst_size}; /*Bust begin写,地址加burst_size*/endelsebeginlocal_address <= local_address;end end MEM_WRITE: beginif(local_ready && (next_state == MEM_WRITE_BURST_BEGIN)) beginlocal_address <= local_address + {14'd0,burst_size}; /*Bust 写,地址加burst_size*/endelsebeginlocal_address <= local_address;end enddefault:beginlocal_address <= local_address;rd_addr_cnt <= 10'd0;endendcaseend/*burst读长度*/
always@(posedge mem_clk)beginif(state == IDLE && rd_burst_req)length <= rd_burst_len;elselength <= length; end/*统计读数据counter*/
always@(posedge mem_clk)beginif(state == MEM_READ || state == MEM_READ_WAIT)if(local_rdata_valid)rd_data_cnt <= rd_data_cnt + 10'd1;elserd_data_cnt <= rd_data_cnt;elserd_data_cnt <= 10'd0;end/*输出信号赋值*/
assign rd_burst_data_valid = local_rdata_valid;
assign rd_burst_data = local_rdata;
assign local_wdata = wr_burst_data;
assign local_read_req = (state == MEM_READ);
assign rd_burst_finish = (state == MEM_READ_WAIT) && (next_state == IDLE);
assign wr_burst_finish = (local_ready && wr_remain_len == 10'd1);
assign local_be = {MEM_DATA_BITS/8{1'b1}};
endmodule
2. 顶层模块
module ddr_test(input wire source_clk, //输入系统时钟50Mhzinput rst_n,output err, //led1, 灯亮DDR读写正常, 灯灭DDR读写出错output [ 14: 0] mem_addr,output [ 2: 0] mem_ba,output mem_cas_n,output [ 0: 0] mem_cke,inout [ 0: 0] mem_clk,inout [ 0: 0] mem_clk_n,output [ 0: 0] mem_cs_n,output [ 1: 0] mem_dm,inout [ 15: 0] mem_dq,inout [ 1: 0] mem_dqs,output [ 0: 0] mem_odt,output mem_ras_n,output mem_we_n
);parameter DATA_WIDTH = 32; //总线数据宽度
parameter ADDR_WIDTH = 25; //总线地址宽度parameter IDLE = 3'd0;
parameter MEM_READ = 3'd1;
parameter MEM_WRITE = 3'd2;
reg[2:0] state;
reg[2:0] next_state;//状态锁存///
always@(posedge phy_clk)beginif(~local_init_done) //等待初始化成功state <= IDLE;else state <= next_state;end//循环产生DDR Burst读,Burst写状态///
always@(*)begin case(state)IDLE:next_state <= MEM_WRITE; MEM_WRITE: //写入数据到DDR2if(wr_burst_finish) next_state <= MEM_READ;elsenext_state <= MEM_WRITE;MEM_READ: //读出数据从DDR2if(rd_burst_finish)next_state <= MEM_WRITE;elsenext_state <= MEM_READ;default:next_state <= IDLE;endcase
endreg [ADDR_WIDTH - 1:0] wr_burst_addr;
wire [ADDR_WIDTH - 1:0] rd_burst_addr;
wire wr_burst_data_req;
wire rd_burst_data_valid;
reg [9:0] wr_burst_len;
reg [9:0] rd_burst_len;
reg wr_burst_req;
reg rd_burst_req;
reg [9:0] wr_cnt;
reg [9:0] rd_cnt;
wire [DATA_WIDTH - 1:0] wr_burst_data;
wire [DATA_WIDTH - 1:0] rd_burst_data;//DDR的读写地址和DDR测试数据//
always@(posedge phy_clk)beginif(state == IDLE && next_state == MEM_WRITE)wr_burst_addr <= {ADDR_WIDTH{1'b0}}; //地址清零else if(state == MEM_READ && next_state == MEM_WRITE) //一次Burst读写完成wr_burst_addr <= wr_burst_addr + {{(ADDR_WIDTH-8){1'b0}},8'd255}; //地址加burst长度255 elsewr_burst_addr <= wr_burst_addr; //锁存地址end
assign rd_burst_addr = wr_burst_addr;
assign wr_burst_data = {(DATA_WIDTH/8){wr_cnt[7:0]}}; //写入DDR的数据//产生burst写请求信号
always@(posedge phy_clk)beginif(next_state == MEM_WRITE && state != MEM_WRITE)beginwr_burst_req <= 1'b1; //产生ddr burst写请求 wr_burst_len <= 10'd255;wr_cnt <= 10'd0;endelse if(wr_burst_data_req) //写入burst数据请求 beginwr_burst_req <= 1'b0;wr_burst_len <= 10'd255;wr_cnt <= wr_cnt + 10'd1; //测试数据(每字节)加1endelsebeginwr_burst_req <= wr_burst_req;wr_burst_len <= 10'd255;wr_cnt <= wr_cnt;endend//产生burst读请求信号
always@(posedge phy_clk)beginif(next_state == MEM_READ && state != MEM_READ)beginrd_burst_req <= 1'b1; //产生ddr burst读请求 rd_burst_len <= 10'd255;rd_cnt <= 10'd1;endelse if(rd_burst_data_valid) //检测到data_valid信号,burst读请求变0beginrd_burst_req <= 1'b0;rd_burst_len <= 10'd255;rd_cnt <= rd_cnt + 10'd1;endelsebeginrd_burst_req <= rd_burst_req;rd_burst_len <= 10'd255;rd_cnt <= rd_cnt;endendassign err = rd_burst_data_valid &(rd_burst_data != {(DATA_WIDTH/8){rd_cnt[7:0]}}); //检查DDR读出的数据是否正确wire [ADDR_WIDTH - 1:0] local_address;
wire local_write_req;
wire local_read_req;
wire [DATA_WIDTH - 1:0] local_wdata;
wire [DATA_WIDTH/8 - 1:0] local_be;
wire [2:0] local_size;
wire local_ready;
wire [DATA_WIDTH - 1:0] local_rdata;
wire local_rdata_valid;
wire local_wdata_req;
wire local_init_done;
wire phy_clk;
wire aux_full_rate_clk;
wire aux_half_rate_clk;
wire rd_burst_finish;
wire wr_burst_finish;
//实例化mem_burst_v2
mem_burst_v2
#(.MEM_DATA_BITS(DATA_WIDTH)
)
mem_burst_m0(.rst_n(rst_n),.mem_clk(phy_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),///.local_init_done(local_init_done),.local_ready(local_ready),.local_burstbegin(local_burstbegin),.local_wdata(local_wdata),.local_rdata_valid(local_rdata_valid),.local_rdata(local_rdata),.local_write_req(local_write_req),.local_read_req(local_read_req),.local_address(local_address),.local_be(local_be),.local_size(local_size)
);//实例化ddr2.v
ddr2 ddr_m0(.local_address(local_address),.local_write_req(local_write_req),.local_read_req(local_read_req),.local_wdata(local_wdata),.local_be(local_be),.local_size(local_size),.global_reset_n(rst_n),//.local_refresh_req(1'b0), //.local_self_rfsh_req(1'b0),.pll_ref_clk(source_clk),.soft_reset_n(1'b1),.local_ready(local_ready),.local_rdata(local_rdata),.local_rdata_valid(local_rdata_valid),.reset_request_n(),.mem_cs_n(mem_cs_n),.mem_cke(mem_cke),.mem_addr(mem_addr),.mem_ba(mem_ba),.mem_ras_n(mem_ras_n),.mem_cas_n(mem_cas_n),.mem_we_n(mem_we_n),.mem_dm(mem_dm),.local_refresh_ack(),.local_burstbegin(local_burstbegin),.local_init_done(local_init_done),.reset_phy_clk_n(),.phy_clk(phy_clk),.aux_full_rate_clk(),.aux_half_rate_clk(),.mem_clk(mem_clk),.mem_clk_n(mem_clk_n),.mem_dq(mem_dq),.mem_dqs(mem_dqs),.mem_odt(mem_odt));endmodule
3. 测试代码
五、波形分析
1. 写过程分析
如下图所示,只有当 local_ready 为高和 local_write_req 信号都为高时,写入的数据才是有效的数据,如果 local_ready 信号为低,local_write_req 和数据需要一直保持,等待local_ready 信号为高才行。 DDR2 的 burst _length 为 2, 所以地址信号 local_address 是每写入 2 个数据就加 2, local_burstbegin 信号为第一个数据写的时候为高,第二个数据写的时候为低。
2. 读过程分析
只有当 local_ready 为高和 local_read_req 信号都为高时,burst 读才是有
效的,如果 local_ready 信号为低,local_ read _req 和地址 local_address 需要一直保持,等待 local_ready 信号为高才行。local_read_req 信号请求为高等待一段时间后,local_rddata_valid 信号会变高,才会有有效的读的数据输出。如下面第一张图所示,地址发出后,数据在第二张图片才发出。