一、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)
begin
if(~rst_n)
cnt_timer <= 12'd0;
else if(state == IDLE || ~local_init_done)
cnt_timer <= 12'd0;
else
cnt_timer <= cnt_timer + 12'd1;
end
/*DDR读等待计数器, 在本程序没有用到*/
always@(posedge mem_clk or negedge rst_n)
begin
if(~rst_n)
ddr_reset_timer <= 12'd0;
else if(state == MEM_READ_WAIT)
ddr_reset_timer <= ddr_reset_timer + 12'd1;
else
ddr_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)
begin
if(~rst_n)
state <= IDLE;
else if(~local_init_done ) //IP核的初始化结束信号
state <= IDLE;
else
state <= next_state;
end
/*burst读写处理程序*/
always@(*)
begin
case(state)
IDLE:
begin
if(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; /*状态转为第一次写*/
else
next_state <= IDLE;
end
MEM_READ: /*burst读命令*/
begin
if( (rd_addr_cnt + burst_size >= length) && local_read_req && local_ready) /*判断burst读请求的有效的长度*/
next_state <= MEM_READ_WAIT;
else
next_state <= MEM_READ;
end
MEM_READ_WAIT: /*等待burst数据读完成*/
begin
if(rd_data_cnt == length - 10'd1 && local_rdata_valid) /*判断读出的有效数据长度*/
next_state <= IDLE;
else
next_state <= MEM_READ_WAIT;
end
MEM_WRITE_FIRST: /*第一次写状态, 用于准备写入的数据*/
next_state <= MEM_WRITE_BURST_BEGIN;
MEM_WRITE_BURST_BEGIN: /*产生burst begin信号*/
begin
if(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完成, 转到数据写入*/
else
next_state <= MEM_WRITE_BURST_BEGIN;
end
MEM_WRITE:
begin
if(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;
end
default:
next_state <= IDLE;
endcase
end
assign local_burstbegin = ((state == MEM_WRITE_BURST_BEGIN) || (state == MEM_READ)); /*产生local burst begin信号*/
/*计算最后一个burst数据写请求信号*/
always@(posedge mem_clk)
begin
if(state == MEM_WRITE_BURST_BEGIN || state == MEM_WRITE)
if(wr_remain_len == 10'd2 && local_ready)
last_wr_burst_data_req <= 1'b1;
else
last_wr_burst_data_req <= last_wr_burst_data_req;
else
last_wr_burst_data_req <= 1'b0;
end
/*计算外部burst写的剩余数据长度*/
always@(posedge mem_clk)
begin
case(state)
IDLE:
if(wr_burst_req)
wr_remain_len <= wr_burst_len; /*wr_remain_len赋初值*/
else
wr_remain_len <= wr_remain_len;
MEM_WRITE_BURST_BEGIN:
if(local_ready)
wr_remain_len <= wr_remain_len - 10'd1;
else
wr_remain_len <= wr_remain_len;
MEM_WRITE:
if(local_ready)
wr_remain_len <= wr_remain_len - 10'd1;
else
wr_remain_len <= wr_remain_len;
default:
wr_remain_len <= wr_remain_len;
endcase
end
/*计算一次local burst的剩余数*/
always@(posedge mem_clk)
begin
if(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;
else
burst_remain <= burst_remain;
end
/*计算local size, 需要判断剩余的数据是否大于burst_size*/
always@(posedge mem_clk)
begin
if(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;
else
local_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;
else
local_size <= wr_remain_len - 1;
else if(state == MEM_READ && local_ready )
local_size <= (rd_addr_cnt + burst_size > length) ? 1 : burst_size;
else
local_size <= local_size;
end
/*计算地址local_address*/
always@(posedge mem_clk)
begin
case(state)
IDLE:
begin
if(rd_burst_req) /*读burst请求有效*/
begin
local_address <= rd_burst_addr;
rd_addr_cnt <= 10'd0;
end
else if(wr_burst_req) /*读burst请求有效*/
begin
local_address <= wr_burst_addr;
rd_addr_cnt <= 10'd0;
end
else
begin
local_address <= local_address;
rd_addr_cnt <= 10'd0;
end
end
MEM_READ:
begin
if(local_ready)
begin
local_address <= local_address + {14'd0,burst_size}; /*Bust读,地址加burst_size*/
rd_addr_cnt <= rd_addr_cnt + burst_size;
end
else
begin
local_address <= local_address;
rd_addr_cnt <= rd_addr_cnt;
end
end
MEM_WRITE_BURST_BEGIN:
begin
if(local_ready && (next_state == MEM_WRITE_BURST_BEGIN))
begin
local_address <= local_address + {14'd0,burst_size}; /*Bust begin写,地址加burst_size*/
end
else
begin
local_address <= local_address;
end
end
MEM_WRITE:
begin
if(local_ready && (next_state == MEM_WRITE_BURST_BEGIN))
begin
local_address <= local_address + {14'd0,burst_size}; /*Bust 写,地址加burst_size*/
end
else
begin
local_address <= local_address;
end
end
default:
begin
local_address <= local_address;
rd_addr_cnt <= 10'd0;
end
endcase
end
/*burst读长度*/
always@(posedge mem_clk)
begin
if(state == IDLE && rd_burst_req)
length <= rd_burst_len;
else
length <= length;
end
/*统计读数据counter*/
always@(posedge mem_clk)
begin
if(state == MEM_READ || state == MEM_READ_WAIT)
if(local_rdata_valid)
rd_data_cnt <= rd_data_cnt + 10'd1;
else
rd_data_cnt <= rd_data_cnt;
else
rd_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, //输入系统时钟50Mhz
input 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)
begin
if(~local_init_done) //等待初始化成功
state <= IDLE;
else
state <= next_state;
end
//循环产生DDR Burst读,Burst写状态///
always@(*)
begin
case(state)
IDLE:
next_state <= MEM_WRITE;
MEM_WRITE: //写入数据到DDR2
if(wr_burst_finish)
next_state <= MEM_READ;
else
next_state <= MEM_WRITE;
MEM_READ: //读出数据从DDR2
if(rd_burst_finish)
next_state <= MEM_WRITE;
else
next_state <= MEM_READ;
default:
next_state <= IDLE;
endcase
end
reg [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)
begin
if(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
else
wr_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)
begin
if(next_state == MEM_WRITE && state != MEM_WRITE)
begin
wr_burst_req <= 1'b1; //产生ddr burst写请求
wr_burst_len <= 10'd255;
wr_cnt <= 10'd0;
end
else if(wr_burst_data_req) //写入burst数据请求
begin
wr_burst_req <= 1'b0;
wr_burst_len <= 10'd255;
wr_cnt <= wr_cnt + 10'd1; //测试数据(每字节)加1
end
else
begin
wr_burst_req <= wr_burst_req;
wr_burst_len <= 10'd255;
wr_cnt <= wr_cnt;
end
end
//产生burst读请求信号
always@(posedge phy_clk)
begin
if(next_state == MEM_READ && state != MEM_READ)
begin
rd_burst_req <= 1'b1; //产生ddr burst读请求
rd_burst_len <= 10'd255;
rd_cnt <= 10'd1;
end
else if(rd_burst_data_valid) //检测到data_valid信号,burst读请求变0
begin
rd_burst_req <= 1'b0;
rd_burst_len <= 10'd255;
rd_cnt <= rd_cnt + 10'd1;
end
else
begin
rd_burst_req <= rd_burst_req;
rd_burst_len <= 10'd255;
rd_cnt <= rd_cnt;
end
end
assign 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 信号会变高,才会有有效的读的数据输出。如下面第一张图所示,地址发出后,数据在第二张图片才发出。