SDRAM-学习3-自动化刷新模块

146 阅读6分钟

自动刷新模块

由于电容存储电荷会随时间流逝,所以要对电容进行刷新也就是充电,防止数据丢失。

image.png

时序分析

自动刷新: 为了保证数据不丢失,自动刷新需要外部时钟参与,刷新行地址是由内部刷新计数器控制。

自刷新是sdram休眠模式,低功耗下数据保存,不需要外部时钟。

刷新命令都为0001。 时钟使能信号为高电平 再写入命令进入自动刷新,时钟使能信号为低电平,写入命令进入自刷新模式。

开始自刷新时,要对所有bank进行预充电,写入预充电指令后等待一段时间tRP,写入刷新指令,这时候时钟使能信号为低电平,进入自刷新模式,时钟信号拉高,退出自刷新模式。随后就可以进行正常操作。

自动刷新时序:

要对所有bank进行预充电,A10为高电平对所有bank进行预充电。等待一段时间tRP,时钟使能信号一直为高电平,并在等待过程中写入NOP空操作指令,防止其他指令写入。结束后写入自动刷新命令,等待tRFC时间写入NOP,需要自动刷新两次,完成两次操作。

image.png

所有正常的操作都需要在SDRAM完成初始化后进行,所以引入init_end信号。

init_end高电平时,才可以进行其他操作。

计数器cnt_ref对刷新时间进行间隔,计数器初值为0,当完成初始化操作后就可以开始计数,计数值为1-MAX。

完成计数后发出自动刷新请求信号,发送到仲裁模块,告知仲裁模块。aref_req。 从0开始计数,在MAX-1开始拉高。

随后仲裁模块接收到aref_req,会做出判断,发送给自动刷新模块aref_en信号。

接收到有效自动刷新使能信号aref_en,作出响应,aref_ack信号保持一个周期高电平。

状态机来实现自动刷新的实现。

定义状态变量aref_state

初始状态 IDLE

预充电状态AREF_PCH 随后进行等待AREF_TRP

自动刷新AUTO_PCH 等到AREF_TRF

自动刷新AUTO_PCH 等到AREF_TRF

AREF_END 回到IDLE状态

等待时间需要计数器来实现,cnt_clk来计数周期,参考技术手册查看计数周期。

计数使能信号cnt_clk_rst,高电平清零,低电平计数,完成计数后清零。

trp_end 等待时间做判断

trf_end

cnt_tref 对自动刷新次数做判断,每完成一次归零

image.png

四路输出信号波形图的绘制:

aref_cmd 命令,一开始写入nop,写入预充电指令,等待过程中写入nop,自动刷新命令,等待过程中写入nop,写入自动刷新命令,写入nop。

aref_ba bank地址一直为全1就行 2'b11

aref_addr 地址总线也为全1 , 13'h1fff

aref_end 自动刷新结束时,拉高一个时钟周期。

image.png

编写模块代码:

分析:

刷新时间间隔cnt_ref

//cnt_ref为刷新的时间间隔 13位宽 2^13 = 8192行,刷新时间间隔为64ms //64ms/8192 = 7812.5ns. 当前时钟信号为100mhz,每个周期10ns,也就是781.25个周期刷新一次 //当仲裁模块发送仲裁请求信号后还需要等待一段时间,所以7500/10,完成750个周期的计数即可。

程序:

input wire sys_clk,
input wire sys_rst_n,
input wire init_end,
input wire aref_en,


output reg [3:0] aref_cmd,
output reg [1:0] aref_ba,
output reg [12:0] aref_addr,
output wire aref_end,
output reg aref_req

);

parameter CNT_REF_MAX = 10'd749;


parameter TRP = 3'd2,
TRF = 3'd7;

//声明状态机状态,使用格雷码进行定义

parameter AREF_IDLE = 3'd000,
AREF_PCH = 3'd001,
AREF_TRP = 3'd011,
AREF_REF = 3'd010,
AREF_TRF = 3'd110,
AREF_END = 3'd111;


//四个命令  空命令 预充电 自动刷新  模式寄存器配置
parameter NOP = 4'b0111,
P_CHARGE =4'b0010,
AUTO_REF =4'b0001;




reg [9:0] cnt_ref;
wire 		aref_ack;
reg  [2:0] aref_state;
reg  [2:0] cnt_clk;
reg cnt_clk_rst;
wire trp_end;
wire trf_end;
reg [1:0]cnt_aref;

//cnt_ref为刷新的时间间隔  13位宽 2^13 = 8192行,刷新时间间隔为64ms
//64ms/8192 = 7812.5ns.  当前时钟信号为100mhz,每个周期10ns,也就是781.25个周期刷新一次
//当仲裁模块发送仲裁请求信号后还需要等待一段时间,所以7500/10,完成750个周期的计数即可。

//初始化结束后,开始计数
always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		cnt_ref <= 10'd0;
	else if(cnt_ref >= CNT_REF_MAX)
		cnt_ref <= 10'd0;
	else if(aref_end == 1'b1)
		cnt_ref <= cnt_ref + 1'b1;
		
// 自动刷新请求信号  当自动刷新响应信号为高电平拉低该信号
always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		
		aref_req <= 1'b0;
	else if(cnt_ref == (CNT_REF_MAX - 1'b1))
		aref_req <= 1'b1;
		
	else if(aref_ack == 1'b1)
		aref_req <= 1'b0;

//自动刷新响应信号aref_ack,是与状态机预充电命令重合的

assign aref_ack = (aref_state == AREF_PCH)? 1'b1: 1'b0;

		
//状态机的赋值
always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		aref_state <= AREF_IDLE;
	else
		case(aref_state)
			AREF_IDLE:
				//完成初始化,并且仲裁模块同意
				if(aref_end == 1'b1 && aref_en == 1'b1)
					aref_state <= AREF_PCH;
					
				else	
				
					aref_state <= aref_state;
			AREF_PCH:
			
					aref_state <= AREF_TRP;
			AREF_TRP:
					if(trp_end == 1'b1)
					aref_state <= AREF_REF;
					
				else	
				
					aref_state <= aref_state;
			AREF_REF:
					aref_state <= AREF_TRF;
			AREF_TRF:
					if(trf_end == 1'b1)
						if(cnt_aref == 2'd2)
								aref_state <= AREF_END;
						else 
							aref_state <= AREF_REF;
					
					else	
				
						aref_state <= aref_state;
			AREF_END:
						aref_state <= AREF_IDLE;
			default:
						aref_state <= AREF_IDLE;
			endcase
		
		//cnt_clk 计数器的赋值
  always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
         cnt_clk <= 3'd0;
     else if(cnt_clk_rst == 1'b1)
         cnt_clk <= 3'd0;
     else
         cnt_clk<= cnt_clk +1'b1;
//复位信号的赋值  组合逻辑赋值  case 语句状态跳转条件

always@(*)
    begin
        case(aref_state)
            AREF_IDLE: cnt_clk_rst <= 1'b1;
            AREF_TRP: cnt_clk_rst <= (trp_end == 1'b1) ? 1'b1:1'b0;
            AREF_TRF :cnt_clk_rst <= (trfc_end ==1'b1)?1'b1:1'b0;
             AREF_END: cnt_clk_rst <=1'b1;
             default: cnt_clk_rst <= 1'b0;           
        endcase
    end
		
	//对结束标志信号进行赋值
	
	 assign trp_end = ((aref_state <= AREF_TRP)&&(cnt_clk == TRP))? 1'b1:1'b0;
   assign  trfc_end =  ((aref_state <= AREF_TRF)&&(cnt_clk == TRF))? 1'b1:1'b0;
		
	
	//自动刷新次数计数器赋值
	//自动刷新次数计数器 cnt_aref  ,当状态机处于初始状态进行清零
 always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_aref <= 2'd0;
     else if(aref_state == AREF_IDLE)
         cnt_aref <= 2'd0;
      else if(aref_state == AREF_REF)
          cnt_aref <= cnt_aref + 1'b1;
       else
           cnt_aref <= cnt_aref;
	
	
	//命令和结束信号的赋值	
  always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        begin
            aref_cmd <=   NOP;
            aref_ba <= 2'b11;
            aref_addr <= 13'h1fff;
        end
       //结合状态机进行赋值
	 else
       case(aref_state)
                       AREF_IDLE,AREF_TRP,AREF_TRF:
           begin
           aref_cmd <=   NOP;
            aref_ba <= 2'b11;
            aref_addr <= 13'h1fff;
           end
           
         AREF_PCH:
          begin
           aref_cmd <=   P_CHARGE;
            aref_ba <= 2'b11;
            aref_addr <= 13'h1fff;
           end
         
         
         AUTO_REF:
              begin
           aref_cmd <=   AUTO_REF;
            aref_ba <= 2'b11;
            aref_addr <= 13'h1fff;
           end
         
         //模式寄存器下地址总线和bank地址都要改一下
         // 地址总线高三位为0 ,第三位为突发长度选择
         
         AREF_END:
               begin
           aref_cmd <=   NOP;
            aref_ba <= 2'b11;
           aref_addr <= 13'h1fff;
           end
           
           default:
           
            begin
            aref_cmd <=   NOP;
            aref_ba <= 2'b11;
            aref_addr <= 13'h1fff;
        end
   
       endcase
    // 结束信号 aref_end 用组合逻辑赋值
    
assign aref_end = (aref_state == AREF_END)?1'b1:1'b0;
    

		

endmodule 





部分仿真程序:

module tb_sdtam_aref();

wire clk_50m;
wire clk_100m;
wire clk_100m_shift;
wire locked;

wire  rst_n;
wire [3:0] init_cmd;

wire  [1:0] init_ba;

wire  [12:0] init_addr;

wire  init_end;


reg aref_en;
wire [3:0]  aref_cmd ;
wire	[1:0] aref_ba   ;
wire	[12:0]aref_addr  ;
wire	aref_end;
wire	aref_req;


reg sys_clk;
reg sys_rst_n;

//整合命令

wire [3:0]  sdram_cmd;
wire	[1:0] sdram_ba;
wire	[12:0]sdram_addr;











initial 
	begin
		sys_clk = 1'b1;
		sys_rst_n <= 1'b0;
		#30
		sys_rst_n <= 1'b1;
	end
always #10 sys_clk = ~sys_clk;
	
assign  	rst_n = sys_rst_n & locked;




//产生自动刷新使能信号

always@(posedge clk_100m or negedge rst_n)
	if(rst_n == 1'b0)
		aref_en <= 1'b0;
	else if(init_end == 1'b1 && aref_req==1'b1)

		aref_en <= 1'b1;

	else if(aref_end == 1'b1)
		
		aref_en <= 1'b0;



 assign   sdram_cmd = (init_end ==1'b1)? aref_cmd : init_cmd;
 assign   sdram_ba  = (init_end ==1'b1)? aref_ba  : init_ba ;
 assign   sdram_addr= (init_end ==1'b1)? aref_addr: init_addr ;

	
	
	
	//pll锁相环 复位信号为高电平有效,
dram_init  dram_init_inst(
 .sys_clk(sys_clk),
 .sys_rst_n(rst_n),

 .init_cmd(init_cmd),
 .init_ba(init_ba),
.init_addr(init_addr),
 .init_end(init_end)
);

//仿真需要用到sdram仿真模型。 这个模块我没有 可以自己在网上找找


clk_gen	clk_gen_inst (
	.areset ( sys_clk ),
	.inclk0 ( ~sys_rst_n ),
	.c0 ( clk_50m ),
	.c1 ( clk_100m ),
	.c2 ( clk_100m_shift ),
	.locked ( locked )
	);


sdram_aref sdram_aref_inst(
.sys_clk(sdram_aref),
.sys_rst_n(rst_n),
.init_end(init_end),
.aref_en(aref_en),


.aref_cmd(aref_cmd),
.aref_ba(aref_ba),
.aref_addr(aref_addr),
.aref_end(aref_end),
.aref_req(aref_req)

);








endmodule 
```