前言:本文主要学习并针对DDR2的IP和控制信号功能和说明,理解如何使用IP核,后续会继续学习如何该核的模型跑自己的仿真。
主要参考的是ALtera 2008--- DDR and DDR2 SDRAM High-Performance Controller User Guide
本文只分析和讨论FULL_RATE,不分析HALF_RATE
没有废话,开始。
//------------------------------------------------------------------------------------------------------//
前言
控制器框图
LOCAL为IP核控制器为设计者引出的简化操作信号,也是我着重学习的部分,关于DDR2内部如何运作及其物理结构,其他学习者可以去看小梅哥关于这部分的讲解(小梅哥在SDRAM讲得比较细致)。
//------------------------------------------------------------------------------------------------------//
注:这里默认大家都懂SDRAM的基本概念:NOP、激活、读、写、BUSRT传输、预充电和自动充电。
如果不清楚的话,可以去搜索SDRAM手册看一下,是比较基础的东西,也比较简单。
算了,还是大概说一下吧。
NOP:表示空操作,原地罚站,可能是在等待读的数据,也可能是在等待充电等等,虽然操作是NOP,但是要求还是很多的,并不是仅仅表示控制器与DDR之间没有任何的操作。
Active :激活命令。首先要知道SDRAM是由多个BANK组成的,一个BANK就像印满网格的纸。我如果想读写某一BANK中的值,首先要选定我要那张纸(那个BANK)其次同时选的出在这张纸上我要那一行。配合CAS和RAS一起,就可以选定某一张纸上的某一行某一列,从而确定最终的地址。
R/W:可以从表中看到,读写操作都会伴随着CAS拉低,也就是说,读写操作也就是选定ACTIVE激活行的列,完后再看是R||W。
Burst terminate:Burst 传输中止。
Precharge/Auto refresh:因为SDRAM这类器件,存储的方式是电容,需要不断的上电,并且在传输一次后,经过放大器,电荷量跑了很多,就需要自动的上电刷新。具体的内部东西,我还没有理解的很透彻,就不误人子弟了,感兴趣的可以查SDRAM的手册,里面讲得很多。
LMR:命令用来配置SDRAM模式寄存器。该寄存器存储CAS延迟、突发长度和突发类型。
Latency的概念:读和写延迟
读延迟(CAS Latency):是在向控制器发送读请求信号之后,读数据出现在接受读数据线上所花费的时间。
这里就是花费了两个周期T,CL是一个特定的数值,基于你MEMORY特性的数值X。我们只关心DQ线上在几个周期读出数据是稳定可靠的,CL*T > x。之所以存在这个延迟,归根结底还是跟SDRAM存储原理有关。
SDRAM 存储数据是利用了电容能够保持电荷以及其充放电特性。一位数据的存取电路
如下图所示,该存储单元主要由行列选通三极管,存储电容,刷新放大器组成。对于这一位
的数据,要想将其读取出来,首先需要打开行地址,然后打开列地址,则电容的电平状态就
能呈现在数据线(data_bit)上,即实现了读取。或者,数据线上的电平值被送到电容上,从
而实现写入数据。但是,打开行地址(激活)是需要一定时间的,即从打开行地址到可以打开列地址进行
读写,这之间有一定的时间间隔,这个间隔叫做 tRCD(ACTIVE-to-READ or WRITE delay),对于
不同型号规格的器件,这个值是不一样的。例如,某个型号的 SDRAM 器件,对于最大运行
频率为 133M 的器件,该值为 15ns。当 SDRAM 工作在 133M 时,其一个时钟周期为 7.52ns,
因此,在实际操作时,必须等待 2 个时钟周期之后才能选通列地址。而另一款 SDRAM 器件,
其最大运行频率也为 133M,但是 tRCD 值为 20,因此,必须等待 3 个时钟周期(7.52*3 >
20)之后才能打开列地址。
当列地址被打开后,数据并不是立即出现在最终的数据总线引脚上,而是有若干个时钟
的延迟,这个延迟叫做列选通潜伏期(CL,CL = CAS READ latency),注意,列选通潜伏期的值
针对不同速度等级的器件,其值是不一样的。
---------------------------------------------来自小梅哥SDRAM设计流程
写延迟:是在向控制器发出写请求信号后,写数据出现在内存接口上所花费的时间。
这与CAL不同的是,我们的控制器不需要管数据多久出现,我们可以继续写操作。
介绍就到此为止吧,再多就不礼貌了。还是推荐去看一看小梅哥的,我感觉对于不了解SDRAM的同学,他的视频很不错,喝水不忘挖井人
再补充一些位宽与CELL的关系,这与BURST传输有关
DDR SDRAM内部存储单元容量是芯片位宽(芯片I/O口位宽)的一倍
DDR2 SDRAM内部存储单元容量是芯片位宽的四倍
DDR3 SDRAM内部存储单元容量是芯片位宽的八倍
DDR4 SDRAM内部存储单元容量是芯片位宽的八倍
而SDRAM与DDR不同的区别就是预读取prefetch技术,
DDR是两位预取(2-bit Prefetch)也称为2-n Prefetch(n代表芯片位宽)。
DDR2是四位预取(4-bit Prefetch)
DDR3和DDR4都是八位预取(8-bit Prefetch)而8-bit Prefetch可以使得内核时钟是DDR时钟的四分之一,这也是Prefetch的根本意义所在。
就拿我的DDR2举例:
FULL全速率情况下,我的local_wdata宽为32BIT,而DDR2的一个单元有4*16位宽,因此,如果想传输满一个存储单元的话,需要在local_wdata上传输两次,即BURST=2;
//------------------------------------------------------------------------------------------------------//
正文
全速率和半速率的概念FULL_RATE / HALF_RATE
F对于半速率控制器,本地的时钟频率是存储器时钟频率的一半。
H对于全速率控制器,本地的时钟频率等于存储器时钟频率。
那么就有东西需要思考了
Q:如何在半速率的情况下传输数据呢?
A:通过增大位宽,弥补时钟频率上差异
Q:现在如果想用全速率控制器,首先我们知道DDR2是双速率的,CLK和CLK_N都会边沿触发一次数据的读写,相当于DDR2的速率比我全速率控制器的频率又高一倍,全功率也不足以一次传输完成,怎么办呢?
A:方法一样滴,通过增大位宽,弥补时钟频率上差异
FULL RATE WRITE
FULL RATE READ
写操作:
1.在开始写操作时,需要对local_ready进行分析,判断IP核提供的控制器是否处于准备好状态。
2.当准备好了,对local_write_req、local_size、local_be、local_wdata和local_address接口传输对应的数据。
local_size的含义表示,一次操作需要几个节拍。举例说明:
如果只想要传输32位的数据,那么只需要一次打拍子,因为我LOCAL_wdata是32位。那么local_size就是1.
如果我要传输2个32位的数据,那么就需要打两拍,local_size就为2.
ps:对于我设置的DDR2来说,LOCAL_SIZE为2是最大值。
下图由于设置的是[31:0] local_wdata,(而DDR2一次传输的是16bit,一个周期能传输两次 ,就是一个CLK能传输32bit,而DDR2的一个CELL是4*16的大小),设置local_size=1,所以是需要一拍传输完整的32BIT数据。-------------------------不可以实现最高吞吐量。
但是如果将local_size设为2,连续传输写入64bit的数据,,那么在连续的两个周期,能传输满一个CELL。
总结:由于DDR存在预读取的结构,如果使用BURST传输,并local_size大小设置为1,那么吞吐量将会是最大吞吐的一半。如果local_size = 2,那么可以实现最高的吞吐量。
可以看下图是local_size为2的,进行理解。
HALF RATE WRITE
HALF RATE READ
注意:WRITE_REQ与READ_REQ不能同时触发;
//------------------------------------------------------------------------------------------------------//
FULL_RATE:
LOCAL_WDATA与LOCAL_BE对应于DDR中MEM_DQ和MEM_DM
local_wdata=<22334455> <667788AA> <BBCCDDEE>
local_be= <1100> <0110> <1010>
对应
mem_dq= <4455><2233><88AA><6677><DDEE><BBCC>
mem_dm= <1 1> <0 0> <0 1> <1 0> <1 1> <0 1>
这里只强调local_be是与mem_dm作用相反,dm是掩码
(dm:掩盖掉那8bit;例如:dq=AB------dm=10即掩盖掉高8位,留下低8位,B)
(local_be:表示那几位有效,wdata=AB----be = 11即高8低8都有效,都传输,AB)
所以22334455的local_be为1100,即低16为无效,换为掩码DM为0011
//------------------------------------------------------------------------------------------------------//
如果想自己跑DDR2IP核的简单自定义仿真可以参考下博客,比较基础
QURATUS平台:
VIVODO平台:https://blog.csdn.net/xiaoxiaoxianjia/article/details/125770829
并附上自己的MODELSIM仿真波形DDR2的:
//模拟循环生成数据与地址,进行读写。
module ddr_test(
input wire source_clk, //输入系统时钟50Mhz
input rst_n,
output err, //led1, 灯亮DDR读写正常, 灯灭DDR读写出错
output [ 12: 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; //总线地址宽度
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;
parameter IDLE = 3'd0;
parameter MEM_READ = 3'd1;
parameter MEM_WRITE = 3'd2;
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;
reg[2:0] state;
reg[2:0] next_state;
//状态锁存///
always@(posedge phy_clk or negedge rst_n)
if ( rst_n == 1'b0) begin
state <= IDLE;
end
else
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
//DDR的读写地址和DDR测试数据//
always@(posedge phy_clk or negedge rst_n)
if ( rst_n == 1'b0) begin
wr_burst_addr <= {ADDR_WIDTH{1'b0}};
end
else
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
//产生burst写请求信号
always@(posedge phy_clk or negedge rst_n)
if ( rst_n == 1'b0) begin
wr_burst_req <= 1'b0; //产生ddr burst写请求
wr_burst_len <= 10'd0;
wr_cnt <= 10'd0;
end
else
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 or negedge rst_n)
if ( rst_n == 1'b0) begin
rd_burst_req <= 1'b0; //产生ddr burst写请求
rd_burst_len <= 10'd0;
rd_cnt <= 10'd0;
end
else
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
mem_burst_ddr
#(
.MEM_DATA_WIDTH (6'd32 ),
.ADDR_WIDTH (6'd25 ),
.LOCAL_SIZE_BITS (3'd3 )
)
mem_burst_m1(
.MEM_CLK ( phy_clk ),
.RST_N (rst_n ),
.WR_BURST_REQ ( wr_burst_req ),//done
.WR_BURST_LEN ( wr_burst_len ),//done
.WR_BURST_ADDR ( wr_burst_addr ),
.WR_BURST_DATA ( wr_burst_data ),
.WR_BURST_DATA_REQ ( wr_burst_data_req ),//done
//--.----------------------//
.RD_BURST_REQ ( rd_burst_req ),//done
.RD_BURST_LEN ( rd_burst_len ),//done
.RD_BURST_ADDR ( rd_burst_addr ),//done
.RD_BURST_DATA ( rd_burst_data ),//done
.RD_BURST_DATA_VALID ( rd_burst_data_valid ),//done
//--.----------------------//
.RD_FINISH ( rd_burst_finish ),//done
.WR_FINISH ( wr_burst_finish ),//done
.BURST_FINISH ( ),//done
//--.--DDR_IP_CORE------------//
.LOCAL_INITIAL_DONE ( local_init_done ),
.RST_DDR_N ( ),//done
.LOCAL_READY ( local_ready ),//done
.LOCAL_WDATA ( local_wdata ),//done
.LOCAL_WRITE_REQ ( local_write_req ),//done
.LOCAL_BE ( local_be ),//done
.LOCAL_ADDR ( local_address ),//done
.LOCAL_BURSTBEGIN ( local_burstbegin ),//done
.LOCAL_RDATA_VALID ( local_rdata_valid ),//done
.LOCAL_RDATA ( local_rdata ),//done
.LOCAL_READ_REQ ( local_read_req ),//done
.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)
);
//assign rd_burst_addr = wr_burst_addr;
assign rd_burst_addr = {ADDR_WIDTH{1'b0}};
assign wr_burst_data = {(DATA_WIDTH/8){wr_cnt[7:0]}}; //写入DDR的数据
assign err = rd_burst_data_valid &(rd_burst_data != {(DATA_WIDTH/8){rd_cnt[7:0]}}); //检查DDR读出的数据是否正确
endmodule
//DDR2的控制器
module mem_burst_ddr
#(
parameter MEM_DATA_WIDTH=6'd32,
parameter ADDR_WIDTH=6'd25,
parameter LOCAL_SIZE_BITS=3'd3
)(
input wire MEM_CLK,
input wire RST_N,
input wire WR_BURST_REQ, //done
input wire [9:0] WR_BURST_LEN, //done
input wire [ADDR_WIDTH-1:0]WR_BURST_ADDR,
input wire [MEM_DATA_WIDTH-1:0]WR_BURST_DATA,
output wire WR_BURST_DATA_REQ, //done
//-----------------------------------//
input wire RD_BURST_REQ, //done
input wire [9:0] RD_BURST_LEN, //done
input wire [ADDR_WIDTH-1:0] RD_BURST_ADDR, //done
output wire [MEM_DATA_WIDTH-1:0] RD_BURST_DATA,//done
output wire RD_BURST_DATA_VALID,//done
//-----------------------------------//
output wire RD_FINISH,//done
output wire WR_FINISH,//done
output wire BURST_FINISH,//done
//---------------DDR_IP_CORE------------//
input wire LOCAL_INITIAL_DONE,
output wire RST_DDR_N,//done
input wire LOCAL_READY,//done
output wire [MEM_DATA_WIDTH-1:0] LOCAL_WDATA,//done
output wire LOCAL_WRITE_REQ,//done
output wire [LOCAL_SIZE_BITS:0] LOCAL_BE,//done
output reg [ADDR_WIDTH -1 :0] LOCAL_ADDR,//done
output wire LOCAL_BURSTBEGIN,//done
input wire LOCAL_RDATA_VALID,//done
input wire [MEM_DATA_WIDTH-1:0] LOCAL_RDATA,//done
output wire LOCAL_READ_REQ,//done
output reg [LOCAL_SIZE_BITS-1:0] LOCAL_SIZE//done
);
reg [9:0] rd_remain_len;
reg [9:0] rd_valid_cnt;
reg [9:0] rd_addr_cnt;
reg [ADDR_WIDTH -1 :0] rd_addr_reg;
parameter burst_param = 10'd2;
reg [9:0] wr_burst_len ;
reg last_wr_burst_data_flag ;
parameter IDLE = 3'd0;
parameter BURST_RD_MEM = 3'd1;
parameter BURST_RD_WAIT = 3'd2;
parameter BURST_WR_BUFFER_STAGE = 3'd3;
parameter BURST_WR_FIRST_STAGE = 3'd4;
parameter BURST_WR_SECOND_STAGE = 3'd5;
reg [2:0] state , nextstate;
//-------------FSM--------------//
always @(posedge MEM_CLK or negedge RST_N) begin
if (RST_N == 1'b0) begin
state <= IDLE;
end
else if (LOCAL_INITIAL_DONE == 1'b0) begin
state <= IDLE;
end
else begin
state <= nextstate ;
end
end
always @(*) begin
case (state)
IDLE: begin
if (WR_BURST_REQ) begin
nextstate <= BURST_WR_BUFFER_STAGE;
end
else if (RD_BURST_REQ) begin
nextstate <= BURST_RD_MEM;
end
else begin
nextstate <= IDLE;
end
end
BURST_RD_MEM:begin
if (((rd_addr_cnt + burst_param)>=rd_remain_len)&&LOCAL_READY) begin
nextstate <= BURST_RD_WAIT;
end
else begin
nextstate <= BURST_RD_MEM;
end
end
BURST_RD_WAIT :begin
if ((rd_valid_cnt >= rd_remain_len - 10'd1)&&LOCAL_RDATA_VALID) begin
nextstate <= IDLE;
end
else begin
nextstate <= BURST_RD_WAIT;
end
end
BURST_WR_BUFFER_STAGE :begin
nextstate <= BURST_WR_FIRST_STAGE ;
end
BURST_WR_FIRST_STAGE:begin
if (LOCAL_READY&& wr_burst_len == 10'd1) begin
nextstate <= IDLE ;
end
else if (LOCAL_READY) begin
nextstate <= BURST_WR_SECOND_STAGE;
end
else begin
nextstate <= BURST_WR_FIRST_STAGE;
end
end
BURST_WR_SECOND_STAGE :begin
if (LOCAL_READY && wr_burst_len== 10'd1) begin
nextstate <= IDLE;
end
else if (LOCAL_READY) begin
nextstate <= BURST_WR_FIRST_STAGE;
end
else begin
nextstate <= BURST_WR_SECOND_STAGE;
end
end
default: begin
nextstate <= IDLE ;
end
endcase
end
//-------------------LOCAL_ADDR-rd_addr_cnt-------------------//done
always @(posedge MEM_CLK) begin
case (state)
IDLE:
begin
if (WR_BURST_REQ) begin
LOCAL_ADDR <= WR_BURST_ADDR;
end
else if (RD_BURST_REQ) begin
LOCAL_ADDR <= RD_BURST_ADDR;
rd_addr_cnt <= 10'd0;
end
else begin
LOCAL_ADDR <= 25'hffff_ff;
rd_addr_cnt <= 10'd0;
end
end
BURST_RD_MEM :
begin
if (LOCAL_READY) begin
LOCAL_ADDR <= LOCAL_ADDR + {14'd0,burst_param};
rd_addr_cnt <= rd_addr_cnt + burst_param;
end
else begin
LOCAL_ADDR <= LOCAL_ADDR ;
rd_addr_cnt <= rd_addr_cnt ;
end
end
BURST_WR_SECOND_STAGE:
begin
if (LOCAL_READY && (nextstate == BURST_WR_FIRST_STAGE)) begin
LOCAL_ADDR <= LOCAL_ADDR + {14'd0,burst_param};
end
else begin
LOCAL_ADDR <= LOCAL_ADDR ;
end
end
default: begin
LOCAL_ADDR <= LOCAL_ADDR;
rd_addr_cnt <= rd_addr_cnt;
end
endcase
end
//-------------------rd_remain_len------------------//done
always @(posedge MEM_CLK or negedge RST_N) begin
if (RST_N == 1'b0) begin
rd_remain_len <= 10'd0;
end
else if (RD_BURST_REQ==1'b1&&(state==IDLE)) begin
rd_remain_len <= RD_BURST_LEN;
end
else begin
rd_remain_len <= rd_remain_len;
end
end
//--------------------rd_valid_cnt------------------//done
always @(posedge MEM_CLK or negedge RST_N ) begin
if (RST_N == 1'b0) begin
rd_valid_cnt<= 10'd0;
end
else if ((LOCAL_RDATA_VALID==1'b1)&&(state == BURST_RD_MEM || state == BURST_RD_WAIT)) begin
rd_valid_cnt <= rd_valid_cnt + 10'd1;
end
else if (state == IDLE ) begin
rd_valid_cnt <= 10'd0;
end
else begin
rd_valid_cnt <= rd_valid_cnt;
end
end
//--------------------wr_burst_len------------------//done
always @(posedge MEM_CLK or negedge RST_N) begin
if (RST_N == 1'b0) begin
wr_burst_len <= 10'd0;
end
else if (state == IDLE && WR_BURST_REQ) begin
wr_burst_len <= WR_BURST_LEN;
end
else if ((state == BURST_WR_FIRST_STAGE ||state == BURST_WR_SECOND_STAGE)&& LOCAL_READY) begin
wr_burst_len <= wr_burst_len - 10'd1;
end
else begin
wr_burst_len <= wr_burst_len;
end
end
//----------------------------------LOCAL_SIZE----------------------------------//
always @(posedge MEM_CLK) begin
if ((state == IDLE)&&WR_BURST_REQ) begin
LOCAL_SIZE <= (RD_BURST_LEN >= burst_param) ? burst_param : RD_BURST_REQ ;
end
else if ((state == IDLE) && RD_BURST_REQ) begin
LOCAL_SIZE <= (WR_BURST_LEN >= burst_param) ? burst_param : WR_BURST_LEN ;
end
else if (state == BURST_RD_MEM && LOCAL_READY) begin
LOCAL_SIZE <= ((rd_addr_cnt + burst_param)>= rd_remain_len ) ? 1 : burst_param;
end
else if ((state == BURST_WR_FIRST_STAGE)&&(nextstate == BURST_WR_SECOND_STAGE))begin
if (wr_burst_len - 1 >= burst_param) begin
LOCAL_SIZE <= burst_param ;
end
else begin
LOCAL_SIZE <= wr_burst_len - 1;
end
end
else if ((state == BURST_WR_SECOND_STAGE)&&(nextstate == BURST_WR_FIRST_STAGE))begin
if (wr_burst_len - 1 >= burst_param) begin
LOCAL_SIZE <= burst_param;
end
else begin
LOCAL_SIZE <= wr_burst_len - 1;
end
end
else begin
LOCAL_SIZE <= LOCAL_SIZE ;
end
end
//----------------------------------last_wr_burst_data_flag----------------------------------//
always @(posedge MEM_CLK or negedge RST_N) begin
if (RST_N == 1'b0) begin
last_wr_burst_data_flag <= 1'd0;
end
else if ((state == BURST_WR_FIRST_STAGE ||state == BURST_WR_SECOND_STAGE)&& LOCAL_READY) begin
if(wr_burst_len == 10'd2) begin
last_wr_burst_data_flag <= 1'd1;
end
else begin
last_wr_burst_data_flag <= last_wr_burst_data_flag;
end
end
else begin
last_wr_burst_data_flag <= 1'd0;
end
end
//--------assign------------------------------------------------------
assign RST_DDR_N = RST_N ;
assign WR_BURST_DATA_REQ = (((state == BURST_WR_BUFFER_STAGE)||(state == BURST_WR_FIRST_STAGE))||(state == BURST_WR_SECOND_STAGE))&& LOCAL_READY && ~last_wr_burst_data_flag;
assign LOCAL_WDATA = WR_BURST_DATA ;
assign LOCAL_WRITE_REQ = ((state == BURST_WR_FIRST_STAGE)||(state == BURST_WR_SECOND_STAGE));
assign LOCAL_READ_REQ = (state == BURST_RD_MEM);
assign LOCAL_BE = 4'b1111;
assign RD_BURST_DATA_VALID = LOCAL_RDATA_VALID;
assign RD_BURST_DATA = LOCAL_RDATA;
assign LOCAL_BURSTBEGIN = ((state == BURST_WR_FIRST_STAGE ) || (state == BURST_RD_MEM ));
assign RD_FINISH = (state == BURST_RD_WAIT)&&(nextstate == IDLE );
assign WR_FINISH = (state == BURST_WR_FIRST_STAGE || state == BURST_WR_SECOND_STAGE ) && (nextstate == IDLE ) ;
assign BURST_FINISH = WR_FINISH | RD_FINISH ;
//-------------------------------------------------------------------
endmodule
`timescale 1ns / 1ns
//仿真TOPTB
module ddr2_tb_top ();
reg clk ;
reg rst ;
wire error ;
wire [ 12: 0]wire_mem_addr ;
wire [ 2: 0]wire_mem_ba ;
wire wire_mem_cas_n ;
wire wire_mem_cke ;
wire wire_mem_clk ;
wire wire_mem_clk_n ;
wire wire_mem_cs_n ;
wire [ 1: 0]wire_mem_dm ;
wire [ 15: 0]wire_mem_dq ;
wire [ 1: 0]wire_mem_dqs ;
wire wire_mem_odt ;
wire wire_mem_ras_n ;
wire wire_mem_we_n ;
ddr_test test(
.source_clk(clk), //输入系统时钟50Mhz
.rst_n(rst),
.err(error),
//led1, 灯亮DDR读写正常, 灯灭DDR读写出错
.mem_addr ( wire_mem_addr ),
.mem_ba ( wire_mem_ba ),
.mem_cas_n ( wire_mem_cas_n ),
.mem_cke ( wire_mem_cke ),
.mem_clk ( wire_mem_clk ),
.mem_clk_n ( wire_mem_clk_n ),
.mem_cs_n ( wire_mem_cs_n ),
.mem_dm ( wire_mem_dm ),
.mem_dq ( wire_mem_dq ),
.mem_dqs ( wire_mem_dqs ),
.mem_odt ( wire_mem_odt ),
.mem_ras_n ( wire_mem_ras_n ),
.mem_we_n ( wire_mem_we_n )
);
ddr2_mem_model mem (
.mem_dq (wire_mem_dq),
.mem_dqs (wire_mem_dqs[0]),
.mem_dqs_n (wire_mem_dqs[1]),
.mem_addr (wire_mem_addr),
.mem_ba (wire_mem_ba),
.mem_clk (wire_mem_clk),
.mem_clk_n (wire_mem_clk_n),
.mem_cke (wire_mem_cke),
.mem_cs_n (wire_mem_cs_n),
.mem_ras_n (wire_mem_ras_n),
.mem_cas_n (wire_mem_cas_n),
.mem_we_n (wire_mem_we_n),
.mem_dm (wire_mem_dm),
.mem_odt (wire_mem_odt)
);
always# 10 clk = ~clk ;
initial begin
clk <= 1'b0;
rst <= 1'b1;
#100 rst <= 1'b0;
#20 rst <= 1'b1;
end
endmodule
//DDR2仿真模型
//Legal Notice: (C)2023 Altera Corporation. All rights reserved. Your
//use of Altera Corporation's design tools, logic functions and other
//software and tools, and its AMPP partner logic functions, and any
//output files any of the foregoing (including device programming or
//simulation files), and any associated documentation or information are
//expressly subject to the terms and conditions of the Altera Program
//License Subscription Agreement or other applicable license agreement,
//including, without limitation, that your use is for the sole purpose
//of programming logic devices manufactured by Altera and sold by Altera
//or its authorized distributors. Please refer to the applicable
//agreement for further details.
// synthesis translate_off
`timescale 1ns / 1ps
// synthesis translate_on
// turn off superfluous verilog processor warnings
// altera message_level Level1
// altera message_off 10034 10035 10036 10037 10230 10240 10030
//Default depth for this memory model is 2048, do these when
//changing the depth.
//1)Set ARRAY_DEPTH generic/parameter from 2048 to new depth.
//2)Change mem_array depth from 2047 to (new depth - 1).
//3)VHDL only, don't forget the generic in component declaration
module ddr2_mem_model_ram_module (
// inputs:
data,
rdaddress,
wraddress,
wrclock,
wren,
// outputs:
q
)
;
parameter ARRAY_DEPTH = 2048;
output [ 31: 0] q;
input [ 31: 0] data;
input [ 24: 0] rdaddress;
input [ 24: 0] wraddress;
input wrclock;
input wren;
wire [ 31: 0] aq;
reg [ 57: 0] mem_array [2047: 0];
wire [ 31: 0] q;
assign aq = mem_array[0][31:0];
//synthesis translate_off
SIMULATION-ONLY CONTENTS
reg [ 32 - 1: 0] out;
integer i;
reg found_valid_data;
reg data_written;
initial
begin
for (i = 0; i < ARRAY_DEPTH; i = i + 1)
mem_array[i][0] <= 1'b0;
data_written <= 1'b0;
end
always @(rdaddress)
begin
found_valid_data <= 1'b0;
for (i = 0; i < ARRAY_DEPTH; i = i + 1)
begin
if (rdaddress == mem_array[i][58 - 1:58 - 25] && mem_array[i][0])
begin
out = mem_array[i][58 - 25 - 1:58 - 25 - 32];
found_valid_data = 1'b1;
end
end
if (!found_valid_data)
out = 32'dX;
end
always @(posedge wrclock)
if (wren)
begin
data_written <= 1'b0;
for (i = 0; i < ARRAY_DEPTH; i = i + 1)
begin
if (wraddress == mem_array[i][58 - 1:58 - 25] && !data_written)
begin
mem_array[i][58 - 25 - 1:58 - 25 - 32] <= data;
mem_array[i][0] <= 1'b1;
data_written = 1'b1;
end
else if (!mem_array[i][0] && !data_written)
begin
mem_array[i] <= {wraddress,data,1'b1};
data_written = 1'b1;
end
end
if (!data_written)
begin
$write($time);
$write(" --- Data could not be written, increase array depth or use full memory model --- ");
$stop;
end
end
assign q = out;
END SIMULATION-ONLY CONTENTS
//synthesis translate_on
endmodule
// synthesis translate_off
`timescale 1ns / 1ps
// synthesis translate_on
// turn off superfluous verilog processor warnings
// altera message_level Level1
// altera message_off 10034 10035 10036 10037 10230 10240 10030
module ddr2_mem_model (
// inputs:
mem_addr,
mem_ba,
mem_cas_n,
mem_cke,
mem_clk,
mem_clk_n,
mem_cs_n,
mem_dm,
mem_odt,
mem_ras_n,
mem_we_n,
// outputs:
global_reset_n,
mem_dq,
mem_dqs,
mem_dqs_n
)
;
output global_reset_n;
inout [ 15: 0] mem_dq;
inout [ 1: 0] mem_dqs;
inout [ 1: 0] mem_dqs_n;
input [ 12: 0] mem_addr;
input [ 2: 0] mem_ba;
input mem_cas_n;
input mem_cke;
input mem_clk;
input mem_clk_n;
input mem_cs_n;
input [ 1: 0] mem_dm;
input mem_odt;
input mem_ras_n;
input mem_we_n;
wire [ 23: 0] CODE;
wire [ 12: 0] a;
reg [ 3: 0] additive_latency;
wire [ 8: 0] addr_col;
wire [ 2: 0] ba;
reg [ 2: 0] burstlength;
reg burstmode;
wire cas_n;
wire cke;
wire clk;
wire [ 2: 0] cmd_code;
wire cs_n;
wire [ 2: 0] current_row;
wire [ 1: 0] dm;
reg [ 3: 0] dm_captured;
reg [ 31: 0] dq_captured;
wire [ 15: 0] dq_temp;
wire dq_valid;
wire [ 1: 0] dqs_temp;
wire dqs_valid;
reg dqs_valid_temp;
reg [ 15: 0] first_half_dq;
wire global_reset_n;
wire [ 31: 0] mem_bytes;
wire [ 15: 0] mem_dq;
wire [ 1: 0] mem_dqs;
wire [ 1: 0] mem_dqs_n;
reg [ 12: 0] open_rows [ 7: 0];
wire ras_n;
reg [ 24: 0] rd_addr_pipe_0;
reg [ 24: 0] rd_addr_pipe_1;
reg [ 24: 0] rd_addr_pipe_10;
reg [ 24: 0] rd_addr_pipe_11;
reg [ 24: 0] rd_addr_pipe_12;
reg [ 24: 0] rd_addr_pipe_13;
reg [ 24: 0] rd_addr_pipe_14;
reg [ 24: 0] rd_addr_pipe_15;
reg [ 24: 0] rd_addr_pipe_16;
reg [ 24: 0] rd_addr_pipe_17;
reg [ 24: 0] rd_addr_pipe_18;
reg [ 24: 0] rd_addr_pipe_19;
reg [ 24: 0] rd_addr_pipe_2;
reg [ 24: 0] rd_addr_pipe_20;
reg [ 24: 0] rd_addr_pipe_21;
reg [ 24: 0] rd_addr_pipe_3;
reg [ 24: 0] rd_addr_pipe_4;
reg [ 24: 0] rd_addr_pipe_5;
reg [ 24: 0] rd_addr_pipe_6;
reg [ 24: 0] rd_addr_pipe_7;
reg [ 24: 0] rd_addr_pipe_8;
reg [ 24: 0] rd_addr_pipe_9;
reg [ 24: 0] rd_burst_counter;
reg [ 25: 0] rd_valid_pipe;
wire [ 24: 0] read_addr_delayed;
reg read_cmd;
reg read_cmd_echo;
wire [ 31: 0] read_data;
wire [ 15: 0] read_dq;
reg [ 4: 0] read_latency;
wire read_valid;
reg read_valid_r;
reg read_valid_r2;
reg read_valid_r3;
reg read_valid_r4;
reg reset_n;
wire [ 24: 0] rmw_address;
reg [ 31: 0] rmw_temp;
reg [ 15: 0] second_half_dq;
reg [ 3: 0] tcl;
wire [ 23: 0] txt_code;
wire we_n;
wire [ 24: 0] wr_addr_delayed;
reg [ 24: 0] wr_addr_delayed_r;
reg [ 24: 0] wr_addr_pipe_0;
reg [ 24: 0] wr_addr_pipe_1;
reg [ 24: 0] wr_addr_pipe_10;
reg [ 24: 0] wr_addr_pipe_11;
reg [ 24: 0] wr_addr_pipe_12;
reg [ 24: 0] wr_addr_pipe_13;
reg [ 24: 0] wr_addr_pipe_14;
reg [ 24: 0] wr_addr_pipe_15;
reg [ 24: 0] wr_addr_pipe_16;
reg [ 24: 0] wr_addr_pipe_17;
reg [ 24: 0] wr_addr_pipe_18;
reg [ 24: 0] wr_addr_pipe_2;
reg [ 24: 0] wr_addr_pipe_3;
reg [ 24: 0] wr_addr_pipe_4;
reg [ 24: 0] wr_addr_pipe_5;
reg [ 24: 0] wr_addr_pipe_6;
reg [ 24: 0] wr_addr_pipe_7;
reg [ 24: 0] wr_addr_pipe_8;
reg [ 24: 0] wr_addr_pipe_9;
reg [ 24: 0] wr_burst_counter;
reg [ 25: 0] wr_valid_pipe;
reg [ 25: 0] write_burst_length_pipe;
reg write_cmd;
reg write_cmd_echo;
reg [ 4: 0] write_latency;
wire write_to_ram;
reg write_to_ram_r;
wire write_valid;
reg write_valid_r;
reg write_valid_r2;
reg write_valid_r3;
initial
begin
$write("\n");
$write("**********************************************************************\n");
$write("This testbench includes a generated Altera memory model:\n");
$write("'ddr2_mem_model.v', to simulate accesses to the DDR2 SDRAM memory.\n");
$write(" \n");
$write("**********************************************************************\n");
end
//Synchronous write when (CODE == 24'h205752 (write))
ddr2_mem_model_ram_module ddr2_mem_model_ram
(
.data (rmw_temp),
.q (read_data),
.rdaddress (rmw_address),
.wraddress (wr_addr_delayed_r),
.wrclock (clk),
.wren (write_to_ram_r)
);
assign clk = mem_clk;
assign dm = mem_dm;
assign cke = mem_cke;
assign cs_n = mem_cs_n;
assign ras_n = mem_ras_n;
assign cas_n = mem_cas_n;
assign we_n = mem_we_n;
assign ba = mem_ba;
assign a = mem_addr;
//generate a fake reset inside the memory model
assign global_reset_n = reset_n;
initial
begin
reset_n <= 0;
#100 reset_n <= 1;
end
assign cmd_code = (&cs_n) ? 3'b111 : {ras_n, cas_n, we_n};
assign CODE = (&cs_n) ? 24'h494e48 : txt_code;
assign addr_col = a[9 : 1];
assign current_row = {ba};
// Decode commands into their actions
always @(posedge clk or negedge reset_n)
begin
if (reset_n == 0)
begin
write_cmd_echo <= 0;
read_cmd_echo <= 0;
end
else // No Activity if the clock is
if (cke)
begin
// Checks whether to echo read cmd
if (read_cmd_echo && !read_cmd)
begin
read_cmd <= 1'b1;
read_cmd_echo <= 1'b0;
end
else // This is a read command
if (cmd_code == 3'b101)
begin
read_cmd <= 1'b1;
read_cmd_echo <= 1'b1;
end
else
read_cmd <= 1'b0;
// Checks whether to echo write cmd
if (write_cmd_echo && !write_cmd)
begin
write_cmd <= 1'b1;
write_cmd_echo <= 1'b0;
end
else // This is a write command
if (cmd_code == 3'b100)
begin
write_cmd <= 1'b1;
write_cmd_echo <= 1'b1;
write_burst_length_pipe[0] <= a[0];
end
else
write_cmd <= 1'b0;
// This is an activate - store the chip/row/bank address in the same order as the DDR controller
if (cmd_code == 3'b011)
open_rows[current_row] <= a;
end
end
// Pipes are flushed here
always @(posedge clk or negedge reset_n)
begin
if (reset_n == 0)
begin
wr_addr_pipe_1 <= 0;
wr_addr_pipe_2 <= 0;
wr_addr_pipe_3 <= 0;
wr_addr_pipe_4 <= 0;
wr_addr_pipe_5 <= 0;
wr_addr_pipe_6 <= 0;
wr_addr_pipe_7 <= 0;
wr_addr_pipe_8 <= 0;
wr_addr_pipe_9 <= 0;
wr_addr_pipe_10 <= 0;
wr_addr_pipe_11 <= 0;
wr_addr_pipe_12 <= 0;
wr_addr_pipe_13 <= 0;
wr_addr_pipe_14 <= 0;
wr_addr_pipe_15 <= 0;
wr_addr_pipe_16 <= 0;
wr_addr_pipe_17 <= 0;
wr_addr_pipe_18 <= 0;
rd_addr_pipe_1 <= 0;
rd_addr_pipe_2 <= 0;
rd_addr_pipe_3 <= 0;
rd_addr_pipe_4 <= 0;
rd_addr_pipe_5 <= 0;
rd_addr_pipe_6 <= 0;
rd_addr_pipe_7 <= 0;
rd_addr_pipe_8 <= 0;
rd_addr_pipe_9 <= 0;
rd_addr_pipe_10 <= 0;
rd_addr_pipe_11 <= 0;
rd_addr_pipe_12 <= 0;
rd_addr_pipe_13 <= 0;
rd_addr_pipe_14 <= 0;
rd_addr_pipe_15 <= 0;
rd_addr_pipe_16 <= 0;
rd_addr_pipe_17 <= 0;
rd_addr_pipe_18 <= 0;
rd_addr_pipe_19 <= 0;
rd_addr_pipe_20 <= 0;
rd_addr_pipe_21 <= 0;
end
else // No Activity if the clock is
if (cke)
begin
rd_addr_pipe_21 <= rd_addr_pipe_20;
rd_addr_pipe_20 <= rd_addr_pipe_19;
rd_addr_pipe_19 <= rd_addr_pipe_18;
rd_addr_pipe_18 <= rd_addr_pipe_17;
rd_addr_pipe_17 <= rd_addr_pipe_16;
rd_addr_pipe_16 <= rd_addr_pipe_15;
rd_addr_pipe_15 <= rd_addr_pipe_14;
rd_addr_pipe_14 <= rd_addr_pipe_13;
rd_addr_pipe_13 <= rd_addr_pipe_12;
rd_addr_pipe_12 <= rd_addr_pipe_11;
rd_addr_pipe_11 <= rd_addr_pipe_10;
rd_addr_pipe_10 <= rd_addr_pipe_9;
rd_addr_pipe_9 <= rd_addr_pipe_8;
rd_addr_pipe_8 <= rd_addr_pipe_7;
rd_addr_pipe_7 <= rd_addr_pipe_6;
rd_addr_pipe_6 <= rd_addr_pipe_5;
rd_addr_pipe_5 <= rd_addr_pipe_4;
rd_addr_pipe_4 <= rd_addr_pipe_3;
rd_addr_pipe_3 <= rd_addr_pipe_2;
rd_addr_pipe_2 <= rd_addr_pipe_1;
rd_addr_pipe_1 <= rd_addr_pipe_0;
rd_valid_pipe[25 : 1] <= rd_valid_pipe[24 : 0];
rd_valid_pipe[0] <= cmd_code == 3'b101;
wr_addr_pipe_18 <= wr_addr_pipe_17;
wr_addr_pipe_17 <= wr_addr_pipe_16;
wr_addr_pipe_16 <= wr_addr_pipe_15;
wr_addr_pipe_15 <= wr_addr_pipe_14;
wr_addr_pipe_14 <= wr_addr_pipe_13;
wr_addr_pipe_13 <= wr_addr_pipe_12;
wr_addr_pipe_12 <= wr_addr_pipe_11;
wr_addr_pipe_11 <= wr_addr_pipe_10;
wr_addr_pipe_10 <= wr_addr_pipe_9;
wr_addr_pipe_9 <= wr_addr_pipe_8;
wr_addr_pipe_8 <= wr_addr_pipe_7;
wr_addr_pipe_7 <= wr_addr_pipe_6;
wr_addr_pipe_6 <= wr_addr_pipe_5;
wr_addr_pipe_5 <= wr_addr_pipe_4;
wr_addr_pipe_4 <= wr_addr_pipe_3;
wr_addr_pipe_3 <= wr_addr_pipe_2;
wr_addr_pipe_2 <= wr_addr_pipe_1;
wr_addr_pipe_1 <= wr_addr_pipe_0;
wr_valid_pipe[25 : 1] <= wr_valid_pipe[24 : 0];
wr_valid_pipe[0] <= cmd_code == 3'b100;
wr_addr_delayed_r <= wr_addr_delayed;
write_burst_length_pipe[25 : 1] <= write_burst_length_pipe[24 : 0];
end
end
// Decode CAS Latency from bits a[6:4]
always @(posedge clk)
begin
// No Activity if the clock is disabled
if (cke)
//Load mode register - set CAS latency, burst mode and length
if (cmd_code == 3'b000 && ba == 2'b00)
begin
burstmode <= a[3];
burstlength <= a[2 : 0] << 1;
//CAS Latency = 3.0
if (a[6 : 4] == 3'b011)
tcl <= 4'b0010;
else //CAS Latency = 4.0
if (a[6 : 4] == 3'b100)
tcl <= 4'b0011;
else //CAS Latency = 5.0
if (a[6 : 4] == 3'b101)
tcl <= 4'b0100;
else //CAS Latency = 6.0
if (a[6 : 4] == 3'b110)
tcl <= 4'b0101;
else
tcl <= 4'b0110;
end
else //Get additive latency
if (cmd_code == 3'b000 && ba == 2'b01)
additive_latency <= {1'b0,a[5 : 3]};
end
//Calculate actual write and read latency
always @(additive_latency or tcl)
begin
read_latency = tcl + additive_latency;
write_latency = tcl + additive_latency;
end
// Burst support - make the wr_addr & rd_addr keep counting
always @(posedge clk or negedge reset_n)
begin
if (reset_n == 0)
begin
wr_addr_pipe_0 <= 0;
rd_addr_pipe_0 <= 0;
end
else
begin
// Reset write address otherwise if the first write is partial it breaks!
if (cmd_code == 3'b000 && ba == 2'b00)
begin
wr_addr_pipe_0 <= 0;
wr_burst_counter <= 0;
end
else if (cmd_code == 3'b100)
begin
wr_addr_pipe_0 <= {ba,open_rows[current_row],addr_col};
wr_burst_counter[24 : 2] <= {ba,open_rows[current_row],addr_col[8 : 2]};
wr_burst_counter[1 : 0] <= addr_col[1 : 0] + 1;
end
else if (write_cmd || write_to_ram || write_cmd_echo)
begin
wr_addr_pipe_0 <= wr_burst_counter;
wr_burst_counter[1 : 0] <= wr_burst_counter[1 : 0] + 1;
end
else
wr_addr_pipe_0 <= 0;
// Reset read address otherwise if the first write is partial it breaks!
if (cmd_code == 3'b000 && ba == 2'b00)
rd_addr_pipe_0 <= 0;
else if (cmd_code == 3'b101)
begin
rd_addr_pipe_0 <= {ba,open_rows[current_row],addr_col};
rd_burst_counter[24 : 2] <= {ba,open_rows[current_row],addr_col[8 : 2]};
rd_burst_counter[1 : 0] <= addr_col[1 : 0] + 1;
end
else if (read_cmd || dq_valid || read_valid || read_cmd_echo)
begin
rd_addr_pipe_0 <= rd_burst_counter;
rd_burst_counter[1 : 0] <= rd_burst_counter[1 : 0] + 1;
end
else
rd_addr_pipe_0 <= 0;
end
end
// read data transition from single to double clock rate
always @(posedge clk)
begin
first_half_dq <= read_data[31 : 16];
second_half_dq <= read_data[15 : 0];
end
assign read_dq = clk ? second_half_dq : first_half_dq;
assign dq_temp = dq_valid ? read_dq : {16{1'bz}};
assign dqs_temp = dqs_valid ? {2{clk}} : {2{1'bz}};
assign mem_dqs = dqs_temp;
assign mem_dq = dq_temp;
//Pipelining registers for burst counting
always @(posedge clk)
begin
write_valid_r <= write_valid;
read_valid_r <= read_valid;
write_valid_r2 <= write_valid_r;
write_valid_r3 <= write_valid_r2;
write_to_ram_r <= write_to_ram;
read_valid_r2 <= read_valid_r;
read_valid_r3 <= read_valid_r2;
read_valid_r4 <= read_valid_r3;
end
assign write_to_ram = burstlength[1] ? write_valid || write_valid_r || write_valid_r2 || write_valid_r3 : write_valid || write_valid_r;
assign dq_valid = burstlength[1] ? read_valid_r || read_valid_r2 || read_valid_r3 || read_valid_r4 : read_valid_r || read_valid_r2;
assign dqs_valid = dq_valid || dqs_valid_temp;
//
always @(negedge clk)
begin
dqs_valid_temp <= read_valid;
end
//capture first half of write data with rising edge of DQS, for simulation use only 1 DQS pin
always @(posedge mem_dqs[0])
begin
#0.1 dq_captured[15 : 0] <= mem_dq[15 : 0];
#0.1 dm_captured[1 : 0] <= mem_dm[1 : 0];
end
//capture second half of write data with falling edge of DQS, for simulation use only 1 DQS pin
always @(negedge mem_dqs[0])
begin
#0.1 dq_captured[31 : 16] <= mem_dq[15 : 0];
#0.1 dm_captured[3 : 2] <= mem_dm[1 : 0];
end
//Support for incomplete writes, do a read-modify-write with mem_bytes and the write data
always @(posedge clk)
begin
if (write_to_ram)
rmw_temp[7 : 0] <= dm_captured[0] ? mem_bytes[7 : 0] : dq_captured[7 : 0];
end
always @(posedge clk)
begin
if (write_to_ram)
rmw_temp[15 : 8] <= dm_captured[1] ? mem_bytes[15 : 8] : dq_captured[15 : 8];
end
always @(posedge clk)
begin
if (write_to_ram)
rmw_temp[23 : 16] <= dm_captured[2] ? mem_bytes[23 : 16] : dq_captured[23 : 16];
end
always @(posedge clk)
begin
if (write_to_ram)
rmw_temp[31 : 24] <= dm_captured[3] ? mem_bytes[31 : 24] : dq_captured[31 : 24];
end
//DDR2 has variable write latency too, so use write_latency to select which pipeline stage drives valid
assign write_valid = (write_latency == 0)? wr_valid_pipe[0] :
(write_latency == 1)? wr_valid_pipe[1] :
(write_latency == 2)? wr_valid_pipe[2] :
(write_latency == 3)? wr_valid_pipe[3] :
(write_latency == 4)? wr_valid_pipe[4] :
(write_latency == 5)? wr_valid_pipe[5] :
(write_latency == 6)? wr_valid_pipe[6] :
(write_latency == 7)? wr_valid_pipe[7] :
(write_latency == 8)? wr_valid_pipe[8] :
(write_latency == 9)? wr_valid_pipe[9] :
(write_latency == 10)? wr_valid_pipe[10] :
(write_latency == 11)? wr_valid_pipe[11] :
(write_latency == 12)? wr_valid_pipe[12] :
(write_latency == 13)? wr_valid_pipe[13] :
(write_latency == 14)? wr_valid_pipe[14] :
(write_latency == 15)? wr_valid_pipe[15] :
(write_latency == 16)? wr_valid_pipe[16] :
(write_latency == 17)? wr_valid_pipe[17] :
wr_valid_pipe[18];
//DDR2 has variable write latency too, so use write_latency to select which pipeline stage drives addr
assign wr_addr_delayed = (write_latency == 0)? wr_addr_pipe_0 :
(write_latency == 1)? wr_addr_pipe_1 :
(write_latency == 2)? wr_addr_pipe_2 :
(write_latency == 3)? wr_addr_pipe_3 :
(write_latency == 4)? wr_addr_pipe_4 :
(write_latency == 5)? wr_addr_pipe_5 :
(write_latency == 6)? wr_addr_pipe_6 :
(write_latency == 7)? wr_addr_pipe_7 :
(write_latency == 8)? wr_addr_pipe_8 :
(write_latency == 9)? wr_addr_pipe_9 :
(write_latency == 10)? wr_addr_pipe_10 :
(write_latency == 11)? wr_addr_pipe_11 :
(write_latency == 12)? wr_addr_pipe_12 :
(write_latency == 13)? wr_addr_pipe_13 :
(write_latency == 14)? wr_addr_pipe_14 :
(write_latency == 15)? wr_addr_pipe_15 :
(write_latency == 16)? wr_addr_pipe_16 :
(write_latency == 17)? wr_addr_pipe_17 :
wr_addr_pipe_18;
assign mem_bytes = (rmw_address == wr_addr_delayed_r && write_to_ram_r) ? rmw_temp : read_data;
assign rmw_address = (write_to_ram) ? wr_addr_delayed : read_addr_delayed;
//use read_latency to select which pipeline stage drives addr
assign read_addr_delayed = (read_latency == 0)? rd_addr_pipe_0 :
(read_latency == 1)? rd_addr_pipe_1 :
(read_latency == 2)? rd_addr_pipe_2 :
(read_latency == 3)? rd_addr_pipe_3 :
(read_latency == 4)? rd_addr_pipe_4 :
(read_latency == 5)? rd_addr_pipe_5 :
(read_latency == 6)? rd_addr_pipe_6 :
(read_latency == 7)? rd_addr_pipe_7 :
(read_latency == 8)? rd_addr_pipe_8 :
(read_latency == 9)? rd_addr_pipe_9 :
(read_latency == 10)? rd_addr_pipe_10 :
(read_latency == 11)? rd_addr_pipe_11 :
(read_latency == 12)? rd_addr_pipe_12 :
(read_latency == 13)? rd_addr_pipe_13 :
(read_latency == 14)? rd_addr_pipe_14 :
(read_latency == 15)? rd_addr_pipe_15 :
(read_latency == 16)? rd_addr_pipe_16 :
(read_latency == 17)? rd_addr_pipe_17 :
(read_latency == 18)? rd_addr_pipe_18 :
(read_latency == 19)? rd_addr_pipe_19 :
(read_latency == 20)? rd_addr_pipe_20 :
rd_addr_pipe_21;
//use read_latency to select which pipeline stage drives valid
assign read_valid = (read_latency == 0)? rd_valid_pipe[0] :
(read_latency == 1)? rd_valid_pipe[1] :
(read_latency == 2)? rd_valid_pipe[2] :
(read_latency == 3)? rd_valid_pipe[3] :
(read_latency == 4)? rd_valid_pipe[4] :
(read_latency == 5)? rd_valid_pipe[5] :
(read_latency == 6)? rd_valid_pipe[6] :
(read_latency == 7)? rd_valid_pipe[7] :
(read_latency == 8)? rd_valid_pipe[8] :
(read_latency == 9)? rd_valid_pipe[9] :
(read_latency == 10)? rd_valid_pipe[10] :
(read_latency == 11)? rd_valid_pipe[11] :
(read_latency == 12)? rd_valid_pipe[12] :
(read_latency == 13)? rd_valid_pipe[13] :
(read_latency == 14)? rd_valid_pipe[14] :
(read_latency == 15)? rd_valid_pipe[15] :
(read_latency == 16)? rd_valid_pipe[16] :
(read_latency == 17)? rd_valid_pipe[17] :
(read_latency == 18)? rd_valid_pipe[18] :
(read_latency == 19)? rd_valid_pipe[19] :
(read_latency == 20)? rd_valid_pipe[20] :
rd_valid_pipe[21];
//synthesis translate_off
SIMULATION-ONLY CONTENTS
assign txt_code = (cmd_code == 3'h0)? 24'h4c4d52 :
(cmd_code == 3'h1)? 24'h415246 :
(cmd_code == 3'h2)? 24'h505245 :
(cmd_code == 3'h3)? 24'h414354 :
(cmd_code == 3'h4)? 24'h205752 :
(cmd_code == 3'h5)? 24'h205244 :
(cmd_code == 3'h6)? 24'h425354 :
(cmd_code == 3'h7)? 24'h4e4f50 :
24'h424144;
END SIMULATION-ONLY CONTENTS
//synthesis translate_on
endmodule
最后的这个是生成IP核时生成的仿真模型,如果你不想自己生成了,可以COPY我这个。
LOCAL_ADDR 是25位,DATA位是32位。
IP核的生成,我就没有写了,网上这部分的教程非常多,可以翻一翻学一学。看看参数手册也能自己设置。