[附源码]Quartus平台DDR2IP核讲解、仿真及其代码

前言:本文主要学习并针对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):是在向控制器发送读请求信号之后,读数据出现在接受读数据线上所花费的时间。

这里就是花费了两个周期TCL是一个特定的数值,基于你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的含义表示,一次操作需要几个节拍。举例说明:

  1. 如果只想要传输32位的数据,那么只需要一次打拍子,因为我LOCAL_wdata是32位。那么local_size就是1.

  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_WDATALOCAL_BE对应于DDR中MEM_DQMEM_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平台:

https://blog.csdn.net/qq_42705817/article/details/107858023?ops_request_misc=&request_id=&biz_id=102&utm_term=%E5%9F%BA%E4%BA%8Equartus2%E7%9A%84DDR2&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-8-107858023.142^v74^insert_down38,201^v4^add_ask,239^v2^insert_chatgpt&spm=1018.2226.3001.4187

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核的生成,我就没有写了,网上这部分的教程非常多,可以翻一翻学一学。看看参数手册也能自己设置。

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值