FPGA SDRAM读写控制器

感谢邓堪文大佬 !

SDRAM

        同步动态随机存取内存(synchronousdynamic randon-access menory,简称SDRAM)是有一个同步接口的动态随机存取内存(DRAM)。通常DRAM是有一个异步接口的,这样它可以随时响应控制输入的变化。而SDRAM有一个同步接口,在响应控制输入前会等待一个时钟信号,这样就能和计算机的系统总线同步。时钟被用来驱动一个有限状态机,对进入的指令进行管线操作。这使得SDRAM与没有同步接口的异步DRAM相比,可以有一个更复杂的操作模式。

        管线意味着芯片可以在处理完之前的指令前,接受一个新的指令。在一个写入的管线中,写入命令在另一个指令执行完之后可以立刻执行,而不需要等到数据写入存储队列的时间。在一个读取的流水线中,需要的数据在读取指令发出之后固定数量的时钟频率后到达,而这个等待的过程可以发出其他附加指令。这种延迟被称为等待时间(Latency),在为计算机购买内存时是一个很重要的参数。

         SDRAM之所以称为DRAM就是因为他要不断进行刷新才能保留住数据,因为刷新是DRAM最重要的操作。那么要隔多长时间重复一次刷新,目前公认的标准是,存储体中电容的数据有效保存期上限是64ms,也就是每一行刷新的循环周期是64ms。 SDRAM是多Bank结构,例如在一个具有两个Bank的SDRAM模组中,其中一个Bank在进行预充电期间,另一个Bank却马上可以被读取,这样当进行一次读取后,又马上读取已经预充电Bank的数据时,就无需等待而是可以直接读取了,这也就大大提高了存储器的访问速度

        随机访问存储器(RAM)分为静态RAM(SRAM)动态RAM(DRAM)。由于动态存储器存储单元的结构非常简单,所以它能达到的集成度远高于静态存储器。但是动态存储器的存取速度不如静态存储器快

        RAM的动态存储单元是利用电容可以存储电荷的原理制成的。由于存储单元的机构能够做得很简单,所以在大容量、高集成度的RAM中得到了普遍的应用。但是由于电容的容量很小,而漏电流又不可能绝对等于零,所以电荷保存的时间有限。为了及时补充漏掉的电荷以避免存储的信号丢失,必须定时地给电容补充电荷,通常将这种操作称为刷新

        行列地址线被选中后,数据线(data_bit)直接和电容相连接。当写入时,数据线给电容充放电;读取时,电容将数据线拉高或者置低。

        SDRAM 的全称即同步动态随机存储器(Synchronous Dynamic Random Access Memory);这里的同步是指其时钟频率与对应控制器的系统时钟频率相同,并且内部命令的发送与数据传输都是以该时钟为基准动态是指存储阵列需要不断的刷新来保证数据不丢失

        SDR SDRAM中的SDR是指单数据速率,即每一根数据线上,每个时钟只传输一个bit的数据。SDR SDRAM的时钟频率可以达到100MHz以上,按照100MHz的速率计算,一片16位数据宽度的SDR SDRAM的读写数据带宽可以达到1.6Gbit/s。

        SDR SDRAM需要时钟端和时钟使能端。SDR SDRAM所有的操作都依靠于此时钟;当时钟使能端无效时,SDR SDRAM自动忽略时钟上升沿。

        SDR SDRAM拥有四个命令控制线,分别为CSRASCASWE。组成的命令表如下:

        在写入数据时,有时会出现不想对某8bit进行写入,就可以采用DQM进行控制。

SDR SDRAM的内部机构为:

        由于SDR SDRAM为DRAM,内部的存储都是靠电容进行保存数据,电容的保持数据的时间为64ms,SDR SDRAM每次只能够刷新一行,为了不丢失任何数据,所以要保证64ms内,将所有的行都要刷新一遍。

        在SDR SDRAM正常使用之前,需要进行初始化。初始化的时序图如下:

        在PRECHARGE时,A10为高,表示选中所有的bank;A10为低,表示选中BA0、BA1所指定的bank。初始化中,A10置高。

        在LOAD MOOE REGISTER中,采用地址线进行配置模式寄存器。说明如下:

        在模式配置中,利用CL(CAS Latency)表示列选通潜伏期,利用BL(Burst Length)表示突发长度。

        SDR SDRAM中有内部的刷新控制器和刷新的行计数器,外部控制器只需要保证在64ms之内进行8192次刷新即可。

        在进行PRECHARGE时,A10要为高电平。

自动刷新时序:

        SDR SDRAM中,可以在任意位置进行写入。写入的时序图如下

        SDR SDRAM中,可以在任意位置进行读出。读出的时序图如下:

各个时序中的时序参数如下:

设计分析

该控制器共有四部分功能,初始化刷新。四部分的执行控制采用一个模块来控制。

SDR SDRAM必须要进行初始化,初始化只用执行一次。然后启动一个计时器,等计时器达到后,进行刷新。在刷新的间隔中,根据读写的要求进行读写。

四个模块都会对SDR SDRAM的命令线和地址线进行控制,所以输出时,采用多路选择器对齐进行选择输出。

四个模块按照对应的时序图进行编写代码即可。

初始化sdram_init模块

// -----------------------------------------------------------------------------
// Author : RLG
// File   : sdram_init.v

`timescale 1ns / 1ps
module sdram_init(
	input 						clk 				,
	input 						reset_n 			,
	output	reg 	[3 :0]		cmd_reg 			,
	output 	wire	[11:0]		sdram_addr 			,
	output  wire 				flag_init_end
    );
	
	/*-------------------------------------------------------------*\
	*********	 Define Parameter and Internal Signals	 **********
	\*-------------------------------------------------------------*/
	localparam 				DELAY_200US = 10000		;
	//SDRAM Command
	localparam 				NOP 	= 4'b0111		;
	localparam 				PRE 	= 4'b0010		;
	localparam 				AREF	= 4'b0001		;
	localparam 				MSET	= 4'b0000		;

	reg 	[13:0]			cnt_200us 				;
	wire 					flag_200us				;
	reg 	[3 :0]			cnt_cmd 				;	
	/*-------------------------------------------------------------*\
	*********	 				main code	 				**********
	\*-------------------------------------------------------------*/
	always @(posedge clk ) begin 
		if(~reset_n) begin
			cnt_200us <= 0;
		end else if(~flag_200us) begin
			cnt_200us <= cnt_200us +1 ;
		end
	end


	always @(posedge clk) begin 
		if(~reset_n) begin
			cnt_cmd <= 0;
		end 
		else if(flag_200us && ~flag_init_end )begin
			cnt_cmd <= cnt_cmd + 1;
		end
	end

	always @(posedge clk or negedge reset_n) begin
		if(~reset_n) begin
			cmd_reg <= NOP;
		end else if(flag_200us) begin
			case (cnt_cmd)
					0 	:	cmd_reg 	<= 	PRE 	;
					1 	: 	cmd_reg 	<= 	AREF 	;
					5 	: 	cmd_reg 	<= 	AREF 	;
					9 	: 	cmd_reg 	<= 	MSET 	;  	
				default : 	cmd_reg 	<= 	PRE 	;
			endcase
		end
	end

	assign flag_init_end    =    (cnt_cmd >= 4'd10)    ?    1'b1    :    1'b0;
	assign sdram_addr = (cmd_reg 	== 	MSET) ? 12'b0000_0011_0010 : 12'b0100_0000_0000;
	assign flag_200us = (cnt_200us >= DELAY_200US) ? 1'b1 : 1'b0;
	
endmodule

刷新sdram_aref模块
 

`timescale 1ns / 1ps
module sdram_aref(
	input 						clk 			,
	input						reset_n 		,

	input 						ref_en 			,
	output	reg					ref_req 		,
	output 						flag_ref_end 	,

	output	reg  	[3 :0] 		aref_cmd		,
	output   	 	[11:0] 		sdram_addr 		,
	input 						flag_init_end
    );
		
	/*-------------------------------------------------------------*\
	*********	 Define Parameter and Internal Signals	 **********
	\*-------------------------------------------------------------*/
	localparam		DELAY_15US = 749 		;

	localparam		CMD_AREF = 4'b0001		;
	localparam		CMD_NOP  = 4'b0111		;
	localparam 		CMD_PRE  = 4'b0010		;


	reg 	[3:0]				cmd_cnt		;
	reg 	[9:0]				ref_cnt;

	reg 						flag_ref 	;
	/*-------------------------------------------------------------*\
	*********	 				main code	 				**********
	\*-------------------------------------------------------------*/
	always @(posedge clk ) begin
		if(~reset_n) begin
			ref_cnt <= 0;
		end else if(ref_cnt == DELAY_15US) begin
			ref_cnt <= 0;
		end
		else if(flag_init_end)
			ref_cnt <= ref_cnt + 1'b1;
	end
	always @(posedge clk ) begin
		if(~reset_n) begin
			flag_ref <= 0;
		end 
		else if (flag_ref_end)
			flag_ref <= 0;
		else if(ref_en)begin
			flag_ref <= 1;
		end
	end

	always @(posedge clk ) begin
		if(~reset_n) begin
			cmd_cnt <= 0;
		end else if(flag_ref ) begin
			cmd_cnt <= cmd_cnt + 1'b1;
		end
		else
			cmd_cnt <= 0;
	end
	always @(posedge clk ) begin
		if(~reset_n) begin
			aref_cmd <= CMD_NOP;
		end else if(cmd_cnt == 2)
			aref_cmd <= CMD_AREF;
		else
			aref_cmd <= CMD_NOP;
	end

	always @(posedge clk) begin
		if(~reset_n) begin
			ref_req <= 0;
		end 
		else if(ref_en)
			ref_req <= 0;
		else if(ref_cnt >= DELAY_15US)begin
			ref_req <= 1;
		end
		// else
		// 	ref_req <= 0;
	end

 	assign flag_ref_end = (cmd_cnt >= 4'd3) ? 1'b1 : 1'b0;
	assign sdram_addr = 12'b0100_0000_0000;


endmodule

写sdram_write模块

`timescale 1ns / 1ps
module sdram_write(
    input                           clk             ,
    input                           reset_n         ,
    
    input                           wr_en           ,
    output                          wr_req          ,
    output reg                      flag_wr_end     ,
    
    output reg  [3 :0]              wr_cmd          ,                           
    output reg  [11:0]              wr_addr         ,       
    output wire [1 :0]              bank_addr       ,
    
    input                           ref_req         ,
    input                           wr_trig         ,

    output reg  [15:0]              wr_data         
    );
    /*-------------------------------------------------------------*\
    *********    Define Parameter and Internal Signals   **********
    \*-------------------------------------------------------------*/
    localparam      S_IDLE      = 5'b00001          ; //1
    localparam      S_REQ       = 5'b00010          ; //2
    localparam      S_ACT       = 5'b00100          ; //4
    localparam      S_WR        = 5'b01000          ; //8
    localparam      S_PRE       = 5'b10000          ; //16
    //SDRAM Commend
    localparam      CMD_NOP     = 4'b0111           ;
    localparam      CMD_PRE     = 4'b0010           ;
    localparam      CMD_AREF    = 4'b0001           ;
    localparam      CMD_ACT     = 4'b0011           ;
    localparam      CMD_WR      = 4'b0100           ;

    reg                             flag_wr         ;
    reg     [4:0]                   state           ;
    //--------------------------------------------------------------
    reg                             flag_act_end    ;
    reg                             flag_pre_end    ;
    reg                             sd_row_end      ;
    reg     [1 :0]                  burst_cnt       ;
    reg     [1 :0]                  burst_cnt_t     ;
    reg                             wr_data_end     ;
    //--------------------------------------------------------------
    reg     [3 :0]                  act_cnt         ;
    reg     [3 :0]                  break_cnt       ;
    reg     [6 :0]                  col_cnt         ;
    reg     [6 :0]                  col_cnt_t       ;
    //--------------------------------------------------------------
    reg     [11:0]                  row_addr        ;
    wire    [8 :0]                  col_addr        ;

    /*-------------------------------------------------------------*\
    *********                   main code                   **********
    \*-------------------------------------------------------------*/
    //flag_wr
    always @(posedge clk) begin 
        if(~reset_n) begin
            flag_wr <= 0;
        end 
        else if(wr_trig && ~flag_wr)begin
            flag_wr <= 1;
        end
        else if(wr_data_end)
            flag_wr <= 0;
    end

    //burst_cnt
    always @(posedge clk ) begin 
        if(~reset_n) begin
            burst_cnt <= 0;
        end else if(state == S_WR)begin
            burst_cnt <= burst_cnt + 1;
        end
        else
            burst_cnt <= 0;
    end
    //burst_cnt_t
    always @(posedge clk) begin
        burst_cnt_t <= burst_cnt;
    end


    //-----------------------------STATE-------------------------
    always @(posedge clk ) begin
        if(~reset_n) begin
            state <= S_IDLE;
        end else begin
            case (state)
                S_IDLE  : 
                    if(wr_trig)
                        state <= S_REQ;
                    else
                        state <= S_IDLE;
                 S_REQ  :
                    if(wr_en)
                        state <= S_ACT;
                    else
                        state <= S_REQ;
                 S_ACT  :
                    if(flag_act_end)
                        state <= S_WR;
                    else
                        state <= S_ACT;
                 S_WR   :
                    if(wr_data_end)
                        state <= S_PRE;
                    else if(ref_req && burst_cnt_t == 2 && flag_wr)
                        state <= S_PRE;
                    else if(sd_row_end && flag_wr)
                        state <= S_PRE;
                S_PRE   :
                    if(ref_req && flag_wr)
                        state <= S_REQ;
                    else if(flag_pre_end && flag_wr)
                        state <= S_ACT;
                    else if(~flag_wr)
                        state <= S_IDLE;
                // S_PRE:
          //           if(ref_req == 1'b1 && flag_wr == 1'b1)
          //                   state   <=      S_REQ;
          //           else if(flag_pre_end == 1'b1 && flag_wr == 1'b1)
          //                   state   <=      S_ACT;
          //           else if(flag_wr == 1'b0)    
          //                   state   <=      S_IDLE;  
                default : state <= S_IDLE;
            endcase
        end
    end

    //wr_cmd
    always @(posedge clk ) begin 
        if(~reset_n) begin
            wr_cmd <= CMD_NOP;
        end else begin
            case (state)
                S_ACT   :   
                    if(act_cnt == 0)
                        wr_cmd <= CMD_ACT;
                    else
                        wr_cmd <= CMD_NOP;
                S_WR    :
                    if(burst_cnt ==0)
                        wr_cmd <= CMD_WR;
                    else
                        wr_cmd <= CMD_NOP;
                S_PRE :
                    if(break_cnt ==0)
                        wr_cmd <= CMD_PRE;
                    else 
                        wr_cmd <= CMD_NOP;
                default : wr_cmd <= CMD_NOP;
            endcase
        end
    end

    //wr_addr
    always @(*) begin
         begin
            case (state)
                S_ACT   :
                    if(act_cnt == 0)
                        wr_addr <= row_addr;
                    else
                        wr_addr <= wr_addr;
                S_WR    :
                        wr_addr <= {3'b000,col_addr};
                S_PRE   :
                    if(break_cnt == 0)
                        wr_addr <= {12'b0100_0000_0000};
                    else
                        wr_addr <= 0;
                default : wr_addr <= 0;
            endcase
        end
    end

    //--------------------------------------------------------------
    //flag_act_end
    always @(posedge clk ) begin 
        if(~reset_n) begin
            flag_act_end <= 0;
        end else if(act_cnt == 'd3)begin
            flag_act_end <= 1;
        end
        else
            flag_act_end <= 0;
    end
    //act_cnt
    always @(posedge clk ) begin 
        if(~reset_n) begin
            act_cnt <= 0;
        end else if(state == S_ACT)begin
            act_cnt <= act_cnt + 1;
        end
        else
            act_cnt <= 0;
    end

    always @(posedge clk or negedge reset_n) begin 
        if(~reset_n) begin
            flag_wr_end <= 0;
        end 
        else if((state == S_PRE && ref_req) || (state == S_PRE && ~flag_wr))begin
            flag_wr_end <= 1;
        end
        else
            flag_wr_end <= 0;
    end
    //flag_pre_end
    always @(posedge clk ) begin 
        if(~reset_n) begin
            flag_pre_end <= 0;
        end else if(break_cnt == 'd3)begin
            flag_pre_end <= 1;
        end
        else
            flag_pre_end <= 0;
    end

    //break_cnt
    always @(posedge clk ) begin 
        if(~reset_n) begin
            break_cnt <= 0;
        end else if(state == S_PRE)begin
            break_cnt <= break_cnt + 1;
        end
        else
            break_cnt <= 0;
    end


    //wr_data_end
    always @(posedge clk ) begin 
        if(~reset_n) begin
            wr_data_end <= 0;
        end else if(row_addr == 1 && col_addr == 510) begin
            wr_data_end <= 1;
        end
        else
            wr_data_end <= 0;
    end

    //col_cnt
    always @(posedge clk ) begin
        if(~reset_n) begin
            col_cnt <= 0;
        end 
        else if(col_addr == 511 )
            col_cnt <= 0;
        else if(burst_cnt == 3 ) begin
            col_cnt <= col_cnt + 1;
        end
    end

    //row_addr
    always @(posedge clk) begin 
        if(~reset_n) begin
            row_addr <= 0;
        end else if(sd_row_end) begin
            row_addr <= row_addr + 1;
        end
    end

    always @(posedge clk ) begin 
        if(~reset_n) begin
            sd_row_end <= 0;
        end 
        else if(col_addr == 'd509) begin
            sd_row_end <= 1;
        end
        else
            sd_row_end <= 0;
    end

    always @(posedge clk ) begin 
        col_cnt_t <= col_cnt;
    end

    //col_addr
    assign col_addr = {col_cnt_t,burst_cnt_t};
    assign bank_addr = 2'b00;
    assign wr_req = state[1];

    always @(*) begin 
        case (burst_cnt_t)
                0   : wr_data <= 'd3;
                1   : wr_data <= 'd5;
                2   : wr_data <= 'd7;
                3   : wr_data <= 'd9;
        endcase
    end


endmodule

读sdram_read模块

`timescale 1ns / 1ps
module sdram_read(
    input                           clk             ,
    input                           reset_n         ,
    
    input                           rd_en           ,
    output                          rd_req          ,
    output reg                      flag_rd_end     ,
    
    output reg  [3 :0]              rd_cmd          ,                           
    output reg  [11:0]              rd_addr         ,       
    output wire [1 :0]              bank_addr       ,
    
    input                           ref_req         ,
    input                           rd_trig         

    // output reg  [15:0]              wr_data         
    );
    /*-------------------------------------------------------------*\
    *********    Define Parameter and Internal Signals   **********
    \*-------------------------------------------------------------*/
    localparam      S_IDLE      = 5'b00001          ; //1
    localparam      S_REQ       = 5'b00010          ; //2
    localparam      S_ACT       = 5'b00100          ; //4
    localparam      S_RD        = 5'b01000          ; //8
    localparam      S_PRE       = 5'b10000          ; //16
    //SDRAM Commend
    localparam      CMD_NOP     = 4'b0111           ;
    localparam      CMD_PRE     = 4'b0010           ;
    localparam      CMD_AREF    = 4'b0001           ;
    localparam      CMD_ACT     = 4'b0011           ;
    localparam      CMD_RD      = 4'b0101           ;

    reg                             flag_rd         ;
    reg     [4:0]                   state           ;
    //--------------------------------------------------------------
    reg                             flag_act_end    ;
    reg                             flag_pre_end    ;
    reg                             sd_row_end      ;
    reg     [1 :0]                  burst_cnt       ;
    reg     [1 :0]                  burst_cnt_t     ;
    reg                             rd_data_end     ;
    //--------------------------------------------------------------
    reg     [3 :0]                  act_cnt         ;
    reg     [3 :0]                  break_cnt       ;
    reg     [6 :0]                  col_cnt         ;
    reg     [6 :0]                  col_cnt_t       ;
    //--------------------------------------------------------------
    reg     [11:0]                  row_addr        ;
    wire    [8 :0]                  col_addr        ;

    /*-------------------------------------------------------------*\
    *********                   main code                   **********
    \*-------------------------------------------------------------*/
    //flag_rd
    always @(posedge clk) begin 
        if(~reset_n) begin
            flag_rd <= 0;
        end 
        else if(rd_trig && ~flag_rd)begin
            flag_rd <= 1;
        end
        else if(rd_data_end)
            flag_rd <= 0;
    end

    //burst_cnt
    always @(posedge clk ) begin 
        if(~reset_n) begin
            burst_cnt <= 0;
        end else if(state == S_RD)begin
            burst_cnt <= burst_cnt + 1;
        end
        else
            burst_cnt <= 0;
    end
    //burst_cnt_t
    always @(posedge clk) begin
        burst_cnt_t <= burst_cnt;
    end


    //-----------------------------STATE-------------------------
    always @(posedge clk ) begin
        if(~reset_n) begin
            state <= S_IDLE;
        end else begin
            case (state)
                S_IDLE  : 
                    if(rd_trig)
                        state <= S_REQ;
                    else
                        state <= S_IDLE;
                 S_REQ  :
                    if(rd_en)
                        state <= S_ACT;
                    else
                        state <= S_REQ;
                 S_ACT  :
                    if(flag_act_end)
                        state <= S_RD;
                    else
                        state <= S_ACT;
                 S_RD   :
                    if(rd_data_end)
                        state <= S_PRE;
                    else if(ref_req && burst_cnt_t == 2 && flag_rd)
                        state <= S_PRE;
                    else if(sd_row_end && flag_rd)
                        state <= S_PRE;
                S_PRE   :
                    if(ref_req && flag_rd)
                        state <= S_REQ;
                    else if(flag_pre_end && flag_rd)
                        state <= S_ACT;
                    else if(~flag_rd)
                        state <= S_IDLE;
                // S_PRE:
          //           if(ref_req == 1'b1 && flag_rd == 1'b1)
          //                   state   <=      S_REQ;
          //           else if(flag_pre_end == 1'b1 && flag_rd == 1'b1)
          //                   state   <=      S_ACT;
          //           else if(flag_rd == 1'b0)    
          //                   state   <=      S_IDLE;  
                default : state <= S_IDLE;
            endcase
        end
    end

    //rd_cmd
    always @(posedge clk ) begin 
        if(~reset_n) begin
            rd_cmd <= CMD_NOP;
        end else begin
            case (state)
                S_ACT   :   
                    if(act_cnt == 0)
                        rd_cmd <= CMD_ACT;
                    else
                        rd_cmd <= CMD_NOP;
                S_RD    :
                    if(burst_cnt ==0)
                        rd_cmd <= CMD_RD;
                    else
                        rd_cmd <= CMD_NOP;
                S_PRE :
                    if(break_cnt ==0)
                        rd_cmd <= CMD_PRE;
                    else 
                        rd_cmd <= CMD_NOP;
                default : rd_cmd <= CMD_NOP;
            endcase
        end
    end

    //rd_addr
    always @(*) begin
         begin
            case (state)
                S_ACT   :
                    if(act_cnt == 0)
                        rd_addr <= row_addr;
                    else
                        rd_addr <= rd_addr;
                S_RD    :
                        rd_addr <= {3'b000,col_addr};
                S_PRE   :
                    if(break_cnt == 0)
                        rd_addr <= {12'b0100_0000_0000};
                    else
                        rd_addr <= 0;
                default : rd_addr <= 0;
            endcase
        end
    end

    //--------------------------------------------------------------
    //flag_act_end
    always @(posedge clk ) begin 
        if(~reset_n) begin
            flag_act_end <= 0;
        end else if(act_cnt == 'd3)begin
            flag_act_end <= 1;
        end
        else
            flag_act_end <= 0;
    end
    //act_cnt
    always @(posedge clk ) begin 
        if(~reset_n) begin
            act_cnt <= 0;
        end else if(state == S_ACT)begin
            act_cnt <= act_cnt + 1;
        end
        else
            act_cnt <= 0;
    end

    always @(posedge clk or negedge reset_n) begin 
        if(~reset_n) begin
            flag_rd_end <= 0;
        end 
        else if((state == S_PRE && ref_req) || (state == S_PRE && ~flag_rd))begin
            flag_rd_end <= 1;
        end
        else
            flag_rd_end <= 0;
    end
    //flag_pre_end
    always @(posedge clk ) begin 
        if(~reset_n) begin
            flag_pre_end <= 0;
        end else if(break_cnt == 'd3)begin
            flag_pre_end <= 1;
        end
        else
            flag_pre_end <= 0;
    end

    //break_cnt
    always @(posedge clk ) begin 
        if(~reset_n) begin
            break_cnt <= 0;
        end else if(state == S_PRE)begin
            break_cnt <= break_cnt + 1;
        end
        else
            break_cnt <= 0;
    end


    //rd_data_end
    always @(posedge clk ) begin 
        if(~reset_n) begin
            rd_data_end <= 0;
        end else if(row_addr == 1 && col_addr == 510) begin
            rd_data_end <= 1;
        end
        else
            rd_data_end <= 0;
    end

    //col_cnt
    always @(posedge clk ) begin
        if(~reset_n) begin
            col_cnt <= 0;
        end 
        else if(col_addr == 511 )
            col_cnt <= 0;
        else if(burst_cnt == 3 ) begin
            col_cnt <= col_cnt + 1;
        end
    end

    //row_addr
    always @(posedge clk) begin 
        if(~reset_n) begin
            row_addr <= 0;
        end else if(sd_row_end) begin
            row_addr <= row_addr + 1;
        end
    end

    always @(posedge clk ) begin 
        if(~reset_n) begin
            sd_row_end <= 0;
        end 
        else if(col_addr == 'd509) begin
            sd_row_end <= 1;
        end
        else
            sd_row_end <= 0;
    end


    always @(posedge clk ) begin 
        col_cnt_t <= col_cnt;
    end

    //col_addr
    assign col_addr = {col_cnt_t,burst_cnt_t};
    assign bank_addr = 2'b00;
    assign rd_req = state[1];




endmodule

顶层sdram_top模块

`timescale 1ns / 1ps
module sdram_top(
	input 						clk				,
	input 						reset_n			,
	output 	wire				sdram_clk		,
	output 	wire				sdram_cke		,
	output 	wire				sdram_cs_n 		,
	output 	wire				sdram_cas_n		,
	output 	wire				sdram_ras_n		,
	output 	wire				sdram_we_n		,
	output 	wire	[ 1:0]		sdram_bank		,
	output 	reg 	[11:0]		sdram_addr 		,
	output 	wire	[ 1:0]		sdram_dqm		,
	
	inout 			[15:0]		sdram_dq 		,
	//other
	input 						wr_trig 		,
	input						rd_trig 	
    );
	localparam IDLE   = 5'b0_0001;
	localparam ARBIT  = 5'b0_0010;
	localparam AREF   = 5'b0_0100;
	localparam WRITE  = 5'b0_1000;
	localparam READ   = 5'b1_0000;
	// localparam IDLE = 5'B0_1000;
	// localparam IDLE = 5'B1_0000;

	reg 	 [3 :0]				sd_cmd			;

	wire	 [3 :0] 			init_cmd 		;
	wire	        			flag_init_end 	;
	wire	 [11:0]       		init_addr 		;

	reg  	 [4 :0]				state 			;

	//Refresh  module
	wire 						ref_req			;
	reg  						ref_en			;
	wire 						flag_ref_end	;
	wire	 [3 :0] 			ref_cmd 		;
	wire	 [11:0] 			ref_addr 		;
	//write module
	wire 	 [11:0] 			wr_addr 		;
	wire 	 [1 :0] 			wr_bank_addr 	;
	wire 	 [3 :0] 			wr_cmd 			;
	reg  				        wr_en 			;
	wire 				        wr_req 			; 
	wire 				        flag_wr_end 	;
	wire 	 [15:0]				wr_data 		;

	reg         				rd_en 			;
	wire        				rd_req 			;
	wire        				flag_rd_end 	;
	wire 	 [3 :0] 			rd_cmd 			;
	wire 	 [11:0] 			rd_addr 		;
	wire 	 [1 :0] 			rd_bank_addr 	;


	always @(posedge clk ) begin 
		if(~reset_n) begin
			state <= IDLE;
		end else begin
			case (state)
				IDLE :
					if(flag_init_end)
						state <= ARBIT;
					else
						state <= IDLE;
				ARBIT :  
					if(ref_en)
						state <= AREF;
					else if(wr_en)
						state <= WRITE;
					else if(rd_en)
						state <= READ;
					else 
						state <= ARBIT;
				AREF :
					if(flag_ref_end)
						state <= ARBIT;
					else
						state <= AREF;
				WRITE :
					if(flag_wr_end)
						state <= ARBIT;
					else
						state <= WRITE;
				READ :
					if(flag_rd_end)
						state <= ARBIT;
					else
						state <= READ;
				default : 
						state <= IDLE;
			endcase
		end
	end
	//ref_en
	always @(posedge clk)  begin 
		if(~reset_n) begin
			ref_en <= 0;
		end else  if(state == ARBIT && ref_req)begin
			ref_en <= 1;
		end
		else
			ref_en <= 0;
	end
	//wr_en
	always @(posedge clk) begin 
		if(~reset_n) begin
			wr_en <= 0;
		end else if(state == ARBIT && wr_req && ~ref_req)begin
			wr_en <= 1;
		end
		else
			wr_en <= 0;
	end
	//rd_en
	always @(posedge clk ) begin
		if(~reset_n) begin
			rd_en <= 0;
		end 
		else if(state == ARBIT && ~wr_req && ~ref_req && rd_req)begin
			rd_en <= 1;
		end
		else 
			rd_en <= 0;
	end


	always @(*) begin 
		case (state)
			IDLE 	: begin 
				sd_cmd 		<= init_cmd ;	
				sdram_addr  <= init_addr;	
			end

			AREF 	: begin 
				sd_cmd 		<= ref_cmd ;	
				sdram_addr  <= ref_addr;	
			end
			WRITE 	: begin 
				sd_cmd		<= wr_cmd;
				sdram_addr  <= wr_addr;	
			end		
			READ 	:begin 
				sd_cmd		<= rd_cmd;
				sdram_addr  <= rd_addr;	
			end		
			default :begin 
				sd_cmd		<= 4'b0111 	; //NOP
				sdram_addr  <= 'd0 		;
			end
		endcase
	end

	// assign 	sdram_addr  = (state == IDLE) ? init_addr : ref_addr;
	assign 	sdram_dqm   = 2'b00 							;
	assign 	sdram_cke   = 1									;
	assign  sdram_clk   = ~clk 								;
	assign 	{sdram_cs_n ,sdram_ras_n ,sdram_cas_n,sdram_we_n} = sd_cmd;
			// (state == IDLE) ? init_cmd : ref_cmd;
	assign  sdram_dq  = (state == WRITE) ? wr_data : {16{1'bz}};
	assign 	sdram_bank = (state == WRITE) ? wr_bank_addr : rd_bank_addr; 
	sdram_init sdram_init
		(
			.clk           (clk 				),
			.reset_n       (reset_n				),
			.cmd_reg       (init_cmd			),
			.sdram_addr    (init_addr			),
			.flag_init_end (flag_init_end		)
		);

	sdram_aref sdram_aref
		(
			.clk           (clk 				),
			.reset_n       (reset_n 			),
			.ref_en        (ref_en 				),
			.ref_req       (ref_req 			),
			.flag_ref_end  (flag_ref_end 		),
			.aref_cmd      (ref_cmd 			),
			.sdram_addr    (ref_addr 			),
			.flag_init_end (flag_init_end 		)
		);

	sdram_write sdram_write
		(
			.clk         (clk 					),
			.reset_n     (reset_n 				),

			.wr_en       (wr_en 				),
			.wr_req      (wr_req 				),
			.flag_wr_end (flag_wr_end 			),

			.wr_cmd      (wr_cmd 				),
			.wr_addr     (wr_addr 				),
			.bank_addr   (wr_bank_addr 			),

			.ref_req     (ref_req 				),
			.wr_trig     (wr_trig 				),
			.wr_data     (wr_data 				)
		);

	sdram_read sdram_read
		(
			.clk         (clk 					),
			.reset_n     (reset_n 				),

			.rd_en       (rd_en 				),
			.rd_req      (rd_req 				),
			.flag_rd_end (flag_rd_end  			),

			.rd_cmd      (rd_cmd 				),
			.rd_addr     (rd_addr 				),
			.bank_addr   (rd_bank_addr 			),

			.ref_req     (ref_req 				),
			.rd_trig     (rd_trig 				)
		);
endmodule

测试模块

测试顶层:

// -----------------------------------------------------------------------------
// Author : RLG
// File   : tb_sdram_top.v
`timescale 1ns / 1ps
module tb_sdram_top;
	reg 			clk;
	reg 			reset_n;
	wire        	sdram_clk;
	wire        	sdram_cke;
	wire        	sdram_cs_n;
	wire        	sdram_cas_n;
	wire        	sdram_ras_n;
	wire        	sdram_we_n;
	wire [ 1:0] 	sdram_bank;
	wire [11:0] 	sdram_addr;
	wire [ 1:0] 	sdram_dqm;
	wire [15:0] 	sdram_dq;
	reg 			wr_trig ;
	reg 			rd_trig;


	defparam 		sdram_model_plus_inst.addr_bits = 12 			;
	defparam 		sdram_model_plus_inst.data_bits = 16 			;
	defparam 		sdram_model_plus_inst.col_bits  = 9  			;
	defparam 		sdram_model_plus_inst.mem_sizes = 2*1024*1024	;  //2m

	initial begin 
		wr_trig <= 0;
		rd_trig <= 0;
		#205000;
		wr_trig <= 1;
		#20;
		wr_trig <= 0;
		#226500;
		rd_trig <= 1;
		#20;
		rd_trig <= 0;
	end

	initial begin
		clk = 1;
		reset_n = 0;
		#100;
		reset_n = 1;
	end
	always #10 clk  = ~clk;
	
	sdram_top sdram_top
		(
			.clk         (clk),
			.reset_n     (reset_n),
			.sdram_clk   (sdram_clk),
			.sdram_cke   (sdram_cke),
			.sdram_cs_n  (sdram_cs_n),
			.sdram_cas_n (sdram_cas_n), 
			.sdram_ras_n (sdram_ras_n),
			.sdram_we_n  (sdram_we_n),
			.sdram_bank  (sdram_bank),
			.sdram_addr  (sdram_addr),
			.sdram_dqm   (sdram_dqm),
			.sdram_dq    (sdram_dq),
			.wr_trig  	 (wr_trig ),
			.rd_trig    (rd_trig)
		);

	
		sdram_model_plus sdram_model_plus_inst (
			.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 		),
			.Debug (	1 			)
		);
endmodule

其中sdram_model_plus在这---> 【免费】SDRAM读写控制仿真模型资源-CSDN文库  获取。

以及SDRAM数据手册在这---> 【免费】SDRAM手册资源-CSDN文库 获取。

  • 20
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值