Hardware ---SDRAM控制器

使用说明
  • SDRAM初始化代码说明:

    sdr.v : SDRAM模型
    sdram_init.v 初始化SDRAM控制器
    sdram_init_tb.v 初始化测试文件

  • 仿真时令:

    iverilog -o wave_sdram_init sdr.v sdram_init.v sdram_init_tb.v
    vvp wave_sdram_init
    gtkwave sdram_init_tb.vcd

  • SDRAM控制器代码说明:

    sdram_control.v :SDRAM控制器
    sdram_control_tb.v :SDRAM控制器测试代码
    sdr.v : SDRAM模型
    sdram_init.v 初始化SDRAM控制器

  • 仿真时令:

    iverilog -o wave_sdram_control sdr.v sdram_control_tb.v sdram_control.v sdram_init.v
    vvp wave_sdram_control
    gtkwave sdram_control_tb.vcd

代码
  • sdr_parameters.h 是SDRAM参数信息
/****************************************************************************************
*
*   Disclaimer   This software code and all associated documentation, comments or other 
*  of Warranty:  information (collectively "Software") is provided "AS IS" without 
*                warranty of any kind. MICRON TECHNOLOGY, INC. ("MTI") EXPRESSLY 
*                DISCLAIMS ALL WARRANTIES EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 
*                TO, NONINFRINGEMENT OF THIRD PARTY RIGHTS, AND ANY IMPLIED WARRANTIES 
*                OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE. MTI DOES NOT 
*                WARRANT THAT THE SOFTWARE WILL MEET YOUR REQUIREMENTS, OR THAT THE 
*                OPERATION OF THE SOFTWARE WILL BE UNINTERRUPTED OR ERROR-FREE. 
*                FURTHERMORE, MTI DOES NOT MAKE ANY REPRESENTATIONS REGARDING THE USE OR 
*                THE RESULTS OF THE USE OF THE SOFTWARE IN TERMS OF ITS CORRECTNESS, 
*                ACCURACY, RELIABILITY, OR OTHERWISE. THE ENTIRE RISK ARISING OUT OF USE 
*                OR PERFORMANCE OF THE SOFTWARE REMAINS WITH YOU. IN NO EVENT SHALL MTI, 
*                ITS AFFILIATED COMPANIES OR THEIR SUPPLIERS BE LIABLE FOR ANY DIRECT, 
*                INDIRECT, CONSEQUENTIAL, INCIDENTAL, OR SPECIAL DAMAGES (INCLUDING, 
*                WITHOUT LIMITATION, DAMAGES FOR LOSS OF PROFITS, BUSINESS INTERRUPTION, 
*                OR LOSS OF INFORMATION) ARISING OUT OF YOUR USE OF OR INABILITY TO USE 
*                THE SOFTWARE, EVEN IF MTI HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 
*                DAMAGES. Because some jurisdictions prohibit the exclusion or 
*                limitation of liability for consequential or incidental damages, the 
*                above limitation may not apply to you.
*
*                Copyright 2005 Micron Technology, Inc. All rights reserved.
*
*	        	 bhoffman - 07/18/06
*
****************************************************************************************/

    // Timing parameters based on Speed Grade and part type (Y37M)

`define sg6a
`define den256Mb
`define x16                                          // SYMBOL UNITS DESCRIPTION
                                          // ------ ----- -----------
`ifdef sg6a                               //              Timing Parameters for -6A (CL = 3)
    parameter tCK              =     6.0; // tCK    ns    Nominal Clock Cycle Time
    parameter tCK3_min         =     6.0; // tCK    ns    Nominal Clock Cycle Time
    parameter tCK2_min         =    10.0; // tCK    ns    Nominal Clock Cycle Time
    parameter tCK1_min         =    20.0; // tCK    ns    Nominal Clock Cycle Time
    parameter tAC3             =     5.4; // tAC3   ns    Access time from CLK (pos edge) CL = 3
    parameter tAC2             =     7.5; // tAC2   ns    Access time from CLK (pos edge) CL = 2
    parameter tAC1             =    17.0; // tAC1   ns    Parameter definition for compilation - CL = 1 illegal for sg75
    parameter tHZ3             =     5.4; // tHZ3   ns    Data Out High Z time - CL = 3
    parameter tHZ2             =     7.5; // tHZ2   ns    Data Out High Z time - CL = 2
    parameter tHZ1             =    17.0; // tHZ1   ns    Parameter definition for compilation - CL = 1 illegal for sg75
    parameter tOH              =    3.0; // tOH    ns    Data Out Hold time
    parameter tMRD             =     2.0; // tMRD   tCK   Load Mode Register command cycle time (2 * tCK)
    parameter tRAS             =    42.0; // tRAS   ns    Active to Precharge command time
    parameter tRC              =    60.0; // tRC    ns    Active to Active/Auto Refresh command time
    parameter tRFC             =    60.0; // tRFC   ns    Refresh to Refresh Command interval time
    parameter tRCD             =    18.0; // tRCD   ns    Active to Read/Write command time
    parameter tRP              =    18.0; // tRP    ns    Precharge command period
    parameter tRRD             =     2.0; // tRRD   tCK   Active bank a to Active bank b command time (2 * tCK)
    parameter tWRa             =     6.0; // tWR    ns    Write recovery time (auto-precharge mode - must add 1 CLK)
    parameter tWRm             =    12.0; // tWR    ns    Write recovery time
`elsif sg7e                               //              Timing Parameters for -7E (CL = 3)
    parameter tCK              =     7.0; // tCK    ns    Nominal Clock Cycle Time
    parameter tCK3_min         =     7.0; // tCK    ns    Nominal Clock Cycle Time
    parameter tCK2_min         =     7.5; // tCK    ns    Nominal Clock Cycle Time
    parameter tCK1_min         =     0.0; // tCK    ns    Nominal Clock Cycle Time
    parameter tAC3             =     5.4; // tAC3   ns    Access time from CLK (pos edge) CL = 3
    parameter tAC2             =     5.4; // tAC2   ns    Access time from CLK (pos edge) CL = 2
    parameter tAC1             =     0.0; // tAC1   ns    Parameter definition for compilation - CL = 1 illegal for sg75
    parameter tHZ3             =     5.4; // tHZ3   ns    Data Out High Z time - CL = 3
    parameter tHZ2             =     5.4; // tHZ2   ns    Data Out High Z time - CL = 2
    parameter tHZ1             =     0.0; // tHZ1   ns    Parameter definition for compilation - CL = 1 illegal for sg75
    parameter tOH              =     2.7; // tOH    ns    Data Out Hold time
    parameter tMRD             =     2.0; // tMRD   tCK   Load Mode Register command cycle time (2 * tCK)
    parameter tRAS             =    37.0; // tRAS   ns    Active to Precharge command time
    parameter tRC              =    60.0; // tRC    ns    Active to Active/Auto Refresh command time
    parameter tRFC             =    66.0; // tRFC   ns    Refresh to Refresh Command interval time
    parameter tRCD             =    15.0; // tRCD   ns    Active to Read/Write command time
    parameter tRP              =    15.0; // tRP    ns    Precharge command period
    parameter tRRD             =     2.0; // tRRD   tCK   Active bank a to Active bank b command time (2 * tCK)
    parameter tWRa             =     7.0; // tWR    ns    Write recovery time (auto-precharge mode - must add 1 CLK)
    parameter tWRm             =    14.0; // tWR    ns    Write recovery time
`else `define sg75                        //              Timing Parameters for -75 (CL = 3)
    parameter tCK              =     7.5; // tCK    ns    Nominal Clock Cycle Time
    parameter tCK3_min         =     7.5; // tCK    ns    Nominal Clock Cycle Time
    parameter tCK2_min         =    10.0; // tCK    ns    Nominal Clock Cycle Time
    parameter tCK1_min         =     0.0; // tCK    ns    Nominal Clock Cycle Time
    parameter tAC3             =     5.4; // tAC3   ns    Access time from CLK (pos edge) CL = 3
    parameter tAC2             =     6.0; // tAC2   ns    Access time from CLK (pos edge) CL = 2
    parameter tAC1             =     0.0; // tAC1   ns    Parameter definition for compilation - CL = 1 illegal for sg75
    parameter tHZ3             =     5.4; // tHZ3   ns    Data Out High Z time - CL = 3
    parameter tHZ2             =     6.0; // tHZ2   ns    Data Out High Z time - CL = 2
    parameter tHZ1             =     0.0; // tHZ1   ns    Parameter definition for compilation - CL = 1 illegal for sg75
    parameter tOH              =     2.7; // tOH    ns    Data Out Hold time
    parameter tMRD             =     2.0; // tMRD   tCK   Load Mode Register command cycle time (2 * tCK)
    parameter tRAS             =    44.0; // tRAS   ns    Active to Precharge command time
    parameter tRC              =    66.0; // tRC    ns    Active to Active/Auto Refresh command time
    parameter tRFC             =    66.0; // tRFC   ns    Refresh to Refresh Command interval time
    parameter tRCD             =    20.0; // tRCD   ns    Active to Read/Write command time
    parameter tRP              =    20.0; // tRP    ns    Precharge command period
    parameter tRRD             =     2.0; // tRRD   tCK   Active bank a to Active bank b command time (2 * tCK)
    parameter tWRa             =     7.5; // tWR    ns    Write recovery time (auto-precharge mode - must add 1 CLK)
    parameter tWRm             =    15.0; // tWR    ns    Write recovery time
`endif

   // Size Parameters based on Part Width

`ifdef den64Mb
    `ifdef x4
        parameter ADDR_BITS        =      12; // Set this parameter to control how many Address bits are used
        parameter ROW_BITS         =      12; // Set this parameter to control how many Row bits are used
        parameter COL_BITS         =      10; // Set this parameter to control how many Column bits are used
        parameter DQ_BITS          =       4; // Set this parameter to control how many Data bits are used
        parameter DM_BITS          =       1; // Set this parameter to control how many DM bits are used
        parameter BA_BITS          =       2; // Bank bits
    `elsif x8
        parameter ADDR_BITS        =      12; // Set this parameter to control how many Address bits are used
        parameter ROW_BITS         =      12; // Set this parameter to control how many Row bits are used
        parameter COL_BITS         =       9; // Set this parameter to control how many Column bits are used
        parameter DQ_BITS          =       8; // Set this parameter to control how many Data bits are used
        parameter DM_BITS          =       1; // Set this parameter to control how many DM bits are used
        parameter BA_BITS          =       2; // Bank bits
    `else `define x16
        parameter ADDR_BITS        =      12; // Set this parameter to control how many Address bits are used
        parameter ROW_BITS         =      12; // Set this parameter to control how many Row bits are used
        parameter COL_BITS         =       8; // Set this parameter to control how many Column bits are used
        parameter DQ_BITS          =      16; // Set this parameter to control how many Data bits are used
        parameter DM_BITS          =       2; // Set this parameter to control how many DM bits are used
        parameter BA_BITS          =       2; // Bank bits
    `endif
`elsif den128Mb
    `ifdef x4
        parameter ADDR_BITS        =      12; // Set this parameter to control how many Address bits are used
        parameter ROW_BITS         =      12; // Set this parameter to control how many Row bits are used
        parameter COL_BITS         =      11; // Set this parameter to control how many Column bits are used
        parameter DQ_BITS          =       4; // Set this parameter to control how many Data bits are used
        parameter DM_BITS          =       1; // Set this parameter to control how many DM bits are used
        parameter BA_BITS          =       2; // Bank bits
    `elsif x8
        parameter ADDR_BITS        =      12; // Set this parameter to control how many Address bits are used
        parameter ROW_BITS         =      12; // Set this parameter to control how many Row bits are used
        parameter COL_BITS         =      10; // Set this parameter to control how many Column bits are used
        parameter DQ_BITS          =       8; // Set this parameter to control how many Data bits are used
        parameter DM_BITS          =       1; // Set this parameter to control how many DM bits are used
        parameter BA_BITS          =       2; // Bank bits
    `else `define x16
        parameter ADDR_BITS        =      12; // Set this parameter to control how many Address bits are used
        parameter ROW_BITS         =      12; // Set this parameter to control how many Row bits are used
        parameter COL_BITS         =       9; // Set this parameter to control how many Column bits are used
        parameter DQ_BITS          =      16; // Set this parameter to control how many Data bits are used
        parameter DM_BITS          =       2; // Set this parameter to control how many DM bits are used
        parameter BA_BITS          =       2; // Bank bits
    `endif
`elsif den256Mb
    `ifdef x4
        parameter ADDR_BITS        =      13; // Set this parameter to control how many Address bits are used
        parameter ROW_BITS         =      13; // Set this parameter to control how many Row bits are used
        parameter COL_BITS         =      11; // Set this parameter to control how many Column bits are used
        parameter DQ_BITS          =       4; // Set this parameter to control how many Data bits are used
        parameter DM_BITS          =       1; // Set this parameter to control how many DM bits are used
        parameter BA_BITS          =       2; // Bank bits
    `elsif x8
        parameter ADDR_BITS        =      13; // Set this parameter to control how many Address bits are used
        parameter ROW_BITS         =      13; // Set this parameter to control how many Row bits are used
        parameter COL_BITS         =      10; // Set this parameter to control how many Column bits are used
        parameter DQ_BITS          =       8; // Set this parameter to control how many Data bits are used
        parameter DM_BITS          =       1; // Set this parameter to control how many DM bits are used
        parameter BA_BITS          =       2; // Bank bits
    `else `define x16
        parameter ADDR_BITS        =      13; // Set this parameter to control how many Address bits are used
        parameter ROW_BITS         =      13; // Set this parameter to control how many Row bits are used
        parameter COL_BITS         =       9; // Set this parameter to control how many Column bits are used
        parameter DQ_BITS          =      16; // Set this parameter to control how many Data bits are used
        parameter DM_BITS          =       2; // Set this parameter to control how many DM bits are used
        parameter BA_BITS          =       2; // Bank bits
    `endif
`else `define den512Mb
    `ifdef x4
        parameter ADDR_BITS        =      13; // Set this parameter to control how many Address bits are used
        parameter ROW_BITS         =      13; // Set this parameter to control how many Row bits are used
        parameter COL_BITS         =      12; // Set this parameter to control how many Column bits are used
        parameter DQ_BITS          =       4; // Set this parameter to control how many Data bits are used
        parameter DM_BITS          =       1; // Set this parameter to control how many DM bits are used
        parameter BA_BITS          =       2; // Bank bits
    `elsif x8
        parameter ADDR_BITS        =      13; // Set this parameter to control how many Address bits are used
        parameter ROW_BITS         =      13; // Set this parameter to control how many Row bits are used
        parameter COL_BITS         =      11; // Set this parameter to control how many Column bits are used
        parameter DQ_BITS          =       8; // Set this parameter to control how many Data bits are used
        parameter DM_BITS          =       1; // Set this parameter to control how many DM bits are used
        parameter BA_BITS          =       2; // Bank bits
    `else `define x16
        parameter ADDR_BITS        =      13; // Set this parameter to control how many Address bits are used
        parameter ROW_BITS         =      13; // Set this parameter to control how many Row bits are used
        parameter COL_BITS         =      10; // Set this parameter to control how many Column bits are used
        parameter DQ_BITS          =      16; // Set this parameter to control how many Data bits are used
        parameter DM_BITS          =       2; // Set this parameter to control how many DM bits are used
        parameter BA_BITS          =       2; // Bank bits
    `endif
`endif
    // Other Parameters
    parameter mem_sizes = 2**(ROW_BITS+COL_BITS) - 1;

  • Sdram_Params.h 是SDRAM控制器宏定义
// 地址和数据位宽
`define  ASIZE   13   //SDRAM地址位宽
`define  DSIZE   16   //SDRAM数据位宽
`define  BSIZE   2    //SDRAM的bank地址位宽

//操作命令{CS_N,RAS_N,CAS_N,WE}
parameter   C_NOP = 4'b0111,  //空操作命令
            C_PRE = 4'b0010,  //预充电命令
            C_AREF = 4'b0001, //自动刷新命令
            C_MSET = 4'b0000, //加载模式寄存器命令
            C_ACT = 4'b0011,  //激活命令
            C_RD = 4'b0101,   //读命令
            C_WR = 4'b0100;   //写命令


	133 MHz	///
/*
parameter	INIT_PRE	   =	26600;       //初始化等待时间>100us,取200us
parameter	REF_PRE		=	3;           //tRP  >=18ns,取30ns
parameter	REF_REF		=	10;          //tRFC  >=60ns,取100ns
parameter	AUTO_REF		=	2000;         //自动刷新周期<64ms/4096=15625ns
parameter	LMR_ACT		=	2;           //装载模式寄存器到可激活延时(2个时钟周期)
parameter	WR_PRE		=	2;           //WRITE recovery time 1CLK+6ns
parameter	SC_CL		   =	2;           //列选通潜伏期
parameter	SC_RCD		=	3;           //18ns--ACTIVE to READ or WRITE delay
parameter	SC_BL		   =	4;           //Burst Length,1,2,4,8可选
*/
///

	100 MHz	///
parameter	INIT_PRE = 20000;//初始化等待时间>100us,取200us
parameter	REF_PRE = 3;     //tRP  >=20ns,取30ns
parameter	REF_REF = 10;    //tRRC  >=66ns,取100ns

parameter	AUTO_REF	= 1560; //自动刷新周期<64ms/4096=15625ns
parameter	LMR_ACT = 2;     //装载模式寄存器到可激活延时
parameter	WR_PRE =	2;      //写操作写数据完成到预充电时间间隔
parameter	SC_RCD =	3;      //激活到读命令或写命令延时tRCD>18ns
parameter	SC_CL	= 3;       //列选通潜伏期
parameter	SC_BL	= 8;       //突发长度设置,1,2,4,8可选
///

	50 MHz	///
/*
parameter	INIT_PRE	   =	10000;       //初始化等待时间>100us,取200us
parameter	REF_PRE		=	1;           //tRP  >=18ns,取30ns
parameter	REF_REF		=	5;           //tRFC  >=60ns,取100ns
parameter	AUTO_REF		=	780;         //自动刷新周期<64ms/4096=15625ns
parameter	LMR_ACT		=	2;           //装载模式寄存器到可激活延时(2个时钟周期)LOAD MODE REGISTER command to ACTIVE or REFRESH command
parameter	PRE_ACT		=	1;           //precharge 到可激活需要延时最小20ns
parameter	WR_PRE		=	2;           //WRITE recovery time 1CLK+6ns
parameter	SC_CL		   =	2;           //列选通潜伏期
parameter	SC_RCD		=	3;           //ACTIVE to READ or WRITE delay
parameter	SC_BL		   =	4;           //Burst Length,1,2,4,8可选
*/
///

//	SDRAM模式寄存器参数化表示
//写突发模式设置
parameter	OP_CODE = 1'b0;  
//突发长度设置
parameter	SDR_BL = (SC_BL == 1)?	3'b000:
							(SC_BL == 2)?	3'b001:
							(SC_BL == 4)?	3'b010:
							(SC_BL == 8)?  3'b011:
							3'b111;	
//突发类型设置								 
parameter	SDR_BT = 1'b0;
//列选通潜伏期设置
parameter	SDR_CL = (SC_CL == 2)? 3'b10: 3'b11;
  • sdr.v
/**************************************************************************
*
*    File Name:  sdr.v  
*      Version:  2.2
*         Date:  October 12th, 2010
*        Model:  BUS Functional
*    Simulator:  Model Technology
*
* Dependencies:  None
*
*        Email:  modelsupport@micron.com
*      Company:  Micron Technology, Inc.
*
*  Description:  Micron SDRAM Verilog model
*
*   Limitation:  - Doesn't check for refresh timing
*
*         Note:  - Set simulator resolution to "ps" accuracy
*                - Set Debug = 0 to disable $display messages
*
*   Disclaimer:  THESE DESIGNS ARE PROVIDED "AS IS" WITH NO WARRANTY 
*                WHATSOEVER AND MICRON SPECIFICALLY DISCLAIMS ANY 
*                IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR
*                A PARTICULAR PURPOSE, OR AGAINST INFRINGEMENT.
*
*                Copyright � 2001 Micron Semiconductor Products, Inc.
*                All rights researved
*
* Rev  Author          Date        Changes
* ---  --------------------------  ---------------------------------------
* 2.3  SH              05/12/2016  - Update tAC, tHZ timing
*      Micron Technology Inc.
*
* 2.2  SH              10/12/2010  - Combine all parts into sdr_parameters.vh
*      Micron Technology Inc.
*
* 2.1  SH              06/06/2002  - Typo in bank multiplex
*      Micron Technology Inc.
*
* 2.0  SH              04/30/2002  - Second release
*      Micron Technology Inc.
*
**************************************************************************/

`timescale 1ns / 1ps


module sdr (
	Dq, 
	Addr,
	Ba, 
	Clk, 
	Cke, 
	Cs_n, 
	Ras_n, 
	Cas_n, 
	We_n, 
	Dqm
);

`include "sdr_parameters.h"

    input                         Clk;
    input                         Cke;
    input                         Cs_n;
    input                         Ras_n;
    input                         Cas_n;
    input                         We_n;
    input     [ADDR_BITS - 1 : 0] Addr;
    input       [BA_BITS - 1 : 0] Ba;
    inout       [DQ_BITS - 1 : 0] Dq;
    input       [DM_BITS - 1 : 0] Dqm;

    reg         [DQ_BITS - 1 : 0] Bank0 [0 : mem_sizes];
    reg         [DQ_BITS - 1 : 0] Bank1 [0 : mem_sizes];
    reg         [DQ_BITS - 1 : 0] Bank2 [0 : mem_sizes];
    reg         [DQ_BITS - 1 : 0] Bank3 [0 : mem_sizes];

    reg                   [1 : 0] Bank_addr [0 : 3];                // Bank Address Pipeline
    reg        [COL_BITS - 1 : 0] Col_addr [0 : 3];                 // Column Address Pipeline
    reg                   [3 : 0] Command [0 : 3];                  // Command Operation Pipeline
    reg                   [1 : 0] Dqm_reg0, Dqm_reg1;               // DQM Operation Pipeline
    reg       [ADDR_BITS - 1 : 0] B0_row_addr, B1_row_addr, B2_row_addr, B3_row_addr;

    reg       [ADDR_BITS - 1 : 0] Mode_reg;
    reg         [DQ_BITS - 1 : 0] Dq_reg, Dq_dqm;
    reg        [COL_BITS - 1 : 0] Col_temp, Burst_counter;

    reg                           Act_b0, Act_b1, Act_b2, Act_b3;   // Bank Activate
    reg                           Pc_b0, Pc_b1, Pc_b2, Pc_b3;       // Bank Precharge

    reg                   [1 : 0] Bank_precharge       [0 : 3];     // Precharge Command
    reg                           A10_precharge        [0 : 3];     // Addr[10] = 1 (All banks)
    reg                           Auto_precharge       [0 : 3];     // RW Auto Precharge (Bank)
    reg                           Read_precharge       [0 : 3];     // R  Auto Precharge
    reg                           Write_precharge      [0 : 3];     //  W Auto Precharge
    reg                           RW_interrupt_read    [0 : 3];     // RW Interrupt Read with Auto Precharge
    reg                           RW_interrupt_write   [0 : 3];     // RW Interrupt Write with Auto Precharge
    reg                   [1 : 0] RW_interrupt_bank;                // RW Interrupt Bank
    integer                       RW_interrupt_counter [0 : 3];     // RW Interrupt Counter
    integer                       Count_precharge      [0 : 3];     // RW Auto Precharge Counter

    reg                           Data_in_enable;
    reg                           Data_out_enable;

    reg                   [1 : 0] Bank, Prev_bank;
    reg       [ADDR_BITS - 1 : 0] Row;
    reg        [COL_BITS - 1 : 0] Col, Col_brst;

    // Internal system clock
    reg                           CkeZ, Sys_clk;

    // Commands Decode
    wire      Active_enable    = ~Cs_n & ~Ras_n &  Cas_n &  We_n;
    wire      Aref_enable      = ~Cs_n & ~Ras_n & ~Cas_n &  We_n;
    wire      Burst_term       = ~Cs_n &  Ras_n &  Cas_n & ~We_n;
    wire      Mode_reg_enable  = ~Cs_n & ~Ras_n & ~Cas_n & ~We_n;
    wire      Prech_enable     = ~Cs_n & ~Ras_n &  Cas_n & ~We_n;
    wire      Read_enable      = ~Cs_n &  Ras_n & ~Cas_n &  We_n;
    wire      Write_enable     = ~Cs_n &  Ras_n & ~Cas_n & ~We_n;

    // Burst Length Decode
    wire      Burst_length_1   = ~Mode_reg[2] & ~Mode_reg[1] & ~Mode_reg[0];
    wire      Burst_length_2   = ~Mode_reg[2] & ~Mode_reg[1] &  Mode_reg[0];
    wire      Burst_length_4   = ~Mode_reg[2] &  Mode_reg[1] & ~Mode_reg[0];
    wire      Burst_length_8   = ~Mode_reg[2] &  Mode_reg[1] &  Mode_reg[0];
    wire      Burst_length_f   =  Mode_reg[2] &  Mode_reg[1] &  Mode_reg[0];

    // CAS Latency Decode
    wire [2 : 0] Cas_latency   =  {Mode_reg[6],  Mode_reg[5],   Mode_reg[4]};

    // Write Burst Mode
    wire      Write_burst_mode = Mode_reg[9];

    wire      Debug            = 1'b1;                          // Debug messages : 1 = On
    wire      Dq_chk           = Sys_clk & Data_in_enable;      // Check setup/hold time for DQ
    
    assign    Dq               = Dq_reg;                        // DQ buffer

    // Commands Operation
    `define   ACT       0
    `define   NOP       1
    `define   READ      2
    `define   WRITE     3
    `define   PRECH     4
    `define   A_REF     5
    `define   BST       6
    `define   LMR       7

    // These timing dynamically adjust based on CAS Latency
    time  tAC, tHZ;

    // Timing Check variable
    time  MRD_chk;
    time  WR_chkm [0 : 3];
    time  RFC_chk, RRD_chk;
    time  RC_chk0, RC_chk1, RC_chk2, RC_chk3;
    time  RAS_chk0, RAS_chk1, RAS_chk2, RAS_chk3;
    time  RCD_chk0, RCD_chk1, RCD_chk2, RCD_chk3;
    time  RP_chk0, RP_chk1, RP_chk2, RP_chk3;

    initial begin
        Dq_reg = {DQ_BITS{1'bz}};
        Data_in_enable = 0; Data_out_enable = 0;
        Act_b0 = 1; Act_b1 = 1; Act_b2 = 1; Act_b3 = 1;
        Pc_b0 = 0; Pc_b1 = 0; Pc_b2 = 0; Pc_b3 = 0;
        WR_chkm[0] = 0; WR_chkm[1] = 0; WR_chkm[2] = 0; WR_chkm[3] = 0;
        RW_interrupt_read[0] = 0; RW_interrupt_read[1] = 0; RW_interrupt_read[2] = 0; RW_interrupt_read[3] = 0;
        RW_interrupt_write[0] = 0; RW_interrupt_write[1] = 0; RW_interrupt_write[2] = 0; RW_interrupt_write[3] = 0;
        MRD_chk = 0; RFC_chk = 0; RRD_chk = 0;
        RAS_chk0 = 0; RAS_chk1 = 0; RAS_chk2 = 0; RAS_chk3 = 0;
        RCD_chk0 = 0; RCD_chk1 = 0; RCD_chk2 = 0; RCD_chk3 = 0;
        RC_chk0 = 0; RC_chk1 = 0; RC_chk2 = 0; RC_chk3 = 0;
        RP_chk0 = 0; RP_chk1 = 0; RP_chk2 = 0; RP_chk3 = 0;
        $timeformat (-9, 1, " ns", 12);
    end

    // System clock generator
    always begin
        @ (posedge Clk) begin
            Sys_clk = CkeZ;
            CkeZ = Cke;
        end
        @ (negedge Clk) begin
            Sys_clk = 1'b0;
        end
    end

    // Adjust tAC, tHZ based on CAS Latency
    always @ (Cas_latency) begin
        case (Cas_latency)
            1 : begin tAC = tAC1; tHZ = tHZ1; end
            2 : begin tAC = tAC2; tHZ = tHZ2; end
            3 : begin tAC = tAC3; tHZ = tHZ3; end
        endcase
    end

    always @ (posedge Sys_clk) begin
        // Internal Commamd Pipelined
        Command[0] = Command[1];
        Command[1] = Command[2];
        Command[2] = Command[3];
        Command[3] = `NOP;

        Col_addr[0] = Col_addr[1];
        Col_addr[1] = Col_addr[2];
        Col_addr[2] = Col_addr[3];
        Col_addr[3] = {COL_BITS{1'b0}};

        Bank_addr[0] = Bank_addr[1];
        Bank_addr[1] = Bank_addr[2];
        Bank_addr[2] = Bank_addr[3];
        Bank_addr[3] = 2'b0;

        Bank_precharge[0] = Bank_precharge[1];
        Bank_precharge[1] = Bank_precharge[2];
        Bank_precharge[2] = Bank_precharge[3];
        Bank_precharge[3] = 2'b0;

        A10_precharge[0] = A10_precharge[1];
        A10_precharge[1] = A10_precharge[2];
        A10_precharge[2] = A10_precharge[3];
        A10_precharge[3] = 1'b0;

        // Dqm pipeline for Read
        Dqm_reg0 = Dqm_reg1;
        Dqm_reg1 = Dqm;

        // Read or Write with Auto Precharge Counter
        if (Auto_precharge[0] === 1'b1) begin
            Count_precharge[0] = Count_precharge[0] + 1;
        end
        if (Auto_precharge[1] === 1'b1) begin
            Count_precharge[1] = Count_precharge[1] + 1;
        end
        if (Auto_precharge[2] === 1'b1) begin
            Count_precharge[2] = Count_precharge[2] + 1;
        end
        if (Auto_precharge[3] === 1'b1) begin
            Count_precharge[3] = Count_precharge[3] + 1;
        end

        // Read or Write Interrupt Counter
        if (RW_interrupt_write[0] === 1'b1) begin
            RW_interrupt_counter[0] = RW_interrupt_counter[0] + 1;
        end
        if (RW_interrupt_write[1] === 1'b1) begin
            RW_interrupt_counter[1] = RW_interrupt_counter[1] + 1;
        end
        if (RW_interrupt_write[2] === 1'b1) begin
            RW_interrupt_counter[2] = RW_interrupt_counter[2] + 1;
        end
        if (RW_interrupt_write[3] === 1'b1) begin
            RW_interrupt_counter[3] = RW_interrupt_counter[3] + 1;
        end

        // tMRD Counter
        MRD_chk = MRD_chk + 1;

        // Auto Refresh
        if (Aref_enable === 1'b1) begin
            if (Debug) begin
                $display ("%m : at time %t AREF : Auto Refresh", $time);
            end

            // Auto Refresh to Auto Refresh
            if ($time - RFC_chk < tRFC) begin
                $display ("%m : at time %t ERROR: tRFC violation during Auto Refresh", $time);
            end

            // Precharge to Auto Refresh
            if (($time - RP_chk0 < tRP) || ($time - RP_chk1 < tRP) ||
                ($time - RP_chk2 < tRP) || ($time - RP_chk3 < tRP)) begin
                $display ("%m : at time %t ERROR: tRP violation during Auto Refresh", $time);
            end

            // Precharge to Refresh
            if (Pc_b0 === 1'b0 || Pc_b1 === 1'b0 || Pc_b2 === 1'b0 || Pc_b3 === 1'b0) begin
                $display ("%m : at time %t ERROR: All banks must be Precharge before Auto Refresh", $time);
            end

            // Load Mode Register to Auto Refresh
            if (MRD_chk < tMRD) begin
                $display ("%m : at time %t ERROR: tMRD violation during Auto Refresh", $time);
            end

            // Record Current tRFC time
            RFC_chk = $time;
        end
        
        // Load Mode Register
        if (Mode_reg_enable === 1'b1) begin
            // Register Mode
            Mode_reg = Addr;

            // Decode CAS Latency, Burst Length, Burst Type, and Write Burst Mode
            if (Debug) begin
                $display ("%m : at time %t LMR  : Load Mode Register", $time);
                // CAS Latency
                case (Addr[6 : 4])
                    3'b010  : $display ("%m :                             CAS Latency      = 2");
                    3'b011  : $display ("%m :                             CAS Latency      = 3");
                    default : $display ("%m :                             CAS Latency      = Reserved");
                endcase

                // Burst Length
                case (Addr[2 : 0])
                    3'b000  : $display ("%m :                             Burst Length     = 1");
                    3'b001  : $display ("%m :                             Burst Length     = 2");
                    3'b010  : $display ("%m :                             Burst Length     = 4");
                    3'b011  : $display ("%m :                             Burst Length     = 8");
                    3'b111  : $display ("%m :                             Burst Length     = Full");
                    default : $display ("%m :                             Burst Length     = Reserved");
                endcase

                // Burst Type
                if (Addr[3] === 1'b0) begin
                    $display ("%m :                             Burst Type       = Sequential");
                end else if (Addr[3] === 1'b1) begin
                    $display ("%m :                             Burst Type       = Interleaved");
                end else begin
                    $display ("%m :                             Burst Type       = Reserved");
                end

                // Write Burst Mode
                if (Addr[9] === 1'b0) begin
                    $display ("%m :                             Write Burst Mode = Programmed Burst Length");
                end else if (Addr[9] === 1'b1) begin
                    $display ("%m :                             Write Burst Mode = Single Location Access");
                end else begin
                    $display ("%m :                             Write Burst Mode = Reserved");
                end
            end

            // Precharge to Load Mode Register
            if (Pc_b0 === 1'b0 && Pc_b1 === 1'b0 && Pc_b2 === 1'b0 && Pc_b3 === 1'b0) begin
                $display ("%m : at time %t ERROR: all banks must be Precharge before Load Mode Register", $time);
            end

            // Precharge to Load Mode Register
            if (($time - RP_chk0 < tRP) || ($time - RP_chk1 < tRP) ||
                ($time - RP_chk2 < tRP) || ($time - RP_chk3 < tRP)) begin
                $display ("%m : at time %t ERROR: tRP violation during Load Mode Register", $time);
            end

            // Auto Refresh to Load Mode Register
            if ($time - RFC_chk < tRFC) begin
                $display ("%m : at time %t ERROR: tRFC violation during Load Mode Register", $time);
            end

            // Load Mode Register to Load Mode Register
            if (MRD_chk < tMRD) begin
                $display ("%m : at time %t ERROR: tMRD violation during Load Mode Register", $time);
            end

            // Reset MRD Counter
            MRD_chk = 0;
        end
        
        // Active Block (Latch Bank Address and Row Address)
        if (Active_enable === 1'b1) begin
            // Activate an open bank can corrupt data
            if ((Ba === 2'b00 && Act_b0 === 1'b1) || (Ba === 2'b01 && Act_b1 === 1'b1) ||
                (Ba === 2'b10 && Act_b2 === 1'b1) || (Ba === 2'b11 && Act_b3 === 1'b1)) begin
                $display ("%m : at time %t ERROR: Bank already activated -- data can be corrupted", $time);
            end

            // Activate Bank 0
            if (Ba === 2'b00 && Pc_b0 === 1'b1) begin
                // Debug Message
                if (Debug) begin
                    $display ("%m : at time %t ACT  : Bank = 0 Row = %d", $time, Addr);
                end

                // ACTIVE to ACTIVE command period
                if ($time - RC_chk0 < tRC) begin
                    $display ("%m : at time %t ERROR: tRC violation during Activate bank 0", $time);
                end

                // Precharge to Activate Bank 0
                if ($time - RP_chk0 < tRP) begin
                    $display ("%m : at time %t ERROR: tRP violation during Activate bank 0", $time);
                end

                // Record variables
                Act_b0 = 1'b1;
                Pc_b0 = 1'b0;
                B0_row_addr = Addr [ADDR_BITS - 1 : 0];
                RAS_chk0 = $time;
                RC_chk0 = $time;
                RCD_chk0 = $time;
            end

            if (Ba == 2'b01 && Pc_b1 == 1'b1) begin
                // Debug Message
                if (Debug) begin
                    $display ("%m : at time %t ACT  : Bank = 1 Row = %d", $time, Addr);
                end

                // ACTIVE to ACTIVE command period
                if ($time - RC_chk1 < tRC) begin
                    $display ("%m : at time %t ERROR: tRC violation during Activate bank 1", $time);
                end

                // Precharge to Activate Bank 1
                if ($time - RP_chk1 < tRP) begin
                    $display ("%m : at time %t ERROR: tRP violation during Activate bank 1", $time);
                end

                // Record variables
                Act_b1 = 1'b1;
                Pc_b1 = 1'b0;
                B1_row_addr = Addr [ADDR_BITS - 1 : 0];
                RAS_chk1 = $time;
                RC_chk1 = $time;
                RCD_chk1 = $time;
            end

            if (Ba == 2'b10 && Pc_b2 == 1'b1) begin
                // Debug Message
                if (Debug) begin
                    $display ("%m : at time %t ACT  : Bank = 2 Row = %d", $time, Addr);
                end

                // ACTIVE to ACTIVE command period
                if ($time - RC_chk2 < tRC) begin
                    $display ("%m : at time %t ERROR: tRC violation during Activate bank 2", $time);
                end

                // Precharge to Activate Bank 2
                if ($time - RP_chk2 < tRP) begin
                    $display ("%m : at time %t ERROR: tRP violation during Activate bank 2", $time);
                end

                // Record variables
                Act_b2 = 1'b1;
                Pc_b2 = 1'b0;
                B2_row_addr = Addr [ADDR_BITS - 1 : 0];
                RAS_chk2 = $time;
                RC_chk2 = $time;
                RCD_chk2 = $time;
            end

            if (Ba == 2'b11 && Pc_b3 == 1'b1) begin
                // Debug Message
                if (Debug) begin
                    $display ("%m : at time %t ACT  : Bank = 3 Row = %d", $time, Addr);
                end

                // ACTIVE to ACTIVE command period
                if ($time - RC_chk3 < tRC) begin
                    $display ("%m : at time %t ERROR: tRC violation during Activate bank 3", $time);
                end

                // Precharge to Activate Bank 3
                if ($time - RP_chk3 < tRP) begin
                    $display ("%m : at time %t ERROR: tRP violation during Activate bank 3", $time);
                end

                // Record variables
                Act_b3 = 1'b1;
                Pc_b3 = 1'b0;
                B3_row_addr = Addr [ADDR_BITS - 1 : 0];
                RAS_chk3 = $time;
                RC_chk3 = $time;
                RCD_chk3 = $time;
            end

            // Active Bank A to Active Bank B
            if ((Prev_bank != Ba) && ($time - RRD_chk < tRRD)) begin
                $display ("%m : at time %t ERROR: tRRD violation during Activate bank = %d", $time, Ba);
            end

            // Auto Refresh to Activate
            if ($time - RFC_chk < tRFC) begin
                $display ("%m : at time %t ERROR: tRFC violation during Activate bank = %d", $time, Ba);
            end

            // Load Mode Register to Active
            if (MRD_chk < tMRD ) begin
                $display ("%m : at time %t ERROR: tMRD violation during Activate bank = %d", $time, Ba);
            end

            // Record variables for checking violation
            RRD_chk = $time;
            Prev_bank = Ba;
        end
        
        // Precharge Block
        if (Prech_enable == 1'b1) begin
            // Load Mode Register to Precharge
            if ($time - MRD_chk < tMRD) begin
                $display ("%m : at time %t ERROR: tMRD violaiton during Precharge", $time);
            end

            // Precharge Bank 0
            if ((Addr[10] === 1'b1 || (Addr[10] === 1'b0 && Ba === 2'b00)) && Act_b0 === 1'b1) begin
                Act_b0 = 1'b0;
                Pc_b0 = 1'b1;
                RP_chk0 = $time;

                // Activate to Precharge
                if ($time - RAS_chk0 < tRAS) begin
                    $display ("%m : at time %t ERROR: tRAS violation during Precharge", $time);
                end

                // tWR violation check for write
                if ($time - WR_chkm[0] < tWRm) begin
                    $display ("%m : at time %t ERROR: tWR violation during Precharge", $time);
                end
            end

            // Precharge Bank 1
            if ((Addr[10] === 1'b1 || (Addr[10] === 1'b0 && Ba === 2'b01)) && Act_b1 === 1'b1) begin
                Act_b1 = 1'b0;
                Pc_b1 = 1'b1;
                RP_chk1 = $time;

                // Activate to Precharge
                if ($time - RAS_chk1 < tRAS) begin
                    $display ("%m : at time %t ERROR: tRAS violation during Precharge", $time);
                end

                // tWR violation check for write
                if ($time - WR_chkm[1] < tWRm) begin
                    $display ("%m : at time %t ERROR: tWR violation during Precharge", $time);
                end
            end

            // Precharge Bank 2
            if ((Addr[10] === 1'b1 || (Addr[10] === 1'b0 && Ba === 2'b10)) && Act_b2 === 1'b1) begin
                Act_b2 = 1'b0;
                Pc_b2 = 1'b1;
                RP_chk2 = $time;

                // Activate to Precharge
                if ($time - RAS_chk2 < tRAS) begin
                    $display ("%m : at time %t ERROR: tRAS violation during Precharge", $time);
                end

                // tWR violation check for write
                if ($time - WR_chkm[2] < tWRm) begin
                    $display ("%m : at time %t ERROR: tWR violation during Precharge", $time);
                end
            end

            // Precharge Bank 3
            if ((Addr[10] === 1'b1 || (Addr[10] === 1'b0 && Ba === 2'b11)) && Act_b3 === 1'b1) begin
                Act_b3 = 1'b0;
                Pc_b3 = 1'b1;
                RP_chk3 = $time;

                // Activate to Precharge
                if ($time - RAS_chk3 < tRAS) begin
                    $display ("%m : at time %t ERROR: tRAS violation during Precharge", $time);
                end

                // tWR violation check for write
                if ($time - WR_chkm[3] < tWRm) begin
                    $display ("%m : at time %t ERROR: tWR violation during Precharge", $time);
                end
            end

            // Precharge truncation with DQM set
            if ((Data_in_enable == 1'b1) && ~(&Dqm)) begin
                $display ("%m : at time %t ERROR: DQM not asserted during Precharge truncation", $time);
            end

            // Terminate a Write Immediately (if same bank or all banks)
            if (Data_in_enable === 1'b1 && (Bank === Ba || Addr[10] === 1'b1)) begin
                Data_in_enable = 1'b0;
            end

            // Precharge Command Pipeline for Read
            Command[Cas_latency - 1] = `PRECH;
            Bank_precharge[Cas_latency - 1] = Ba;
            A10_precharge[Cas_latency - 1] = Addr[10];
        end
        
        // Burst terminate
        if (Burst_term === 1'b1) begin
            // Terminate a Write Immediately
            if (Data_in_enable == 1'b1) begin
                Data_in_enable = 1'b0;
            end

            // Terminate a Read Depend on CAS Latency
            Command[Cas_latency - 1] = `BST;

            // Display debug message
            if (Debug) begin
                $display ("%m : at time %t BST  : Burst Terminate",$time);
            end
        end
        
        // Read, Write, Column Latch
        if (Read_enable === 1'b1) begin
            // Check to see if bank is open (ACT)
            if ((Ba == 2'b00 && Pc_b0 == 1'b1) || (Ba == 2'b01 && Pc_b1 == 1'b1) ||
                (Ba == 2'b10 && Pc_b2 == 1'b1) || (Ba == 2'b11 && Pc_b3 == 1'b1)) begin
                $display("%m : at time %t ERROR: Bank is not Activated for Read", $time);
            end

            // Activate to Read or Write
            if ((Ba == 2'b00) && ($time - RCD_chk0 < tRCD) ||
                (Ba == 2'b01) && ($time - RCD_chk1 < tRCD) ||
                (Ba == 2'b10) && ($time - RCD_chk2 < tRCD) ||
                (Ba == 2'b11) && ($time - RCD_chk3 < tRCD)) begin
                $display("%m : at time %t ERROR: tRCD violation during Read", $time);
            end

            // CAS Latency pipeline
            Command[Cas_latency - 1] = `READ;
            Col_addr[Cas_latency - 1] = Addr;
            Bank_addr[Cas_latency - 1] = Ba;

            // Read interrupt Write (terminate Write immediately)
            if (Data_in_enable == 1'b1) begin
                Data_in_enable = 1'b0;

                // Interrupting a Write with Autoprecharge
                if (Auto_precharge[RW_interrupt_bank] == 1'b1 && Write_precharge[RW_interrupt_bank] == 1'b1) begin
                    RW_interrupt_write[RW_interrupt_bank] = 1'b1;
                    RW_interrupt_counter[RW_interrupt_bank] = 0;

                    // Display debug message
                    if (Debug) begin
                        $display ("%m : at time %t NOTE : Read interrupt Write with Autoprecharge", $time);
                    end
                end
            end

            // Read with Auto Precharge
            if (Addr[10] == 1'b1) begin
                Auto_precharge[Ba] = 1'b1;
                Count_precharge[Ba] = 0;
                RW_interrupt_bank = Ba;
                Read_precharge[Ba] = 1'b1;
            end
        end

        // Write Command
        if (Write_enable == 1'b1) begin
            // Activate to Write
            if ((Ba == 2'b00 && Pc_b0 == 1'b1) || (Ba == 2'b01 && Pc_b1 == 1'b1) ||
                (Ba == 2'b10 && Pc_b2 == 1'b1) || (Ba == 2'b11 && Pc_b3 == 1'b1)) begin
                $display("%m : at time %t ERROR: Bank is not Activated for Write", $time);
            end

            // Activate to Read or Write
            if ((Ba == 2'b00) && ($time - RCD_chk0 < tRCD) ||
                (Ba == 2'b01) && ($time - RCD_chk1 < tRCD) ||
                (Ba == 2'b10) && ($time - RCD_chk2 < tRCD) ||
                (Ba == 2'b11) && ($time - RCD_chk3 < tRCD)) begin
                $display("%m : at time %t ERROR: tRCD violation during Read", $time);
            end

            // Latch Write command, Bank, and Column
            Command[0] = `WRITE;
            Col_addr[0] = Addr;
            Bank_addr[0] = Ba;

            // Write interrupt Write (terminate Write immediately)
            if (Data_in_enable == 1'b1) begin
                Data_in_enable = 1'b0;

                // Interrupting a Write with Autoprecharge
                if (Auto_precharge[RW_interrupt_bank] == 1'b1 && Write_precharge[RW_interrupt_bank] == 1'b1) begin
                    RW_interrupt_write[RW_interrupt_bank] = 1'b1;

                    // Display debug message
                    if (Debug) begin
                        $display ("%m : at time %t NOTE : Read Bank %d interrupt Write Bank %d with Autoprecharge", $time, Ba, RW_interrupt_bank);
                    end
                end
            end

            // Write interrupt Read (terminate Read immediately)
            if (Data_out_enable == 1'b1) begin
                Data_out_enable = 1'b0;
                
                // Interrupting a Read with Autoprecharge
                if (Auto_precharge[RW_interrupt_bank] == 1'b1 && Read_precharge[RW_interrupt_bank] == 1'b1) begin
                    RW_interrupt_read[RW_interrupt_bank] = 1'b1;

                    // Display debug message
                    if (Debug) begin
                        $display ("%m : at time %t NOTE : Write Bank %d interrupt Read Bank %d with Autoprecharge", $time, Ba, RW_interrupt_bank);
                    end
                end
            end

            // Write with Auto Precharge
            if (Addr[10] == 1'b1) begin
                Auto_precharge[Ba] = 1'b1;
                Count_precharge[Ba] = 0;
                RW_interrupt_bank = Ba;
                Write_precharge[Ba] = 1'b1;
            end
        end

        /*
            Write with Auto Precharge Calculation
                The device start internal precharge when:
                    1.  Meet minimum tRAS requirement
                and 2.  tWR cycle(s) after last valid data
                 or 3.  Interrupt by a Read or Write (with or without Auto Precharge)

            Note: Model is starting the internal precharge 1 cycle after they meet all the
                  requirement but tRP will be compensate for the time after the 1 cycle.
        */
        if ((Auto_precharge[0] == 1'b1) && (Write_precharge[0] == 1'b1)) begin
            if ((($time - RAS_chk0 >= tRAS) &&                                                          // Case 1
               (((Burst_length_1 == 1'b1 || Write_burst_mode == 1'b1) && Count_precharge [0] >= 1) ||   // Case 2
                 (Burst_length_2 == 1'b1                              && Count_precharge [0] >= 2) ||
                 (Burst_length_4 == 1'b1                              && Count_precharge [0] >= 4) ||
                 (Burst_length_8 == 1'b1                              && Count_precharge [0] >= 8))) ||
                 (RW_interrupt_write[0] == 1'b1 && RW_interrupt_counter[0] >= 1)) begin                 // Case 3
                    Auto_precharge[0] = 1'b0;
                    Write_precharge[0] = 1'b0;
                    RW_interrupt_write[0] = 1'b0;
                    Pc_b0 = 1'b1;
                    Act_b0 = 1'b0;
                    RP_chk0 = $time + tWRa;
                    if (Debug) begin
                        $display ("%m : at time %t NOTE : Start Internal Auto Precharge for Bank 0", $time);
                    end
            end
        end
        if ((Auto_precharge[1] == 1'b1) && (Write_precharge[1] == 1'b1)) begin
            if ((($time - RAS_chk1 >= tRAS) &&                                                          // Case 1
               (((Burst_length_1 == 1'b1 || Write_burst_mode == 1'b1) && Count_precharge [1] >= 1) ||   // Case 2
                 (Burst_length_2 == 1'b1                              && Count_precharge [1] >= 2) ||
                 (Burst_length_4 == 1'b1                              && Count_precharge [1] >= 4) ||
                 (Burst_length_8 == 1'b1                              && Count_precharge [1] >= 8))) ||
                 (RW_interrupt_write[1] == 1'b1 && RW_interrupt_counter[1] >= 1)) begin                 // Case 3
                    Auto_precharge[1] = 1'b0;
                    Write_precharge[1] = 1'b0;
                    RW_interrupt_write[1] = 1'b0;
                    Pc_b1 = 1'b1;
                    Act_b1 = 1'b0;
                    RP_chk1 = $time + tWRa;
                    if (Debug) begin
                        $display ("%m : at time %t NOTE : Start Internal Auto Precharge for Bank 1", $time);
                    end
            end
        end
        if ((Auto_precharge[2] == 1'b1) && (Write_precharge[2] == 1'b1)) begin
            if ((($time - RAS_chk2 >= tRAS) &&                                                          // Case 1
               (((Burst_length_1 == 1'b1 || Write_burst_mode == 1'b1) && Count_precharge [2] >= 1) ||   // Case 2
                 (Burst_length_2 == 1'b1                              && Count_precharge [2] >= 2) ||
                 (Burst_length_4 == 1'b1                              && Count_precharge [2] >= 4) ||
                 (Burst_length_8 == 1'b1                              && Count_precharge [2] >= 8))) ||
                 (RW_interrupt_write[2] == 1'b1 && RW_interrupt_counter[2] >= 1)) begin                 // Case 3
                    Auto_precharge[2] = 1'b0;
                    Write_precharge[2] = 1'b0;
                    RW_interrupt_write[2] = 1'b0;
                    Pc_b2 = 1'b1;
                    Act_b2 = 1'b0;
                    RP_chk2 = $time + tWRa;
                    if (Debug) begin
                        $display ("%m : at time %t NOTE : Start Internal Auto Precharge for Bank 2", $time);
                    end
            end
        end
        if ((Auto_precharge[3] == 1'b1) && (Write_precharge[3] == 1'b1)) begin
            if ((($time - RAS_chk3 >= tRAS) &&                                                          // Case 1
               (((Burst_length_1 == 1'b1 || Write_burst_mode == 1'b1) && Count_precharge [3] >= 1) ||   // Case 2
                 (Burst_length_2 == 1'b1                              && Count_precharge [3] >= 2) ||
                 (Burst_length_4 == 1'b1                              && Count_precharge [3] >= 4) ||
                 (Burst_length_8 == 1'b1                              && Count_precharge [3] >= 8))) ||
                 (RW_interrupt_write[3] == 1'b1 && RW_interrupt_counter[3] >= 1)) begin                 // Case 3
                    Auto_precharge[3] = 1'b0;
                    Write_precharge[3] = 1'b0;
                    RW_interrupt_write[3] = 1'b0;
                    Pc_b3 = 1'b1;
                    Act_b3 = 1'b0;
                    RP_chk3 = $time + tWRa;
                    if (Debug) begin
                        $display ("%m : at time %t NOTE : Start Internal Auto Precharge for Bank 3", $time);
                    end
            end
        end

        //  Read with Auto Precharge Calculation
        //      The device start internal precharge:
        //          1.  Meet minimum tRAS requirement
        //      and 2.  CAS Latency - 1 cycles before last burst
        //       or 3.  Interrupt by a Read or Write (with or without AutoPrecharge)
        if ((Auto_precharge[0] == 1'b1) && (Read_precharge[0] == 1'b1)) begin
            if ((($time - RAS_chk0 >= tRAS) &&                                                      // Case 1
                ((Burst_length_1 == 1'b1 && Count_precharge[0] >= 1) ||                             // Case 2
                 (Burst_length_2 == 1'b1 && Count_precharge[0] >= 2) ||
                 (Burst_length_4 == 1'b1 && Count_precharge[0] >= 4) ||
                 (Burst_length_8 == 1'b1 && Count_precharge[0] >= 8))) ||
                 (RW_interrupt_read[0] == 1'b1)) begin                                              // Case 3
                    Pc_b0 = 1'b1;
                    Act_b0 = 1'b0;
                    RP_chk0 = $time;
                    Auto_precharge[0] = 1'b0;
                    Read_precharge[0] = 1'b0;
                    RW_interrupt_read[0] = 1'b0;
                    if (Debug) begin
                        $display ("%m : at time %t NOTE : Start Internal Auto Precharge for Bank 0", $time);
                    end
            end
        end
        if ((Auto_precharge[1] == 1'b1) && (Read_precharge[1] == 1'b1)) begin
            if ((($time - RAS_chk1 >= tRAS) &&
                ((Burst_length_1 == 1'b1 && Count_precharge[1] >= 1) || 
                 (Burst_length_2 == 1'b1 && Count_precharge[1] >= 2) ||
                 (Burst_length_4 == 1'b1 && Count_precharge[1] >= 4) ||
                 (Burst_length_8 == 1'b1 && Count_precharge[1] >= 8))) ||
                 (RW_interrupt_read[1] == 1'b1)) begin
                    Pc_b1 = 1'b1;
                    Act_b1 = 1'b0;
                    RP_chk1 = $time;
                    Auto_precharge[1] = 1'b0;
                    Read_precharge[1] = 1'b0;
                    RW_interrupt_read[1] = 1'b0;
                    if (Debug) begin
                        $display ("%m : at time %t NOTE : Start Internal Auto Precharge for Bank 1", $time);
                    end
            end
        end
        if ((Auto_precharge[2] == 1'b1) && (Read_precharge[2] == 1'b1)) begin
            if ((($time - RAS_chk2 >= tRAS) &&
                ((Burst_length_1 == 1'b1 && Count_precharge[2] >= 1) || 
                 (Burst_length_2 == 1'b1 && Count_precharge[2] >= 2) ||
                 (Burst_length_4 == 1'b1 && Count_precharge[2] >= 4) ||
                 (Burst_length_8 == 1'b1 && Count_precharge[2] >= 8))) ||
                 (RW_interrupt_read[2] == 1'b1)) begin
                    Pc_b2 = 1'b1;
                    Act_b2 = 1'b0;
                    RP_chk2 = $time;
                    Auto_precharge[2] = 1'b0;
                    Read_precharge[2] = 1'b0;
                    RW_interrupt_read[2] = 1'b0;
                    if (Debug) begin
                        $display ("%m : at time %t NOTE : Start Internal Auto Precharge for Bank 2", $time);
                    end
            end
        end
        if ((Auto_precharge[3] == 1'b1) && (Read_precharge[3] == 1'b1)) begin
            if ((($time - RAS_chk3 >= tRAS) &&
                ((Burst_length_1 == 1'b1 && Count_precharge[3] >= 1) || 
                 (Burst_length_2 == 1'b1 && Count_precharge[3] >= 2) ||
                 (Burst_length_4 == 1'b1 && Count_precharge[3] >= 4) ||
                 (Burst_length_8 == 1'b1 && Count_precharge[3] >= 8))) ||
                 (RW_interrupt_read[3] == 1'b1)) begin
                    Pc_b3 = 1'b1;
                    Act_b3 = 1'b0;
                    RP_chk3 = $time;
                    Auto_precharge[3] = 1'b0;
                    Read_precharge[3] = 1'b0;
                    RW_interrupt_read[3] = 1'b0;
                    if (Debug) begin
                        $display("%m : at time %t NOTE : Start Internal Auto Precharge for Bank 3", $time);
                    end
            end
        end

        // Internal Precharge or Bst
        if (Command[0] == `PRECH) begin                         // Precharge terminate a read with same bank or all banks
            if (Bank_precharge[0] == Bank || A10_precharge[0] == 1'b1) begin
                if (Data_out_enable == 1'b1) begin
                    Data_out_enable = 1'b0;
                end
            end
        end else if (Command[0] == `BST) begin                  // BST terminate a read to current bank
            if (Data_out_enable == 1'b1) begin
                Data_out_enable = 1'b0;
            end
        end

        if (Data_out_enable == 1'b0) begin
            Dq_reg <= #tOH {DQ_BITS{1'bz}};
        end

        // Detect Read or Write command
        if (Command[0] == `READ) begin
            Bank = Bank_addr[0];
            Col = Col_addr[0];
            Col_brst = Col_addr[0];
            case (Bank_addr[0])
                2'b00 : Row = B0_row_addr;
                2'b01 : Row = B1_row_addr;
                2'b10 : Row = B2_row_addr;
                2'b11 : Row = B3_row_addr;
            endcase
            Burst_counter = 0;
            Data_in_enable = 1'b0;
            Data_out_enable = 1'b1;
        end else if (Command[0] == `WRITE) begin
            Bank = Bank_addr[0];
            Col = Col_addr[0];
            Col_brst = Col_addr[0];
            case (Bank_addr[0])
                2'b00 : Row = B0_row_addr;
                2'b01 : Row = B1_row_addr;
                2'b10 : Row = B2_row_addr;
                2'b11 : Row = B3_row_addr;
            endcase
            Burst_counter = 0;
            Data_in_enable = 1'b1;
            Data_out_enable = 1'b0;
        end

        // DQ buffer (Driver/Receiver)
        if (Data_in_enable == 1'b1) begin                                   // Writing Data to Memory
            // Array buffer
            case (Bank)
                2'b00 : Dq_dqm = Bank0 [{Row, Col}];
                2'b01 : Dq_dqm = Bank1 [{Row, Col}];
                2'b10 : Dq_dqm = Bank2 [{Row, Col}];
                2'b11 : Dq_dqm = Bank3 [{Row, Col}];
            endcase

            // Dqm operation
`ifdef x4
            if (Dqm[0] == 1'b0) begin
                Dq_dqm [ 3 : 0] = Dq [ 3 : 0];
            end
`elsif x8
            if (Dqm[0] == 1'b0) begin
                Dq_dqm [ 7 : 0] = Dq [ 7 : 0];
            end
`elsif x16
            if (Dqm[0] == 1'b0) begin
                Dq_dqm [ 7 : 0] = Dq [ 7 : 0];
            end
            if (Dqm[1] == 1'b0) begin
                Dq_dqm [15 : 8] = Dq [15 : 8];
            end
`endif

            // Write to memory
            case (Bank)
                2'b00 : Bank0 [{Row, Col}] = Dq_dqm;
                2'b01 : Bank1 [{Row, Col}] = Dq_dqm;
                2'b10 : Bank2 [{Row, Col}] = Dq_dqm;
                2'b11 : Bank3 [{Row, Col}] = Dq_dqm;
            endcase

            // Display debug message
            if (Dqm !== 2'b11) begin
                // Record tWR for manual precharge
                WR_chkm [Bank] = $time;

                if (Debug) begin
                    $display("%m : at time %t WRITE: Bank = %d Row = %d, Col = %d, Data = %h", $time, Bank, Row, Col, Dq_dqm);
                end
            end else begin
                if (Debug) begin
                    $display("%m : at time %t WRITE: Bank = %d Row = %d, Col = %d, Data = Hi-Z due to DQM", $time, Bank, Row, Col);
                end
            end

            // Advance burst counter subroutine
            #tHZ Burst_decode;

        end else if (Data_out_enable == 1'b1) begin                         // Reading Data from Memory
            // Array buffer
            case (Bank)
                2'b00 : Dq_dqm = Bank0[{Row, Col}];
                2'b01 : Dq_dqm = Bank1[{Row, Col}];
                2'b10 : Dq_dqm = Bank2[{Row, Col}];
                2'b11 : Dq_dqm = Bank3[{Row, Col}];
            endcase

            // Dqm operation
`ifdef x4
            if (Dqm_reg0 [0] == 1'b1) begin
                Dq_dqm [ 3 : 0] = 4'bz;
            end
`elsif x8
            if (Dqm_reg0 [0] == 1'b1) begin
                Dq_dqm [ 7 : 0] = 8'bz;
            end
`elsif x16
            if (Dqm_reg0 [0] == 1'b1) begin
                Dq_dqm [ 7 : 0] = 8'bz;
            end
            if (Dqm_reg0 [1] == 1'b1) begin
                Dq_dqm [15 : 8] = 8'bz;
            end
`endif

            // Display debug message
            Dq_reg = #tAC Dq_dqm;
            if (Debug) begin
                $display("%m : at time %t READ : Bank = %d Row = %d, Col = %d, Dqm = %b, Data = %h", $time, Bank, Row, Col, Dqm_reg0, Dq_reg);
            end

            // Advance burst counter subroutine
            Burst_decode;
        end
    end

    // Burst counter decode
    task Burst_decode;
        begin
            // Advance Burst Counter
            Burst_counter = Burst_counter + 1;

            // Burst Type
            if (Mode_reg[3] == 1'b0) begin                                  // Sequential Burst
                Col_temp = Col + 1;
            end else if (Mode_reg[3] == 1'b1) begin                         // Interleaved Burst
                Col_temp[2] =  Burst_counter[2] ^  Col_brst[2];
                Col_temp[1] =  Burst_counter[1] ^  Col_brst[1];
                Col_temp[0] =  Burst_counter[0] ^  Col_brst[0];
            end

            // Burst Length
            if (Burst_length_2) begin                                       // Burst Length = 2
                Col [0] = Col_temp [0];
            end else if (Burst_length_4) begin                              // Burst Length = 4
                Col [1 : 0] = Col_temp [1 : 0];
            end else if (Burst_length_8) begin                              // Burst Length = 8
                Col [2 : 0] = Col_temp [2 : 0];
            end else begin                                                  // Burst Length = FULL
                Col = Col_temp;
            end

            // Burst Read Single Write            
            if (Write_burst_mode == 1'b1) begin
                Data_in_enable = 1'b0;
            end

            // Data Counter
            if (Burst_length_1 == 1'b1) begin
                if (Burst_counter >= 1) begin
                    Data_in_enable = 1'b0;
                    Data_out_enable = 1'b0;
                end
            end else if (Burst_length_2 == 1'b1) begin
                if (Burst_counter >= 2) begin
                    Data_in_enable = 1'b0;
                    Data_out_enable = 1'b0;
                end
            end else if (Burst_length_4 == 1'b1) begin
                if (Burst_counter >= 4) begin
                    Data_in_enable = 1'b0;
                    Data_out_enable = 1'b0;
                end
            end else if (Burst_length_8 == 1'b1) begin
                if (Burst_counter >= 8) begin
                    Data_in_enable = 1'b0;
                    Data_out_enable = 1'b0;
                end
            end
        end
    endtask

    // Timing Parameters for -75 (133 MHz @ CL3)
    specify
        specparam
            tAH  =  0.8,                                        // Addr, Ba Hold Time
            tAS  =  1.5,                                        // Addr, Ba Setup Time
            tCK3 =  7.5,
            tCH  =  2.5,                                        // Clock High-Level Width
            tCL  =  2.5,                                        // Clock Low-Level Width
            tCKH =  0.8,                                        // CKE Hold  Time
            tCKS =  1.5,                                        // CKE Setup Time
            tCMH =  0.8,                                        // CS#, RAS#, CAS#, WE#, DQM# Hold  Time
            tCMS =  1.5,                                        // CS#, RAS#, CAS#, WE#, DQM# Setup Time
            tDH  =  0.8,                                        // Data-in Hold Time
            tDS  =  1.5;                                        // Data-in Setup Time
        $width    (posedge Clk,           tCH);
        $width    (negedge Clk,           tCL);
        $period   (negedge Clk,           tCK3);
        $period   (posedge Clk,           tCK3);
        $setuphold(posedge Clk,    Cke,   tCKS, tCKH);
        $setuphold(posedge Clk,    Cs_n,  tCMS, tCMH);
        $setuphold(posedge Clk,    Cas_n, tCMS, tCMH);
        $setuphold(posedge Clk,    Ras_n, tCMS, tCMH);
        $setuphold(posedge Clk,    We_n,  tCMS, tCMH);
        $setuphold(posedge Clk,    Addr,  tAS,  tAH);
        $setuphold(posedge Clk,    Ba,    tAS,  tAH);
        $setuphold(posedge Clk,    Dqm,   tCMS, tCMH);
        $setuphold(posedge Dq_chk, Dq,    tDS,  tDH);
    endspecify

endmodule
  • sdram_init.v
/***************************************************
*	Module Name		:	sdram_init		   
*	Description		:  SDRAM初始化模块
**************************************************/
module sdram_init(
	Clk,
	Rst_n,
	Command,
	Saddr,
	Init_done
);

`include        "Sdram_Params.h"

	input                   Clk;       //系统时钟信号
	input                   Rst_n;     //系统复位信号
	output reg [3:0]        Command;   //SDRAM命令信号
	output reg [`ASIZE-1:0] Saddr;     //SDRAM地址信号
	output                  Init_done; //初始化完成标识位

	localparam init_PRE_TIME   = INIT_PRE,
	           init_AREF1_TIME = INIT_PRE+REF_PRE,
				  init_AREF2_TIME = INIT_PRE+REF_PRE+REF_REF,
				  init_LMR_TIME   = INIT_PRE+REF_PRE+REF_REF*2,
				  init_END_TIME   = INIT_PRE+REF_PRE+REF_REF*2+LMR_ACT;
	
	//SDRAM初始化过程时间计数器
	reg [15:0]init_cnt;
	always@(posedge Clk or negedge Rst_n)
	begin
		if(!Rst_n)
			init_cnt <= 16'd0;
		else if(init_cnt < init_END_TIME)
			init_cnt <= init_cnt + 16'd1;
		else
			init_cnt <= 16'd0;
	end
	
	//SDRAM初始化完成结束标志位
	assign Init_done = (init_cnt == init_END_TIME);	

	//SDRAM初始化过程,类似线性序列机
	//相应时刻发出对应的命令和操作
	always@(posedge Clk or negedge Rst_n)
	begin
		if(!Rst_n)begin
			Command   <= C_NOP;
			Saddr     <= 0;
		end
		else begin		
			case(init_cnt)
				init_PRE_TIME:begin
					Command   <= C_PRE;
					Saddr[10] <= 1'b1;
				end
				
				init_AREF1_TIME:begin
					Command <= C_AREF;
				end
				
				init_AREF2_TIME:begin
					Command <= C_AREF;
				end
				
				init_LMR_TIME:begin
					Command <= C_MSET;
					Saddr   <= {OP_CODE,2'b00,SDR_CL,SDR_BT,SDR_BL};
				end
				
				default:begin
					Command <= C_NOP;
					Saddr   <= 0;
				end	
			endcase
		end
	end

endmodule 
  • sdram_control.v
/***************************************************
*	Module Name		:	sdram_control		   
*	Description		:  SDRAM控制器模块
**************************************************/
module sdram_control(
	Clk,
	Rst_n,
	
	Wr,
	Rd,
	Caddr,
	Raddr,
	Baddr,
	Wr_data,
	Rd_data,
	Rd_data_vaild,
	Wr_data_vaild,
	Wdata_done,
	Rdata_done,	

	Sa,
	Ba,
	Cs_n,
	Cke,
	Ras_n,
	Cas_n,
	We_n,
	Dq,
	Dqm
);

`include        "Sdram_Params.h"

	input             Clk;           //系统时钟信号
	input             Rst_n;         //复位信号,低电平有效 
	input             Wr;            //写SDRAM使能信号
	input             Rd;            //读SDRAM使能信号
	input [`ASIZE-1:0]Caddr;         //写SDRAM时列地址
	input [`ASIZE-1:0]Raddr;         //写SDRAM时行地址
	input [`BSIZE-1:0]Baddr;         //写SDRAM时Bank地址
	input [`DSIZE-1:0]Wr_data;       //待写入SDRAM数据
	output[`DSIZE-1:0]Rd_data;       //读出SDRAM的数据
	output reg        Rd_data_vaild; //读SDRAM时数据有效区
	output reg        Wr_data_vaild; //写SDRAM时数据有效区
	output	         Wdata_done;    //一次写突发完成标识位
	output            Rdata_done;    //一次读突发完成标识位

	//	SDRAM Side
	output reg[`ASIZE-1:0]Sa;        //SDRAM地址总线
	output reg[`BSIZE-1:0]Ba;        //SDRAMBank地址
	output                Cs_n;      //SDRAM片选信号
	output                Cke;       //SDRAM时钟使能
	output                Ras_n;     //SDRAM行地址选通
	output                Cas_n;     //SDRAM列地址选通
	output                We_n;      //SDRAM写使能
	inout [`DSIZE-1:0]    Dq;        //SDRAM数据总线
	output[`DSIZE/8-1:0]  Dqm;       //SDRAM数据掩码
	
	//---------------------------------
	wire [3:0]      init_cmd;        //SDRAM初始化命令输出
	wire[`ASIZE-1:0]init_addr;       //SDRAM初始化地址输出
	wire            init_done;       //SDRAM初始化完成标志位
	reg [31:0]      ref_time_cnt;    //刷新定时计数器	
	wire            ref_time_flag;   //刷新定时时间标志位,定时到达时置1
	reg [3:0]       main_state;      //主状态寄存器
	reg             ref_req;         //刷新操作请求
	reg             ref_opt_done;    //一次刷新操作完成标志位
	reg             wr_opt_done;     //一次突发写操作完成标志位
	reg             rd_opt_done;     //一次突发读操作完成标志位
	reg             wr_req;          //写操作请求
	reg             rd_req;          //读操作请求	
	reg             FF;              //标记寄存器
   reg [3:0]       Command;	      //操作命令,等于{CS_N,RAS_N,CAS_N,WE}
	reg [15:0]      ref_cnt;         //自动刷新过程时间计数器	
	reg [`ASIZE-1:0]raddr_r;         //读写行地址寄存器
	reg [`ASIZE-1:0]caddr_r;         //读写列地址寄存器
	reg [`BSIZE-1:0]baddr_r;         //读写bank地址寄存器	
	reg [15:0]      rd_cnt;          //一次突发读操作过程时间计数器	
	reg [15:0]      wr_cnt;          //一次突发写操作过程时间计数器
	
	wire            ref_break_wr;    //写操作过程中,刷新定时到来到此次写操作结束有效区间
	wire            ref_break_rd;    //读操作过程中,刷新定时到来到此次读操作结束有效区间
	wire            wr_break_ref;    //刷新过程中,写操作到来到此次刷新结束有效区间
	wire            rd_break_ref;    //刷新过程中,读操作到来到此次刷新结束有效区间

	//时钟使能信号
	assign Cke = Rst_n;
	
   //SDRAM命令信号组合
	assign {Cs_n,Ras_n,Cas_n,We_n} = Command;
	
	//SDRAM数据线,采用三态输出
	assign Dq = Wr_data_vaild ? Wr_data:16'bz;
	
	//数据掩码,采用16位数据,掩码为全零
	assign Dqm = 2'b00;	
	//---------------------------------
	//主状态机状态
	localparam 
		IDLE   = 4'b0001,  //空闲
	   AREF   = 4'b0010,  //刷新
		WRITE  = 4'b0100,  //写
		READ   = 4'b1000;  //读
	//---------------------------------	
	//SDRAM前期初始化模块例化	
	sdram_init sdram_init(
		.Clk(Clk),
		.Rst_n(Rst_n),
		.Command(init_cmd),
		.Saddr(init_addr),
		.Init_done(init_done)
	);	
	
	//刷新定时计数器
	always@(posedge Clk or negedge Rst_n)
	begin
		if(!Rst_n)
			ref_time_cnt <= 0;
		else if(ref_time_cnt == AUTO_REF)
			ref_time_cnt <= 1;
		else if(init_done || ref_time_cnt > 0)
			ref_time_cnt <= ref_time_cnt + 10'd1;
		else
			ref_time_cnt <= ref_time_cnt;
	end

	//刷新定时时间标志位,定时到达时置1
	assign ref_time_flag = (ref_time_cnt == AUTO_REF);	
	
	//---------------------------------		
   //主状态机
	always@(posedge Clk or negedge Rst_n)
	begin
		if(!Rst_n)begin
			main_state <= IDLE;
			FF <= 1'b1;
		end
		else begin
			case(main_state)
				IDLE:begin
					Command <= init_cmd;
					Sa <= init_addr;
					if(init_done)
						main_state <= AREF;
					else
						main_state <= IDLE;
				end

				AREF:begin
					if(FF == 1'b0)
						auto_ref;
					else begin
						if(ref_req)begin
							main_state <= AREF;
							FF <= 1'b0;
						end
						else if(wr_req)begin
							main_state <= WRITE;
							FF <= 1'b0;
						end
						else if(rd_req)begin
							main_state <= READ;
							FF <= 1'b0;
						end
						else
							main_state <= AREF;
					end
				end

				WRITE:begin
					if(FF == 1'b0)
						write_data;
					else begin
						if(ref_req == 1'b1)begin
							main_state <= AREF;
							FF <= 1'b0;
						end
						else if(wr_opt_done & wr_req)begin
							main_state <= WRITE;
							FF <= 1'b0;
						end
						else if(wr_opt_done & rd_req)begin
							main_state <= READ;
							FF <= 1'b0;
						end
						else if(wr_opt_done&!wr_req&!rd_req)
							main_state <= AREF;
						else
							main_state <= WRITE;
					end
				end

				READ:begin
					if(FF == 1'b0)
						read_data;
					else begin
						if(ref_req == 1'b1)begin
							main_state <= AREF;
							FF <= 1'b0;
						end
						else if(rd_opt_done & wr_req)begin
							main_state <= WRITE;
							FF <= 1'b0;
						end
						else if(rd_opt_done & rd_req)begin
							main_state <= READ;
							FF <= 1'b0;
						end
						else if( rd_opt_done&!wr_req&!rd_req)
							main_state <= AREF;
						else
							main_state <= READ;
					end
				end
			endcase
		end
	end
	//---------------------------------	
	
	//---------------------------------	
	//读写行列地址寄存器
	always@(posedge Clk or negedge Rst_n)
	begin
		if(!Rst_n)
		begin
			raddr_r <= 0;
			caddr_r <= 0;
			baddr_r <= 0;
		end
		else if(rd_req || wr_req)
		begin
			raddr_r <= Raddr;
			caddr_r <= Caddr;
			baddr_r <= Baddr;
		end
		else
			;
	end	
	//---------------------------------
	
	//---------------------------------	
	//自动刷新操作任务,采用线性序列机方法	
   localparam 
      ref_PRE_TIME = 1'b1,              //预充电时刻
      ref_REF1_TIME = REF_PRE+1,        //第一次自动刷新时刻
      ref_REF2_TIME = REF_PRE+REF_REF+1,//第二次自动刷新时刻
      ref_END = REF_PRE+REF_REF*2;      //自动刷新结束时刻

	//自动刷新过程时间计数器
	always@(posedge Clk or negedge Rst_n)
	begin
		if(!Rst_n)
			ref_cnt <= 16'd0;
		else if(ref_cnt == ref_END)
			ref_cnt <= 16'd0;
		else if(ref_req || ref_cnt>1'b0)
			ref_cnt <= ref_cnt + 16'd1;
		else
			ref_cnt <= ref_cnt;
	end	

	//一次刷新操作完成标志位
	always@(posedge Clk or negedge Rst_n)
	begin
		if(!Rst_n)
			ref_opt_done <= 1'b0;
		else if(ref_cnt == ref_END)
			ref_opt_done <= 1'b1;
		else
			ref_opt_done <= 1'b0;
	end

	//一次突发写操作过程状态标识信号
	reg ref_opt;
	always@(posedge Clk or negedge Rst_n)
	begin
		if(!Rst_n)
			ref_opt <= 1'b0;
		else if(ref_req == 1'b1)
			ref_opt <= 1'b1;
		else if(ref_opt_done == 1'b1)
			ref_opt <= 1'b0;
		else
			ref_opt <= ref_opt;
	end

	//自动刷新操作,线性序列机
	task auto_ref;   
	begin
		case(ref_cnt)
			ref_PRE_TIME:begin
				Command <= C_PRE;     //预充电
				Sa[10] <= 1'b1;					
			end				
			
			ref_REF1_TIME:begin
				Command <= C_AREF;    //自动刷新
			end
			
			ref_REF2_TIME:begin
				Command <= C_AREF;    //自动刷新
			end
			
			ref_END:begin
				FF <= 1'b1;
				Command <= C_NOP;
			end

			default:
				Command <= C_NOP;			
		endcase		
	end
	endtask	
	//---------------------------------

	//---------------------------------
	//一次突发写操作任务,线性序列机方法
	localparam 
      wr_ACT_TIME = 1'b1,                       //激活行时刻
      wr_WRITE_TIME = SC_RCD+1,                 //写命令时刻
      wr_PRE_TIME = SC_RCD+SC_BL+WR_PRE+1,      //预充电时刻
      wr_END_TIME = SC_RCD+SC_BL+WR_PRE+REF_PRE;//写操作结束时刻
				  
	//一次突发写操作过程时间计数器
	always@(posedge Clk or negedge Rst_n)
	begin
		if(!Rst_n)	
			wr_cnt <= 16'd0;
		else if(wr_cnt == wr_END_TIME)
			wr_cnt <= 16'd0;
		else if(wr_req||wr_cnt>1'b0)
			wr_cnt <= wr_cnt + 16'd1;
		else
			wr_cnt <= 16'd0;
	end

	//一次写操作过程完成标志位
	always@(posedge Clk or negedge Rst_n)
	begin
		if(!Rst_n)
			wr_opt_done <= 1'b0;
		else if(wr_cnt == wr_END_TIME)
			wr_opt_done <= 1'b1;
		else
			wr_opt_done <= 1'b0;
	end

	//一次突发写操作过程状态标识信号
	reg wr_opt;
	always@(posedge Clk or negedge Rst_n)
	begin
		if(!Rst_n)
			wr_opt <= 1'b0;
		else if(wr_req == 1'b1)
			wr_opt <= 1'b1;
		else if(wr_opt_done == 1'b1)
			wr_opt <= 1'b0;
		else
			wr_opt <= wr_opt;
	end

	//写数据操作,数据写入(改变)时刻有效区间
	always@(posedge Clk or negedge Rst_n)
	begin
		if(!Rst_n)
			Wr_data_vaild <= 1'b0;
		else if((wr_cnt > SC_RCD)&&(wr_cnt <= SC_RCD+SC_BL))
			Wr_data_vaild <= 1'b1; 
		else
			Wr_data_vaild <= 1'b0;
	end

	//一次突发写操作数据写完成标志位
	assign Wdata_done = (wr_cnt == SC_RCD+SC_BL+1)?1'b1:1'b0;
	
	//一次突发写操作任务,类似线性序列机方法
	task write_data;
	begin
		case(wr_cnt)
			wr_ACT_TIME:begin
				Command <= C_ACT;
				Sa <= raddr_r;             //激活行	
				Ba <= baddr_r;
			end

			wr_WRITE_TIME:begin
				Command <= C_WR;
				Sa <= {1'b0,caddr_r[8:0]}; //激活列
				Ba <= baddr_r;
			end

			wr_PRE_TIME:begin				
				Command <= C_PRE;          //预充电
				Sa[10] <= 1'b1;
			end

			wr_END_TIME:begin
				Command <= C_NOP;
				FF <= 1'b1;
			end

			default:
				Command <= C_NOP;
		endcase
	end	
	endtask
	//---------------------------------	
	
	//---------------------------------
	//一次突发读操作任务,线性序列机方法	
	localparam
		rd_ACT_TIME  = 1'b1,              //激活行时刻
		rd_READ_TIME = SC_RCD+1,          //读命令时刻
		rd_PRE_TIME  = SC_RCD+SC_BL+1,    //预充电时刻
		rd_END_TIME  = SC_RCD+SC_CL+SC_BL;//读操作结束时刻
 
	//一次突发读操作过程时间计数器
	always@(posedge Clk or negedge Rst_n)
	begin
		if(!Rst_n)
			rd_cnt <= 16'd0;
		else if(rd_cnt == rd_END_TIME)
			rd_cnt <= 16'd0;
		else if(rd_req ||rd_cnt>1'b0)
			rd_cnt <= rd_cnt + 16'd1;
		else
			rd_cnt <= 16'd0;
	end

	//一次突发读操作过程完成标志位	
	always@(posedge Clk or negedge Rst_n)
	begin
		if(!Rst_n)
			rd_opt_done <= 1'b0;
		else if(rd_cnt == rd_END_TIME)
			rd_opt_done <= 1'b1;
		else
			rd_opt_done <= 1'b0;
	end

	//一次突发读操作过程状态标识信号	
	reg rd_opt;
	always@(posedge Clk or negedge Rst_n)
	begin
		if(!Rst_n)
			rd_opt <= 1'b0;
		else if(rd_req == 1'b1)
			rd_opt <= 1'b1;
		else if(rd_opt_done == 1'b1)
			rd_opt <= 1'b0;
		else
			rd_opt <= rd_opt;
	end

   //一次突发读操作过程中数据读完标志位
	assign Rdata_done = (rd_cnt == rd_END_TIME)?1'b1:1'b0;

	//读数据操作,数据有效区
	always@(posedge Clk or negedge Rst_n)
	begin
		if(!Rst_n)
			Rd_data_vaild <= 1'b0;
		else if((rd_cnt > SC_RCD+SC_CL)
		        &&(rd_cnt <= SC_RCD+SC_CL+SC_BL))
			Rd_data_vaild <= 1'b1;
		else
			Rd_data_vaild <= 1'b0;
	end

	//读数据
	assign Rd_data = Dq;
	
	//一次突发读操作任务,类似线性序列机方法
	task read_data;
	begin
		case(rd_cnt)
			rd_ACT_TIME:begin			     //激活命令
				Command <= C_ACT;
				Sa <= raddr_r; 
				Ba <= baddr_r;
			end

			rd_READ_TIME:begin			  //读命令
				Command <= C_RD;
				Sa <= {1'b0,caddr_r[8:0]};
				Ba <= baddr_r;
			end

			rd_PRE_TIME:begin
				Command <= C_PRE;         //预充电
				Sa[10] <= 1'b1;
			end
			
			rd_END_TIME:begin
				FF <= 1'b1;
				Command <= C_NOP;
			end
			
			default:
				Command <= C_NOP;
		endcase
	end
	endtask
	//---------------------------------

	//---------------------------------	
	//写操作过程刷新到记住刷新信号ref_break_wr
	assign ref_break_wr = (ref_time_flag && wr_opt)?1'b1:
	                      ((!wr_opt)?1'b0:ref_break_wr);

	//读操作过程刷新到记住刷新信号ref_break_rd
	assign ref_break_rd = (ref_time_flag&&rd_opt)?1'b1:
	                      ((!rd_opt)?1'b0:ref_break_rd);
								 
	//刷新过程外部写使能到记住写使能信号wr_break_ref
	assign wr_break_ref = ((Wr && ref_opt)?1'b1:
	                      ((!ref_opt)?1'b0:wr_break_ref));	
	
	//刷新过程外部读使能到记住读使能信号rd_break_ref信号
	assign rd_break_ref = ((Rd && ref_opt)?1'b1: 
	                      ((!ref_opt)?1'b0:rd_break_ref));
	//---------------------------------
	
	//---------------------------------
	//刷新请求信号
	always@(*)
	begin
		case(main_state)
			AREF:begin
				if(ref_time_flag)
					ref_req = 1'b1;
				else
					ref_req = 1'b0;
			end

			WRITE:begin
				if(ref_break_wr && wr_opt_done)
					ref_req = 1'b1;
				else
					ref_req = 1'b0;
			end

			READ:begin
				if(ref_break_rd && rd_opt_done)
					ref_req = 1'b1;
				else
					ref_req = 1'b0;
			end

			default:
				ref_req = 1'b0;
		endcase
	end	
	//---------------------------------
	
	//---------------------------------
	//写操作请求信号
	always@(*)
	begin
		case(main_state)
			AREF:begin
				if((!wr_break_ref)&& Wr &&!ref_time_flag)
					wr_req = 1'b1;
				else if(wr_break_ref && ref_opt_done)
					wr_req = 1'b1;
				else
					wr_req = 1'b0;
			end

			WRITE:begin
				if(wr_opt_done && Wr && !ref_break_wr)
					wr_req = 1'b1;
				else
					wr_req = 1'b0;
			end

			READ:begin
				if(rd_opt_done && Wr && !ref_break_rd)
					wr_req = 1'b1;
				else
					wr_req = 1'b0;
			end

			default:
				wr_req = 1'b0;
		endcase		
	end	
	//---------------------------------
	
	//---------------------------------
	//读操作请求信号
	always@(*)
	begin
		case(main_state)
			AREF:begin
				if((!rd_break_ref)&&(!wr_break_ref)&&
			      (!ref_time_flag)&& !Wr && Rd )
					rd_req = 1'b1;
				else if(ref_opt_done &&!wr_break_ref&&
				        rd_break_ref)
					rd_req = 1'b1;
				else
					rd_req = 1'b0;
			end

			WRITE:begin
				if(wr_opt_done &&(!ref_break_wr)&&
				   !Wr && Rd)
					rd_req = 1'b1;
				else
					rd_req = 1'b0;
			end

			READ:begin
				if(rd_opt_done &&(!ref_break_rd)&&
				   !Wr && Rd)
					rd_req = 1'b1;
				else
					rd_req = 1'b0;
			end

			default:
				rd_req = 1'b0;	
		endcase
	end
	//---------------------------------	
endmodule 
  • sdram_control_tb.v
`timescale 1ns/1ns
`define CLK100_PERIOD 10

module sdram_control_tb;

`include        "../src/Sdram_Params.h"

	reg                Clk;
	reg                Rst_n;
	
	reg                Wr;
	reg                Rd;	
	reg [`ASIZE-1:0]   Caddr; 
	reg [`ASIZE-1:0]   Raddr; 
	reg [`BSIZE-1:0]   Baddr;	
	reg [`DSIZE-1:0]   Wr_data;
	wire[`DSIZE-1:0]   Rd_data;
	wire               Wr_data_vaild;
	wire               Rd_data_vaild;

	wire               sdram_clk;
	wire               sdram_cke;
	wire               sdram_cs_n;
	wire               sdram_ras_n;
	wire               sdram_cas_n;
	wire               sdram_we_n;
	wire [`BSIZE-1:0]  sdram_bank;
	wire [`ASIZE-1:0]  sdram_addr;
	wire [`DSIZE-1:0]  sdram_dq;
	wire [`DSIZE/8-1:0]sdram_dqm;

	assign sdram_clk = ~Clk;
	
	wire Rdata_done;
	
	//SDRAM控制器模块例化
	sdram_control sdram_control(
		.Clk(Clk),
		.Rst_n(Rst_n),
		
		.Wr(Wr),
		.Rd(Rd),
		.Caddr(Caddr),
		.Raddr(Raddr),
		.Baddr(Baddr),
		.Wr_data(Wr_data),
		.Rd_data(Rd_data),
		.Rd_data_vaild(Rd_data_vaild),
		.Wr_data_vaild(Wr_data_vaild),
		.Wdata_done(),
		.Rdata_done(Rdata_done),
		
		.Sa(sdram_addr),
		.Ba(sdram_bank),
		.Cs_n(sdram_cs_n),
		.Cke(sdram_cke),
		.Ras_n(sdram_ras_n),
		.Cas_n(sdram_cas_n),
		.We_n(sdram_we_n),
		.Dq(sdram_dq),
		.Dqm(sdram_dqm)
	);
	
	//SDRAM模型例化
	sdr sdram_model(
		.Dq(sdram_dq), 
		.Addr(sdram_addr),
		.Ba(sdram_bank), 
		.Clk(sdram_clk), 
		.Cke(sdram_cke), 
		.Cs_n(sdram_cs_n), 
		.Ras_n(sdram_ras_n), 
		.Cas_n(sdram_cas_n), 
		.We_n(sdram_we_n), 
		.Dqm(sdram_dqm)
	);
		
	initial  
    begin             
        $dumpfile("sdram_control_tb.vcd");   //vcd文件名
        $dumpvars(0,sdram_control_tb);   //test为测试module
    end  
	
	initial Clk = 1'b1;
	always #(`CLK100_PERIOD/2) Clk = ~Clk;
	
	initial
	begin
		Rst_n   = 0;
		Wr      = 0;
		Rd      = 0;
		Caddr   = 0;
		Raddr   = 0;
		Baddr   = 0;
		Wr_data = 0;
		#(`CLK100_PERIOD*200+1);
		Rst_n   = 1;
		
		@(posedge sdram_control.sdram_init.Init_done)
		#2000;

		repeat(100)                 //写入100组数据
		begin
			Wr    = 1;
			Baddr = 2;
			#`CLK100_PERIOD;
			Wr    = 0;
			
			if(Caddr == 512-SC_BL)begin
				Caddr = 0; 
				Raddr = Raddr + 1;   //1行写满,行加1
			end
			else
				Caddr = Caddr + SC_BL;

			#5000;                  //延时5us
		end

		Caddr = 0;
		Raddr = 0;
		#5000;
		repeat(100)                //读出100组数据
		begin
			Rd = 1'b1;
			#`CLK100_PERIOD;
			Rd = 1'b0;

			if(Caddr == 512-SC_BL)begin
				Caddr = 0;
				Raddr = Raddr + 1;   //1行读完,行加1
			end
			else
				Caddr = Caddr + SC_BL;
				
			#5000;                  //延时5us
		end

		#5000;
		$stop;
	end
	
	initial
	begin
		forever begin
			@(posedge Wr_data_vaild);
			repeat(SC_BL)            //改变待写入的数据
			begin
				#`CLK100_PERIOD;
				Wr_data = Wr_data + 1;
			end		
		end
	end

endmodule 
  • sdram_control_tb.v
`timescale 1ns/1ns
`define CLK100_PERIOD 10

module sdram_control_tb;

`include        "../src/Sdram_Params.h"

	reg                Clk;
	reg                Rst_n;
	
	reg                Wr;
	reg                Rd;	
	reg [`ASIZE-1:0]   Caddr; 
	reg [`ASIZE-1:0]   Raddr; 
	reg [`BSIZE-1:0]   Baddr;	
	reg [`DSIZE-1:0]   Wr_data;
	wire[`DSIZE-1:0]   Rd_data;
	wire               Wr_data_vaild;
	wire               Rd_data_vaild;

	wire               sdram_clk;
	wire               sdram_cke;
	wire               sdram_cs_n;
	wire               sdram_ras_n;
	wire               sdram_cas_n;
	wire               sdram_we_n;
	wire [`BSIZE-1:0]  sdram_bank;
	wire [`ASIZE-1:0]  sdram_addr;
	wire [`DSIZE-1:0]  sdram_dq;
	wire [`DSIZE/8-1:0]sdram_dqm;

	assign sdram_clk = ~Clk;
	
	wire Rdata_done;
	
	//SDRAM控制器模块例化
	sdram_control sdram_control(
		.Clk(Clk),
		.Rst_n(Rst_n),
		
		.Wr(Wr),
		.Rd(Rd),
		.Caddr(Caddr),
		.Raddr(Raddr),
		.Baddr(Baddr),
		.Wr_data(Wr_data),
		.Rd_data(Rd_data),
		.Rd_data_vaild(Rd_data_vaild),
		.Wr_data_vaild(Wr_data_vaild),
		.Wdata_done(),
		.Rdata_done(Rdata_done),
		
		.Sa(sdram_addr),
		.Ba(sdram_bank),
		.Cs_n(sdram_cs_n),
		.Cke(sdram_cke),
		.Ras_n(sdram_ras_n),
		.Cas_n(sdram_cas_n),
		.We_n(sdram_we_n),
		.Dq(sdram_dq),
		.Dqm(sdram_dqm)
	);
	
	//SDRAM模型例化
	sdr sdram_model(
		.Dq(sdram_dq), 
		.Addr(sdram_addr),
		.Ba(sdram_bank), 
		.Clk(sdram_clk), 
		.Cke(sdram_cke), 
		.Cs_n(sdram_cs_n), 
		.Ras_n(sdram_ras_n), 
		.Cas_n(sdram_cas_n), 
		.We_n(sdram_we_n), 
		.Dqm(sdram_dqm)
	);
		
	initial  
    begin             
        $dumpfile("sdram_control_tb.vcd");   //vcd文件名
        $dumpvars(0,sdram_control_tb);   //test为测试module
    end  
	
	initial Clk = 1'b1;
	always #(`CLK100_PERIOD/2) Clk = ~Clk;
	
	initial
	begin
		Rst_n   = 0;
		Wr      = 0;
		Rd      = 0;
		Caddr   = 0;
		Raddr   = 0;
		Baddr   = 0;
		Wr_data = 0;
		#(`CLK100_PERIOD*200+1);
		Rst_n   = 1;
		
		@(posedge sdram_control.sdram_init.Init_done)
		#2000;

		repeat(100)                 //写入100组数据
		begin
			Wr    = 1;
			Baddr = 2;
			#`CLK100_PERIOD;
			Wr    = 0;
			
			if(Caddr == 512-SC_BL)begin
				Caddr = 0; 
				Raddr = Raddr + 1;   //1行写满,行加1
			end
			else
				Caddr = Caddr + SC_BL;

			#5000;                  //延时5us
		end

		Caddr = 0;
		Raddr = 0;
		#5000;
		repeat(100)                //读出100组数据
		begin
			Rd = 1'b1;
			#`CLK100_PERIOD;
			Rd = 1'b0;

			if(Caddr == 512-SC_BL)begin
				Caddr = 0;
				Raddr = Raddr + 1;   //1行读完,行加1
			end
			else
				Caddr = Caddr + SC_BL;
				
			#5000;                  //延时5us
		end

		#5000;
		$stop;
	end
	
	initial
	begin
		forever begin
			@(posedge Wr_data_vaild);
			repeat(SC_BL)            //改变待写入的数据
			begin
				#`CLK100_PERIOD;
				Wr_data = Wr_data + 1;
			end		
		end
	end

endmodule 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值